ETH Price: $2,863.34 (-2.56%)
 

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To

There are no matching entries

1 Internal Transaction found.

Latest 1 internal transaction

Parent Transaction Hash Block From To
329013592025-07-15 15:07:45194 days ago1752592065  Contract Creation0 ETH

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ShareClassManager

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 1 runs

Other Settings:
cancun EvmVersion
File 1 of 16 : ShareClassManager.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

import {Auth} from "src/misc/Auth.sol";
import {D18, d18} from "src/misc/types/D18.sol";
import {CastLib} from "src/misc/libraries/CastLib.sol";
import {MathLib} from "src/misc/libraries/MathLib.sol";
import {BytesLib} from "src/misc/libraries/BytesLib.sol";

import {PoolId} from "src/common/types/PoolId.sol";
import {AssetId} from "src/common/types/AssetId.sol";
import {PricingLib} from "src/common/libraries/PricingLib.sol";
import {ShareClassId, newShareClassId} from "src/common/types/ShareClassId.sol";

import {IHubRegistry} from "src/hub/interfaces/IHubRegistry.sol";

import {
    IShareClassManager,
    EpochInvestAmounts,
    EpochRedeemAmounts,
    UserOrder,
    ShareClassMetadata,
    ShareClassMetrics,
    QueuedOrder,
    RequestType,
    EpochId
} from "src/hub/interfaces/IShareClassManager.sol";

/// @title  Share Class Manager
/// @notice Manager for the share classes of a pool, and the core logic for tracking, approving, and fulfilling
///         requests.
contract ShareClassManager is Auth, IShareClassManager {
    using MathLib for *;
    using CastLib for *;
    using BytesLib for bytes;

    IHubRegistry public immutable hubRegistry;

    // Share classes
    mapping(bytes32 salt => bool) public salts;
    mapping(PoolId poolId => uint32) public shareClassCount;
    mapping(ShareClassId scId => ShareClassMetrics) public metrics;
    mapping(ShareClassId scId => ShareClassMetadata) public metadata;
    mapping(PoolId poolId => mapping(ShareClassId => bool)) public shareClassIds;
    mapping(ShareClassId scId => mapping(uint16 centrifugeId => uint128)) public issuance;

    // Epochs
    mapping(ShareClassId scId => mapping(AssetId assetId => EpochId)) public epochId;
    mapping(ShareClassId scId => mapping(AssetId assetId => mapping(uint32 epochId_ => EpochInvestAmounts epoch)))
        public epochInvestAmounts;
    mapping(ShareClassId scId => mapping(AssetId assetId => mapping(uint32 epochId_ => EpochRedeemAmounts epoch)))
        public epochRedeemAmounts;

    // Pending requests
    mapping(ShareClassId scId => mapping(AssetId payoutAssetId => uint128 pending)) public pendingRedeem;
    mapping(ShareClassId scId => mapping(AssetId depositAssetId => uint128 pending)) public pendingDeposit;
    mapping(ShareClassId scId => mapping(AssetId payoutAssetId => mapping(bytes32 investor => UserOrder pending)))
        public redeemRequest;
    mapping(ShareClassId scId => mapping(AssetId depositAssetId => mapping(bytes32 investor => UserOrder pending)))
        public depositRequest;

    // Queued requests
    mapping(ShareClassId scId => mapping(AssetId payoutAssetId => mapping(bytes32 investor => QueuedOrder queued)))
        public queuedRedeemRequest;
    mapping(ShareClassId scId => mapping(AssetId depositAssetId => mapping(bytes32 investor => QueuedOrder queued)))
        public queuedDepositRequest;

    // Force cancel request safeguards
    mapping(ShareClassId scId => mapping(AssetId depositAssetId => mapping(bytes32 investor => bool cancelled))) public
        allowForceDepositCancel;
    mapping(ShareClassId scId => mapping(AssetId payoutAssetId => mapping(bytes32 investor => bool cancelled))) public
        allowForceRedeemCancel;

    constructor(IHubRegistry hubRegistry_, address deployer) Auth(deployer) {
        hubRegistry = hubRegistry_;
    }

    //----------------------------------------------------------------------------------------------
    // Administration
    //----------------------------------------------------------------------------------------------

    /// @inheritdoc IShareClassManager
    function addShareClass(PoolId poolId, string calldata name, string calldata symbol, bytes32 salt)
        external
        auth
        returns (ShareClassId scId_)
    {
        scId_ = previewNextShareClassId(poolId);

        uint32 index = ++shareClassCount[poolId];
        shareClassIds[poolId][scId_] = true;

        _updateMetadata(scId_, name, symbol, salt);

        emit AddShareClass(poolId, scId_, index, name, symbol, salt);
    }

    //----------------------------------------------------------------------------------------------
    // Incoming requests
    //----------------------------------------------------------------------------------------------

    /// @inheritdoc IShareClassManager
    function requestDeposit(PoolId poolId, ShareClassId scId_, uint128 amount, bytes32 investor, AssetId depositAssetId)
        external
        auth
    {
        require(exists(poolId, scId_), ShareClassNotFound());

        // NOTE: Vaults ensure amount > 0
        _updatePending(poolId, scId_, amount, true, investor, depositAssetId, RequestType.Deposit);
    }

    /// @inheritdoc IShareClassManager
    function cancelDepositRequest(PoolId poolId, ShareClassId scId_, bytes32 investor, AssetId depositAssetId)
        external
        auth
        returns (uint128 cancelledAssetAmount)
    {
        require(exists(poolId, scId_), ShareClassNotFound());

        allowForceDepositCancel[scId_][depositAssetId][investor] = true;
        uint128 cancellingAmount = depositRequest[scId_][depositAssetId][investor].pending;

        return _updatePending(poolId, scId_, cancellingAmount, false, investor, depositAssetId, RequestType.Deposit);
    }

    /// @inheritdoc IShareClassManager
    function requestRedeem(PoolId poolId, ShareClassId scId_, uint128 amount, bytes32 investor, AssetId payoutAssetId)
        external
        auth
    {
        require(exists(poolId, scId_), ShareClassNotFound());

        // NOTE: Vaults ensure amount > 0
        _updatePending(poolId, scId_, amount, true, investor, payoutAssetId, RequestType.Redeem);
    }

    /// @inheritdoc IShareClassManager
    function cancelRedeemRequest(PoolId poolId, ShareClassId scId_, bytes32 investor, AssetId payoutAssetId)
        external
        auth
        returns (uint128 cancelledShareAmount)
    {
        require(exists(poolId, scId_), ShareClassNotFound());

        allowForceRedeemCancel[scId_][payoutAssetId][investor] = true;
        uint128 cancellingAmount = redeemRequest[scId_][payoutAssetId][investor].pending;

        return _updatePending(poolId, scId_, cancellingAmount, false, investor, payoutAssetId, RequestType.Redeem);
    }

    //----------------------------------------------------------------------------------------------
    // Manager actions
    //----------------------------------------------------------------------------------------------

    /// @inheritdoc IShareClassManager
    function approveDeposits(
        PoolId poolId,
        ShareClassId scId_,
        AssetId depositAssetId,
        uint32 nowDepositEpochId,
        uint128 approvedAssetAmount,
        D18 pricePoolPerAsset
    ) external auth returns (uint128 pendingAssetAmount, uint128 approvedPoolAmount) {
        require(exists(poolId, scId_), ShareClassNotFound());
        require(
            nowDepositEpochId == nowDepositEpoch(scId_, depositAssetId),
            EpochNotInSequence(nowDepositEpochId, nowDepositEpoch(scId_, depositAssetId))
        );

        // Limit in case approved > pending due to race condition of FM approval and async incoming requests
        pendingAssetAmount = pendingDeposit[scId_][depositAssetId];
        require(approvedAssetAmount <= pendingAssetAmount, InsufficientPending());
        require(approvedAssetAmount > 0, ZeroApprovalAmount());

        approvedPoolAmount = PricingLib.convertWithPrice(
            approvedAssetAmount, hubRegistry.decimals(depositAssetId), hubRegistry.decimals(poolId), pricePoolPerAsset
        );

        // Update epoch data
        EpochInvestAmounts storage epochAmounts = epochInvestAmounts[scId_][depositAssetId][nowDepositEpochId];
        epochAmounts.approvedAssetAmount = approvedAssetAmount;
        epochAmounts.approvedPoolAmount = approvedPoolAmount;
        epochAmounts.pendingAssetAmount = pendingAssetAmount;
        epochAmounts.pricePoolPerAsset = pricePoolPerAsset;

        // Reduce pending
        pendingDeposit[scId_][depositAssetId] -= approvedAssetAmount;
        pendingAssetAmount -= approvedAssetAmount;

        epochId[scId_][depositAssetId].deposit = nowDepositEpochId;
        emit ApproveDeposits(
            poolId,
            scId_,
            depositAssetId,
            nowDepositEpochId,
            approvedPoolAmount,
            approvedAssetAmount,
            pendingAssetAmount
        );
    }

    /// @inheritdoc IShareClassManager
    function approveRedeems(
        PoolId poolId,
        ShareClassId scId_,
        AssetId payoutAssetId,
        uint32 nowRedeemEpochId,
        uint128 approvedShareAmount,
        D18 pricePoolPerAsset
    ) external auth returns (uint128 pendingShareAmount) {
        require(exists(poolId, scId_), ShareClassNotFound());
        require(
            nowRedeemEpochId == nowRedeemEpoch(scId_, payoutAssetId),
            EpochNotInSequence(nowRedeemEpochId, nowRedeemEpoch(scId_, payoutAssetId))
        );

        // Limit in case approved > pending due to race condition of FM approval and async incoming requests
        pendingShareAmount = pendingRedeem[scId_][payoutAssetId];
        require(approvedShareAmount <= pendingShareAmount, InsufficientPending());
        require(approvedShareAmount > 0, ZeroApprovalAmount());

        // Update epoch data
        EpochRedeemAmounts storage epochAmounts = epochRedeemAmounts[scId_][payoutAssetId][nowRedeemEpochId];
        epochAmounts.approvedShareAmount = approvedShareAmount;
        epochAmounts.pendingShareAmount = pendingShareAmount;
        epochAmounts.pricePoolPerAsset = pricePoolPerAsset;

        // Reduce pending
        pendingRedeem[scId_][payoutAssetId] -= approvedShareAmount;
        pendingShareAmount -= approvedShareAmount;
        epochId[scId_][payoutAssetId].redeem = nowRedeemEpochId;
        emit ApproveRedeems(poolId, scId_, payoutAssetId, nowRedeemEpochId, approvedShareAmount, pendingShareAmount);
    }

    /// @inheritdoc IShareClassManager
    function issueShares(
        PoolId poolId,
        ShareClassId scId_,
        AssetId depositAssetId,
        uint32 nowIssueEpochId,
        D18 navPoolPerShare
    ) external auth returns (uint128 issuedShareAmount, uint128 depositAssetAmount, uint128 depositPoolAmount) {
        require(exists(poolId, scId_), ShareClassNotFound());
        require(nowIssueEpochId <= epochId[scId_][depositAssetId].deposit, EpochNotFound());
        require(
            nowIssueEpochId == nowIssueEpoch(scId_, depositAssetId),
            EpochNotInSequence(nowIssueEpochId, nowIssueEpoch(scId_, depositAssetId))
        );

        EpochInvestAmounts storage epochAmounts = epochInvestAmounts[scId_][depositAssetId][nowIssueEpochId];
        epochAmounts.navPoolPerShare = navPoolPerShare;

        issuedShareAmount = navPoolPerShare.isNotZero()
            ? PricingLib.assetToShareAmount(
                epochAmounts.approvedAssetAmount,
                hubRegistry.decimals(depositAssetId),
                hubRegistry.decimals(poolId),
                epochAmounts.pricePoolPerAsset,
                navPoolPerShare,
                MathLib.Rounding.Down
            )
            : 0;

        epochAmounts.issuedAt = block.timestamp.toUint64();
        epochId[scId_][depositAssetId].issue = nowIssueEpochId;

        depositAssetAmount = epochAmounts.approvedAssetAmount;
        depositPoolAmount = epochAmounts.approvedPoolAmount;

        emit IssueShares(
            poolId,
            scId_,
            depositAssetId,
            nowIssueEpochId,
            navPoolPerShare,
            epochAmounts.pricePoolPerAsset.isNotZero()
                ? PricingLib.priceAssetPerShare(epochAmounts.navPoolPerShare, epochAmounts.pricePoolPerAsset)
                : d18(0),
            issuedShareAmount
        );
    }

    /// @inheritdoc IShareClassManager
    function revokeShares(
        PoolId poolId,
        ShareClassId scId_,
        AssetId payoutAssetId,
        uint32 nowRevokeEpochId,
        D18 navPoolPerShare
    ) external auth returns (uint128 revokedShareAmount, uint128 payoutAssetAmount, uint128 payoutPoolAmount) {
        require(exists(poolId, scId_), ShareClassNotFound());
        require(nowRevokeEpochId <= epochId[scId_][payoutAssetId].redeem, EpochNotFound());
        require(
            nowRevokeEpochId == nowRevokeEpoch(scId_, payoutAssetId),
            EpochNotInSequence(nowRevokeEpochId, nowRevokeEpoch(scId_, payoutAssetId))
        );

        EpochRedeemAmounts storage epochAmounts = epochRedeemAmounts[scId_][payoutAssetId][nowRevokeEpochId];
        epochAmounts.navPoolPerShare = navPoolPerShare;

        // NOTE: shares and pool currency have the same decimals - no conversion needed!
        payoutPoolAmount = navPoolPerShare.mulUint128(epochAmounts.approvedShareAmount, MathLib.Rounding.Down);

        payoutAssetAmount = epochAmounts.pricePoolPerAsset.isNotZero()
            ? PricingLib.shareToAssetAmount(
                epochAmounts.approvedShareAmount,
                hubRegistry.decimals(poolId),
                hubRegistry.decimals(payoutAssetId),
                epochAmounts.navPoolPerShare,
                epochAmounts.pricePoolPerAsset,
                MathLib.Rounding.Down
            )
            : 0;
        revokedShareAmount = epochAmounts.approvedShareAmount;

        epochAmounts.payoutAssetAmount = payoutAssetAmount;
        epochAmounts.revokedAt = block.timestamp.toUint64();
        epochId[scId_][payoutAssetId].revoke = nowRevokeEpochId;

        emit RevokeShares(
            poolId,
            scId_,
            payoutAssetId,
            nowRevokeEpochId,
            navPoolPerShare,
            epochAmounts.pricePoolPerAsset.isNotZero()
                ? PricingLib.priceAssetPerShare(epochAmounts.navPoolPerShare, epochAmounts.pricePoolPerAsset)
                : d18(0),
            epochAmounts.approvedShareAmount,
            payoutAssetAmount,
            payoutPoolAmount
        );
    }

    /// @inheritdoc IShareClassManager
    function updateSharePrice(PoolId poolId, ShareClassId scId_, D18 navPoolPerShare) external auth {
        require(exists(poolId, scId_), ShareClassNotFound());

        ShareClassMetrics storage m = metrics[scId_];
        m.navPerShare = navPoolPerShare;
        emit UpdateShareClass(poolId, scId_, navPoolPerShare);
    }

    /// @inheritdoc IShareClassManager
    function updateMetadata(PoolId poolId, ShareClassId scId_, string calldata name, string calldata symbol)
        external
        auth
    {
        require(exists(poolId, scId_), ShareClassNotFound());

        _updateMetadata(scId_, name, symbol, bytes32(0));

        emit UpdateMetadata(poolId, scId_, name, symbol);
    }

    /// @inheritdoc IShareClassManager
    function updateShares(uint16 centrifugeId, PoolId poolId, ShareClassId scId_, uint128 amount, bool isIssuance)
        external
        auth
    {
        require(exists(poolId, scId_), ShareClassNotFound());
        require(isIssuance || issuance[scId_][centrifugeId] >= amount, DecreaseMoreThanIssued());

        uint128 newTotalIssuance =
            isIssuance ? metrics[scId_].totalIssuance + amount : metrics[scId_].totalIssuance - amount;
        metrics[scId_].totalIssuance = newTotalIssuance;

        uint128 newIssuancePerNetwork =
            isIssuance ? issuance[scId_][centrifugeId] + amount : issuance[scId_][centrifugeId] - amount;
        issuance[scId_][centrifugeId] = newIssuancePerNetwork;

        if (isIssuance) emit RemoteIssueShares(centrifugeId, poolId, scId_, amount);
        else emit RemoteRevokeShares(centrifugeId, poolId, scId_, amount);
    }

    /// @inheritdoc IShareClassManager
    function forceCancelDepositRequest(PoolId poolId, ShareClassId scId_, bytes32 investor, AssetId depositAssetId)
        external
        auth
        returns (uint128 cancelledAssetAmount)
    {
        require(exists(poolId, scId_), ShareClassNotFound());
        require(allowForceDepositCancel[scId_][depositAssetId][investor], CancellationInitializationRequired());

        uint128 cancellingAmount = depositRequest[scId_][depositAssetId][investor].pending;

        return _updatePending(poolId, scId_, cancellingAmount, false, investor, depositAssetId, RequestType.Deposit);
    }

    /// @inheritdoc IShareClassManager
    function forceCancelRedeemRequest(PoolId poolId, ShareClassId scId_, bytes32 investor, AssetId payoutAssetId)
        external
        auth
        returns (uint128 cancelledShareAmount)
    {
        require(exists(poolId, scId_), ShareClassNotFound());
        require(allowForceRedeemCancel[scId_][payoutAssetId][investor], CancellationInitializationRequired());

        uint128 cancellingAmount = redeemRequest[scId_][payoutAssetId][investor].pending;

        return _updatePending(poolId, scId_, cancellingAmount, false, investor, payoutAssetId, RequestType.Redeem);
    }

    //----------------------------------------------------------------------------------------------
    // Claiming methods
    //----------------------------------------------------------------------------------------------

    /// @inheritdoc IShareClassManager
    function claimDeposit(PoolId poolId, ShareClassId scId_, bytes32 investor, AssetId depositAssetId)
        public
        auth
        returns (
            uint128 payoutShareAmount,
            uint128 paymentAssetAmount,
            uint128 cancelledAssetAmount,
            bool canClaimAgain
        )
    {
        require(exists(poolId, scId_), ShareClassNotFound());

        UserOrder storage userOrder = depositRequest[scId_][depositAssetId][investor];
        require(userOrder.pending > 0, NoOrderFound());
        require(userOrder.lastUpdate <= epochId[scId_][depositAssetId].issue, IssuanceRequired());
        canClaimAgain = userOrder.lastUpdate < epochId[scId_][depositAssetId].issue;
        EpochInvestAmounts storage epochAmounts = epochInvestAmounts[scId_][depositAssetId][userOrder.lastUpdate];

        paymentAssetAmount = epochAmounts.approvedAssetAmount == 0
            ? 0
            : userOrder.pending.mulDiv(epochAmounts.approvedAssetAmount, epochAmounts.pendingAssetAmount).toUint128();

        // NOTE: Due to precision loss, the sum of claimable user amounts is leq than the amount of minted share class
        // tokens corresponding to the approved share amount (instead of equality). I.e., it is possible for an epoch to
        // have an excess of a share class tokens which cannot be claimed by anyone.
        if (paymentAssetAmount > 0) {
            payoutShareAmount = epochAmounts.navPoolPerShare.isNotZero()
                ? PricingLib.assetToShareAmount(
                    paymentAssetAmount,
                    hubRegistry.decimals(depositAssetId),
                    hubRegistry.decimals(poolId),
                    epochAmounts.pricePoolPerAsset,
                    epochAmounts.navPoolPerShare,
                    MathLib.Rounding.Down
                )
                : 0;

            userOrder.pending -= paymentAssetAmount;
        }

        emit ClaimDeposit(
            poolId,
            scId_,
            userOrder.lastUpdate,
            investor,
            depositAssetId,
            paymentAssetAmount,
            userOrder.pending,
            payoutShareAmount,
            epochAmounts.issuedAt
        );

        // If there is nothing to claim anymore we can short circuit to the latest epoch
        if (userOrder.pending == 0) {
            // The current epoch is always one step ahead of the stored one
            userOrder.lastUpdate = nowDepositEpoch(scId_, depositAssetId);
            canClaimAgain = false;
        } else {
            userOrder.lastUpdate += 1;
        }

        // If user claimed up to latest approval epoch, move queued to pending
        if (userOrder.lastUpdate == nowDepositEpoch(scId_, depositAssetId)) {
            cancelledAssetAmount =
                _postClaimUpdateQueued(poolId, scId_, investor, depositAssetId, userOrder, RequestType.Deposit);
        }
    }

    /// @inheritdoc IShareClassManager
    function claimRedeem(PoolId poolId, ShareClassId scId_, bytes32 investor, AssetId payoutAssetId)
        public
        auth
        returns (
            uint128 payoutAssetAmount,
            uint128 paymentShareAmount,
            uint128 cancelledShareAmount,
            bool canClaimAgain
        )
    {
        require(exists(poolId, scId_), ShareClassNotFound());

        UserOrder storage userOrder = redeemRequest[scId_][payoutAssetId][investor];
        require(userOrder.pending > 0, NoOrderFound());
        require(userOrder.lastUpdate <= epochId[scId_][payoutAssetId].revoke, RevocationRequired());
        canClaimAgain = userOrder.lastUpdate < epochId[scId_][payoutAssetId].revoke;

        EpochRedeemAmounts storage epochAmounts = epochRedeemAmounts[scId_][payoutAssetId][userOrder.lastUpdate];

        paymentShareAmount = epochAmounts.approvedShareAmount == 0
            ? 0
            : userOrder.pending.mulDiv(epochAmounts.approvedShareAmount, epochAmounts.pendingShareAmount).toUint128();

        // NOTE: Due to precision loss, the sum of claimable user amounts is leq than the amount of payout asset
        // corresponding to the approved share class (instead of equality). I.e., it is possible for an epoch to
        // have an excess of payout assets which cannot be claimed by anyone.
        if (paymentShareAmount > 0) {
            payoutAssetAmount = epochAmounts.pricePoolPerAsset.isNotZero()
                ? PricingLib.shareToAssetAmount(
                    paymentShareAmount,
                    hubRegistry.decimals(poolId),
                    hubRegistry.decimals(payoutAssetId),
                    epochAmounts.navPoolPerShare,
                    epochAmounts.pricePoolPerAsset,
                    MathLib.Rounding.Down
                )
                : 0;

            userOrder.pending -= paymentShareAmount;
        }

        emit ClaimRedeem(
            poolId,
            scId_,
            userOrder.lastUpdate,
            investor,
            payoutAssetId,
            paymentShareAmount,
            userOrder.pending,
            payoutAssetAmount,
            epochAmounts.revokedAt
        );

        // If there is nothing to claim anymore we can short circuit the in between epochs
        if (userOrder.pending == 0) {
            // The current epoch is always one step ahead of the stored one
            userOrder.lastUpdate = nowRedeemEpoch(scId_, payoutAssetId);
            canClaimAgain = false;
        } else {
            userOrder.lastUpdate += 1;
        }

        if (userOrder.lastUpdate == nowRedeemEpoch(scId_, payoutAssetId)) {
            cancelledShareAmount =
                _postClaimUpdateQueued(poolId, scId_, investor, payoutAssetId, userOrder, RequestType.Redeem);
        }
    }

    //----------------------------------------------------------------------------------------------
    // View methods
    //----------------------------------------------------------------------------------------------

    /// @inheritdoc IShareClassManager
    function previewNextShareClassId(PoolId poolId) public view returns (ShareClassId scId) {
        return newShareClassId(poolId, shareClassCount[poolId] + 1);
    }

    /// @inheritdoc IShareClassManager
    function previewShareClassId(PoolId poolId, uint32 index) public pure returns (ShareClassId scId) {
        return newShareClassId(poolId, index);
    }

    /// @inheritdoc IShareClassManager
    function exists(PoolId poolId, ShareClassId scId_) public view returns (bool) {
        return shareClassIds[poolId][scId_];
    }

    /// @inheritdoc IShareClassManager
    function nowDepositEpoch(ShareClassId scId_, AssetId depositAssetId) public view returns (uint32) {
        return epochId[scId_][depositAssetId].deposit + 1;
    }

    /// @inheritdoc IShareClassManager
    function nowIssueEpoch(ShareClassId scId_, AssetId depositAssetId) public view returns (uint32) {
        return epochId[scId_][depositAssetId].issue + 1;
    }

    /// @inheritdoc IShareClassManager
    function nowRedeemEpoch(ShareClassId scId_, AssetId depositAssetId) public view returns (uint32) {
        return epochId[scId_][depositAssetId].redeem + 1;
    }

    /// @inheritdoc IShareClassManager
    function nowRevokeEpoch(ShareClassId scId_, AssetId depositAssetId) public view returns (uint32) {
        return epochId[scId_][depositAssetId].revoke + 1;
    }

    /// @inheritdoc IShareClassManager
    function maxDepositClaims(ShareClassId scId_, bytes32 investor, AssetId depositAssetId)
        public
        view
        returns (uint32)
    {
        return _maxClaims(depositRequest[scId_][depositAssetId][investor], epochId[scId_][depositAssetId].deposit);
    }

    /// @inheritdoc IShareClassManager
    function maxRedeemClaims(ShareClassId scId_, bytes32 investor, AssetId payoutAssetId)
        public
        view
        returns (uint32)
    {
        return _maxClaims(redeemRequest[scId_][payoutAssetId][investor], epochId[scId_][payoutAssetId].redeem);
    }

    function _maxClaims(UserOrder memory userOrder, uint32 lastEpoch) internal pure returns (uint32) {
        // User order either not set or not processed
        if (userOrder.pending == 0 || userOrder.lastUpdate > lastEpoch) {
            return 0;
        }

        return lastEpoch - userOrder.lastUpdate + 1;
    }

    //----------------------------------------------------------------------------------------------
    // Internal methods
    //----------------------------------------------------------------------------------------------

    function _updateMetadata(ShareClassId scId_, string calldata name, string calldata symbol, bytes32 salt) internal {
        uint256 nameLen = bytes(name).length;
        require(nameLen > 0 && nameLen <= 128, InvalidMetadataName());

        uint256 symbolLen = bytes(symbol).length;
        require(symbolLen > 0 && symbolLen <= 32, InvalidMetadataSymbol());

        ShareClassMetadata storage meta = metadata[scId_];

        // Ensure that the salt is not being updated or is being set for the first time
        require(
            (salt == bytes32(0) && meta.salt != bytes32(0)) || (salt != bytes32(0) && meta.salt == bytes32(0)),
            InvalidSalt()
        );

        if (salt != bytes32(0) && meta.salt == bytes32(0)) {
            require(!salts[salt], AlreadyUsedSalt());
            salts[salt] = true;
            meta.salt = salt;
        }

        meta.name = name;
        meta.symbol = symbol;
    }

    function _postClaimUpdateQueued(
        PoolId poolId,
        ShareClassId scId_,
        bytes32 investor,
        AssetId assetId,
        UserOrder storage userOrder,
        RequestType requestType
    ) internal returns (uint128 cancelledAmount) {
        QueuedOrder storage queued = requestType == RequestType.Deposit
            ? queuedDepositRequest[scId_][assetId][investor]
            : queuedRedeemRequest[scId_][assetId][investor];

        // Increment pending by queued or cancel everything
        uint128 updatePendingAmount = queued.isCancelling ? userOrder.pending : queued.amount;
        if (queued.isCancelling) {
            cancelledAmount = userOrder.pending + queued.amount;
            userOrder.pending = 0;
        } else {
            userOrder.pending += queued.amount;
        }

        if (requestType == RequestType.Deposit) {
            _updatePendingDeposit(
                poolId,
                scId_,
                updatePendingAmount,
                !queued.isCancelling,
                investor,
                assetId,
                userOrder,
                QueuedOrder(false, 0)
            );
        } else {
            _updatePendingRedeem(
                poolId,
                scId_,
                updatePendingAmount,
                !queued.isCancelling,
                investor,
                assetId,
                userOrder,
                QueuedOrder(false, 0)
            );
        }

        // Clear queued
        queued.isCancelling = false;
        queued.amount = 0;
    }

    /// @notice Updates the amount of a deposit or redeem request.
    ///
    /// @param poolId Identifier of the pool
    /// @param scId_ Identifier of the share class
    /// @param amount Amount which is updated
    /// @param isIncrement Whether the amount is positive (additional request) or negative (cancellation)
    /// @param investor Address of the entity which is depositing
    /// @param assetId Identifier of the asset which the investor either used as deposit or wants to redeem to
    /// @param requestType Flag indicating whether the request is a deposit or redeem request
    /// @return cancelledAmount Pending amount which was cancelled
    function _updatePending(
        PoolId poolId,
        ShareClassId scId_,
        uint128 amount,
        bool isIncrement,
        bytes32 investor,
        AssetId assetId,
        RequestType requestType
    ) internal returns (uint128 cancelledAmount) {
        UserOrder storage userOrder = requestType == RequestType.Deposit
            ? depositRequest[scId_][assetId][investor]
            : redeemRequest[scId_][assetId][investor];
        QueuedOrder storage queued = requestType == RequestType.Deposit
            ? queuedDepositRequest[scId_][assetId][investor]
            : queuedRedeemRequest[scId_][assetId][investor];

        // We must only update either queued or pending
        if (_updateQueued(poolId, scId_, amount, isIncrement, investor, assetId, userOrder, queued, requestType)) {
            return 0;
        }

        cancelledAmount = isIncrement ? 0 : amount;
        // NOTE: If we decrease the pending, we decrease usually by the full amount
        //       We keep subtraction of amount over setting to zero on purpose to not limit future higher level logic
        userOrder.pending = isIncrement ? userOrder.pending + amount : userOrder.pending - amount;

        userOrder.lastUpdate =
            requestType == RequestType.Deposit ? nowDepositEpoch(scId_, assetId) : nowRedeemEpoch(scId_, assetId);

        if (requestType == RequestType.Deposit) {
            _updatePendingDeposit(poolId, scId_, amount, isIncrement, investor, assetId, userOrder, queued);
        } else {
            _updatePendingRedeem(poolId, scId_, amount, isIncrement, investor, assetId, userOrder, queued);
        }
    }

    /// @notice Checks whether the pending amount can be updated. If not, it updates the queued amount.
    ///
    /// @param poolId Identifier of the pool
    /// @param scId_ Identifier of the share class
    /// @param amount Amount which is updated
    /// @param isIncrement Whether the amount is positive (additional request) or negative (cancellation)
    /// @param investor Address of the entity which is depositing
    /// @param assetId Identifier of the asset which the investor either used as deposit or wants to redeem to
    /// @param userOrder User order storage for the deposit or redeem request
    /// @param requestType Flag indicating whether the request is a deposit or redeem request
    /// @return skipPendingUpdate Flag indicating whether the pending amount can be updated which is true if the user
    /// does not need to claim
    function _updateQueued(
        PoolId poolId,
        ShareClassId scId_,
        uint128 amount,
        bool isIncrement,
        bytes32 investor,
        AssetId assetId,
        UserOrder storage userOrder,
        QueuedOrder storage queued,
        RequestType requestType
    ) internal returns (bool skipPendingUpdate) {
        uint32 currentEpoch =
            requestType == RequestType.Deposit ? nowDepositEpoch(scId_, assetId) : nowRedeemEpoch(scId_, assetId);

        // Short circuit if user can mutate pending, i.e. last update happened after latest approval or is first update
        if (_canMutatePending(userOrder, currentEpoch)) {
            return false;
        }

        // Block increasing queued amount if cancelling is already queued
        // NOTE: Can only happen due to race condition as Vaults blocks requests if cancellation is in progress
        require(!(queued.isCancelling && amount > 0), CancellationQueued());

        if (!isIncrement) {
            queued.isCancelling = true;
        } else {
            queued.amount += amount;
        }

        if (requestType == RequestType.Deposit) {
            uint128 pendingTotal = pendingDeposit[scId_][assetId];
            emit UpdateDepositRequest(
                poolId,
                scId_,
                assetId,
                currentEpoch,
                investor,
                userOrder.pending,
                pendingTotal,
                queued.amount,
                queued.isCancelling
            );
        } else {
            uint128 pendingTotal = pendingRedeem[scId_][assetId];

            emit UpdateRedeemRequest(
                poolId,
                scId_,
                assetId,
                currentEpoch,
                investor,
                userOrder.pending,
                pendingTotal,
                queued.amount,
                queued.isCancelling
            );
        }

        return true;
    }

    function _updatePendingDeposit(
        PoolId poolId,
        ShareClassId scId_,
        uint128 amount,
        bool isIncrement,
        bytes32 investor,
        AssetId assetId,
        UserOrder storage userOrder,
        QueuedOrder memory queued
    ) internal {
        uint128 pendingTotal = pendingDeposit[scId_][assetId];
        pendingTotal = isIncrement ? pendingTotal + amount : pendingTotal - amount;
        pendingDeposit[scId_][assetId] = pendingTotal;

        emit UpdateDepositRequest(
            poolId,
            scId_,
            assetId,
            nowDepositEpoch(scId_, assetId),
            investor,
            userOrder.pending,
            pendingTotal,
            queued.amount,
            queued.isCancelling
        );
    }

    function _updatePendingRedeem(
        PoolId poolId,
        ShareClassId scId_,
        uint128 amount,
        bool isIncrement,
        bytes32 investor,
        AssetId assetId,
        UserOrder storage userOrder,
        QueuedOrder memory queued
    ) internal {
        uint128 pendingTotal = pendingRedeem[scId_][assetId];
        pendingTotal = isIncrement ? pendingTotal + amount : pendingTotal - amount;
        pendingRedeem[scId_][assetId] = pendingTotal;

        emit UpdateRedeemRequest(
            poolId,
            scId_,
            assetId,
            nowRedeemEpoch(scId_, assetId),
            investor,
            userOrder.pending,
            pendingTotal,
            queued.amount,
            queued.isCancelling
        );
    }

    /// @dev A user cannot mutate their pending amount at all times because it affects the total pending amount. It is
    ///     restricted to the following three conditions:
    ///         1. It's the first epoch (currentEpoch <= 1), which implies userOrder.lastUpdate == 0
    ///         2. User has no pending amount (userOrder.pending == 0)
    ///         3. User's last update is not behind the current epoch (userOrder.lastUpdate >= currentEpoch)
    function _canMutatePending(UserOrder memory userOrder, uint32 currentEpoch) internal pure returns (bool) {
        return currentEpoch <= 1 || userOrder.pending == 0 || userOrder.lastUpdate >= currentEpoch;
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

import {IAuth} from "src/misc/interfaces/IAuth.sol";

/// @title  Auth
/// @notice Simple authentication pattern
/// @author Based on code from https://github.com/makerdao/dss
abstract contract Auth is IAuth {
    /// @inheritdoc IAuth
    mapping(address => uint256) public wards;

    constructor(address initialWard) {
        wards[initialWard] = 1;
        emit Rely(initialWard);
    }

    /// @dev Check if the msg.sender has permissions
    modifier auth() {
        require(wards[msg.sender] == 1, NotAuthorized());
        _;
    }

    /// @inheritdoc IAuth
    function rely(address user) public auth {
        wards[user] = 1;
        emit Rely(user);
    }

    /// @inheritdoc IAuth
    function deny(address user) public auth {
        wards[user] = 0;
        emit Deny(user);
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

// Small library to handle fixed point number operations with 18 decimals with static typing support.

import {MathLib} from "src/misc/libraries/MathLib.sol";

type D18 is uint128;

using MathLib for uint256;

/// @dev add two D18 types
function add(D18 d1, D18 d2) pure returns (D18) {
    return D18.wrap(D18.unwrap(d1) + D18.unwrap(d2));
}

/// @dev substract two D18 types
function sub(D18 d1, D18 d2) pure returns (D18) {
    return D18.wrap(D18.unwrap(d1) - D18.unwrap(d2));
}

/// @dev Divides one D18 by another one while retaining precision:
/// - nominator (decimal): 50e18
/// - denominator (decimal):  2e19
/// - result (decimal): 25e17
function divD18(D18 d1, D18 d2) pure returns (D18) {
    return D18.wrap(MathLib.mulDiv(D18.unwrap(d1), 1e18, D18.unwrap(d2)).toUint128());
}

/// @dev Multiplies one D18 with another one while retaining precision:
/// - value1 (decimal): 50e18
/// - value2 (decimal):  2e19
/// - result (decimal): 100e19
function mulD18(D18 d1, D18 d2) pure returns (D18) {
    return D18.wrap(MathLib.mulDiv(D18.unwrap(d1), D18.unwrap(d2), 1e18).toUint128());
}

/// @dev Returns the reciprocal of a D18 decimal, i.e. 1 / d.
///      Example: if d = 2.0 (2e18 internally), reciprocal(d) = 0.5 (5e17 internally).
function reciprocal(D18 d) pure returns (D18) {
    uint128 val = D18.unwrap(d);
    require(val != 0, "D18/division-by-zero");
    return d18(1e18, val);
}

/// @dev Multiplies a decimal by an integer. i.e:
/// - d (decimal):      1_500_000_000_000_000_000
/// - value (integer):  4_000_000_000_000_000_000
/// - result (integer): 6_000_000_000_000_000_000
function mulUint128(D18 d, uint128 value, MathLib.Rounding rounding) pure returns (uint128) {
    return MathLib.mulDiv(D18.unwrap(d), value, 1e18, rounding).toUint128();
}

/// @dev Multiplies a decimal by an integer. i.e:
/// - d (decimal):      1_500_000_000_000_000_000
/// - value (integer):  4_000_000_000_000_000_000
/// - result (integer): 6_000_000_000_000_000_000
function mulUint256(D18 d, uint256 value, MathLib.Rounding rounding) pure returns (uint256) {
    return MathLib.mulDiv(D18.unwrap(d), value, 1e18, rounding);
}

/// @dev  Divides an integer by a decimal, i.e.
/// @dev  Same as mulDiv for integers, i.e:
/// - d (decimal):      2_000_000_000_000_000_000
/// - value (integer):  100_000_000_000_000_000_000
/// - result (integer): 50_000_000_000_000_000_000
function reciprocalMulUint128(D18 d, uint128 value, MathLib.Rounding rounding) pure returns (uint128) {
    return MathLib.mulDiv(value, 1e18, d.raw(), rounding).toUint128();
}

/// @dev  Divides an integer by a decimal, i.e.
/// @dev  Same as mulDiv for integers, i.e:
/// - d (decimal):      2_000_000_000_000_000_000
/// - value (integer):  100_000_000_000_000_000_000
/// - result (integer): 50_000_000_000_000_000_000
function reciprocalMulUint256(D18 d, uint256 value, MathLib.Rounding rounding) pure returns (uint256) {
    return MathLib.mulDiv(value, 1e18, d.raw(), rounding);
}

/// @dev Easy way to construct a decimal number
function d18(uint128 value) pure returns (D18) {
    return D18.wrap(value);
}

/// @dev Easy way to construct a decimal number
function d18(uint128 num, uint128 den) pure returns (D18) {
    return D18.wrap(MathLib.mulDiv(num, 1e18, den).toUint128());
}

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

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

function isNotZero(D18 a) pure returns (bool) {
    return D18.unwrap(a) != 0;
}

function raw(D18 d) pure returns (uint128) {
    return D18.unwrap(d);
}

using {
    add as +,
    sub as -,
    divD18 as /,
    eq,
    mulD18 as *,
    mulUint128,
    mulUint256,
    reciprocalMulUint128,
    reciprocalMulUint256,
    reciprocal,
    raw,
    isZero,
    isNotZero
} for D18 global;

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

/// @title  CastLib
library CastLib {
    function toAddressLeftPadded(bytes32 addr) internal pure returns (address) {
        require(bytes12(addr) == 0, "First 12 bytes should be zero");
        return address(uint160(uint256(addr)));
    }

    function toBytes32LeftPadded(address addr) internal pure returns (bytes32) {
        return bytes32(uint256(uint160(addr)));
    }

    function toAddress(bytes32 addr) internal pure returns (address) {
        require(uint96(uint256(addr)) == 0, "Input should be 20 bytes");
        return address(bytes20(addr));
    }

    function toBytes32(address addr) internal pure returns (bytes32) {
        return bytes32(bytes20(addr));
    }

    /// @dev Adds zero padding
    function toBytes32(string memory source) internal pure returns (bytes32) {
        return bytes32(bytes(source));
    }

    /// @dev Removes zero padding
    function bytes128ToString(bytes memory _bytes128) internal pure returns (string memory) {
        require(_bytes128.length == 128, "Input should be 128 bytes");

        uint8 i = 0;
        while (i < 128 && _bytes128[i] != 0) {
            i++;
        }

        bytes memory bytesArray = new bytes(i);

        for (uint8 j; j < i; j++) {
            bytesArray[j] = _bytes128[j];
        }

        return string(bytesArray);
    }

    function toString(bytes32 _bytes32) internal pure returns (string memory) {
        uint8 i = 0;
        while (i < 32 && _bytes32[i] != 0) {
            i++;
        }
        bytes memory bytesArray = new bytes(i);
        for (i = 0; i < 32 && _bytes32[i] != 0; i++) {
            bytesArray[i] = _bytes32[i];
        }
        return string(bytesArray);
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

library MathLib {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero

    }

    error MulDiv_Overflow();
    error Uint8_Overflow();
    error Uint32_Overflow();
    error Uint64_Overflow();
    error Uint128_Overflow();
    error Int128_Overflow();

    uint256 public constant One27 = 10 ** 27;

    /// @notice Returns x^n with rounding precision of base
    ///
    /// @dev Source: https://github.com/makerdao/dss/blob/fa4f6630afb0624d04a003e920b0d71a00331d98/src/jug.sol#L62
    ///
    /// @param x The base value which should be exponentiated
    /// @param n The exponent
    /// @param base The scaling base, typically used for fix-point calculations
    function rpow(uint256 x, uint256 n, uint256 base) public pure returns (uint256 z) {
        assembly {
            switch x
            case 0 {
                switch n
                case 0 { z := base }
                default { z := 0 }
            }
            default {
                switch mod(n, 2)
                case 0 { z := base }
                default { z := x }
                let half := div(base, 2) // for rounding.
                for { n := div(n, 2) } n { n := div(n, 2) } {
                    let xx := mul(x, x)
                    if iszero(eq(div(xx, x), x)) { revert(0, 0) }
                    let xxRound := add(xx, half)
                    if lt(xxRound, xx) { revert(0, 0) }
                    x := div(xxRound, base)
                    if mod(n, 2) {
                        let zx := mul(z, x)
                        if and(iszero(iszero(x)), iszero(eq(div(zx, x), z))) { revert(0, 0) }
                        let zxRound := add(zx, half)
                        if lt(zxRound, zx) { revert(0, 0) }
                        z := div(zxRound, base)
                    }
                }
            }
        }
    }

    /// @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.
    // slither-disable-start divide-before-multiply
    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; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                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.
            require(denominator > prod1, MulDiv_Overflow());

            ///////////////////////////////////////////////
            // 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.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            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;
        }
    }
    // slither-disable-end divide-before-multiply

    /// @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 (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /// @notice Safe type conversion from uint256 to uint8.
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value <= type(uint8).max, Uint8_Overflow());
        return uint8(value);
    }

    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value <= type(uint32).max, Uint32_Overflow());
        return uint32(value);
    }

    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value <= type(uint64).max, Uint64_Overflow());
        return uint64(value);
    }

    /// @notice Safe type conversion from uint256 to uint128.
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value <= type(uint128).max, Uint128_Overflow());
        return uint128(value);
    }

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

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

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

/// @title  Bytes Lib
/// @dev    Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
///         The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
/// @author Modified from Solidity Bytes Arrays Utils v0.8.0
library BytesLib {
    error SliceOverflow();
    error SliceOutOfBounds();

    function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) {
        unchecked {
            require(_length + 31 >= _length, SliceOverflow());
        }
        require(_bytes.length >= _start + _length, SliceOutOfBounds());

        bytes memory tempBytes;

        assembly {
            switch iszero(_length)
            case 0 {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                tempBytes := mload(0x40)

                // The first word of the slice result is potentially a partial
                // word read from the original array. To read it, we calculate
                // the length of that partial word and start copying that many
                // bytes into the array. The first word we copy will start with
                // data we don't care about, but the last `lengthmod` bytes will
                // land at the beginning of the contents of the new array. When
                // we're done copying, we overwrite the full first word with
                // the actual length of the slice.
                let lengthmod := and(_length, 31)

                // The multiplication in the next line is necessary
                // because when slicing multiples of 32 bytes (lengthmod == 0)
                // the following copy loop was copying the origin's length
                // and then ending prematurely not copying everything it should.
                let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                let end := add(mc, _length)

                for {
                    // The multiplication in the next line has the same exact purpose
                    // as the one above.
                    let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } { mstore(mc, mload(cc)) }

                mstore(tempBytes, _length)

                //update free-memory pointer
                //allocating the array padded to 32 bytes like the compiler does now
                mstore(0x40, and(add(mc, 31), not(31)))
            }
            //if we want a zero-length slice let's just return a zero-length array
            default {
                tempBytes := mload(0x40)
                //zero out the 32 bytes slice we are about to return
                //we need to do it because Solidity does not garbage collect
                mstore(tempBytes, 0)

                mstore(0x40, add(tempBytes, 0x20))
            }
        }

        return tempBytes;
    }

    function sliceZeroPadded(bytes memory _bytes, uint256 _start, uint256 _length)
        internal
        pure
        returns (bytes memory)
    {
        bool needsPad = _bytes.length < _start + _length;
        if (!needsPad) return slice(_bytes, _start, _length);

        bytes memory slice_ = slice(_bytes, _start, _bytes.length - _start);
        bytes memory padding = new bytes(_length + _start - _bytes.length);
        return bytes.concat(slice_, padding);
    }

    function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
        require(_bytes.length >= _start + 20, SliceOutOfBounds());
        address tempAddress;

        assembly {
            tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
        }

        return tempAddress;
    }

    function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
        require(_bytes.length >= _start + 1, SliceOutOfBounds());
        uint8 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x1), _start))
        }

        return tempUint;
    }

    function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
        require(_bytes.length >= _start + 2, SliceOutOfBounds());
        uint16 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x2), _start))
        }

        return tempUint;
    }

    function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
        require(_bytes.length >= _start + 4, SliceOutOfBounds());
        uint32 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x4), _start))
        }

        return tempUint;
    }

    function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
        require(_bytes.length >= _start + 8, SliceOutOfBounds());
        uint64 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x8), _start))
        }

        return tempUint;
    }

    function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
        require(_bytes.length >= _start + 16, SliceOutOfBounds());
        uint128 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x10), _start))
        }

        return tempUint;
    }

    function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
        require(_bytes.length >= _start + 32, SliceOutOfBounds());
        uint256 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x20), _start))
        }

        return tempUint;
    }

    function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
        require(_bytes.length >= _start + 32, SliceOutOfBounds());
        bytes32 tempBytes32;

        assembly {
            tempBytes32 := mload(add(add(_bytes, 0x20), _start))
        }

        return tempBytes32;
    }

    function toBytes16(bytes memory _bytes, uint256 _start) internal pure returns (bytes16) {
        require(_bytes.length >= _start + 16, SliceOutOfBounds());
        bytes16 tempBytes16;

        assembly {
            tempBytes16 := mload(add(add(_bytes, 0x20), _start))
        }

        return tempBytes16;
    }

    function toBool(bytes memory _bytes, uint256 _start) internal pure returns (bool) {
        require(_bytes.length > _start, SliceOutOfBounds());
        return _bytes[_start] != 0;
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

import {MathLib} from "src/misc/libraries/MathLib.sol";

using MathLib for uint256;

type PoolId is uint64;

function centrifugeId(PoolId poolId) pure returns (uint16) {
    return uint16(PoolId.unwrap(poolId) >> 48);
}

function newPoolId(uint16 centrifugeId_, uint48 localPoolId) pure returns (PoolId) {
    return PoolId.wrap((uint64(centrifugeId_) << 48) | uint64(localPoolId));
}

function isNull(PoolId poolId) pure returns (bool) {
    return PoolId.unwrap(poolId) == 0;
}

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

function raw(PoolId poolId) pure returns (uint64) {
    return PoolId.unwrap(poolId);
}

using {centrifugeId, isNull, raw, isEqual as ==} for PoolId global;

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

/// @dev Composite Id of the centrifugeId (uint16) where the asset resides
///      and a local counter (uint64) that is part of the contract that registers the asset.
type AssetId is uint128;

function isNull(AssetId assetId) pure returns (bool) {
    return AssetId.unwrap(assetId) == 0;
}

function raw(AssetId assetId) pure returns (uint128) {
    return AssetId.unwrap(assetId);
}

function centrifugeId(AssetId assetId) pure returns (uint16) {
    return uint16(AssetId.unwrap(assetId) >> 112);
}

function newAssetId(uint16 centrifugeId_, uint64 counter) pure returns (AssetId) {
    return AssetId.wrap((uint128(centrifugeId_) << 112) + counter);
}

function newAssetId(uint32 isoCode) pure returns (AssetId) {
    return AssetId.wrap(isoCode);
}

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

using {isNull, raw, centrifugeId, eq} for AssetId global;

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

import {D18, d18} from "src/misc/types/D18.sol";
import {MathLib} from "src/misc/libraries/MathLib.sol";
import {IERC20Metadata} from "src/misc/interfaces/IERC20.sol";
import {IERC6909MetadataExt} from "src/misc/interfaces/IERC6909.sol";

import {PricingLib} from "src/common/libraries/PricingLib.sol";

library PricingLib {
    using MathLib for *;

    /// @dev Prices are fixed-point integers with 18 decimals
    uint8 internal constant PRICE_DECIMALS = 18;

    //----------------------------------------------------------------------------------------------
    // View methods
    //----------------------------------------------------------------------------------------------

    /// @dev Converts the given asset amount to share amount. Returned value is in share decimals.
    /// @dev Assumes handling of zero denominator price (priceAssetPerShare_) by consumer.
    ///
    ///      NOTE: MUST ONLY be used in AsyncRequestManager which rely on priceAssetPerShare that is derived from
    ///      Fulfilled* message amounts. Any other codepath must use the variant with pricePoolPerAsset
    ///      and pricePoolPerShare
    function assetToShareAmount(
        address shareToken,
        address asset,
        uint256 tokenId,
        uint128 assetAmount,
        D18 priceAssetPerShare_,
        MathLib.Rounding rounding
    ) internal view returns (uint128 shares) {
        if (assetAmount == 0) {
            return 0;
        }

        uint8 assetDecimals = _getAssetDecimals(asset, tokenId);
        uint8 shareDecimals = IERC20Metadata(shareToken).decimals();

        return PricingLib.convertWithReciprocalPrice(
            assetAmount, assetDecimals, shareDecimals, priceAssetPerShare_, rounding
        ).toUint128();
    }

    /// @dev Converts the given asset amount to share amount. Returned value is in share decimals.
    /// @dev Assumes handling of zero denominator price (pricePoolPerShare) by consumer.
    function assetToShareAmount(
        address shareToken,
        address asset,
        uint256 tokenId,
        uint128 assetAmount,
        D18 pricePoolPerAsset,
        D18 pricePoolPerShare,
        MathLib.Rounding rounding
    ) internal view returns (uint128 shares) {
        if (assetAmount == 0 || pricePoolPerAsset.isZero()) {
            return 0;
        }

        uint8 assetDecimals = _getAssetDecimals(asset, tokenId);
        uint8 shareDecimals = IERC20Metadata(shareToken).decimals();

        return PricingLib.assetToShareAmount(
            assetAmount, assetDecimals, shareDecimals, pricePoolPerAsset, pricePoolPerShare, rounding
        ).toUint128();
    }

    /// @dev Converts the given share amount to asset amount. Returned value is in share decimals.
    ///
    ///      NOTE: MUST ONLY be used in AsyncRequestManager which rely on priceAssetPerShare that is derived from
    ///      Fulfilled*  message amounts. Any other codepath must use the variant with pricePoolPerAsset and
    ///      pricePoolPerShare
    function shareToAssetAmount(
        address shareToken,
        uint128 shareAmount,
        address asset,
        uint256 tokenId,
        D18 priceAssetPerShare_,
        MathLib.Rounding rounding
    ) internal view returns (uint128 assets) {
        if (shareAmount == 0 || priceAssetPerShare_.isZero()) {
            return 0;
        }

        uint8 assetDecimals = _getAssetDecimals(asset, tokenId);
        uint8 shareDecimals = IERC20Metadata(shareToken).decimals();

        return PricingLib.convertWithPrice(shareAmount, shareDecimals, assetDecimals, priceAssetPerShare_, rounding);
    }

    /// @dev Converts the given share amount to asset amount. Returned value is in share decimals.
    /// @dev Assumes handling of zero denominator price (pricePoolPerAsset) by consumer.
    function shareToAssetAmount(
        address shareToken,
        uint128 shareAmount,
        address asset,
        uint256 tokenId,
        D18 pricePoolPerShare,
        D18 pricePoolPerAsset,
        MathLib.Rounding rounding
    ) internal view returns (uint128 shares) {
        if (shareAmount == 0 || pricePoolPerShare.isZero()) {
            return 0;
        }

        uint8 assetDecimals = _getAssetDecimals(asset, tokenId);
        uint8 shareDecimals = IERC20Metadata(shareToken).decimals();

        // NOTE: Pool and share denomination are always equal by design
        return PricingLib.shareToAssetAmount(
            shareAmount, shareDecimals, assetDecimals, pricePoolPerShare, pricePoolPerAsset, rounding
        ).toUint128();
    }

    /// @dev Calculates the asset price per share returns the value in price decimals
    ///      Denominated in ASSET_UNIT/SHARE_UNIT
    /// @dev Assumes handling of zero denominator (shares == 0) by consumer.
    function calculatePriceAssetPerShare(
        address shareToken,
        uint128 shares,
        address asset,
        uint256 tokenId,
        uint128 assets,
        MathLib.Rounding rounding
    ) internal view returns (D18 priceAssetPerShare_) {
        if (assets == 0) {
            return d18(0);
        }

        uint8 assetDecimals = _getAssetDecimals(asset, tokenId);
        uint8 shareDecimals = IERC20Metadata(shareToken).decimals();

        // NOTE: More precise than utilizing D18
        return d18(
            _toPriceDecimals(assets, assetDecimals).mulDiv(
                10 ** PRICE_DECIMALS, _toPriceDecimals(shares, shareDecimals), rounding
            ).toUint128()
        );
    }

    //----------------------------------------------------------------------------------------------
    // Pure methods
    //----------------------------------------------------------------------------------------------

    /// @dev Converts an amount using decimals and price with implicit rounding down
    function convertWithPrice(uint256 baseAmount, uint8 baseDecimals, uint8 quoteDecimals, D18 priceQuotePerBase)
        internal
        pure
        returns (uint128 quoteAmount)
    {
        return convertWithPrice(baseAmount, baseDecimals, quoteDecimals, priceQuotePerBase, MathLib.Rounding.Down);
    }

    /// @dev Converts an amount using decimals and price with explicit rounding.
    function convertWithPrice(
        uint256 baseAmount,
        uint8 baseDecimals,
        uint8 quoteDecimals,
        D18 priceQuotePerBase,
        MathLib.Rounding rounding
    ) internal pure returns (uint128 quoteAmount) {
        if (baseDecimals == quoteDecimals) {
            return priceQuotePerBase.mulUint256(baseAmount, rounding).toUint128();
        }

        return MathLib.mulDiv(
            priceQuotePerBase.raw(), baseAmount * 10 ** quoteDecimals, 10 ** (baseDecimals + PRICE_DECIMALS), rounding
        ) // cancel out exponentiation from D18 multiplication
            .toUint128();
    }

    /// @dev Converts an amount using decimals and reciprocal price.
    /// @dev Assumes handling of zero denominator price (priceBasePerQuote) by consumer.
    ///
    ///      NOTE: More precise than convertWithPrice(,,,price.reciprocal,)
    function convertWithReciprocalPrice(
        uint256 baseAmount,
        uint8 baseDecimals,
        uint8 quoteDecimals,
        D18 priceBasePerQuote,
        MathLib.Rounding rounding
    ) internal pure returns (uint128 quoteAmount) {
        require(priceBasePerQuote.isNotZero(), "PricingLib/division-by-zero");

        if (baseDecimals == quoteDecimals) {
            return priceBasePerQuote.reciprocalMulUint256(baseAmount, rounding).toUint128();
        }

        return MathLib.mulDiv(
            10 ** quoteDecimals * baseAmount,
            10 ** PRICE_DECIMALS,
            10 ** baseDecimals * priceBasePerQuote.raw(),
            rounding
        ).toUint128();
    }

    /// @dev Converts an amount using decimals and two prices.
    /// @dev Assumes handling of zero denominator price (priceDenominator) by consumer.
    ///
    ///      NOTE: More precise than custom math with one price and convertWith{Reciprocal}Price for the other
    function convertWithPrices(
        uint256 baseAmount,
        uint8 baseDecimals,
        uint8 quoteDecimals,
        D18 priceNumerator,
        D18 priceDenominator,
        MathLib.Rounding rounding
    ) internal pure returns (uint128 quoteAmount) {
        require(priceDenominator.isNotZero(), "PricingLib/division-by-zero");

        return MathLib.mulDiv(
            priceNumerator.raw(),
            10 ** quoteDecimals * baseAmount,
            10 ** baseDecimals * priceDenominator.raw(),
            rounding
        ).toUint128();
    }

    /// @dev Converts asset amount to share amount.
    /// @dev Assumes handling of zero denominator price (pricePoolPerShare) by consumer.
    ///
    ///      NOTE: Pool and share denomination are always equal by design
    function assetToShareAmount(
        uint256 assetAmount,
        uint8 assetDecimals,
        uint8 shareDecimals,
        D18 pricePoolPerAsset,
        D18 pricePoolPerShare,
        MathLib.Rounding rounding
    ) internal pure returns (uint128 shareAmount) {
        return
            convertWithPrices(assetAmount, assetDecimals, shareDecimals, pricePoolPerAsset, pricePoolPerShare, rounding);
    }

    /// @dev Converts share amount to asset asset amount.
    /// @dev Assumes handling of zero denominator price (pricePoolPerAsset) by consumer.
    ///
    ///      NOTE: Pool and share denomination are always equal by design
    function shareToAssetAmount(
        uint256 shareAmount,
        uint8 shareDecimals,
        uint8 assetDecimals,
        D18 pricePoolPerShare,
        D18 pricePoolPerAsset,
        MathLib.Rounding rounding
    ) internal pure returns (uint128 assetAmount) {
        return
            convertWithPrices(shareAmount, shareDecimals, assetDecimals, pricePoolPerShare, pricePoolPerAsset, rounding);
    }

    /// @dev Converts pool amount to asset amount.
    /// @dev Assumes handling of zero denominator price (pricePoolPerAsset) by consumer.
    function poolToAssetAmount(
        uint256 poolAmount,
        uint8 poolDecimals,
        uint8 assetDecimals,
        D18 pricePoolPerAsset,
        MathLib.Rounding rounding
    ) internal pure returns (uint128 assetAmount) {
        return convertWithReciprocalPrice(poolAmount, poolDecimals, assetDecimals, pricePoolPerAsset, rounding);
    }

    /// @dev Converts asset amount to pool amount.
    function assetToPoolAmount(
        uint128 assetAmount,
        uint8 assetDecimals,
        uint8 poolDecimals,
        D18 pricePoolPerAsset,
        MathLib.Rounding rounding
    ) internal pure returns (uint128 poolAmount) {
        return convertWithPrice(assetAmount, assetDecimals, poolDecimals, pricePoolPerAsset, rounding);
    }

    /// @dev Returns the asset price per share denominated in ASSET_UNIT/SHARE_UNIT
    /// @dev Assumes handling of zero denominator price (pricePoolPerAsset) by consumer.
    ///
    ///      NOTE: Should never be used for calculating amounts due to precision loss. Instead, please refer to
    ///      conversion relying on pricePoolPerShare and pricePoolPerAsset.
    function priceAssetPerShare(D18 pricePoolPerShare, D18 pricePoolPerAsset)
        internal
        pure
        returns (D18 priceAssetPerShare_)
    {
        return pricePoolPerShare / pricePoolPerAsset;
    }

    //----------------------------------------------------------------------------------------------
    // Private methods
    //----------------------------------------------------------------------------------------------

    /// @dev Returns the asset decimals
    function _getAssetDecimals(address asset, uint256 tokenId) private view returns (uint8 assetDecimals) {
        return tokenId == 0 ? IERC20Metadata(asset).decimals() : IERC6909MetadataExt(asset).decimals(tokenId);
    }

    /// @dev When converting assets to shares using the price, all values are normalized to PRICE_DECIMALS
    ///      NOTE: We require all assets to have 2 <= decimals <= 18, see `Spoke.registerAsset`
    function _toPriceDecimals(uint128 _value, uint8 decimals) private pure returns (uint256) {
        if (PRICE_DECIMALS == decimals) return uint256(_value);
        return uint256(_value) * 10 ** (PRICE_DECIMALS - decimals);
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

import {PoolId} from "src/common/types/PoolId.sol";

type ShareClassId is bytes16;

function isNull(ShareClassId scId) pure returns (bool) {
    return ShareClassId.unwrap(scId) == 0;
}

function equals(ShareClassId left, ShareClassId right) pure returns (bool) {
    return ShareClassId.unwrap(left) == ShareClassId.unwrap(right);
}

function raw(ShareClassId scId) pure returns (bytes16) {
    return ShareClassId.unwrap(scId);
}

function newShareClassId(PoolId poolId, uint32 index) pure returns (ShareClassId scId) {
    return ShareClassId.wrap(bytes16((uint128(PoolId.unwrap(poolId)) << 64) + index));
}

using {isNull, raw, equals as ==} for ShareClassId global;

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import {IERC6909Decimals} from "src/misc/interfaces/IERC6909.sol";

import {PoolId} from "src/common/types/PoolId.sol";
import {AssetId} from "src/common/types/AssetId.sol";

interface IHubRegistry is IERC6909Decimals {
    event NewAsset(AssetId indexed assetId, uint8 decimals);
    event NewPool(PoolId poolId, address indexed manager, AssetId indexed currency);
    event UpdateManager(PoolId indexed poolId, address indexed manager, bool canManage);
    event SetMetadata(PoolId indexed poolId, bytes metadata);
    event UpdateDependency(bytes32 indexed what, address dependency);
    event UpdateCurrency(PoolId indexed poolId, AssetId currency);

    error NonExistingPool(PoolId id);
    error AssetAlreadyRegistered();
    error PoolAlreadyRegistered();
    error EmptyAccount();
    error EmptyCurrency();
    error EmptyShareClassManager();
    error AssetNotFound();

    /// @notice Register a new asset.
    function registerAsset(AssetId assetId, uint8 decimals_) external;

    /// @notice Register a new pool.
    function registerPool(PoolId poolId, address manager, AssetId currency) external;

    /// @notice allow/disallow an address as a manager for the pool
    function updateManager(PoolId poolId, address newManager, bool canManage) external;

    /// @notice sets metadata for this pool
    function setMetadata(PoolId poolId, bytes calldata metadata) external;

    /// @notice updates a dependency of the system
    function updateDependency(bytes32 what, address dependency) external;

    /// @notice updates the currency of the pool
    function updateCurrency(PoolId poolId, AssetId currency) external;

    /// @notice returns the metadata attached to the pool, if any.
    function metadata(PoolId poolId) external view returns (bytes memory);

    /// @notice returns the currency of the pool
    function currency(PoolId poolId) external view returns (AssetId);

    /// @notice returns the dependency used in the system
    function dependency(bytes32 what) external view returns (address);

    /// @notice returns whether the account is a manager
    function manager(PoolId poolId, address who) external view returns (bool);

    /// @notice compute a pool ID given an ID postfix
    function poolId(uint16 centrifugeId, uint48 postfix) external view returns (PoolId poolId);

    /// @notice returns the decimals for an asset
    function decimals(AssetId assetId) external view returns (uint8);

    /// @notice returns the decimals for a pool
    function decimals(PoolId poolId) external view returns (uint8);

    /// @notice checks the existence of a pool
    function exists(PoolId poolId) external view returns (bool);

    /// @notice checks the existence of an asset
    function isRegistered(AssetId assetId) external view returns (bool);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import {D18} from "src/misc/types/D18.sol";

import {PoolId} from "src/common/types/PoolId.sol";
import {AssetId} from "src/common/types/AssetId.sol";
import {ShareClassId} from "src/common/types/ShareClassId.sol";

struct EpochRedeemAmounts {
    /// @dev Amount of shares pending to be redeemed at time of epoch
    uint128 pendingShareAmount;
    /// @dev Total approved amount of redeemed share class tokens
    uint128 approvedShareAmount;
    /// @dev Total asset amount of revoked share class tokens
    uint128 payoutAssetAmount;
    /// @dev The amount of pool currency per unit of asset at time of approval
    D18 pricePoolPerAsset;
    /// @dev The amount of pool currency per unit of share at time of revocation
    D18 navPoolPerShare;
    /// @dev block timestamp when shares of epoch were revoked
    uint64 revokedAt;
}

struct EpochInvestAmounts {
    /// @dev Total pending asset amount of deposit asset at time of epoch
    uint128 pendingAssetAmount;
    /// @dev Total approved asset amount of deposit asset
    uint128 approvedAssetAmount;
    /// @dev Total approved pool amount of deposit asset
    uint128 approvedPoolAmount;
    /// @dev The amount of pool currency per unit of asset at time of approval
    D18 pricePoolPerAsset;
    /// @dev The amount of pool currency per unit of share at time of issuance
    D18 navPoolPerShare;
    /// @dev block timestamp when shares of epoch were issued
    uint64 issuedAt;
}

struct UserOrder {
    /// @dev Pending amount in deposit asset denomination
    uint128 pending;
    /// @dev Index of epoch in which last order was made
    uint32 lastUpdate;
}

struct ShareClassMetadata {
    /// @dev The name of the share class token
    string name;
    /// @dev The symbol of the share class token
    string symbol;
    /// @dev The salt of the share class token
    bytes32 salt;
}

struct ShareClassMetrics {
    /// @dev Total number of shares
    uint128 totalIssuance;
    /// @dev The latest net asset value per share class token
    D18 navPerShare;
}

struct QueuedOrder {
    /// @dev Whether the user requested a cancellation which is now queued
    bool isCancelling;
    /// @dev The queued increased request amount
    uint128 amount;
}

enum RequestType {
    /// @dev Whether the request is a deposit one
    Deposit,
    /// @dev Whether the request is a redeem one
    Redeem
}

struct EpochId {
    uint32 deposit;
    uint32 redeem;
    uint32 issue;
    uint32 revoke;
}

interface IShareClassManager {
    /// Events
    event AddShareClass(
        PoolId indexed poolId, ShareClassId indexed scId, uint32 indexed index, string name, string symbol, bytes32 salt
    );
    event UpdateMetadata(PoolId indexed poolId, ShareClassId indexed scId, string name, string symbol);
    event ApproveDeposits(
        PoolId indexed poolId,
        ShareClassId indexed scId,
        AssetId indexed depositAssetId,
        uint32 epoch,
        uint128 approvedPoolAmount,
        uint128 approvedAssetAmount,
        uint128 pendingAssetAmount
    );
    event ApproveRedeems(
        PoolId indexed poolId,
        ShareClassId indexed scId,
        AssetId indexed payoutAssetId,
        uint32 epoch,
        uint128 approvedShareAmount,
        uint128 pendingShareAmount
    );
    event IssueShares(
        PoolId indexed poolId,
        ShareClassId indexed scId,
        AssetId indexed depositAssetId,
        uint32 epoch,
        D18 navPoolPerShare,
        D18 navAssetPerShare,
        uint128 issuedShareAmount
    );
    event RemoteIssueShares(
        uint16 centrifugeId, PoolId indexed poolId, ShareClassId indexed scId, uint128 issuedShareAmount
    );
    event RevokeShares(
        PoolId indexed poolId,
        ShareClassId indexed scId,
        AssetId indexed payoutAssetId,
        uint32 epoch,
        D18 navPoolPerShare,
        D18 navAssetPerShare,
        uint128 revokedShareAmount,
        uint128 revokedAssetAmount,
        uint128 revokedPoolAmount
    );
    event RemoteRevokeShares(
        uint16 centrifugeId, PoolId indexed poolId, ShareClassId indexed scId, uint128 revokedShareAmount
    );
    event ClaimDeposit(
        PoolId indexed poolId,
        ShareClassId indexed scId,
        uint32 epoch,
        bytes32 investor,
        AssetId indexed depositAssetId,
        uint128 paymentAssetAmount,
        uint128 pendingAssetAmount,
        uint128 claimedShareAmount,
        uint64 issuedAt
    );
    event ClaimRedeem(
        PoolId indexed poolId,
        ShareClassId indexed scId,
        uint32 epoch,
        bytes32 investor,
        AssetId indexed payoutAssetId,
        uint128 paymentShareAmount,
        uint128 pendingShareAmount,
        uint128 claimedAssetAmount,
        uint64 revokedAt
    );
    event AddShareClass(PoolId indexed poolId, ShareClassId indexed scId, uint32 indexed index);
    event UpdateShareClass(PoolId indexed poolId, ShareClassId indexed scId, D18 navPoolPerShare);
    event UpdateDepositRequest(
        PoolId indexed poolId,
        ShareClassId indexed scId,
        AssetId indexed depositAssetId,
        uint32 epoch,
        bytes32 investor,
        uint128 pendingUserAssetAmount,
        uint128 pendingTotalAssetAmount,
        uint128 queuedUserAssetAmount,
        bool pendingCancellation
    );
    event UpdateRedeemRequest(
        PoolId indexed poolId,
        ShareClassId indexed scId,
        AssetId indexed payoutAssetId,
        uint32 epoch,
        bytes32 investor,
        uint128 pendingUserShareAmount,
        uint128 pendingTotalShareAmount,
        uint128 queuedUserShareAmount,
        bool pendingCancellation
    );

    /// Errors
    error EpochNotInSequence(uint32 providedEpoch, uint32 nowEpoch);
    error NoOrderFound();
    error InsufficientPending();
    error ApprovalRequired();
    error IssuanceRequired();
    error AlreadyIssued();
    error RevocationRequired();
    error ZeroApprovalAmount();
    error InvalidMetadataSize();
    error InvalidMetadataName();
    error InvalidMetadataSymbol();
    error InvalidSalt();
    error AlreadyUsedSalt();
    error RevokeMoreThanIssued();
    error PoolMissing();
    error ShareClassNotFound();
    error EpochNotFound();
    error DecreaseMoreThanIssued();
    error CancellationQueued();
    error CancellationInitializationRequired();

    /// Functions

    /// @notice Creates or updates a request to deposit (exchange) an asset amount for share class tokens.
    ///
    /// @param poolId Identifier of the pool
    /// @param scId Identifier of the share class
    /// @param amount Asset token amount which is deposited
    /// @param investor Centrifuge Vault address of the entity which is depositing
    /// @param depositAssetId Identifier of the asset which the investor used for their deposit request
    function requestDeposit(PoolId poolId, ShareClassId scId, uint128 amount, bytes32 investor, AssetId depositAssetId)
        external;

    /// @notice Cancels a pending deposit request.
    ///
    /// @param poolId Identifier of the pool
    /// @param scId Identifier of the share class
    /// @param investor Centrifuge Vault address of the entity which is cancelling
    /// @param depositAssetId Identifier of the asset which the investor used for their deposit request
    /// @return cancelledAssetAmount The deposit amount which was previously pending and is now cancelled. This amount
    /// was not potentially (partially) swapped to the pool amount in case the deposit asset cannot be exchanged 1:1
    /// into the pool token
    function cancelDepositRequest(PoolId poolId, ShareClassId scId, bytes32 investor, AssetId depositAssetId)
        external
        returns (uint128 cancelledAssetAmount);

    /// @notice Force cancels a pending deposit request.
    /// Only allowed if the user has cancelled a request at least once before. This is to protect against cancelling a
    /// request of a smart contract user that does not support the cancellation interface, and would thus be unable to
    /// claim back the cancelled assets.
    ///
    /// @param poolId Identifier of the pool
    /// @param scId Identifier of the share class
    /// @param investor Centrifuge Vault address of the entity which is cancelling
    /// @param depositAssetId Identifier of the asset which the investor used for their deposit request
    /// @return cancelledAssetAmount The deposit amount which was previously pending and is now cancelled with force
    function forceCancelDepositRequest(PoolId poolId, ShareClassId scId, bytes32 investor, AssetId depositAssetId)
        external
        returns (uint128 cancelledAssetAmount);

    /// @notice Creates or updates a request to redeem (exchange) share class tokens for some asset.
    ///
    /// @param poolId Identifier of the pool
    /// @param scId Identifier of the share class
    /// @param amount Share class token amount which should be redeemed
    /// @param investor Centrifuge Vault address of the entity which is redeeming
    /// @param payoutAssetId Identifier of the asset which the investor eventually receives back for their redeemed
    /// share class tokens
    function requestRedeem(PoolId poolId, ShareClassId scId, uint128 amount, bytes32 investor, AssetId payoutAssetId)
        external;

    /// @notice Cancels a pending redeem request.
    ///
    /// @param poolId Identifier of the pool
    /// @param scId Identifier of the share class
    /// @param investor Centrifuge Vault address of the entity which is cancelling
    /// @param payoutAssetId Identifier of the asset which the investor eventually receives back for their redeemed
    /// share class tokens
    /// @return cancelledShareAmount The redeem amount which was previously pending and is now cancelled
    function cancelRedeemRequest(PoolId poolId, ShareClassId scId, bytes32 investor, AssetId payoutAssetId)
        external
        returns (uint128 cancelledShareAmount);

    /// @notice Force cancels a pending redeem request.
    /// Only allowed if the user has cancelled a request at least once before. This is to protect against cancelling a
    /// request of a smart contract user that does not support the cancellation interface, and would thus be unable to
    /// claim back the cancelled share class tokens.
    ///
    /// @param poolId Identifier of the pool
    /// @param scId Identifier of the share class
    /// @param investor Centrifuge Vault address of the entity which is cancelling
    /// @param payoutAssetId Identifier of the asset which the investor eventually receives back for their redeemed
    /// share class tokens
    /// @return cancelledShareAmount The redeem amount which was previously pending and is now cancelled with force
    function forceCancelRedeemRequest(PoolId poolId, ShareClassId scId, bytes32 investor, AssetId payoutAssetId)
        external
        returns (uint128 cancelledShareAmount);

    /// @notice Approves an asset amount of all deposit requests for the given triplet of pool id, share class id and
    /// deposit asset id.
    /// @dev nowDepositEpochId MUST be called sequentially.
    ///
    /// @param poolId Identifier of the pool
    /// @param scId Identifier of the share class
    /// @param depositAssetId Identifier of the asset locked for the deposit request
    /// @param nowDepositEpochId The epoch for which shares will be approved.
    /// @param approvedAssetAmount Amount of assets that will be approved for deposit
    /// @param pricePoolPerAsset Amount of pool unit one gets for a unit of asset
    /// @return pendingAssetAmount Amount of assets still pending for deposit
    /// @return approvedPoolAmount  Amount of pool units approved for deposit
    function approveDeposits(
        PoolId poolId,
        ShareClassId scId,
        AssetId depositAssetId,
        uint32 nowDepositEpochId,
        uint128 approvedAssetAmount,
        D18 pricePoolPerAsset
    ) external returns (uint128 pendingAssetAmount, uint128 approvedPoolAmount);

    /// @notice Approves a share class token amount of all redeem requests for the given triplet of pool id, share class
    /// id and payout asset id.
    /// @dev nowRedeemEpochId MUST be called sequentially.
    ///
    /// @param poolId Identifier of the pool
    /// @param scId Identifier of the share class
    /// @param payoutAssetId Identifier of the asset for which all requests want to exchange their share class tokens
    /// for
    /// @param nowRedeemEpochId The epoch for which shares will be approved.
    /// @param approvedShareAmount Amount of shares that will be approved for redemption
    /// @param pricePoolPerAsset Amount of pool unit one gets for a unit of asset
    /// @return pendingShareAmount Sum of redemption request amounts in share class token amount which was not approved
    function approveRedeems(
        PoolId poolId,
        ShareClassId scId,
        AssetId payoutAssetId,
        uint32 nowRedeemEpochId,
        uint128 approvedShareAmount,
        D18 pricePoolPerAsset
    ) external returns (uint128 pendingShareAmount);

    /// @notice Emits new shares for the given identifier based on the provided NAV per share.
    /// @dev nowIssueEpochId MUST be called sequentially.
    ///
    /// @param poolId Identifier of the pool
    /// @param scId Identifier of the share class
    /// @param depositAssetId Identifier of the deposit asset for which shares should be issued
    /// @param nowIssueEpochId The epoch for which shares will be issued.
    /// @param navPoolPerShare The nav per share value of the share class (in the pool currency denomination. Conversion
    /// to asset price is done onchain based on the valuation of the asset at approval)
    /// @return issuedShareAmount Amount of shares that have been issued
    function issueShares(
        PoolId poolId,
        ShareClassId scId,
        AssetId depositAssetId,
        uint32 nowIssueEpochId,
        D18 navPoolPerShare
    ) external returns (uint128 issuedShareAmount, uint128 depositAssetAmount, uint128 depositPoolAmount);

    /// @notice Take back shares for the given identifier based on the provided NAV per share.
    /// @dev nowRevokeEpochId MUST be called sequentially.
    ///
    /// @param poolId Identifier of the pool
    /// @param scId Identifier of the share class
    /// @param payoutAssetId Identifier of the payout asset
    /// @param nowRevokeEpochId The epoch for which shares will be revoked.
    /// @param navPoolPerShare The nav per share value of the share class (in the pool currency denomination. Conversion
    /// to asset price is done onchain based on the valuation of the asset at approval)
    /// @return revokedShareAmount Amount of shares that have been revoked
    /// @return payoutAssetAmount Converted amount of payout asset based on number of revoked shares
    /// @return payoutPoolAmount Converted amount of pool currency based on number of revoked shares
    function revokeShares(
        PoolId poolId,
        ShareClassId scId,
        AssetId payoutAssetId,
        uint32 nowRevokeEpochId,
        D18 navPoolPerShare
    ) external returns (uint128 revokedShareAmount, uint128 payoutAssetAmount, uint128 payoutPoolAmount);

    /// @notice Collects shares for an investor after their deposit request was (partially) approved and new shares were
    /// issued.
    ///
    /// @param poolId Identifier of the pool
    /// @param scId Identifier of the share class
    /// @param investor Centrifuge Vault address of the recipient of the claimed share class tokens
    /// @param depositAssetId Identifier of the asset which the investor used for their deposit request
    /// @return payoutShareAmount Amount of shares which the investor receives
    /// @return paymentAssetAmount Amount of deposit asset which was taken as payment
    /// @return cancelledAssetAmount Amount of deposit asset which was cancelled due to being queued
    /// @return canClaimAgain Whether another call to claimRedeem is needed until investor has fully claimed investments
    function claimDeposit(PoolId poolId, ShareClassId scId, bytes32 investor, AssetId depositAssetId)
        external
        returns (
            uint128 payoutShareAmount,
            uint128 paymentAssetAmount,
            uint128 cancelledAssetAmount,
            bool canClaimAgain
        );

    /// @notice Collects an asset amount for an investor after their redeem request was (partially) approved and shares
    /// were revoked.
    ///
    /// @param poolId Identifier of the pool
    /// @param scId Identifier of the share class
    /// @param investor Centrifuge Vault address of the recipient of the claimed asset amount
    /// @param payoutAssetId Identifier of the asset which the investor requested to receive back for their redeemed
    /// shares
    /// @return payoutAssetAmount Amount of payout amount which the investor receives
    /// @return paymentShareAmount Amount of shares which the investor redeemed
    /// @return cancelledShareAmount Amount of shares which were cancelled due to being queued
    /// @return canClaimAgain Whether another call to claimRedeem is needed until investor has fully claimed redemptions
    function claimRedeem(PoolId poolId, ShareClassId scId, bytes32 investor, AssetId payoutAssetId)
        external
        returns (
            uint128 payoutAssetAmount,
            uint128 paymentShareAmount,
            uint128 cancelledShareAmount,
            bool canClaimAgain
        );

    /// @notice Update the share class issuance
    ///
    /// @param centrifugeId Identifier of the chain
    /// @param poolId Identifier of the pool
    /// @param scId Identifier of the share class
    /// @param amount The amount to increase the share class issuance by
    /// @param isIssuance Whether it is an issuance or revocation
    function updateShares(uint16 centrifugeId, PoolId poolId, ShareClassId scId, uint128 amount, bool isIssuance)
        external;

    /// @notice Adds a new share class to the given pool.
    ///
    /// @param poolId Identifier of the pool
    /// @param name The name of the share class
    /// @param symbol The symbol of the share class
    /// @param salt The salt used for deploying the share class tokens
    /// @return scId Identifier of the newly added share class
    function addShareClass(PoolId poolId, string calldata name, string calldata symbol, bytes32 salt)
        external
        returns (ShareClassId scId);

    /// @notice Updates the price pool unit per share unit of a share class
    ///
    /// @param poolId Identifier of the pool
    /// @param scId Identifier of the share class
    /// @param pricePoolPerShare The price per share of the share class (in the pool currency denomination)
    function updateSharePrice(PoolId poolId, ShareClassId scId, D18 pricePoolPerShare) external;

    /// @notice Updates the metadata of a share class.
    ///
    /// @param poolId Identifier of the pool
    /// @param scId Identifier of the share class
    /// @param name The name of the share class
    /// @param symbol The symbol of the share class
    function updateMetadata(PoolId poolId, ShareClassId scId, string calldata name, string calldata symbol) external;

    /// @notice Returns the number of share classes for the given pool
    ///
    /// @param poolId Identifier of the pool in question
    /// @return count Number of share classes for the given pool
    function shareClassCount(PoolId poolId) external view returns (uint32 count);

    /// @notice Checks the existence of a share class.
    ///
    /// @param poolId Identifier of the pool
    /// @param scId Identifier of the share class
    function exists(PoolId poolId, ShareClassId scId) external view returns (bool);

    /// @notice Returns the current ongoing epoch id for deposits
    ///
    /// @param scId Identifier of the share class
    /// @param depositAssetId AssetId of the payment asset
    function nowDepositEpoch(ShareClassId scId, AssetId depositAssetId) external view returns (uint32);

    /// @notice Returns the epoch for which will be issued next
    ///
    /// @param scId Identifier of the share class
    /// @param depositAssetId AssetId of the payment asset
    function nowIssueEpoch(ShareClassId scId, AssetId depositAssetId) external view returns (uint32);

    /// @notice Returns the current ongoing epoch id for deposits
    ///
    /// @param scId Identifier of the share class
    /// @param payoutAssetId AssetId of the payment asset
    function nowRedeemEpoch(ShareClassId scId, AssetId payoutAssetId) external view returns (uint32);

    /// @notice Returns the epoch for which will be revoked next
    ///
    /// @param scId Identifier of the share class
    /// @param depositAssetId AssetId of the payment asset
    function nowRevokeEpoch(ShareClassId scId, AssetId depositAssetId) external view returns (uint32);

    /// @notice Returns an upper bound for possible calls to `function claimDeposit(..)`
    ///
    /// @param scId Identifier of the share class
    /// @param investor Recipient of the share class tokens
    /// @param depositAssetId AssetId of the payment asset
    function maxDepositClaims(ShareClassId scId, bytes32 investor, AssetId depositAssetId)
        external
        view
        returns (uint32 maxClaims);

    /// @notice Returns an upper bound for possible calls to `function claimRedeem(..)`
    ///
    /// @param scId Identifier of the share class
    /// @param investor Recipient of the payout assets
    /// @param payoutAssetId AssetId of the payout asset
    function maxRedeemClaims(ShareClassId scId, bytes32 investor, AssetId payoutAssetId)
        external
        view
        returns (uint32 maxClaims);

    /// @notice Exposes relevant metrics for a share class
    ///
    /// @return totalIssuance The total number of shares known to the Hub side
    /// @return pricePoolPerShare The amount of pool units per unit share
    function metrics(ShareClassId scId) external view returns (uint128 totalIssuance, D18 pricePoolPerShare);

    /// @notice Exposes issuance of a share class on a given network
    ///
    /// @param scId Identifier of the share class
    /// @param centrifugeId Identifier of the chain
    function issuance(ShareClassId scId, uint16 centrifugeId) external view returns (uint128);

    /// @notice Determines the next share class id for the given pool.
    ///
    /// @param poolId Identifier of the pool
    /// @return scId Identifier of the next share class
    function previewNextShareClassId(PoolId poolId) external view returns (ShareClassId scId);

    /// @notice Determines the share class id for the given pool and index.
    ///
    /// @param poolId Identifier of the pool
    /// @param index The pool-internal index of the share class id
    /// @return scId Identifier of the underlying share class
    function previewShareClassId(PoolId poolId, uint32 index) external pure returns (ShareClassId scId);

    /// @notice returns The metadata of the share class.
    ///
    /// @param scId Identifier of the share class
    /// @return name The registered name of the share class token
    /// @return symbol The registered symbol of the share class token
    /// @return salt The registered salt of the share class token, used for deterministic deployments
    function metadata(ShareClassId scId) external returns (string memory name, string memory symbol, bytes32 salt);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

interface IAuth {
    event Rely(address indexed user);
    event Deny(address indexed user);

    error NotAuthorized();

    /// @notice Returns whether the target is a ward (has admin access)
    function wards(address target) external view returns (uint256);

    /// @notice Make user a ward (give them admin access)
    function rely(address user) external;

    /// @notice Remove user as a ward (remove admin access)
    function deny(address user) external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title  IERC20
/// @dev    Interface of the ERC20 standard as defined in the EIP.
/// @author Modified from OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
interface IERC20 {
    error InvalidAddress();
    error InsufficientBalance();
    error InsufficientAllowance();

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

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

/**
 * @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 {
    error PermitExpired();
    error InvalidPermit();

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

interface IERC20Wrapper {
    /**
     * @dev Returns the address of the underlying ERC-20 token that is being wrapped.
     */
    function underlying() external view returns (address);

    /**
     * @dev Allow a user to deposit underlying tokens and mint the corresponding number of wrapped tokens.
     */
    function depositFor(address account, uint256 value) external returns (bool);

    /**
     * @dev Allow a user to burn a number of wrapped tokens and withdraw the corresponding number of underlying tokens.
     */
    function withdrawTo(address account, uint256 value) external returns (bool);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import {IERC165} from "forge-std/interfaces/IERC165.sol";

interface IERC6909 is IERC165 {
    error EmptyOwner();
    error EmptyAmount();
    error InvalidTokenId();
    error InsufficientBalance(address owner, uint256 tokenId);
    error InsufficientAllowance(address sender, uint256 tokenId);

    event OperatorSet(address indexed owner, address indexed operator, bool approved);
    event Approval(address indexed owner, address indexed spender, uint256 indexed tokenId, uint256 amount);
    event Transfer(address caller, address indexed from, address indexed to, uint256 indexed tokenId, uint256 amount);

    /// @notice           Owner balance of a tokenId.
    /// @param owner      The address of the owner.
    /// @param tokenId    The id of the token.
    /// @return amount    The balance of the token.
    function balanceOf(address owner, uint256 tokenId) external view returns (uint256 amount);

    /// @notice           Spender allowance of a tokenId.
    /// @param owner      The address of the owner.
    /// @param spender    The address of the spender.
    /// @param tokenId    The id of the token.
    /// @return amount    The allowance of the token.
    function allowance(address owner, address spender, uint256 tokenId) external view returns (uint256 amount);

    /// @notice           Checks if a spender is approved by an owner as an operator.
    /// @param owner      The address of the owner.
    /// @param spender    The address of the spender.
    /// @return approved  The approval status.
    function isOperator(address owner, address spender) external view returns (bool approved);

    /// @notice           Transfers an amount of a tokenId from the caller to a receiver.
    /// @param receiver   The address of the receiver.
    /// @param tokenId    The id of the token.
    /// @param amount     The amount of the token.
    /// @return bool      True, always, unless the function reverts.
    function transfer(address receiver, uint256 tokenId, uint256 amount) external returns (bool);

    /// @notice           Transfers an amount of a tokenId from a sender to a receiver.
    /// @param sender     The address of the sender.
    /// @param receiver   The address of the receiver.
    /// @param tokenId    The id of the token.
    /// @param amount     The amount of the token.
    /// @return bool      True, always, unless the function reverts.
    function transferFrom(address sender, address receiver, uint256 tokenId, uint256 amount) external returns (bool);

    /// @notice           Approves an amount of a tokenId to a spender.
    /// @param spender    The address of the spender.
    /// @param tokenId    The id of the token.
    /// @param amount     The amount of the token.
    /// @return bool      True, always.
    function approve(address spender, uint256 tokenId, uint256 amount) external returns (bool);

    /// @notice           Sets or removes an operator for the caller.
    /// @param operator   The address of the operator.
    /// @param approved   The approval status.
    /// @return bool      True, always.
    function setOperator(address operator, bool approved) external returns (bool);
}

interface IERC6909URIExt {
    event TokenURISet(uint256 indexed tokenId, string uri);
    event ContractURISet(address indexed target, string uri);

    error EmptyURI();

    /// @return uri     Returns the common token URI.
    function contractURI() external view returns (string memory);

    /// @dev            Returns empty string if tokenId does not exist.
    ///                 MAY implemented to throw MissingURI(tokenId) error.
    /// @param tokenId  The token to query URI for.
    /// @return uri     A string representing the uri for the specific tokenId.
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

interface IERC6909NFT is IERC6909, IERC6909URIExt {
    error UnknownTokenId(address owner, uint256 tokenId);
    error LessThanMinimalDecimal(uint8 minimal, uint8 actual);

    /// @notice             Provide URI for a specific tokenId.
    /// @param tokenId      Token Id.
    /// @param URI          URI to a document defining the collection as a whole.
    function setTokenURI(uint256 tokenId, string memory URI) external;

    /// @dev                Optional method to set up the contract URI if needed.
    /// @param URI          URI to a document defining the collection as a whole.
    function setContractURI(string memory URI) external;

    /// @notice             Mint new tokens for a given owner and sets tokenURI.
    /// @dev                For non-fungible tokens, call with amount = 1, for fungible it could be any amount.
    ///                     TokenId is auto incremented by one.
    ///
    /// @param owner        Creates supply of a given tokenId by amount for owner.
    /// @param tokenURI     URI fortestBurningToken the newly minted token.
    /// @return tokenId     Id of the newly minted token.
    function mint(address owner, string memory tokenURI) external returns (uint256 tokenId);

    /// @notice             Destroy supply of a given tokenId by amount.
    /// @dev                The msg.sender MUST be the owner.
    ///
    /// @param tokenId      Item which have reduced supply.
    function burn(uint256 tokenId) external;
}

/// @notice Extension of ERC6909 Standard for tracking total supply
interface IERC6909TotalSupplyExt {
    /// @notice         The totalSupply for a token id.
    ///
    /// @param tokenId  Id of the token
    /// @return supply  Total supply for a given `tokenId`
    function totalSupply(uint256 tokenId) external returns (uint256 supply);
}

interface IERC6909Decimals {
    /// @notice             Used to retrieve the decimals of an asset
    /// @dev                address is used but the value corresponds to a AssetId
    function decimals(uint256 assetId) external view returns (uint8);
}

interface IERC6909MetadataExt is IERC6909Decimals {
    /// @notice             Used to retrieve the decimals of an asset
    /// @dev                address is used but the value corresponds to a AssetId
    function decimals(uint256 assetId) external view returns (uint8);

    /// @notice             Used to retrieve the name of an asset
    /// @dev                address is used but the value corresponds to a AssetId
    function name(uint256 assetId) external view returns (string memory);

    /// @notice             Used to retrieve the symbol of an asset
    /// @dev                address is used but the value corresponds to a AssetId
    function symbol(uint256 assetId) external view returns (string memory);
}

interface IERC6909Fungible is IERC6909 {
    /// @notice             Mint new tokens for a specific tokenid and assign them to an owner
    ///
    /// @param owner        Creates supply of a given `tokenId` by `amount` for owner.
    /// @param tokenId      Id of the item
    /// @param amount       Adds `amount` to the total supply of the given `tokenId`
    function mint(address owner, uint256 tokenId, uint256 amount) external;

    /// @notice             Destroy supply of a given tokenId by amount.
    /// @dev                The msg.sender MUST be the owner.
    ///
    /// @param owner        Owner of the `tokenId`
    /// @param tokenId      Id of the item.
    /// @param amount       Subtract `amount` from the total supply of the given `tokenId`
    function burn(address owner, uint256 tokenId, uint256 amount) external;

    /// @notice             Enforces a transfer from `spender` point of view.
    ///
    ///
    /// @param sender       The owner of the `tokenId`
    /// @param receiver     Address of the receiving party
    /// @param tokenId      Token Id
    /// @param amount       Amount to be transferred
    function authTransferFrom(address sender, address receiver, uint256 tokenId, uint256 amount)
        external
        returns (bool);
}

/// @dev  A factory contract to deploy new collateral contracts implementing IERC6909.
interface IERC6909Factory {
    /// Events
    event NewTokenDeployment(address indexed owner, address instance);

    /// @notice       Deploys new install of a contract that implements IERC6909.
    /// @dev          Factory should deploy deterministically if possible.
    ///
    /// @param owner  Owner of the deployed collateral contract which has initial full rights.
    /// @param salt   Used to make a deterministic deployment.
    /// @return       An address of the newly deployed contract.
    function deploy(address owner, bytes32 salt) external returns (address);

    /// @notice       Generates a new deterministic address based on the owner and the salt.
    ///
    /// @param owner  Owner of the deployed collateral contract which has initial full rights.
    /// @param salt   Used to make a deterministic deployment.
    /// @return       An address of the newly deployed contract.
    function previewAddress(address owner, bytes32 salt) external returns (address);
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2;

interface IERC165 {
    /// @notice Query if a contract implements an interface
    /// @param interfaceID The interface identifier, as specified in ERC-165
    /// @dev Interface identification is specified in ERC-165. This function
    /// uses less than 30,000 gas.
    /// @return `true` if the contract implements `interfaceID` and
    /// `interfaceID` is not 0xffffffff, `false` otherwise
    function supportsInterface(bytes4 interfaceID) external view returns (bool);
}

Settings
{
  "remappings": [
    "forge-std/=lib/forge-std/src/",
    "@chimera/=lib/chimera/src/",
    "createx-forge/=lib/createx-forge/",
    "chimera/=lib/chimera/src/",
    "ds-test/=lib/chimera/lib/forge-std/lib/ds-test/src/",
    "setup-helpers/=lib/setup-helpers/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"contract IHubRegistry","name":"hubRegistry_","type":"address"},{"internalType":"address","name":"deployer","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyIssued","type":"error"},{"inputs":[],"name":"AlreadyUsedSalt","type":"error"},{"inputs":[],"name":"ApprovalRequired","type":"error"},{"inputs":[],"name":"CancellationInitializationRequired","type":"error"},{"inputs":[],"name":"CancellationQueued","type":"error"},{"inputs":[],"name":"DecreaseMoreThanIssued","type":"error"},{"inputs":[],"name":"EpochNotFound","type":"error"},{"inputs":[{"internalType":"uint32","name":"providedEpoch","type":"uint32"},{"internalType":"uint32","name":"nowEpoch","type":"uint32"}],"name":"EpochNotInSequence","type":"error"},{"inputs":[],"name":"InsufficientPending","type":"error"},{"inputs":[],"name":"InvalidMetadataName","type":"error"},{"inputs":[],"name":"InvalidMetadataSize","type":"error"},{"inputs":[],"name":"InvalidMetadataSymbol","type":"error"},{"inputs":[],"name":"InvalidSalt","type":"error"},{"inputs":[],"name":"IssuanceRequired","type":"error"},{"inputs":[],"name":"MulDiv_Overflow","type":"error"},{"inputs":[],"name":"NoOrderFound","type":"error"},{"inputs":[],"name":"NotAuthorized","type":"error"},{"inputs":[],"name":"PoolMissing","type":"error"},{"inputs":[],"name":"RevocationRequired","type":"error"},{"inputs":[],"name":"RevokeMoreThanIssued","type":"error"},{"inputs":[],"name":"ShareClassNotFound","type":"error"},{"inputs":[],"name":"Uint128_Overflow","type":"error"},{"inputs":[],"name":"Uint64_Overflow","type":"error"},{"inputs":[],"name":"ZeroApprovalAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"PoolId","name":"poolId","type":"uint64"},{"indexed":true,"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"indexed":true,"internalType":"uint32","name":"index","type":"uint32"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"string","name":"symbol","type":"string"},{"indexed":false,"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"AddShareClass","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"PoolId","name":"poolId","type":"uint64"},{"indexed":true,"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"indexed":true,"internalType":"uint32","name":"index","type":"uint32"}],"name":"AddShareClass","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"PoolId","name":"poolId","type":"uint64"},{"indexed":true,"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"indexed":true,"internalType":"AssetId","name":"depositAssetId","type":"uint128"},{"indexed":false,"internalType":"uint32","name":"epoch","type":"uint32"},{"indexed":false,"internalType":"uint128","name":"approvedPoolAmount","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"approvedAssetAmount","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"pendingAssetAmount","type":"uint128"}],"name":"ApproveDeposits","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"PoolId","name":"poolId","type":"uint64"},{"indexed":true,"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"indexed":true,"internalType":"AssetId","name":"payoutAssetId","type":"uint128"},{"indexed":false,"internalType":"uint32","name":"epoch","type":"uint32"},{"indexed":false,"internalType":"uint128","name":"approvedShareAmount","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"pendingShareAmount","type":"uint128"}],"name":"ApproveRedeems","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"PoolId","name":"poolId","type":"uint64"},{"indexed":true,"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"indexed":false,"internalType":"uint32","name":"epoch","type":"uint32"},{"indexed":false,"internalType":"bytes32","name":"investor","type":"bytes32"},{"indexed":true,"internalType":"AssetId","name":"depositAssetId","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"paymentAssetAmount","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"pendingAssetAmount","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"claimedShareAmount","type":"uint128"},{"indexed":false,"internalType":"uint64","name":"issuedAt","type":"uint64"}],"name":"ClaimDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"PoolId","name":"poolId","type":"uint64"},{"indexed":true,"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"indexed":false,"internalType":"uint32","name":"epoch","type":"uint32"},{"indexed":false,"internalType":"bytes32","name":"investor","type":"bytes32"},{"indexed":true,"internalType":"AssetId","name":"payoutAssetId","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"paymentShareAmount","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"pendingShareAmount","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"claimedAssetAmount","type":"uint128"},{"indexed":false,"internalType":"uint64","name":"revokedAt","type":"uint64"}],"name":"ClaimRedeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"Deny","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"PoolId","name":"poolId","type":"uint64"},{"indexed":true,"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"indexed":true,"internalType":"AssetId","name":"depositAssetId","type":"uint128"},{"indexed":false,"internalType":"uint32","name":"epoch","type":"uint32"},{"indexed":false,"internalType":"D18","name":"navPoolPerShare","type":"uint128"},{"indexed":false,"internalType":"D18","name":"navAssetPerShare","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"issuedShareAmount","type":"uint128"}],"name":"IssueShares","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"Rely","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"centrifugeId","type":"uint16"},{"indexed":true,"internalType":"PoolId","name":"poolId","type":"uint64"},{"indexed":true,"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"indexed":false,"internalType":"uint128","name":"issuedShareAmount","type":"uint128"}],"name":"RemoteIssueShares","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"centrifugeId","type":"uint16"},{"indexed":true,"internalType":"PoolId","name":"poolId","type":"uint64"},{"indexed":true,"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"indexed":false,"internalType":"uint128","name":"revokedShareAmount","type":"uint128"}],"name":"RemoteRevokeShares","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"PoolId","name":"poolId","type":"uint64"},{"indexed":true,"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"indexed":true,"internalType":"AssetId","name":"payoutAssetId","type":"uint128"},{"indexed":false,"internalType":"uint32","name":"epoch","type":"uint32"},{"indexed":false,"internalType":"D18","name":"navPoolPerShare","type":"uint128"},{"indexed":false,"internalType":"D18","name":"navAssetPerShare","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"revokedShareAmount","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"revokedAssetAmount","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"revokedPoolAmount","type":"uint128"}],"name":"RevokeShares","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"PoolId","name":"poolId","type":"uint64"},{"indexed":true,"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"indexed":true,"internalType":"AssetId","name":"depositAssetId","type":"uint128"},{"indexed":false,"internalType":"uint32","name":"epoch","type":"uint32"},{"indexed":false,"internalType":"bytes32","name":"investor","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"pendingUserAssetAmount","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"pendingTotalAssetAmount","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"queuedUserAssetAmount","type":"uint128"},{"indexed":false,"internalType":"bool","name":"pendingCancellation","type":"bool"}],"name":"UpdateDepositRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"PoolId","name":"poolId","type":"uint64"},{"indexed":true,"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"string","name":"symbol","type":"string"}],"name":"UpdateMetadata","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"PoolId","name":"poolId","type":"uint64"},{"indexed":true,"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"indexed":true,"internalType":"AssetId","name":"payoutAssetId","type":"uint128"},{"indexed":false,"internalType":"uint32","name":"epoch","type":"uint32"},{"indexed":false,"internalType":"bytes32","name":"investor","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"pendingUserShareAmount","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"pendingTotalShareAmount","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"queuedUserShareAmount","type":"uint128"},{"indexed":false,"internalType":"bool","name":"pendingCancellation","type":"bool"}],"name":"UpdateRedeemRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"PoolId","name":"poolId","type":"uint64"},{"indexed":true,"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"indexed":false,"internalType":"D18","name":"navPoolPerShare","type":"uint128"}],"name":"UpdateShareClass","type":"event"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"uint64"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"addShareClass","outputs":[{"internalType":"ShareClassId","name":"scId_","type":"bytes16"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"internalType":"AssetId","name":"depositAssetId","type":"uint128"},{"internalType":"bytes32","name":"investor","type":"bytes32"}],"name":"allowForceDepositCancel","outputs":[{"internalType":"bool","name":"cancelled","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"internalType":"AssetId","name":"payoutAssetId","type":"uint128"},{"internalType":"bytes32","name":"investor","type":"bytes32"}],"name":"allowForceRedeemCancel","outputs":[{"internalType":"bool","name":"cancelled","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"uint64"},{"internalType":"ShareClassId","name":"scId_","type":"bytes16"},{"internalType":"AssetId","name":"depositAssetId","type":"uint128"},{"internalType":"uint32","name":"nowDepositEpochId","type":"uint32"},{"internalType":"uint128","name":"approvedAssetAmount","type":"uint128"},{"internalType":"D18","name":"pricePoolPerAsset","type":"uint128"}],"name":"approveDeposits","outputs":[{"internalType":"uint128","name":"pendingAssetAmount","type":"uint128"},{"internalType":"uint128","name":"approvedPoolAmount","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"uint64"},{"internalType":"ShareClassId","name":"scId_","type":"bytes16"},{"internalType":"AssetId","name":"payoutAssetId","type":"uint128"},{"internalType":"uint32","name":"nowRedeemEpochId","type":"uint32"},{"internalType":"uint128","name":"approvedShareAmount","type":"uint128"},{"internalType":"D18","name":"pricePoolPerAsset","type":"uint128"}],"name":"approveRedeems","outputs":[{"internalType":"uint128","name":"pendingShareAmount","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"uint64"},{"internalType":"ShareClassId","name":"scId_","type":"bytes16"},{"internalType":"bytes32","name":"investor","type":"bytes32"},{"internalType":"AssetId","name":"depositAssetId","type":"uint128"}],"name":"cancelDepositRequest","outputs":[{"internalType":"uint128","name":"cancelledAssetAmount","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"uint64"},{"internalType":"ShareClassId","name":"scId_","type":"bytes16"},{"internalType":"bytes32","name":"investor","type":"bytes32"},{"internalType":"AssetId","name":"payoutAssetId","type":"uint128"}],"name":"cancelRedeemRequest","outputs":[{"internalType":"uint128","name":"cancelledShareAmount","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"uint64"},{"internalType":"ShareClassId","name":"scId_","type":"bytes16"},{"internalType":"bytes32","name":"investor","type":"bytes32"},{"internalType":"AssetId","name":"depositAssetId","type":"uint128"}],"name":"claimDeposit","outputs":[{"internalType":"uint128","name":"payoutShareAmount","type":"uint128"},{"internalType":"uint128","name":"paymentAssetAmount","type":"uint128"},{"internalType":"uint128","name":"cancelledAssetAmount","type":"uint128"},{"internalType":"bool","name":"canClaimAgain","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"uint64"},{"internalType":"ShareClassId","name":"scId_","type":"bytes16"},{"internalType":"bytes32","name":"investor","type":"bytes32"},{"internalType":"AssetId","name":"payoutAssetId","type":"uint128"}],"name":"claimRedeem","outputs":[{"internalType":"uint128","name":"payoutAssetAmount","type":"uint128"},{"internalType":"uint128","name":"paymentShareAmount","type":"uint128"},{"internalType":"uint128","name":"cancelledShareAmount","type":"uint128"},{"internalType":"bool","name":"canClaimAgain","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"deny","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"internalType":"AssetId","name":"depositAssetId","type":"uint128"},{"internalType":"bytes32","name":"investor","type":"bytes32"}],"name":"depositRequest","outputs":[{"internalType":"uint128","name":"pending","type":"uint128"},{"internalType":"uint32","name":"lastUpdate","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"internalType":"AssetId","name":"assetId","type":"uint128"}],"name":"epochId","outputs":[{"internalType":"uint32","name":"deposit","type":"uint32"},{"internalType":"uint32","name":"redeem","type":"uint32"},{"internalType":"uint32","name":"issue","type":"uint32"},{"internalType":"uint32","name":"revoke","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"internalType":"AssetId","name":"assetId","type":"uint128"},{"internalType":"uint32","name":"epochId_","type":"uint32"}],"name":"epochInvestAmounts","outputs":[{"internalType":"uint128","name":"pendingAssetAmount","type":"uint128"},{"internalType":"uint128","name":"approvedAssetAmount","type":"uint128"},{"internalType":"uint128","name":"approvedPoolAmount","type":"uint128"},{"internalType":"D18","name":"pricePoolPerAsset","type":"uint128"},{"internalType":"D18","name":"navPoolPerShare","type":"uint128"},{"internalType":"uint64","name":"issuedAt","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"internalType":"AssetId","name":"assetId","type":"uint128"},{"internalType":"uint32","name":"epochId_","type":"uint32"}],"name":"epochRedeemAmounts","outputs":[{"internalType":"uint128","name":"pendingShareAmount","type":"uint128"},{"internalType":"uint128","name":"approvedShareAmount","type":"uint128"},{"internalType":"uint128","name":"payoutAssetAmount","type":"uint128"},{"internalType":"D18","name":"pricePoolPerAsset","type":"uint128"},{"internalType":"D18","name":"navPoolPerShare","type":"uint128"},{"internalType":"uint64","name":"revokedAt","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"uint64"},{"internalType":"ShareClassId","name":"scId_","type":"bytes16"}],"name":"exists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"uint64"},{"internalType":"ShareClassId","name":"scId_","type":"bytes16"},{"internalType":"bytes32","name":"investor","type":"bytes32"},{"internalType":"AssetId","name":"depositAssetId","type":"uint128"}],"name":"forceCancelDepositRequest","outputs":[{"internalType":"uint128","name":"cancelledAssetAmount","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"uint64"},{"internalType":"ShareClassId","name":"scId_","type":"bytes16"},{"internalType":"bytes32","name":"investor","type":"bytes32"},{"internalType":"AssetId","name":"payoutAssetId","type":"uint128"}],"name":"forceCancelRedeemRequest","outputs":[{"internalType":"uint128","name":"cancelledShareAmount","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"hubRegistry","outputs":[{"internalType":"contract IHubRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"internalType":"uint16","name":"centrifugeId","type":"uint16"}],"name":"issuance","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"uint64"},{"internalType":"ShareClassId","name":"scId_","type":"bytes16"},{"internalType":"AssetId","name":"depositAssetId","type":"uint128"},{"internalType":"uint32","name":"nowIssueEpochId","type":"uint32"},{"internalType":"D18","name":"navPoolPerShare","type":"uint128"}],"name":"issueShares","outputs":[{"internalType":"uint128","name":"issuedShareAmount","type":"uint128"},{"internalType":"uint128","name":"depositAssetAmount","type":"uint128"},{"internalType":"uint128","name":"depositPoolAmount","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"ShareClassId","name":"scId_","type":"bytes16"},{"internalType":"bytes32","name":"investor","type":"bytes32"},{"internalType":"AssetId","name":"depositAssetId","type":"uint128"}],"name":"maxDepositClaims","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"ShareClassId","name":"scId_","type":"bytes16"},{"internalType":"bytes32","name":"investor","type":"bytes32"},{"internalType":"AssetId","name":"payoutAssetId","type":"uint128"}],"name":"maxRedeemClaims","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"ShareClassId","name":"scId","type":"bytes16"}],"name":"metadata","outputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"ShareClassId","name":"scId","type":"bytes16"}],"name":"metrics","outputs":[{"internalType":"uint128","name":"totalIssuance","type":"uint128"},{"internalType":"D18","name":"navPerShare","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"ShareClassId","name":"scId_","type":"bytes16"},{"internalType":"AssetId","name":"depositAssetId","type":"uint128"}],"name":"nowDepositEpoch","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"ShareClassId","name":"scId_","type":"bytes16"},{"internalType":"AssetId","name":"depositAssetId","type":"uint128"}],"name":"nowIssueEpoch","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"ShareClassId","name":"scId_","type":"bytes16"},{"internalType":"AssetId","name":"depositAssetId","type":"uint128"}],"name":"nowRedeemEpoch","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"ShareClassId","name":"scId_","type":"bytes16"},{"internalType":"AssetId","name":"depositAssetId","type":"uint128"}],"name":"nowRevokeEpoch","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"internalType":"AssetId","name":"depositAssetId","type":"uint128"}],"name":"pendingDeposit","outputs":[{"internalType":"uint128","name":"pending","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"internalType":"AssetId","name":"payoutAssetId","type":"uint128"}],"name":"pendingRedeem","outputs":[{"internalType":"uint128","name":"pending","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"uint64"}],"name":"previewNextShareClassId","outputs":[{"internalType":"ShareClassId","name":"scId","type":"bytes16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"uint64"},{"internalType":"uint32","name":"index","type":"uint32"}],"name":"previewShareClassId","outputs":[{"internalType":"ShareClassId","name":"scId","type":"bytes16"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"internalType":"AssetId","name":"depositAssetId","type":"uint128"},{"internalType":"bytes32","name":"investor","type":"bytes32"}],"name":"queuedDepositRequest","outputs":[{"internalType":"bool","name":"isCancelling","type":"bool"},{"internalType":"uint128","name":"amount","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"internalType":"AssetId","name":"payoutAssetId","type":"uint128"},{"internalType":"bytes32","name":"investor","type":"bytes32"}],"name":"queuedRedeemRequest","outputs":[{"internalType":"bool","name":"isCancelling","type":"bool"},{"internalType":"uint128","name":"amount","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"ShareClassId","name":"scId","type":"bytes16"},{"internalType":"AssetId","name":"payoutAssetId","type":"uint128"},{"internalType":"bytes32","name":"investor","type":"bytes32"}],"name":"redeemRequest","outputs":[{"internalType":"uint128","name":"pending","type":"uint128"},{"internalType":"uint32","name":"lastUpdate","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"rely","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"uint64"},{"internalType":"ShareClassId","name":"scId_","type":"bytes16"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"bytes32","name":"investor","type":"bytes32"},{"internalType":"AssetId","name":"depositAssetId","type":"uint128"}],"name":"requestDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"uint64"},{"internalType":"ShareClassId","name":"scId_","type":"bytes16"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"bytes32","name":"investor","type":"bytes32"},{"internalType":"AssetId","name":"payoutAssetId","type":"uint128"}],"name":"requestRedeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"uint64"},{"internalType":"ShareClassId","name":"scId_","type":"bytes16"},{"internalType":"AssetId","name":"payoutAssetId","type":"uint128"},{"internalType":"uint32","name":"nowRevokeEpochId","type":"uint32"},{"internalType":"D18","name":"navPoolPerShare","type":"uint128"}],"name":"revokeShares","outputs":[{"internalType":"uint128","name":"revokedShareAmount","type":"uint128"},{"internalType":"uint128","name":"payoutAssetAmount","type":"uint128"},{"internalType":"uint128","name":"payoutPoolAmount","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"salts","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"uint64"}],"name":"shareClassCount","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"uint64"},{"internalType":"ShareClassId","name":"","type":"bytes16"}],"name":"shareClassIds","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"uint64"},{"internalType":"ShareClassId","name":"scId_","type":"bytes16"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"}],"name":"updateMetadata","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"uint64"},{"internalType":"ShareClassId","name":"scId_","type":"bytes16"},{"internalType":"D18","name":"navPoolPerShare","type":"uint128"}],"name":"updateSharePrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"centrifugeId","type":"uint16"},{"internalType":"PoolId","name":"poolId","type":"uint64"},{"internalType":"ShareClassId","name":"scId_","type":"bytes16"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"bool","name":"isIssuance","type":"bool"}],"name":"updateShares","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"wards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

60a060405234801561000f575f5ffd5b50604051614a8f380380614a8f83398101604081905261002e9161009b565b6001600160a01b0381165f8181526020819052604080822060019055518392917fdd0e34038ac38b2a1ce960229778ac48a8719bc900b6c4f8d0475c6e8b385a6091a250506001600160a01b03166080526100d3565b6001600160a01b0381168114610098575f5ffd5b50565b5f5f604083850312156100ac575f5ffd5b82516100b781610084565b60208401519092506100c881610084565b809150509250929050565b60805161496561012a5f395f81816108ed01528181610b9e01528181610c41015281816111080152818161119b01528181611a6b015281816123bb0152818161244601528181612c180152612cae01526149655ff3fe608060405234801561000f575f5ffd5b5060043610610218575f3560e01c80630162c7511461021c578063029dbcfe1461024957806305a3aff91461028957806314e2acc9146102f55780631577b7a1146103285780631633f3c11461035457806317ef047a146103675780631ac1e1d51461037a578063293d4b84146103b75780632c1917ee1461046e5780633ba3d070146104d05780633cc7116b146104f557806346bfcc8f1461050a5780634d61dc291461051d5780634e1ea2cd14610560578063516a6a74146105df57806365fae35e146106125780636787053c146106255780637315cf5814610638578063790b56ce1461066b5780637dea42b31461067e57806386a672dd146106c65780638a6fd02a146107325780638baa4083146107655780638bd95b44146107785780638c34e9331461078b5780639b884cd5146107ce5780639c52a7f1146107e15780639ea3d094146107f4578063a3bfe16814610816578063a822014914610829578063aa903d731461083c578063bec24d911461084f578063bf353dbb14610862578063c4035bd01461088f578063c4d49aa3146108a2578063c7ade5c0146108b5578063c88854a3146108e8578063d0d4133114610927578063db254ea71461093a578063ddcdfa3414610982578063e6d283a614610995578063ec762bfe146109b7578063efdd0d0e146109ca578063fb868432146109dd578063fbd2ab3e14610a0a575b5f5ffd5b61022f61022a366004613df7565b610a1d565b60405163ffffffff90911681526020015b60405180910390f35b61027c610257366004613df7565b600a60209081525f92835260408084209091529082529020546001600160801b031681565b6040516102409190613e2c565b6102d1610297366004613e40565b600c60209081525f93845260408085208252928452828420905282529020546001600160801b03811690600160801b900463ffffffff1682565b604080516001600160801b03909316835263ffffffff909116602083015201610240565b610308610303366004613ea5565b610a6b565b604080516001600160801b03938416815292909116602083015201610240565b61033b610336366004613f1d565b610e0d565b6040516001600160801b03199091168152602001610240565b61027c610362366004613f36565b610e41565b61033b610375366004613f82565b610f41565b61038d610388366004613fb3565b610f4c565b604080516001600160801b0394851681529284166020840152921691810191909152606001610240565b6104236103c536600461401a565b600960209081525f93845260408085208252928452828420905282529020805460018201546002909201546001600160801b0380831693600160801b938490048216938282169391819004831692821691046001600160401b031686565b604080516001600160801b039788168152958716602087015293861693850193909352908416606084015290921660808201526001600160401b0390911660a082015260c001610240565b6104b161047c366004613e40565b600f60209081525f938452604080852082529284528284209052825290205460ff81169061010090046001600160801b031682565b6040805192151583526001600160801b03909116602083015201610240565b61022f6104de366004613f1d565b60026020525f908152604090205463ffffffff1681565b61050861050336600461406d565b6113b3565b005b61027c610518366004613f36565b611668565b61055061052b366004613e40565b601060209081525f938452604080852082529284528284209052825290205460ff1681565b6040519015158152602001610240565b6105af61056e366004613df7565b600760209081525f928352604080842090915290825290205463ffffffff80821691600160201b8104821691600160401b8204811691600160601b90041684565b6040805163ffffffff95861681529385166020850152918416918301919091529091166060820152608001610240565b61027c6105ed3660046140ca565b600660209081525f92835260408084209091529082529020546001600160801b031681565b6105086106203660046140f2565b611731565b61022f610633366004613df7565b6117a4565b610308610646366004614118565b60036020525f90815260409020546001600160801b0380821691600160801b90041682565b610508610679366004614131565b6117e9565b61069161068c366004613f36565b611857565b60405161024094939291906001600160801b039485168152928416602084015292166040820152901515606082015260800190565b6104236106d436600461401a565b600860209081525f93845260408085208252928452828420905282529020805460018201546002909201546001600160801b0380831693600160801b938490048216938282169391819004831692821691046001600160401b031686565b610550610740366004613e40565b601160209081525f938452604080852082529284528284209052825290205460ff1681565b610508610773366004614131565b611c5f565b6105086107863660046141c7565b611cc6565b6104b1610799366004613e40565b600e60209081525f938452604080852082529284528284209052825290205460ff81169061010090046001600160801b031682565b61027c6107dc366004613f36565b611d84565b6105086107ef3660046140f2565b611e50565b610807610802366004614118565b611ec2565b60405161024093929190614283565b61027c610824366004613f36565b611fef565b61022f6108373660046142b8565b6120e6565b61022f61084a366004613df7565b61216e565b61069161085d366004613f36565b6121b3565b6108816108703660046140f2565b5f6020819052908152604090205481565b604051908152602001610240565b61022f61089d3660046142b8565b61268c565b61022f6108b0366004613df7565b612703565b61027c6108c3366004613df7565b600b60209081525f92835260408084209091529082529020546001600160801b031681565b61090f7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610240565b61027c610935366004613ea5565b612741565b6102d1610948366004613e40565b600d60209081525f93845260408085208252928452828420905282529020546001600160801b03811690600160801b900463ffffffff1682565b6105086109903660046142f5565b6129c5565b6105506109a336600461432e565b60016020525f908152604090205460ff1681565b61038d6109c5366004613fb3565b612a98565b61033b6109d8366004614345565b612e4f565b6105506109eb3660046143cb565b600560209081525f928352604080842090915290825290205460ff1681565b610550610a183660046143cb565b612f6f565b6001600160801b031982165f9081526007602090815260408083206001600160801b0385168452909152812054610a6290600160401b900463ffffffff166001614407565b90505b92915050565b335f908152602081905260408120548190600114610a9c5760405163ea8e4eb560e01b815260040160405180910390fd5b610aa68888612f6f565b610ac357604051631eda274960e21b815260040160405180910390fd5b610acd8787612703565b63ffffffff168563ffffffff161485610ae68989612703565b9091610b1057604051632c647e3760e21b8152600401610b07929190614423565b60405180910390fd5b50506001600160801b031987165f908152600b602090815260408083206001600160801b038a81168552925290912054811692508416821015610b665760405163776a3aa960e11b815260040160405180910390fd5b5f846001600160801b031611610b8f5760405163035a9e4760e11b815260040160405180910390fd5b610cbb846001600160801b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166309d2f9b4896040518263ffffffff1660e01b8152600401610be89190613e2c565b602060405180830381865afa158015610c03573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c27919061443a565b604051600162fe590960e01b031981526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063ff01a6f790610c76908e9060040161445a565b602060405180830381865afa158015610c91573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cb5919061443a565b86612fa5565b6001600160801b03198089165f8181526008602090815260408083206001600160801b038d811680865291845282852063ffffffff8e16865284528285208a82168d8316600160801b9081029099161781558b8216909702818916176001880155948452600b8352818420908452909152812080549495509293889392610d449185911661446e565b92506101000a8154816001600160801b0302191690836001600160801b031602179055508483610d74919061446e565b6001600160801b031989165f8181526007602090815260408083206001600160801b038d16808552925291829020805463ffffffff191663ffffffff8c161790559051929550916001600160401b038c16907f1785217365be01fea67dbce22680b382886fe6a3a22156830b3cda5823a42a8e90610df9908b9088908c908b9061448d565b60405180910390a450965096945050505050565b6001600160401b0381165f90815260026020526040812054610a65908390610e3c9063ffffffff166001614407565b612fbc565b335f90815260208190526040812054600114610e705760405163ea8e4eb560e01b815260040160405180910390fd5b610e7a8585612f6f565b610e9757604051631eda274960e21b815260040160405180910390fd5b6001600160801b031984165f9081526010602090815260408083206001600160801b0386168452825280832086845290915290205460ff16610eec57604051635fb581dd60e01b815260040160405180910390fd5b6001600160801b031984165f908152600d602090815260408083206001600160801b0386811685529083528184208785529092528220541690610f3790879087908490888882612fe7565b9695505050505050565b5f610a628383612fbc565b335f9081526020819052604081205481908190600114610f7f5760405163ea8e4eb560e01b815260040160405180910390fd5b610f898888612f6f565b610fa657604051631eda274960e21b815260040160405180910390fd5b6001600160801b031987165f9081526007602090815260408083206001600160801b038a16845290915290205463ffffffff600160201b90910481169086161115611004576040516386d1007760e01b815260040160405180910390fd5b61100e878761216e565b63ffffffff168563ffffffff161485611027898961216e565b909161104857604051632c647e3760e21b8152600401610b07929190614423565b50506001600160801b03198088165f9081526009602090815260408083206001600160801b038b8116855290835281842063ffffffff808c16865293529083206002810180548a8416961686179055805490946110b4949093600160801b909204909216919061327216565b60018201549092506110d590600160801b90046001600160801b031661329c565b6110df575f611234565b8054604051600162fe590960e01b0319815261123491600160801b90046001600160801b0316907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063ff01a6f790611145908e9060040161445a565b602060405180830381865afa158015611160573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611184919061443a565b604051630274be6d60e21b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906309d2f9b4906111d0908d90600401613e2c565b602060405180830381865afa1580156111eb573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061120f919061443a565b600285015460018601546001600160801b0391821691600160801b909104165f6132aa565b81546001830180546001600160801b0319166001600160801b0384811691909117909155600160801b909104169450925061126e426132b9565b600282018054600160801b600160c01b031916600160801b6001600160401b039384168102919091179091556001600160801b03198a165f8181526007602090815260408083206001600160801b038e8116808652919093529220805463ffffffff60601b1916600160601b63ffffffff8e1602179055600186015491949293928e16927f14f07afe7ac79d02cc585b7f76d4d9687e82b16083b19e225a2fde74b32bf36e928c928c9261132392041661329c565b61132d575f611354565b60028701546001880154611354916001600160801b0390811691600160801b9004166132e6565b87546040805163ffffffff90951685526001600160801b03938416602086015291831691840191909152600160801b9004811660608301528881166080830152871660a082015260c0015b60405180910390a450955095509592505050565b335f908152602081905260409020546001146113e25760405163ea8e4eb560e01b815260040160405180910390fd5b6113ec8484612f6f565b61140957604051631eda274960e21b815260040160405180910390fd5b808061144857506001600160801b031983165f90815260066020908152604080832061ffff891684529091529020546001600160801b03808416911610155b61146557604051635fe755ed60e01b815260040160405180910390fd5b5f8161149d576001600160801b031984165f908152600360205260409020546114989084906001600160801b031661446e565b6114ca565b6001600160801b031984165f908152600360205260409020546114ca9084906001600160801b03166144bc565b6001600160801b03198581165f90815260036020526040812080549092166001600160801b038416179091559091508261153f576001600160801b031985165f90815260066020908152604080832061ffff8b16845290915290205461153a9085906001600160801b031661446e565b61157b565b6001600160801b031985165f90815260066020908152604080832061ffff8b16845290915290205461157b9085906001600160801b03166144bc565b6001600160801b03198681165f90815260066020908152604080832061ffff8d168452909152902080549091166001600160801b0383161790559050821561161057846001600160801b031916866001600160401b03167f2c63f350e031a7945cf5c5d46ff2bfb9dbf69c9b401dff10c60d6bb1c4d0687489876040516116039291906144db565b60405180910390a361165f565b846001600160801b031916866001600160401b03167fc2f94e30444446144c04ae7a5e046b226a3640ea83c2c7cf1a0a4fee4be36ff089876040516116569291906144db565b60405180910390a35b50505050505050565b335f908152602081905260408120546001146116975760405163ea8e4eb560e01b815260040160405180910390fd5b6116a18585612f6f565b6116be57604051631eda274960e21b815260040160405180910390fd5b6001600160801b031984165f8181526010602090815260408083206001600160801b038781168086529184528285208986528452828520805460ff19166001179055948452600d8352818420908452825280832087845290915281205490911690610f3790879087908490888882612fe7565b335f908152602081905260409020546001146117605760405163ea8e4eb560e01b815260040160405180910390fd5b6001600160a01b0381165f8181526020819052604080822060019055517fdd0e34038ac38b2a1ce960229778ac48a8719bc900b6c4f8d0475c6e8b385a609190a250565b6001600160801b031982165f9081526007602090815260408083206001600160801b0385168452909152812054610a6290600160201b900463ffffffff166001614407565b335f908152602081905260409020546001146118185760405163ea8e4eb560e01b815260040160405180910390fd5b6118228585612f6f565b61183f57604051631eda274960e21b815260040160405180910390fd5b61184f858585600186865f612fe7565b505050505050565b335f9081526020819052604081205481908190819060011461188c5760405163ea8e4eb560e01b815260040160405180910390fd5b6118968888612f6f565b6118b357604051631eda274960e21b815260040160405180910390fd5b6001600160801b031987165f908152600c602090815260408083206001600160801b0389811685529083528184208a8552909252909120805490911661190c5760405163484a885b60e01b815260040160405180910390fd5b6001600160801b031988165f9081526007602090815260408083206001600160801b038a1684529091529020548154600160601b90910463ffffffff908116600160801b90920416111561197357604051639aac3e0b60e01b815260040160405180910390fd5b6001600160801b031988165f8181526007602090815260408083206001600160801b038b81168086529184528285205487549686526009855283862092865291845282852063ffffffff600160801b97889004811680885291909552929094208054600160601b9092049093169091109550909290041615611a215780548254611a1c91611a17916001600160801b0390811691600160801b8104821691166132f1565b6133af565b611a23565b5f5b94506001600160801b03851615611af9576001810154611a5290600160801b90046001600160801b031661329c565b611a5c575f611ab5565b611ab5856001600160801b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ff01a6f78d6040518263ffffffff1660e01b8152600401611145919061445a565b8254909650859083905f90611ad49084906001600160801b031661446e565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b866001600160801b0316896001600160801b0319168b6001600160401b03167fbaedde181510aaa5fcaded7a64ddd81c51a9b07b5ed0209b6e3a92801c93959e855f0160109054906101000a900463ffffffff168c8a885f015f9054906101000a90046001600160801b03168d8960020160109054906101000a90046001600160401b0316604051611b90969594939291906144f8565b60405180910390a481546001600160801b03165f03611bdb57611bb389886117a4565b825463ffffffff91909116600160801b0263ffffffff60801b199091161782555f9250611c1c565b81546001908390601090611bfd908490600160801b900463ffffffff16614407565b92506101000a81548163ffffffff021916908363ffffffff1602179055505b611c2689886117a4565b825463ffffffff918216600160801b90910490911603611c5257611c4f8a8a8a8a8660016133d8565b93505b5050945094509450949050565b335f90815260208190526040902054600114611c8e5760405163ea8e4eb560e01b815260040160405180910390fd5b611c988585612f6f565b611cb557604051631eda274960e21b815260040160405180910390fd5b61184f858585600186866001612fe7565b335f90815260208190526040902054600114611cf55760405163ea8e4eb560e01b815260040160405180910390fd5b611cff8686612f6f565b611d1c57604051631eda274960e21b815260040160405180910390fd5b611d2a85858585855f6135b2565b846001600160801b031916866001600160401b03167f8c0eec6877ffbfd88a45283fadb04a776dc75b31282041dcbc782bb24d3c4a7186868686604051611d749493929190614565565b60405180910390a3505050505050565b335f90815260208190526040812054600114611db35760405163ea8e4eb560e01b815260040160405180910390fd5b611dbd8585612f6f565b611dda57604051631eda274960e21b815260040160405180910390fd5b6001600160801b031984165f8181526011602090815260408083206001600160801b038781168086529184528285208986528452828520805460ff19166001908117909155958552600c84528285209185529083528184208885529092528220541691610f379188918891859189908990612fe7565b335f90815260208190526040902054600114611e7f5760405163ea8e4eb560e01b815260040160405180910390fd5b6001600160a01b0381165f81815260208190526040808220829055517f184450df2e323acec0ed3b5c7531b81f9b4cdef7914dfd4c0a4317416bb5251b9190a250565b60046020525f9081526040902080548190611edc9061458b565b80601f0160208091040260200160405190810160405280929190818152602001828054611f089061458b565b8015611f535780601f10611f2a57610100808354040283529160200191611f53565b820191905f5260205f20905b815481529060010190602001808311611f3657829003601f168201915b505050505090806001018054611f689061458b565b80601f0160208091040260200160405190810160405280929190818152602001828054611f949061458b565b8015611fdf5780601f10611fb657610100808354040283529160200191611fdf565b820191905f5260205f20905b815481529060010190602001808311611fc257829003601f168201915b5050505050908060020154905083565b335f9081526020819052604081205460011461201e5760405163ea8e4eb560e01b815260040160405180910390fd5b6120288585612f6f565b61204557604051631eda274960e21b815260040160405180910390fd5b6001600160801b031984165f9081526011602090815260408083206001600160801b0386168452825280832086845290915290205460ff1661209a57604051635fb581dd60e01b815260040160405180910390fd5b6001600160801b031984165f908152600c602090815260408083206001600160801b0386811685529083528184208785529092528220541690610f379087908790849088886001612fe7565b6001600160801b031983165f818152600c602090815260408083206001600160801b0386811680865291845282852088865284528285208351808501855290549182168152600160801b90910463ffffffff90811682860152958552600784528285209185529252822054919261216492600160201b9004166136fb565b90505b9392505050565b6001600160801b031982165f9081526007602090815260408083206001600160801b0385168452909152812054610a6290600160601b900463ffffffff166001614407565b335f908152602081905260408120548190819081906001146121e85760405163ea8e4eb560e01b815260040160405180910390fd5b6121f28888612f6f565b61220f57604051631eda274960e21b815260040160405180910390fd5b6001600160801b031987165f908152600d602090815260408083206001600160801b0389811685529083528184208a855290925290912080549091166122685760405163484a885b60e01b815260040160405180910390fd5b6001600160801b031988165f9081526007602090815260408083206001600160801b038a1684529091529020548154600160401b90910463ffffffff908116600160801b9092041611156122cf57604051638691a7e760e01b815260040160405180910390fd5b6001600160801b031988165f8181526007602090815260408083206001600160801b038b81168086529184528285205487549686526008855283862092865291845282852063ffffffff600160801b97889004811680885291909552929094208054600160401b9092049093169091109550909290041615612378578054825461237391611a17916001600160801b0390811691600160801b8104821691166132f1565b61237a565b5f5b94506001600160801b038516156125375760028101546123a2906001600160801b031661329c565b6123ac575f6124f3565b6124f3856001600160801b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166309d2f9b48a6040518263ffffffff1660e01b81526004016124059190613e2c565b602060405180830381865afa158015612420573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612444919061443a565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ff01a6f78e6040518263ffffffff1660e01b8152600401612490919061445a565b602060405180830381865afa1580156124ab573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906124cf919061443a565b600185015460028601546001600160801b03600160801b909204821691165f6132aa565b8254909650859083905f906125129084906001600160801b031661446e565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b866001600160801b0316896001600160801b0319168b6001600160401b03167f49c87cda7a22a30abb60733a70e9787c6607d32928c46528890206fdc06fabce855f0160109054906101000a900463ffffffff168c8a885f015f9054906101000a90046001600160801b03168d8960020160109054906101000a90046001600160401b03166040516125ce969594939291906144f8565b60405180910390a481546001600160801b03165f03612619576125f18988612703565b825463ffffffff91909116600160801b0263ffffffff60801b199091161782555f925061265a565b8154600190839060109061263b908490600160801b900463ffffffff16614407565b92506101000a81548163ffffffff021916908363ffffffff1602179055505b6126648988612703565b825463ffffffff918216600160801b90910490911603611c5257611c4f8a8a8a8a865f6133d8565b6001600160801b031983165f818152600d602090815260408083206001600160801b0386811680865291845282852088865284528285208351808501855290549182168152600160801b90910463ffffffff90811682860152958552600784528285209185529252822054919261216492166136fb565b6001600160801b031982165f9081526007602090815260408083206001600160801b0385168452909152812054610a629063ffffffff166001614407565b335f908152602081905260408120546001146127705760405163ea8e4eb560e01b815260040160405180910390fd5b61277a8787612f6f565b61279757604051631eda274960e21b815260040160405180910390fd5b6127a186866117a4565b63ffffffff168463ffffffff1614846127ba88886117a4565b90916127db57604051632c647e3760e21b8152600401610b07929190614423565b5050506001600160801b031985165f908152600a602090815260408083206001600160801b03888116855292529091205481169083168110156128315760405163776a3aa960e11b815260040160405180910390fd5b5f836001600160801b03161161285a5760405163035a9e4760e11b815260040160405180910390fd5b6001600160801b03198681165f8181526009602090815260408083206001600160801b038b811680865291845282852063ffffffff8c1686528452828520600160801b8b8316810290981689831617815560018101805483168b841690990298909817909755948452600a83528184209084529091528120805487939192916128e59185911661446e565b92506101000a8154816001600160801b0302191690836001600160801b031602179055508382612915919061446e565b6001600160801b031988165f8181526007602090815260408083206001600160801b038c811680865291845293829020805463ffffffff60201b1916600160201b63ffffffff8e169081029190911790915582519081528a85169381019390935292851690820152929450916001600160401b038b16907f679cd78fb2cbc9b1fa61043bb803ac3d5a5a8e0dd7aaa5a94e8bc5721dd2b8aa906060015b60405180910390a4509695505050505050565b335f908152602081905260409020546001146129f45760405163ea8e4eb560e01b815260040160405180910390fd5b6129fe8383612f6f565b612a1b57604051631eda274960e21b815260040160405180910390fd5b6001600160801b031982165f818152600360205260409081902080546001600160801b03858116600160801b02911617815590519091906001600160401b038616907f870617e0cf43c45abe8ec9d49e4f116c6c53d5412d17476212c2db8b0913141690612a8a908690613e2c565b60405180910390a350505050565b335f9081526020819052604081205481908190600114612acb5760405163ea8e4eb560e01b815260040160405180910390fd5b612ad58888612f6f565b612af257604051631eda274960e21b815260040160405180910390fd5b6001600160801b031987165f9081526007602090815260408083206001600160801b038a16845290915290205463ffffffff9081169086161115612b49576040516386d1007760e01b815260040160405180910390fd5b612b538787610a1d565b63ffffffff168563ffffffff161485612b6c8989610a1d565b9091612b8d57604051632c647e3760e21b8152600401610b07929190614423565b50506001600160801b03198088165f9081526008602090815260408083206001600160801b038b8116855290835281842063ffffffff8b168552909252909120600281018054928816929093168217909255612be89061329c565b612bf2575f612d3e565b8054604051630274be6d60e21b8152612d3e91600160801b90046001600160801b0316907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906309d2f9b490612c55908c90600401613e2c565b602060405180830381865afa158015612c70573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c94919061443a565b604051600162fe590960e01b031981526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063ff01a6f790612ce3908f9060040161445a565b602060405180830381865afa158015612cfe573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d22919061443a565b6001850154600160801b90046001600160801b0316895f6132aa565b9350612d49426132b9565b600282018054600160801b600160c01b031916600160801b6001600160401b039384168102919091179091556001600160801b03198a165f8181526007602090815260408083206001600160801b038e8116808652919093529220805463ffffffff60401b1916600160401b63ffffffff8e1602179055855460018701549085900482169850808216975091949293928e16927f75d54fc68ce51f85af6cc4bc67a5d7d3267296e63539b2d428743a1a8a8380de928c928c92612e0d92041661329c565b612e17575f612e3e565b60028701546001880154612e3e916001600160801b0390811691600160801b9004166132e6565b8a60405161139f949392919061448d565b335f90815260208190526040812054600114612e7e5760405163ea8e4eb560e01b815260040160405180910390fd5b612e8787610e0d565b6001600160401b0388165f908152600260205260408120805492935090918290612eb69063ffffffff166145c3565b825463ffffffff8083166101009490940a93840293021916919091179091556001600160401b0389165f9081526005602090815260408083206001600160801b0319871684529091529020805460ff191660011790559050612f1c8288888888886135b2565b8063ffffffff16826001600160801b031916896001600160401b03167fbbd56df32f5dff1bd3d121e1d899391d578077964dcda006d45771892e71eda68a8a8a8a8a6040516129b29594939291906145e7565b6001600160401b0382165f9081526005602090815260408083206001600160801b03198516845290915290205460ff1692915050565b5f612fb3858585855f613749565b95945050505050565b5f612fdd63ffffffff8316600160401b600160801b03604086901b166144bc565b60801b9392505050565b5f8080836001811115612ffc57612ffc614620565b1461303a576001600160801b031988165f908152600c602090815260408083206001600160801b03881684528252808320888452909152902061306f565b6001600160801b031988165f908152600d602090815260408083206001600160801b0388168452825280832088845290915290205b90505f8084600181111561308557613085614620565b146130c3576001600160801b031989165f908152600e602090815260408083206001600160801b0389168452825280832089845290915290206130f8565b6001600160801b031989165f908152600f602090815260408083206001600160801b0389168452825280832089845290915290205b905061310b8a8a8a8a8a8a88888c6137b6565b1561311a575f92505050613267565b866131255787613127565b5f5b9250866131495781546131449089906001600160801b031661446e565b61315f565b815461315f9089906001600160801b03166144bc565b82546001600160801b0319166001600160801b03919091161782555f84600181111561318d5761318d614620565b146131a15761319c89866117a4565b6131ab565b6131ab8986612703565b825463ffffffff91909116600160801b0263ffffffff60801b199091161782555f8460018111156131de576131de614620565b036132265760408051808201909152815460ff81161515825261010090046001600160801b03166020820152613221908b908b908b908b908b908b908990613aba565b613264565b60408051808201909152815460ff81161515825261010090046001600160801b03166020820152613264908b908b908b908b908b908b908990613ba2565b50505b979650505050505050565b5f612164611a17856001600160801b0316856001600160801b0316670de0b6b3a764000086613c51565b6001600160801b0316151590565b5f613267878787878787613ca0565b5f6001600160401b038211156132e25760405163397df9e960e21b815260040160405180910390fd5b5090565b5f610a628383613d42565b5f80805f19858709858702925082811083820303915050805f036133285783828161331e5761331e614634565b0492505050612167565b80841161334857604051637ee8242b60e01b815260040160405180910390fd5b5f8486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091025f889003889004909101858311909403939093029303949094049190911702949350505050565b5f6001600160801b038211156132e25760405163e999826d60e01b815260040160405180910390fd5b5f80808360018111156133ed576133ed614620565b1461342b576001600160801b031987165f908152600e602090815260408083206001600160801b038916845282528083208984529091529020613460565b6001600160801b031987165f908152600f602090815260408083206001600160801b0389168452825280832089845290915290205b80549091505f9060ff1661348357815461010090046001600160801b031661348f565b84546001600160801b03165b825490915060ff16156134d057815485546134bb916001600160801b03610100909104811691166144bc565b85546001600160801b0319168655925061351a565b815485546001600160801b0361010090920482169187915f916134f5918591166144bc565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b5f84600181111561352d5761352d614620565b03613568578154604080518082019091525f8082526020820152613563918b918b91859160ff90911615908c908c908c90613aba565b613599565b8154604080518082019091525f8082526020820152613599918b918b91859160ff90911615908c908c908c90613ba2565b5080546001600160881b03191690559695505050505050565b8380158015906135c3575060808111155b6135e057604051637a08c50b60e01b815260040160405180910390fd5b8280158015906135f1575060208111155b61360e576040516314ec8c2560e21b815260040160405180910390fd5b6001600160801b031988165f908152600460205260409020831580156136375750600281015415155b8061364e5750831580159061364e57506002810154155b61366b576040516381e69d9b60e01b815260040160405180910390fd5b831580159061367c57506002810154155b156136d3575f8481526001602052604090205460ff16156136b057604051633ca87f7960e21b815260040160405180910390fd5b5f848152600160208190526040909120805460ff19169091179055600281018490555b806136df888a836146a8565b50600181016136ef8688836146a8565b50505050505050505050565b81515f906001600160801b0316158061372357508163ffffffff16836020015163ffffffff16115b1561372f57505f610a65565b602083015161373e9083614761565b610a62906001614407565b5f8360ff168560ff16036137755761376e611a176001600160801b0385168885613d6b565b9050612fb3565b610f37611a176001600160801b03851661379087600a614860565b61379a908a61486e565b6137a560128a614885565b6137b090600a614860565b86613c51565b5f80808360018111156137cb576137cb614620565b146137df576137da8a876117a4565b6137e9565b6137e98a87612703565b6040805180820190915286546001600160801b0381168252600160801b900463ffffffff1660208201529091506138209082613d89565b1561382e575f915050613aad565b835460ff16801561384757505f896001600160801b0316115b1561386557604051637643c0f960e01b815260040160405180910390fd5b8761387a57835460ff191660011784556138c1565b83548990859060019061389c90849061010090046001600160801b03166144bc565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b5f8360018111156138d4576138d4614620565b036139c2575f600b5f8c6001600160801b0319166001600160801b03191681526020019081526020015f205f886001600160801b03166001600160801b031681526020019081526020015f205f9054906101000a90046001600160801b03169050866001600160801b03168b6001600160801b0319168d6001600160401b03165f5160206148f05f395f51905f52858c8b5f015f9054906101000a90046001600160801b0316878c5f0160019054906101000a90046001600160801b03168d5f015f9054906101000a900460ff166040516139b49695949392919061489e565b60405180910390a450613aa7565b5f600a5f8c6001600160801b0319166001600160801b03191681526020019081526020015f205f886001600160801b03166001600160801b031681526020019081526020015f205f9054906101000a90046001600160801b03169050866001600160801b03168b6001600160801b0319168d6001600160401b03165f5160206149105f395f51905f52858c8b5f015f9054906101000a90046001600160801b0316878c5f0160019054906101000a90046001600160801b03168d5f015f9054906101000a900460ff16604051613a9d9695949392919061489e565b60405180910390a4505b60019150505b9998505050505050505050565b6001600160801b031987165f908152600b602090815260408083206001600160801b0387811685529252909120541685613afd57613af8878261446e565b613b07565b613b0787826144bc565b6001600160801b03198981165f818152600b602090815260408083206001600160801b038b8116808652919093529220805490941690851617909255919250906001600160401b038b165f5160206148f05f395f51905f52613b698c89612703565b875460208801518851604051613b8f94938e936001600160801b03909116928b9261489e565b60405180910390a4505050505050505050565b6001600160801b031987165f908152600a602090815260408083206001600160801b0387811685529252909120541685613be557613be0878261446e565b613bef565b613bef87826144bc565b6001600160801b03198981165f818152600a602090815260408083206001600160801b038b8116808652919093529220805490941690851617909255919250906001600160401b038b165f5160206149105f395f51905f52613b698c896117a4565b5f5f613c5e8686866132f1565b90506001836002811115613c7457613c74614620565b148015613c9057505f8480613c8b57613c8b614634565b868809115b15612fb357610f376001826148dc565b5f613cb3836001600160801b031661329c565b613cfd5760405162461bcd60e51b815260206004820152601b60248201527a50726963696e674c69622f6469766973696f6e2d62792d7a65726f60281b6044820152606401610b07565b613267611a176001600160801b03861689613d1989600a614860565b613d23919061486e565b6001600160801b038716613d388b600a614860565b6137b0919061486e565b5f610a62611a17846001600160801b0316670de0b6b3a7640000856001600160801b03166132f1565b5f612164846001600160801b031684670de0b6b3a764000085613c51565b5f60018263ffffffff16111580613da8575082516001600160801b0316155b80610a6257505060209091015163ffffffff9182169116101590565b80356001600160801b031981168114613ddb575f5ffd5b919050565b6001600160801b0381168114613df4575f5ffd5b50565b5f5f60408385031215613e08575f5ffd5b613e1183613dc4565b91506020830135613e2181613de0565b809150509250929050565b6001600160801b0391909116815260200190565b5f5f5f60608486031215613e52575f5ffd5b613e5b84613dc4565b92506020840135613e6b81613de0565b929592945050506040919091013590565b80356001600160401b0381168114613ddb575f5ffd5b803563ffffffff81168114613ddb575f5ffd5b5f5f5f5f5f5f60c08789031215613eba575f5ffd5b613ec387613e7c565b9550613ed160208801613dc4565b94506040870135613ee181613de0565b9350613eef60608801613e92565b92506080870135613eff81613de0565b915060a0870135613f0f81613de0565b809150509295509295509295565b5f60208284031215613f2d575f5ffd5b610a6282613e7c565b5f5f5f5f60808587031215613f49575f5ffd5b613f5285613e7c565b9350613f6060208601613dc4565b9250604085013591506060850135613f7781613de0565b939692955090935050565b5f5f60408385031215613f93575f5ffd5b613f9c83613e7c565b9150613faa60208401613e92565b90509250929050565b5f5f5f5f5f60a08688031215613fc7575f5ffd5b613fd086613e7c565b9450613fde60208701613dc4565b93506040860135613fee81613de0565b9250613ffc60608701613e92565b9150608086013561400c81613de0565b809150509295509295909350565b5f5f5f6060848603121561402c575f5ffd5b61403584613dc4565b9250602084013561404581613de0565b915061405360408501613e92565b90509250925092565b803561ffff81168114613ddb575f5ffd5b5f5f5f5f5f60a08688031215614081575f5ffd5b61408a8661405c565b945061409860208701613e7c565b93506140a660408701613dc4565b925060608601356140b681613de0565b91506080860135801515811461400c575f5ffd5b5f5f604083850312156140db575f5ffd5b6140e483613dc4565b9150613faa6020840161405c565b5f60208284031215614102575f5ffd5b81356001600160a01b0381168114612167575f5ffd5b5f60208284031215614128575f5ffd5b610a6282613dc4565b5f5f5f5f5f60a08688031215614145575f5ffd5b61414e86613e7c565b945061415c60208701613dc4565b9350604086013561416c81613de0565b925060608601359150608086013561400c81613de0565b5f5f83601f840112614193575f5ffd5b5081356001600160401b038111156141a9575f5ffd5b6020830191508360208285010111156141c0575f5ffd5b9250929050565b5f5f5f5f5f5f608087890312156141dc575f5ffd5b6141e587613e7c565b95506141f360208801613dc4565b945060408701356001600160401b0381111561420d575f5ffd5b61421989828a01614183565b90955093505060608701356001600160401b03811115614237575f5ffd5b61424389828a01614183565b979a9699509497509295939492505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b606081525f6142956060830186614255565b82810360208401526142a78186614255565b915050826040830152949350505050565b5f5f5f606084860312156142ca575f5ffd5b6142d384613dc4565b92506020840135915060408401356142ea81613de0565b809150509250925092565b5f5f5f60608486031215614307575f5ffd5b61431084613e7c565b925061431e60208501613dc4565b915060408401356142ea81613de0565b5f6020828403121561433e575f5ffd5b5035919050565b5f5f5f5f5f5f6080878903121561435a575f5ffd5b61436387613e7c565b955060208701356001600160401b0381111561437d575f5ffd5b61438989828a01614183565b90965094505060408701356001600160401b038111156143a7575f5ffd5b6143b389828a01614183565b979a9699509497949695606090950135949350505050565b5f5f604083850312156143dc575f5ffd5b6143e583613e7c565b9150613faa60208401613dc4565b634e487b7160e01b5f52601160045260245ffd5b63ffffffff8181168382160190811115610a6557610a656143f3565b63ffffffff92831681529116602082015260400190565b5f6020828403121561444a575f5ffd5b815160ff81168114612167575f5ffd5b6001600160401b0391909116815260200190565b6001600160801b038281168282160390811115610a6557610a656143f3565b63ffffffff9490941684526001600160801b039283166020850152908216604084015216606082015260800190565b6001600160801b038181168382160190811115610a6557610a656143f3565b61ffff9290921682526001600160801b0316602082015260400190565b63ffffffff96909616865260208601949094526001600160801b03928316604086015290821660608501521660808301526001600160401b031660a082015260c00190565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b604081525f61457860408301868861453d565b828103602084015261326781858761453d565b600181811c9082168061459f57607f821691505b6020821081036145bd57634e487b7160e01b5f52602260045260245ffd5b50919050565b5f63ffffffff821663ffffffff81036145de576145de6143f3565b60010192915050565b606081525f6145fa60608301878961453d565b828103602084015261460d81868861453d565b9150508260408301529695505050505050565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52601260045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b601f8211156146a357805f5260205f20601f840160051c810160208510156146815750805b601f840160051c820191505b818110156146a0575f815560010161468d565b50505b505050565b6001600160401b038311156146bf576146bf614648565b6146d3836146cd835461458b565b8361465c565b5f601f841160018114614704575f85156146ed5750838201355b5f19600387901b1c1916600186901b1783556146a0565b5f83815260208120601f198716915b828110156147335786850135825560209485019460019092019101614713565b508682101561474f575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b63ffffffff8281168282160390811115610a6557610a656143f3565b6001815b60018411156147b85780850481111561479c5761479c6143f3565b60018416156147aa57908102905b60019390931c928002614781565b935093915050565b5f826147ce57506001610a65565b816147da57505f610a65565b81600181146147f057600281146147fa57614816565b6001915050610a65565b60ff84111561480b5761480b6143f3565b50506001821b610a65565b5060208310610133831016604e8410600b8410161715614839575081810a610a65565b6148455f19848461477d565b805f1904821115614858576148586143f3565b029392505050565b5f610a6260ff8416836147c0565b8082028115828204841417610a6557610a656143f3565b60ff8181168382160190811115610a6557610a656143f3565b63ffffffff96909616865260208601949094526001600160801b0392831660408601529082166060850152166080830152151560a082015260c00190565b80820180821115610a6557610a656143f356fe1f5a5e518a04d4ae04df91bdcdfc726fd04628d7968eb87c8a177f55336765ff688670c8bdbd011a5a7703624480229795dd54b1ead47cb9a66061fa8b2e0b7aa264697066735822122037736688b1e69a7f3366472d63f827c0884c18babd2501e7b3530c0cf1c1ba3864736f6c634300081c003300000000000000000000000012044ef361cc3446cb7d36541c8411ee4e6f52cb0000000000000000000000005f3f8ea3b54bff7795de7754866e0eac52e0881d

Deployed Bytecode

0x608060405234801561000f575f5ffd5b5060043610610218575f3560e01c80630162c7511461021c578063029dbcfe1461024957806305a3aff91461028957806314e2acc9146102f55780631577b7a1146103285780631633f3c11461035457806317ef047a146103675780631ac1e1d51461037a578063293d4b84146103b75780632c1917ee1461046e5780633ba3d070146104d05780633cc7116b146104f557806346bfcc8f1461050a5780634d61dc291461051d5780634e1ea2cd14610560578063516a6a74146105df57806365fae35e146106125780636787053c146106255780637315cf5814610638578063790b56ce1461066b5780637dea42b31461067e57806386a672dd146106c65780638a6fd02a146107325780638baa4083146107655780638bd95b44146107785780638c34e9331461078b5780639b884cd5146107ce5780639c52a7f1146107e15780639ea3d094146107f4578063a3bfe16814610816578063a822014914610829578063aa903d731461083c578063bec24d911461084f578063bf353dbb14610862578063c4035bd01461088f578063c4d49aa3146108a2578063c7ade5c0146108b5578063c88854a3146108e8578063d0d4133114610927578063db254ea71461093a578063ddcdfa3414610982578063e6d283a614610995578063ec762bfe146109b7578063efdd0d0e146109ca578063fb868432146109dd578063fbd2ab3e14610a0a575b5f5ffd5b61022f61022a366004613df7565b610a1d565b60405163ffffffff90911681526020015b60405180910390f35b61027c610257366004613df7565b600a60209081525f92835260408084209091529082529020546001600160801b031681565b6040516102409190613e2c565b6102d1610297366004613e40565b600c60209081525f93845260408085208252928452828420905282529020546001600160801b03811690600160801b900463ffffffff1682565b604080516001600160801b03909316835263ffffffff909116602083015201610240565b610308610303366004613ea5565b610a6b565b604080516001600160801b03938416815292909116602083015201610240565b61033b610336366004613f1d565b610e0d565b6040516001600160801b03199091168152602001610240565b61027c610362366004613f36565b610e41565b61033b610375366004613f82565b610f41565b61038d610388366004613fb3565b610f4c565b604080516001600160801b0394851681529284166020840152921691810191909152606001610240565b6104236103c536600461401a565b600960209081525f93845260408085208252928452828420905282529020805460018201546002909201546001600160801b0380831693600160801b938490048216938282169391819004831692821691046001600160401b031686565b604080516001600160801b039788168152958716602087015293861693850193909352908416606084015290921660808201526001600160401b0390911660a082015260c001610240565b6104b161047c366004613e40565b600f60209081525f938452604080852082529284528284209052825290205460ff81169061010090046001600160801b031682565b6040805192151583526001600160801b03909116602083015201610240565b61022f6104de366004613f1d565b60026020525f908152604090205463ffffffff1681565b61050861050336600461406d565b6113b3565b005b61027c610518366004613f36565b611668565b61055061052b366004613e40565b601060209081525f938452604080852082529284528284209052825290205460ff1681565b6040519015158152602001610240565b6105af61056e366004613df7565b600760209081525f928352604080842090915290825290205463ffffffff80821691600160201b8104821691600160401b8204811691600160601b90041684565b6040805163ffffffff95861681529385166020850152918416918301919091529091166060820152608001610240565b61027c6105ed3660046140ca565b600660209081525f92835260408084209091529082529020546001600160801b031681565b6105086106203660046140f2565b611731565b61022f610633366004613df7565b6117a4565b610308610646366004614118565b60036020525f90815260409020546001600160801b0380821691600160801b90041682565b610508610679366004614131565b6117e9565b61069161068c366004613f36565b611857565b60405161024094939291906001600160801b039485168152928416602084015292166040820152901515606082015260800190565b6104236106d436600461401a565b600860209081525f93845260408085208252928452828420905282529020805460018201546002909201546001600160801b0380831693600160801b938490048216938282169391819004831692821691046001600160401b031686565b610550610740366004613e40565b601160209081525f938452604080852082529284528284209052825290205460ff1681565b610508610773366004614131565b611c5f565b6105086107863660046141c7565b611cc6565b6104b1610799366004613e40565b600e60209081525f938452604080852082529284528284209052825290205460ff81169061010090046001600160801b031682565b61027c6107dc366004613f36565b611d84565b6105086107ef3660046140f2565b611e50565b610807610802366004614118565b611ec2565b60405161024093929190614283565b61027c610824366004613f36565b611fef565b61022f6108373660046142b8565b6120e6565b61022f61084a366004613df7565b61216e565b61069161085d366004613f36565b6121b3565b6108816108703660046140f2565b5f6020819052908152604090205481565b604051908152602001610240565b61022f61089d3660046142b8565b61268c565b61022f6108b0366004613df7565b612703565b61027c6108c3366004613df7565b600b60209081525f92835260408084209091529082529020546001600160801b031681565b61090f7f00000000000000000000000012044ef361cc3446cb7d36541c8411ee4e6f52cb81565b6040516001600160a01b039091168152602001610240565b61027c610935366004613ea5565b612741565b6102d1610948366004613e40565b600d60209081525f93845260408085208252928452828420905282529020546001600160801b03811690600160801b900463ffffffff1682565b6105086109903660046142f5565b6129c5565b6105506109a336600461432e565b60016020525f908152604090205460ff1681565b61038d6109c5366004613fb3565b612a98565b61033b6109d8366004614345565b612e4f565b6105506109eb3660046143cb565b600560209081525f928352604080842090915290825290205460ff1681565b610550610a183660046143cb565b612f6f565b6001600160801b031982165f9081526007602090815260408083206001600160801b0385168452909152812054610a6290600160401b900463ffffffff166001614407565b90505b92915050565b335f908152602081905260408120548190600114610a9c5760405163ea8e4eb560e01b815260040160405180910390fd5b610aa68888612f6f565b610ac357604051631eda274960e21b815260040160405180910390fd5b610acd8787612703565b63ffffffff168563ffffffff161485610ae68989612703565b9091610b1057604051632c647e3760e21b8152600401610b07929190614423565b60405180910390fd5b50506001600160801b031987165f908152600b602090815260408083206001600160801b038a81168552925290912054811692508416821015610b665760405163776a3aa960e11b815260040160405180910390fd5b5f846001600160801b031611610b8f5760405163035a9e4760e11b815260040160405180910390fd5b610cbb846001600160801b03167f00000000000000000000000012044ef361cc3446cb7d36541c8411ee4e6f52cb6001600160a01b03166309d2f9b4896040518263ffffffff1660e01b8152600401610be89190613e2c565b602060405180830381865afa158015610c03573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c27919061443a565b604051600162fe590960e01b031981526001600160a01b037f00000000000000000000000012044ef361cc3446cb7d36541c8411ee4e6f52cb169063ff01a6f790610c76908e9060040161445a565b602060405180830381865afa158015610c91573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cb5919061443a565b86612fa5565b6001600160801b03198089165f8181526008602090815260408083206001600160801b038d811680865291845282852063ffffffff8e16865284528285208a82168d8316600160801b9081029099161781558b8216909702818916176001880155948452600b8352818420908452909152812080549495509293889392610d449185911661446e565b92506101000a8154816001600160801b0302191690836001600160801b031602179055508483610d74919061446e565b6001600160801b031989165f8181526007602090815260408083206001600160801b038d16808552925291829020805463ffffffff191663ffffffff8c161790559051929550916001600160401b038c16907f1785217365be01fea67dbce22680b382886fe6a3a22156830b3cda5823a42a8e90610df9908b9088908c908b9061448d565b60405180910390a450965096945050505050565b6001600160401b0381165f90815260026020526040812054610a65908390610e3c9063ffffffff166001614407565b612fbc565b335f90815260208190526040812054600114610e705760405163ea8e4eb560e01b815260040160405180910390fd5b610e7a8585612f6f565b610e9757604051631eda274960e21b815260040160405180910390fd5b6001600160801b031984165f9081526010602090815260408083206001600160801b0386168452825280832086845290915290205460ff16610eec57604051635fb581dd60e01b815260040160405180910390fd5b6001600160801b031984165f908152600d602090815260408083206001600160801b0386811685529083528184208785529092528220541690610f3790879087908490888882612fe7565b9695505050505050565b5f610a628383612fbc565b335f9081526020819052604081205481908190600114610f7f5760405163ea8e4eb560e01b815260040160405180910390fd5b610f898888612f6f565b610fa657604051631eda274960e21b815260040160405180910390fd5b6001600160801b031987165f9081526007602090815260408083206001600160801b038a16845290915290205463ffffffff600160201b90910481169086161115611004576040516386d1007760e01b815260040160405180910390fd5b61100e878761216e565b63ffffffff168563ffffffff161485611027898961216e565b909161104857604051632c647e3760e21b8152600401610b07929190614423565b50506001600160801b03198088165f9081526009602090815260408083206001600160801b038b8116855290835281842063ffffffff808c16865293529083206002810180548a8416961686179055805490946110b4949093600160801b909204909216919061327216565b60018201549092506110d590600160801b90046001600160801b031661329c565b6110df575f611234565b8054604051600162fe590960e01b0319815261123491600160801b90046001600160801b0316907f00000000000000000000000012044ef361cc3446cb7d36541c8411ee4e6f52cb6001600160a01b03169063ff01a6f790611145908e9060040161445a565b602060405180830381865afa158015611160573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611184919061443a565b604051630274be6d60e21b81526001600160a01b037f00000000000000000000000012044ef361cc3446cb7d36541c8411ee4e6f52cb16906309d2f9b4906111d0908d90600401613e2c565b602060405180830381865afa1580156111eb573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061120f919061443a565b600285015460018601546001600160801b0391821691600160801b909104165f6132aa565b81546001830180546001600160801b0319166001600160801b0384811691909117909155600160801b909104169450925061126e426132b9565b600282018054600160801b600160c01b031916600160801b6001600160401b039384168102919091179091556001600160801b03198a165f8181526007602090815260408083206001600160801b038e8116808652919093529220805463ffffffff60601b1916600160601b63ffffffff8e1602179055600186015491949293928e16927f14f07afe7ac79d02cc585b7f76d4d9687e82b16083b19e225a2fde74b32bf36e928c928c9261132392041661329c565b61132d575f611354565b60028701546001880154611354916001600160801b0390811691600160801b9004166132e6565b87546040805163ffffffff90951685526001600160801b03938416602086015291831691840191909152600160801b9004811660608301528881166080830152871660a082015260c0015b60405180910390a450955095509592505050565b335f908152602081905260409020546001146113e25760405163ea8e4eb560e01b815260040160405180910390fd5b6113ec8484612f6f565b61140957604051631eda274960e21b815260040160405180910390fd5b808061144857506001600160801b031983165f90815260066020908152604080832061ffff891684529091529020546001600160801b03808416911610155b61146557604051635fe755ed60e01b815260040160405180910390fd5b5f8161149d576001600160801b031984165f908152600360205260409020546114989084906001600160801b031661446e565b6114ca565b6001600160801b031984165f908152600360205260409020546114ca9084906001600160801b03166144bc565b6001600160801b03198581165f90815260036020526040812080549092166001600160801b038416179091559091508261153f576001600160801b031985165f90815260066020908152604080832061ffff8b16845290915290205461153a9085906001600160801b031661446e565b61157b565b6001600160801b031985165f90815260066020908152604080832061ffff8b16845290915290205461157b9085906001600160801b03166144bc565b6001600160801b03198681165f90815260066020908152604080832061ffff8d168452909152902080549091166001600160801b0383161790559050821561161057846001600160801b031916866001600160401b03167f2c63f350e031a7945cf5c5d46ff2bfb9dbf69c9b401dff10c60d6bb1c4d0687489876040516116039291906144db565b60405180910390a361165f565b846001600160801b031916866001600160401b03167fc2f94e30444446144c04ae7a5e046b226a3640ea83c2c7cf1a0a4fee4be36ff089876040516116569291906144db565b60405180910390a35b50505050505050565b335f908152602081905260408120546001146116975760405163ea8e4eb560e01b815260040160405180910390fd5b6116a18585612f6f565b6116be57604051631eda274960e21b815260040160405180910390fd5b6001600160801b031984165f8181526010602090815260408083206001600160801b038781168086529184528285208986528452828520805460ff19166001179055948452600d8352818420908452825280832087845290915281205490911690610f3790879087908490888882612fe7565b335f908152602081905260409020546001146117605760405163ea8e4eb560e01b815260040160405180910390fd5b6001600160a01b0381165f8181526020819052604080822060019055517fdd0e34038ac38b2a1ce960229778ac48a8719bc900b6c4f8d0475c6e8b385a609190a250565b6001600160801b031982165f9081526007602090815260408083206001600160801b0385168452909152812054610a6290600160201b900463ffffffff166001614407565b335f908152602081905260409020546001146118185760405163ea8e4eb560e01b815260040160405180910390fd5b6118228585612f6f565b61183f57604051631eda274960e21b815260040160405180910390fd5b61184f858585600186865f612fe7565b505050505050565b335f9081526020819052604081205481908190819060011461188c5760405163ea8e4eb560e01b815260040160405180910390fd5b6118968888612f6f565b6118b357604051631eda274960e21b815260040160405180910390fd5b6001600160801b031987165f908152600c602090815260408083206001600160801b0389811685529083528184208a8552909252909120805490911661190c5760405163484a885b60e01b815260040160405180910390fd5b6001600160801b031988165f9081526007602090815260408083206001600160801b038a1684529091529020548154600160601b90910463ffffffff908116600160801b90920416111561197357604051639aac3e0b60e01b815260040160405180910390fd5b6001600160801b031988165f8181526007602090815260408083206001600160801b038b81168086529184528285205487549686526009855283862092865291845282852063ffffffff600160801b97889004811680885291909552929094208054600160601b9092049093169091109550909290041615611a215780548254611a1c91611a17916001600160801b0390811691600160801b8104821691166132f1565b6133af565b611a23565b5f5b94506001600160801b03851615611af9576001810154611a5290600160801b90046001600160801b031661329c565b611a5c575f611ab5565b611ab5856001600160801b03167f00000000000000000000000012044ef361cc3446cb7d36541c8411ee4e6f52cb6001600160a01b031663ff01a6f78d6040518263ffffffff1660e01b8152600401611145919061445a565b8254909650859083905f90611ad49084906001600160801b031661446e565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b866001600160801b0316896001600160801b0319168b6001600160401b03167fbaedde181510aaa5fcaded7a64ddd81c51a9b07b5ed0209b6e3a92801c93959e855f0160109054906101000a900463ffffffff168c8a885f015f9054906101000a90046001600160801b03168d8960020160109054906101000a90046001600160401b0316604051611b90969594939291906144f8565b60405180910390a481546001600160801b03165f03611bdb57611bb389886117a4565b825463ffffffff91909116600160801b0263ffffffff60801b199091161782555f9250611c1c565b81546001908390601090611bfd908490600160801b900463ffffffff16614407565b92506101000a81548163ffffffff021916908363ffffffff1602179055505b611c2689886117a4565b825463ffffffff918216600160801b90910490911603611c5257611c4f8a8a8a8a8660016133d8565b93505b5050945094509450949050565b335f90815260208190526040902054600114611c8e5760405163ea8e4eb560e01b815260040160405180910390fd5b611c988585612f6f565b611cb557604051631eda274960e21b815260040160405180910390fd5b61184f858585600186866001612fe7565b335f90815260208190526040902054600114611cf55760405163ea8e4eb560e01b815260040160405180910390fd5b611cff8686612f6f565b611d1c57604051631eda274960e21b815260040160405180910390fd5b611d2a85858585855f6135b2565b846001600160801b031916866001600160401b03167f8c0eec6877ffbfd88a45283fadb04a776dc75b31282041dcbc782bb24d3c4a7186868686604051611d749493929190614565565b60405180910390a3505050505050565b335f90815260208190526040812054600114611db35760405163ea8e4eb560e01b815260040160405180910390fd5b611dbd8585612f6f565b611dda57604051631eda274960e21b815260040160405180910390fd5b6001600160801b031984165f8181526011602090815260408083206001600160801b038781168086529184528285208986528452828520805460ff19166001908117909155958552600c84528285209185529083528184208885529092528220541691610f379188918891859189908990612fe7565b335f90815260208190526040902054600114611e7f5760405163ea8e4eb560e01b815260040160405180910390fd5b6001600160a01b0381165f81815260208190526040808220829055517f184450df2e323acec0ed3b5c7531b81f9b4cdef7914dfd4c0a4317416bb5251b9190a250565b60046020525f9081526040902080548190611edc9061458b565b80601f0160208091040260200160405190810160405280929190818152602001828054611f089061458b565b8015611f535780601f10611f2a57610100808354040283529160200191611f53565b820191905f5260205f20905b815481529060010190602001808311611f3657829003601f168201915b505050505090806001018054611f689061458b565b80601f0160208091040260200160405190810160405280929190818152602001828054611f949061458b565b8015611fdf5780601f10611fb657610100808354040283529160200191611fdf565b820191905f5260205f20905b815481529060010190602001808311611fc257829003601f168201915b5050505050908060020154905083565b335f9081526020819052604081205460011461201e5760405163ea8e4eb560e01b815260040160405180910390fd5b6120288585612f6f565b61204557604051631eda274960e21b815260040160405180910390fd5b6001600160801b031984165f9081526011602090815260408083206001600160801b0386168452825280832086845290915290205460ff1661209a57604051635fb581dd60e01b815260040160405180910390fd5b6001600160801b031984165f908152600c602090815260408083206001600160801b0386811685529083528184208785529092528220541690610f379087908790849088886001612fe7565b6001600160801b031983165f818152600c602090815260408083206001600160801b0386811680865291845282852088865284528285208351808501855290549182168152600160801b90910463ffffffff90811682860152958552600784528285209185529252822054919261216492600160201b9004166136fb565b90505b9392505050565b6001600160801b031982165f9081526007602090815260408083206001600160801b0385168452909152812054610a6290600160601b900463ffffffff166001614407565b335f908152602081905260408120548190819081906001146121e85760405163ea8e4eb560e01b815260040160405180910390fd5b6121f28888612f6f565b61220f57604051631eda274960e21b815260040160405180910390fd5b6001600160801b031987165f908152600d602090815260408083206001600160801b0389811685529083528184208a855290925290912080549091166122685760405163484a885b60e01b815260040160405180910390fd5b6001600160801b031988165f9081526007602090815260408083206001600160801b038a1684529091529020548154600160401b90910463ffffffff908116600160801b9092041611156122cf57604051638691a7e760e01b815260040160405180910390fd5b6001600160801b031988165f8181526007602090815260408083206001600160801b038b81168086529184528285205487549686526008855283862092865291845282852063ffffffff600160801b97889004811680885291909552929094208054600160401b9092049093169091109550909290041615612378578054825461237391611a17916001600160801b0390811691600160801b8104821691166132f1565b61237a565b5f5b94506001600160801b038516156125375760028101546123a2906001600160801b031661329c565b6123ac575f6124f3565b6124f3856001600160801b03167f00000000000000000000000012044ef361cc3446cb7d36541c8411ee4e6f52cb6001600160a01b03166309d2f9b48a6040518263ffffffff1660e01b81526004016124059190613e2c565b602060405180830381865afa158015612420573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612444919061443a565b7f00000000000000000000000012044ef361cc3446cb7d36541c8411ee4e6f52cb6001600160a01b031663ff01a6f78e6040518263ffffffff1660e01b8152600401612490919061445a565b602060405180830381865afa1580156124ab573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906124cf919061443a565b600185015460028601546001600160801b03600160801b909204821691165f6132aa565b8254909650859083905f906125129084906001600160801b031661446e565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b866001600160801b0316896001600160801b0319168b6001600160401b03167f49c87cda7a22a30abb60733a70e9787c6607d32928c46528890206fdc06fabce855f0160109054906101000a900463ffffffff168c8a885f015f9054906101000a90046001600160801b03168d8960020160109054906101000a90046001600160401b03166040516125ce969594939291906144f8565b60405180910390a481546001600160801b03165f03612619576125f18988612703565b825463ffffffff91909116600160801b0263ffffffff60801b199091161782555f925061265a565b8154600190839060109061263b908490600160801b900463ffffffff16614407565b92506101000a81548163ffffffff021916908363ffffffff1602179055505b6126648988612703565b825463ffffffff918216600160801b90910490911603611c5257611c4f8a8a8a8a865f6133d8565b6001600160801b031983165f818152600d602090815260408083206001600160801b0386811680865291845282852088865284528285208351808501855290549182168152600160801b90910463ffffffff90811682860152958552600784528285209185529252822054919261216492166136fb565b6001600160801b031982165f9081526007602090815260408083206001600160801b0385168452909152812054610a629063ffffffff166001614407565b335f908152602081905260408120546001146127705760405163ea8e4eb560e01b815260040160405180910390fd5b61277a8787612f6f565b61279757604051631eda274960e21b815260040160405180910390fd5b6127a186866117a4565b63ffffffff168463ffffffff1614846127ba88886117a4565b90916127db57604051632c647e3760e21b8152600401610b07929190614423565b5050506001600160801b031985165f908152600a602090815260408083206001600160801b03888116855292529091205481169083168110156128315760405163776a3aa960e11b815260040160405180910390fd5b5f836001600160801b03161161285a5760405163035a9e4760e11b815260040160405180910390fd5b6001600160801b03198681165f8181526009602090815260408083206001600160801b038b811680865291845282852063ffffffff8c1686528452828520600160801b8b8316810290981689831617815560018101805483168b841690990298909817909755948452600a83528184209084529091528120805487939192916128e59185911661446e565b92506101000a8154816001600160801b0302191690836001600160801b031602179055508382612915919061446e565b6001600160801b031988165f8181526007602090815260408083206001600160801b038c811680865291845293829020805463ffffffff60201b1916600160201b63ffffffff8e169081029190911790915582519081528a85169381019390935292851690820152929450916001600160401b038b16907f679cd78fb2cbc9b1fa61043bb803ac3d5a5a8e0dd7aaa5a94e8bc5721dd2b8aa906060015b60405180910390a4509695505050505050565b335f908152602081905260409020546001146129f45760405163ea8e4eb560e01b815260040160405180910390fd5b6129fe8383612f6f565b612a1b57604051631eda274960e21b815260040160405180910390fd5b6001600160801b031982165f818152600360205260409081902080546001600160801b03858116600160801b02911617815590519091906001600160401b038616907f870617e0cf43c45abe8ec9d49e4f116c6c53d5412d17476212c2db8b0913141690612a8a908690613e2c565b60405180910390a350505050565b335f9081526020819052604081205481908190600114612acb5760405163ea8e4eb560e01b815260040160405180910390fd5b612ad58888612f6f565b612af257604051631eda274960e21b815260040160405180910390fd5b6001600160801b031987165f9081526007602090815260408083206001600160801b038a16845290915290205463ffffffff9081169086161115612b49576040516386d1007760e01b815260040160405180910390fd5b612b538787610a1d565b63ffffffff168563ffffffff161485612b6c8989610a1d565b9091612b8d57604051632c647e3760e21b8152600401610b07929190614423565b50506001600160801b03198088165f9081526008602090815260408083206001600160801b038b8116855290835281842063ffffffff8b168552909252909120600281018054928816929093168217909255612be89061329c565b612bf2575f612d3e565b8054604051630274be6d60e21b8152612d3e91600160801b90046001600160801b0316907f00000000000000000000000012044ef361cc3446cb7d36541c8411ee4e6f52cb6001600160a01b0316906309d2f9b490612c55908c90600401613e2c565b602060405180830381865afa158015612c70573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c94919061443a565b604051600162fe590960e01b031981526001600160a01b037f00000000000000000000000012044ef361cc3446cb7d36541c8411ee4e6f52cb169063ff01a6f790612ce3908f9060040161445a565b602060405180830381865afa158015612cfe573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d22919061443a565b6001850154600160801b90046001600160801b0316895f6132aa565b9350612d49426132b9565b600282018054600160801b600160c01b031916600160801b6001600160401b039384168102919091179091556001600160801b03198a165f8181526007602090815260408083206001600160801b038e8116808652919093529220805463ffffffff60401b1916600160401b63ffffffff8e1602179055855460018701549085900482169850808216975091949293928e16927f75d54fc68ce51f85af6cc4bc67a5d7d3267296e63539b2d428743a1a8a8380de928c928c92612e0d92041661329c565b612e17575f612e3e565b60028701546001880154612e3e916001600160801b0390811691600160801b9004166132e6565b8a60405161139f949392919061448d565b335f90815260208190526040812054600114612e7e5760405163ea8e4eb560e01b815260040160405180910390fd5b612e8787610e0d565b6001600160401b0388165f908152600260205260408120805492935090918290612eb69063ffffffff166145c3565b825463ffffffff8083166101009490940a93840293021916919091179091556001600160401b0389165f9081526005602090815260408083206001600160801b0319871684529091529020805460ff191660011790559050612f1c8288888888886135b2565b8063ffffffff16826001600160801b031916896001600160401b03167fbbd56df32f5dff1bd3d121e1d899391d578077964dcda006d45771892e71eda68a8a8a8a8a6040516129b29594939291906145e7565b6001600160401b0382165f9081526005602090815260408083206001600160801b03198516845290915290205460ff1692915050565b5f612fb3858585855f613749565b95945050505050565b5f612fdd63ffffffff8316600160401b600160801b03604086901b166144bc565b60801b9392505050565b5f8080836001811115612ffc57612ffc614620565b1461303a576001600160801b031988165f908152600c602090815260408083206001600160801b03881684528252808320888452909152902061306f565b6001600160801b031988165f908152600d602090815260408083206001600160801b0388168452825280832088845290915290205b90505f8084600181111561308557613085614620565b146130c3576001600160801b031989165f908152600e602090815260408083206001600160801b0389168452825280832089845290915290206130f8565b6001600160801b031989165f908152600f602090815260408083206001600160801b0389168452825280832089845290915290205b905061310b8a8a8a8a8a8a88888c6137b6565b1561311a575f92505050613267565b866131255787613127565b5f5b9250866131495781546131449089906001600160801b031661446e565b61315f565b815461315f9089906001600160801b03166144bc565b82546001600160801b0319166001600160801b03919091161782555f84600181111561318d5761318d614620565b146131a15761319c89866117a4565b6131ab565b6131ab8986612703565b825463ffffffff91909116600160801b0263ffffffff60801b199091161782555f8460018111156131de576131de614620565b036132265760408051808201909152815460ff81161515825261010090046001600160801b03166020820152613221908b908b908b908b908b908b908990613aba565b613264565b60408051808201909152815460ff81161515825261010090046001600160801b03166020820152613264908b908b908b908b908b908b908990613ba2565b50505b979650505050505050565b5f612164611a17856001600160801b0316856001600160801b0316670de0b6b3a764000086613c51565b6001600160801b0316151590565b5f613267878787878787613ca0565b5f6001600160401b038211156132e25760405163397df9e960e21b815260040160405180910390fd5b5090565b5f610a628383613d42565b5f80805f19858709858702925082811083820303915050805f036133285783828161331e5761331e614634565b0492505050612167565b80841161334857604051637ee8242b60e01b815260040160405180910390fd5b5f8486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091025f889003889004909101858311909403939093029303949094049190911702949350505050565b5f6001600160801b038211156132e25760405163e999826d60e01b815260040160405180910390fd5b5f80808360018111156133ed576133ed614620565b1461342b576001600160801b031987165f908152600e602090815260408083206001600160801b038916845282528083208984529091529020613460565b6001600160801b031987165f908152600f602090815260408083206001600160801b0389168452825280832089845290915290205b80549091505f9060ff1661348357815461010090046001600160801b031661348f565b84546001600160801b03165b825490915060ff16156134d057815485546134bb916001600160801b03610100909104811691166144bc565b85546001600160801b0319168655925061351a565b815485546001600160801b0361010090920482169187915f916134f5918591166144bc565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b5f84600181111561352d5761352d614620565b03613568578154604080518082019091525f8082526020820152613563918b918b91859160ff90911615908c908c908c90613aba565b613599565b8154604080518082019091525f8082526020820152613599918b918b91859160ff90911615908c908c908c90613ba2565b5080546001600160881b03191690559695505050505050565b8380158015906135c3575060808111155b6135e057604051637a08c50b60e01b815260040160405180910390fd5b8280158015906135f1575060208111155b61360e576040516314ec8c2560e21b815260040160405180910390fd5b6001600160801b031988165f908152600460205260409020831580156136375750600281015415155b8061364e5750831580159061364e57506002810154155b61366b576040516381e69d9b60e01b815260040160405180910390fd5b831580159061367c57506002810154155b156136d3575f8481526001602052604090205460ff16156136b057604051633ca87f7960e21b815260040160405180910390fd5b5f848152600160208190526040909120805460ff19169091179055600281018490555b806136df888a836146a8565b50600181016136ef8688836146a8565b50505050505050505050565b81515f906001600160801b0316158061372357508163ffffffff16836020015163ffffffff16115b1561372f57505f610a65565b602083015161373e9083614761565b610a62906001614407565b5f8360ff168560ff16036137755761376e611a176001600160801b0385168885613d6b565b9050612fb3565b610f37611a176001600160801b03851661379087600a614860565b61379a908a61486e565b6137a560128a614885565b6137b090600a614860565b86613c51565b5f80808360018111156137cb576137cb614620565b146137df576137da8a876117a4565b6137e9565b6137e98a87612703565b6040805180820190915286546001600160801b0381168252600160801b900463ffffffff1660208201529091506138209082613d89565b1561382e575f915050613aad565b835460ff16801561384757505f896001600160801b0316115b1561386557604051637643c0f960e01b815260040160405180910390fd5b8761387a57835460ff191660011784556138c1565b83548990859060019061389c90849061010090046001600160801b03166144bc565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b5f8360018111156138d4576138d4614620565b036139c2575f600b5f8c6001600160801b0319166001600160801b03191681526020019081526020015f205f886001600160801b03166001600160801b031681526020019081526020015f205f9054906101000a90046001600160801b03169050866001600160801b03168b6001600160801b0319168d6001600160401b03165f5160206148f05f395f51905f52858c8b5f015f9054906101000a90046001600160801b0316878c5f0160019054906101000a90046001600160801b03168d5f015f9054906101000a900460ff166040516139b49695949392919061489e565b60405180910390a450613aa7565b5f600a5f8c6001600160801b0319166001600160801b03191681526020019081526020015f205f886001600160801b03166001600160801b031681526020019081526020015f205f9054906101000a90046001600160801b03169050866001600160801b03168b6001600160801b0319168d6001600160401b03165f5160206149105f395f51905f52858c8b5f015f9054906101000a90046001600160801b0316878c5f0160019054906101000a90046001600160801b03168d5f015f9054906101000a900460ff16604051613a9d9695949392919061489e565b60405180910390a4505b60019150505b9998505050505050505050565b6001600160801b031987165f908152600b602090815260408083206001600160801b0387811685529252909120541685613afd57613af8878261446e565b613b07565b613b0787826144bc565b6001600160801b03198981165f818152600b602090815260408083206001600160801b038b8116808652919093529220805490941690851617909255919250906001600160401b038b165f5160206148f05f395f51905f52613b698c89612703565b875460208801518851604051613b8f94938e936001600160801b03909116928b9261489e565b60405180910390a4505050505050505050565b6001600160801b031987165f908152600a602090815260408083206001600160801b0387811685529252909120541685613be557613be0878261446e565b613bef565b613bef87826144bc565b6001600160801b03198981165f818152600a602090815260408083206001600160801b038b8116808652919093529220805490941690851617909255919250906001600160401b038b165f5160206149105f395f51905f52613b698c896117a4565b5f5f613c5e8686866132f1565b90506001836002811115613c7457613c74614620565b148015613c9057505f8480613c8b57613c8b614634565b868809115b15612fb357610f376001826148dc565b5f613cb3836001600160801b031661329c565b613cfd5760405162461bcd60e51b815260206004820152601b60248201527a50726963696e674c69622f6469766973696f6e2d62792d7a65726f60281b6044820152606401610b07565b613267611a176001600160801b03861689613d1989600a614860565b613d23919061486e565b6001600160801b038716613d388b600a614860565b6137b0919061486e565b5f610a62611a17846001600160801b0316670de0b6b3a7640000856001600160801b03166132f1565b5f612164846001600160801b031684670de0b6b3a764000085613c51565b5f60018263ffffffff16111580613da8575082516001600160801b0316155b80610a6257505060209091015163ffffffff9182169116101590565b80356001600160801b031981168114613ddb575f5ffd5b919050565b6001600160801b0381168114613df4575f5ffd5b50565b5f5f60408385031215613e08575f5ffd5b613e1183613dc4565b91506020830135613e2181613de0565b809150509250929050565b6001600160801b0391909116815260200190565b5f5f5f60608486031215613e52575f5ffd5b613e5b84613dc4565b92506020840135613e6b81613de0565b929592945050506040919091013590565b80356001600160401b0381168114613ddb575f5ffd5b803563ffffffff81168114613ddb575f5ffd5b5f5f5f5f5f5f60c08789031215613eba575f5ffd5b613ec387613e7c565b9550613ed160208801613dc4565b94506040870135613ee181613de0565b9350613eef60608801613e92565b92506080870135613eff81613de0565b915060a0870135613f0f81613de0565b809150509295509295509295565b5f60208284031215613f2d575f5ffd5b610a6282613e7c565b5f5f5f5f60808587031215613f49575f5ffd5b613f5285613e7c565b9350613f6060208601613dc4565b9250604085013591506060850135613f7781613de0565b939692955090935050565b5f5f60408385031215613f93575f5ffd5b613f9c83613e7c565b9150613faa60208401613e92565b90509250929050565b5f5f5f5f5f60a08688031215613fc7575f5ffd5b613fd086613e7c565b9450613fde60208701613dc4565b93506040860135613fee81613de0565b9250613ffc60608701613e92565b9150608086013561400c81613de0565b809150509295509295909350565b5f5f5f6060848603121561402c575f5ffd5b61403584613dc4565b9250602084013561404581613de0565b915061405360408501613e92565b90509250925092565b803561ffff81168114613ddb575f5ffd5b5f5f5f5f5f60a08688031215614081575f5ffd5b61408a8661405c565b945061409860208701613e7c565b93506140a660408701613dc4565b925060608601356140b681613de0565b91506080860135801515811461400c575f5ffd5b5f5f604083850312156140db575f5ffd5b6140e483613dc4565b9150613faa6020840161405c565b5f60208284031215614102575f5ffd5b81356001600160a01b0381168114612167575f5ffd5b5f60208284031215614128575f5ffd5b610a6282613dc4565b5f5f5f5f5f60a08688031215614145575f5ffd5b61414e86613e7c565b945061415c60208701613dc4565b9350604086013561416c81613de0565b925060608601359150608086013561400c81613de0565b5f5f83601f840112614193575f5ffd5b5081356001600160401b038111156141a9575f5ffd5b6020830191508360208285010111156141c0575f5ffd5b9250929050565b5f5f5f5f5f5f608087890312156141dc575f5ffd5b6141e587613e7c565b95506141f360208801613dc4565b945060408701356001600160401b0381111561420d575f5ffd5b61421989828a01614183565b90955093505060608701356001600160401b03811115614237575f5ffd5b61424389828a01614183565b979a9699509497509295939492505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b606081525f6142956060830186614255565b82810360208401526142a78186614255565b915050826040830152949350505050565b5f5f5f606084860312156142ca575f5ffd5b6142d384613dc4565b92506020840135915060408401356142ea81613de0565b809150509250925092565b5f5f5f60608486031215614307575f5ffd5b61431084613e7c565b925061431e60208501613dc4565b915060408401356142ea81613de0565b5f6020828403121561433e575f5ffd5b5035919050565b5f5f5f5f5f5f6080878903121561435a575f5ffd5b61436387613e7c565b955060208701356001600160401b0381111561437d575f5ffd5b61438989828a01614183565b90965094505060408701356001600160401b038111156143a7575f5ffd5b6143b389828a01614183565b979a9699509497949695606090950135949350505050565b5f5f604083850312156143dc575f5ffd5b6143e583613e7c565b9150613faa60208401613dc4565b634e487b7160e01b5f52601160045260245ffd5b63ffffffff8181168382160190811115610a6557610a656143f3565b63ffffffff92831681529116602082015260400190565b5f6020828403121561444a575f5ffd5b815160ff81168114612167575f5ffd5b6001600160401b0391909116815260200190565b6001600160801b038281168282160390811115610a6557610a656143f3565b63ffffffff9490941684526001600160801b039283166020850152908216604084015216606082015260800190565b6001600160801b038181168382160190811115610a6557610a656143f3565b61ffff9290921682526001600160801b0316602082015260400190565b63ffffffff96909616865260208601949094526001600160801b03928316604086015290821660608501521660808301526001600160401b031660a082015260c00190565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b604081525f61457860408301868861453d565b828103602084015261326781858761453d565b600181811c9082168061459f57607f821691505b6020821081036145bd57634e487b7160e01b5f52602260045260245ffd5b50919050565b5f63ffffffff821663ffffffff81036145de576145de6143f3565b60010192915050565b606081525f6145fa60608301878961453d565b828103602084015261460d81868861453d565b9150508260408301529695505050505050565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52601260045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b601f8211156146a357805f5260205f20601f840160051c810160208510156146815750805b601f840160051c820191505b818110156146a0575f815560010161468d565b50505b505050565b6001600160401b038311156146bf576146bf614648565b6146d3836146cd835461458b565b8361465c565b5f601f841160018114614704575f85156146ed5750838201355b5f19600387901b1c1916600186901b1783556146a0565b5f83815260208120601f198716915b828110156147335786850135825560209485019460019092019101614713565b508682101561474f575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b63ffffffff8281168282160390811115610a6557610a656143f3565b6001815b60018411156147b85780850481111561479c5761479c6143f3565b60018416156147aa57908102905b60019390931c928002614781565b935093915050565b5f826147ce57506001610a65565b816147da57505f610a65565b81600181146147f057600281146147fa57614816565b6001915050610a65565b60ff84111561480b5761480b6143f3565b50506001821b610a65565b5060208310610133831016604e8410600b8410161715614839575081810a610a65565b6148455f19848461477d565b805f1904821115614858576148586143f3565b029392505050565b5f610a6260ff8416836147c0565b8082028115828204841417610a6557610a656143f3565b60ff8181168382160190811115610a6557610a656143f3565b63ffffffff96909616865260208601949094526001600160801b0392831660408601529082166060850152166080830152151560a082015260c00190565b80820180821115610a6557610a656143f356fe1f5a5e518a04d4ae04df91bdcdfc726fd04628d7968eb87c8a177f55336765ff688670c8bdbd011a5a7703624480229795dd54b1ead47cb9a66061fa8b2e0b7aa264697066735822122037736688b1e69a7f3366472d63f827c0884c18babd2501e7b3530c0cf1c1ba3864736f6c634300081c0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000012044ef361cc3446cb7d36541c8411ee4e6f52cb0000000000000000000000005f3f8ea3b54bff7795de7754866e0eac52e0881d

-----Decoded View---------------
Arg [0] : hubRegistry_ (address): 0x12044ef361Cc3446Cb7d36541C8411EE4e6f52cb
Arg [1] : deployer (address): 0x5f3f8ea3b54BFF7795dE7754866e0Eac52e0881d

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 00000000000000000000000012044ef361cc3446cb7d36541c8411ee4e6f52cb
Arg [1] : 0000000000000000000000005f3f8ea3b54bff7795de7754866e0eac52e0881d


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
Loading...
Loading
[ 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.