ETH Price: $2,391.71 (+3.11%)
 

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

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Resolver

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

import {SchemaResolver} from "eas-contracts/resolver/SchemaResolver.sol";
import {IEAS, Attestation} from "eas-contracts/IEAS.sol";
import {IInvestorRegistry} from "src/interfaces/IInvestorRegistry.sol";
import "src/utils/Constant.sol";

/**
 * @title Resolver
 * @notice This contract handles the validation of the custom attestation data
 * @dev The contract utilizes SchemaResolver for EAS-based attestation validation
 * @author Kaio
 */
contract Resolver is SchemaResolver {
    /// @notice Investor registry to manage investor-related data and credentials.
    IInvestorRegistry public immutable investorRegistry;

    constructor(IEAS eas, IInvestorRegistry _investorRegistry) SchemaResolver(eas) {
        investorRegistry = _investorRegistry;
    }

    /**
     * @notice Handles logic for attestation verification.
     * @param _attestation The attestation data.
     * @return Whether the attestation passes verification.
     * @dev Includes credential validation by performing schema integrity and origin checks
     */
    function onAttest(
        Attestation calldata _attestation,
        uint256 /*value*/
    )
        internal
        view
        override
        returns (bool)
    {
        if (_attestation.attester != address(investorRegistry)) {
            return false;
        }
        //Decode attestation schema
        (, bytes32[] memory instrumentIds, uint64[] memory instrumentExpiryTimestamps) =
            abi.decode(_attestation.data, (bytes32, bytes32[], uint64[]));

        //Check the sizes of instrument list and their corresponding expiries are equal
        if (instrumentIds.length != instrumentExpiryTimestamps.length) {
            return false;
        }

        return true;
    }

    /**
     * @notice Handles logic for attestation revocation.
     * @return Always returns true as all revocation requests are allowed.
     */
    function onRevoke(
        Attestation calldata,
        /*attestation*/
        uint256 /*value*/
    )
        internal
        pure
        override
        returns (bool)
    {
        return true;
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

import { AccessDenied, InvalidEAS, InvalidLength, uncheckedInc } from "./../Common.sol";
import { IEAS, Attestation } from "./../IEAS.sol";
import { Semver } from "./../Semver.sol";
import { ISchemaResolver } from "./ISchemaResolver.sol";

/// @title SchemaResolver
/// @notice The base schema resolver contract.
abstract contract SchemaResolver is ISchemaResolver, Semver {
    error InsufficientValue();
    error NotPayable();

    // The global EAS contract.
    IEAS internal immutable _eas;

    /// @dev Creates a new resolver.
    /// @param eas The address of the global EAS contract.
    constructor(IEAS eas) Semver(1, 3, 0) {
        if (address(eas) == address(0)) {
            revert InvalidEAS();
        }

        _eas = eas;
    }

    /// @dev Ensures that only the EAS contract can make this call.
    modifier onlyEAS() {
        _onlyEAS();

        _;
    }

    /// @inheritdoc ISchemaResolver
    function isPayable() public pure virtual returns (bool) {
        return false;
    }

    /// @dev ETH callback.
    receive() external payable virtual {
        if (!isPayable()) {
            revert NotPayable();
        }
    }

    /// @inheritdoc ISchemaResolver
    function attest(Attestation calldata attestation) external payable onlyEAS returns (bool) {
        return onAttest(attestation, msg.value);
    }

    /// @inheritdoc ISchemaResolver
    function multiAttest(
        Attestation[] calldata attestations,
        uint256[] calldata values
    ) external payable onlyEAS returns (bool) {
        uint256 length = attestations.length;
        if (length != values.length) {
            revert InvalidLength();
        }

        // We are keeping track of the remaining ETH amount that can be sent to resolvers and will keep deducting
        // from it to verify that there isn't any attempt to send too much ETH to resolvers. Please note that unless
        // some ETH was stuck in the contract by accident (which shouldn't happen in normal conditions), it won't be
        // possible to send too much ETH anyway.
        uint256 remainingValue = msg.value;

        for (uint256 i = 0; i < length; i = uncheckedInc(i)) {
            // Ensure that the attester/revoker doesn't try to spend more than available.
            uint256 value = values[i];
            if (value > remainingValue) {
                revert InsufficientValue();
            }

            // Forward the attestation to the underlying resolver and return false in case it isn't approved.
            if (!onAttest(attestations[i], value)) {
                return false;
            }

            unchecked {
                // Subtract the ETH amount, that was provided to this attestation, from the global remaining ETH amount.
                remainingValue -= value;
            }
        }

        return true;
    }

    /// @inheritdoc ISchemaResolver
    function revoke(Attestation calldata attestation) external payable onlyEAS returns (bool) {
        return onRevoke(attestation, msg.value);
    }

    /// @inheritdoc ISchemaResolver
    function multiRevoke(
        Attestation[] calldata attestations,
        uint256[] calldata values
    ) external payable onlyEAS returns (bool) {
        uint256 length = attestations.length;
        if (length != values.length) {
            revert InvalidLength();
        }

        // We are keeping track of the remaining ETH amount that can be sent to resolvers and will keep deducting
        // from it to verify that there isn't any attempt to send too much ETH to resolvers. Please note that unless
        // some ETH was stuck in the contract by accident (which shouldn't happen in normal conditions), it won't be
        // possible to send too much ETH anyway.
        uint256 remainingValue = msg.value;

        for (uint256 i = 0; i < length; i = uncheckedInc(i)) {
            // Ensure that the attester/revoker doesn't try to spend more than available.
            uint256 value = values[i];
            if (value > remainingValue) {
                revert InsufficientValue();
            }

            // Forward the revocation to the underlying resolver and return false in case it isn't approved.
            if (!onRevoke(attestations[i], value)) {
                return false;
            }

            unchecked {
                // Subtract the ETH amount, that was provided to this attestation, from the global remaining ETH amount.
                remainingValue -= value;
            }
        }

        return true;
    }

    /// @notice A resolver callback that should be implemented by child contracts.
    /// @param attestation The new attestation.
    /// @param value An explicit ETH amount that was sent to the resolver. Please note that this value is verified in
    ///     both attest() and multiAttest() callbacks EAS-only callbacks and that in case of multi attestations, it'll
    ///     usually hold that msg.value != value, since msg.value aggregated the sent ETH amounts for all the
    ///     attestations in the batch.
    /// @return Whether the attestation is valid.
    function onAttest(Attestation calldata attestation, uint256 value) internal virtual returns (bool);

    /// @notice Processes an attestation revocation and verifies if it can be revoked.
    /// @param attestation The existing attestation to be revoked.
    /// @param value An explicit ETH amount that was sent to the resolver. Please note that this value is verified in
    ///     both revoke() and multiRevoke() callbacks EAS-only callbacks and that in case of multi attestations, it'll
    ///     usually hold that msg.value != value, since msg.value aggregated the sent ETH amounts for all the
    ///     attestations in the batch.
    /// @return Whether the attestation can be revoked.
    function onRevoke(Attestation calldata attestation, uint256 value) internal virtual returns (bool);

    /// @dev Ensures that only the EAS contract can make this call.
    function _onlyEAS() private view {
        if (msg.sender != address(_eas)) {
            revert AccessDenied();
        }
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { ISchemaRegistry } from "./ISchemaRegistry.sol";
import { ISemver } from "./ISemver.sol";
import { Attestation, Signature } from "./Common.sol";

/// @notice A struct representing the arguments of the attestation request.
struct AttestationRequestData {
    address recipient; // The recipient of the attestation.
    uint64 expirationTime; // The time when the attestation expires (Unix timestamp).
    bool revocable; // Whether the attestation is revocable.
    bytes32 refUID; // The UID of the related attestation.
    bytes data; // Custom attestation data.
    uint256 value; // An explicit ETH amount to send to the resolver. This is important to prevent accidental user errors.
}

/// @notice A struct representing the full arguments of the attestation request.
struct AttestationRequest {
    bytes32 schema; // The unique identifier of the schema.
    AttestationRequestData data; // The arguments of the attestation request.
}

/// @notice A struct representing the full arguments of the full delegated attestation request.
struct DelegatedAttestationRequest {
    bytes32 schema; // The unique identifier of the schema.
    AttestationRequestData data; // The arguments of the attestation request.
    Signature signature; // The ECDSA signature data.
    address attester; // The attesting account.
    uint64 deadline; // The deadline of the signature/request.
}

/// @notice A struct representing the full arguments of the multi attestation request.
struct MultiAttestationRequest {
    bytes32 schema; // The unique identifier of the schema.
    AttestationRequestData[] data; // The arguments of the attestation request.
}

/// @notice A struct representing the full arguments of the delegated multi attestation request.
struct MultiDelegatedAttestationRequest {
    bytes32 schema; // The unique identifier of the schema.
    AttestationRequestData[] data; // The arguments of the attestation requests.
    Signature[] signatures; // The ECDSA signatures data. Please note that the signatures are assumed to be signed with increasing nonces.
    address attester; // The attesting account.
    uint64 deadline; // The deadline of the signature/request.
}

/// @notice A struct representing the arguments of the revocation request.
struct RevocationRequestData {
    bytes32 uid; // The UID of the attestation to revoke.
    uint256 value; // An explicit ETH amount to send to the resolver. This is important to prevent accidental user errors.
}

/// @notice A struct representing the full arguments of the revocation request.
struct RevocationRequest {
    bytes32 schema; // The unique identifier of the schema.
    RevocationRequestData data; // The arguments of the revocation request.
}

/// @notice A struct representing the arguments of the full delegated revocation request.
struct DelegatedRevocationRequest {
    bytes32 schema; // The unique identifier of the schema.
    RevocationRequestData data; // The arguments of the revocation request.
    Signature signature; // The ECDSA signature data.
    address revoker; // The revoking account.
    uint64 deadline; // The deadline of the signature/request.
}

/// @notice A struct representing the full arguments of the multi revocation request.
struct MultiRevocationRequest {
    bytes32 schema; // The unique identifier of the schema.
    RevocationRequestData[] data; // The arguments of the revocation request.
}

/// @notice A struct representing the full arguments of the delegated multi revocation request.
struct MultiDelegatedRevocationRequest {
    bytes32 schema; // The unique identifier of the schema.
    RevocationRequestData[] data; // The arguments of the revocation requests.
    Signature[] signatures; // The ECDSA signatures data. Please note that the signatures are assumed to be signed with increasing nonces.
    address revoker; // The revoking account.
    uint64 deadline; // The deadline of the signature/request.
}

/// @title IEAS
/// @notice EAS - Ethereum Attestation Service interface.
interface IEAS is ISemver {
    /// @notice Emitted when an attestation has been made.
    /// @param recipient The recipient of the attestation.
    /// @param attester The attesting account.
    /// @param uid The UID of the new attestation.
    /// @param schemaUID The UID of the schema.
    event Attested(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schemaUID);

    /// @notice Emitted when an attestation has been revoked.
    /// @param recipient The recipient of the attestation.
    /// @param attester The attesting account.
    /// @param schemaUID The UID of the schema.
    /// @param uid The UID the revoked attestation.
    event Revoked(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schemaUID);

    /// @notice Emitted when a data has been timestamped.
    /// @param data The data.
    /// @param timestamp The timestamp.
    event Timestamped(bytes32 indexed data, uint64 indexed timestamp);

    /// @notice Emitted when a data has been revoked.
    /// @param revoker The address of the revoker.
    /// @param data The data.
    /// @param timestamp The timestamp.
    event RevokedOffchain(address indexed revoker, bytes32 indexed data, uint64 indexed timestamp);

    /// @notice Returns the address of the global schema registry.
    /// @return The address of the global schema registry.
    function getSchemaRegistry() external view returns (ISchemaRegistry);

    /// @notice Attests to a specific schema.
    /// @param request The arguments of the attestation request.
    /// @return The UID of the new attestation.
    ///
    /// Example:
    ///     attest({
    ///         schema: "0facc36681cbe2456019c1b0d1e7bedd6d1d40f6f324bf3dd3a4cef2999200a0",
    ///         data: {
    ///             recipient: "0xdEADBeAFdeAdbEafdeadbeafDeAdbEAFdeadbeaf",
    ///             expirationTime: 0,
    ///             revocable: true,
    ///             refUID: "0x0000000000000000000000000000000000000000000000000000000000000000",
    ///             data: "0xF00D",
    ///             value: 0
    ///         }
    ///     })
    function attest(AttestationRequest calldata request) external payable returns (bytes32);

    /// @notice Attests to a specific schema via the provided ECDSA signature.
    /// @param delegatedRequest The arguments of the delegated attestation request.
    /// @return The UID of the new attestation.
    ///
    /// Example:
    ///     attestByDelegation({
    ///         schema: '0x8e72f5bc0a8d4be6aa98360baa889040c50a0e51f32dbf0baa5199bd93472ebc',
    ///         data: {
    ///             recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    ///             expirationTime: 1673891048,
    ///             revocable: true,
    ///             refUID: '0x0000000000000000000000000000000000000000000000000000000000000000',
    ///             data: '0x1234',
    ///             value: 0
    ///         },
    ///         signature: {
    ///             v: 28,
    ///             r: '0x148c...b25b',
    ///             s: '0x5a72...be22'
    ///         },
    ///         attester: '0xc5E8740aD971409492b1A63Db8d83025e0Fc427e',
    ///         deadline: 1673891048
    ///     })
    function attestByDelegation(
        DelegatedAttestationRequest calldata delegatedRequest
    ) external payable returns (bytes32);

    /// @notice Attests to multiple schemas.
    /// @param multiRequests The arguments of the multi attestation requests. The requests should be grouped by distinct
    ///     schema ids to benefit from the best batching optimization.
    /// @return The UIDs of the new attestations.
    ///
    /// Example:
    ///     multiAttest([{
    ///         schema: '0x33e9094830a5cba5554d1954310e4fbed2ef5f859ec1404619adea4207f391fd',
    ///         data: [{
    ///             recipient: '0xdEADBeAFdeAdbEafdeadbeafDeAdbEAFdeadbeaf',
    ///             expirationTime: 1673891048,
    ///             revocable: true,
    ///             refUID: '0x0000000000000000000000000000000000000000000000000000000000000000',
    ///             data: '0x1234',
    ///             value: 1000
    ///         },
    ///         {
    ///             recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    ///             expirationTime: 0,
    ///             revocable: false,
    ///             refUID: '0x480df4a039efc31b11bfdf491b383ca138b6bde160988222a2a3509c02cee174',
    ///             data: '0x00',
    ///             value: 0
    ///         }],
    ///     },
    ///     {
    ///         schema: '0x5ac273ce41e3c8bfa383efe7c03e54c5f0bff29c9f11ef6ffa930fc84ca32425',
    ///         data: [{
    ///             recipient: '0xdEADBeAFdeAdbEafdeadbeafDeAdbEAFdeadbeaf',
    ///             expirationTime: 0,
    ///             revocable: true,
    ///             refUID: '0x75bf2ed8dca25a8190c50c52db136664de25b2449535839008ccfdab469b214f',
    ///             data: '0x12345678',
    ///             value: 0
    ///         },
    ///     }])
    function multiAttest(MultiAttestationRequest[] calldata multiRequests) external payable returns (bytes32[] memory);

    /// @notice Attests to multiple schemas using via provided ECDSA signatures.
    /// @param multiDelegatedRequests The arguments of the delegated multi attestation requests. The requests should be
    ///     grouped by distinct schema ids to benefit from the best batching optimization.
    /// @return The UIDs of the new attestations.
    ///
    /// Example:
    ///     multiAttestByDelegation([{
    ///         schema: '0x8e72f5bc0a8d4be6aa98360baa889040c50a0e51f32dbf0baa5199bd93472ebc',
    ///         data: [{
    ///             recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    ///             expirationTime: 1673891048,
    ///             revocable: true,
    ///             refUID: '0x0000000000000000000000000000000000000000000000000000000000000000',
    ///             data: '0x1234',
    ///             value: 0
    ///         },
    ///         {
    ///             recipient: '0xdEADBeAFdeAdbEafdeadbeafDeAdbEAFdeadbeaf',
    ///             expirationTime: 0,
    ///             revocable: false,
    ///             refUID: '0x0000000000000000000000000000000000000000000000000000000000000000',
    ///             data: '0x00',
    ///             value: 0
    ///         }],
    ///         signatures: [{
    ///             v: 28,
    ///             r: '0x148c...b25b',
    ///             s: '0x5a72...be22'
    ///         },
    ///         {
    ///             v: 28,
    ///             r: '0x487s...67bb',
    ///             s: '0x12ad...2366'
    ///         }],
    ///         attester: '0x1D86495b2A7B524D747d2839b3C645Bed32e8CF4',
    ///         deadline: 1673891048
    ///     }])
    function multiAttestByDelegation(
        MultiDelegatedAttestationRequest[] calldata multiDelegatedRequests
    ) external payable returns (bytes32[] memory);

    /// @notice Revokes an existing attestation to a specific schema.
    /// @param request The arguments of the revocation request.
    ///
    /// Example:
    ///     revoke({
    ///         schema: '0x8e72f5bc0a8d4be6aa98360baa889040c50a0e51f32dbf0baa5199bd93472ebc',
    ///         data: {
    ///             uid: '0x101032e487642ee04ee17049f99a70590c735b8614079fc9275f9dd57c00966d',
    ///             value: 0
    ///         }
    ///     })
    function revoke(RevocationRequest calldata request) external payable;

    /// @notice Revokes an existing attestation to a specific schema via the provided ECDSA signature.
    /// @param delegatedRequest The arguments of the delegated revocation request.
    ///
    /// Example:
    ///     revokeByDelegation({
    ///         schema: '0x8e72f5bc0a8d4be6aa98360baa889040c50a0e51f32dbf0baa5199bd93472ebc',
    ///         data: {
    ///             uid: '0xcbbc12102578c642a0f7b34fe7111e41afa25683b6cd7b5a14caf90fa14d24ba',
    ///             value: 0
    ///         },
    ///         signature: {
    ///             v: 27,
    ///             r: '0xb593...7142',
    ///             s: '0x0f5b...2cce'
    ///         },
    ///         revoker: '0x244934dd3e31bE2c81f84ECf0b3E6329F5381992',
    ///         deadline: 1673891048
    ///     })
    function revokeByDelegation(DelegatedRevocationRequest calldata delegatedRequest) external payable;

    /// @notice Revokes existing attestations to multiple schemas.
    /// @param multiRequests The arguments of the multi revocation requests. The requests should be grouped by distinct
    ///     schema ids to benefit from the best batching optimization.
    ///
    /// Example:
    ///     multiRevoke([{
    ///         schema: '0x8e72f5bc0a8d4be6aa98360baa889040c50a0e51f32dbf0baa5199bd93472ebc',
    ///         data: [{
    ///             uid: '0x211296a1ca0d7f9f2cfebf0daaa575bea9b20e968d81aef4e743d699c6ac4b25',
    ///             value: 1000
    ///         },
    ///         {
    ///             uid: '0xe160ac1bd3606a287b4d53d5d1d6da5895f65b4b4bab6d93aaf5046e48167ade',
    ///             value: 0
    ///         }],
    ///     },
    ///     {
    ///         schema: '0x5ac273ce41e3c8bfa383efe7c03e54c5f0bff29c9f11ef6ffa930fc84ca32425',
    ///         data: [{
    ///             uid: '0x053d42abce1fd7c8fcddfae21845ad34dae287b2c326220b03ba241bc5a8f019',
    ///             value: 0
    ///         },
    ///     }])
    function multiRevoke(MultiRevocationRequest[] calldata multiRequests) external payable;

    /// @notice Revokes existing attestations to multiple schemas via provided ECDSA signatures.
    /// @param multiDelegatedRequests The arguments of the delegated multi revocation attestation requests. The requests
    ///     should be grouped by distinct schema ids to benefit from the best batching optimization.
    ///
    /// Example:
    ///     multiRevokeByDelegation([{
    ///         schema: '0x8e72f5bc0a8d4be6aa98360baa889040c50a0e51f32dbf0baa5199bd93472ebc',
    ///         data: [{
    ///             uid: '0x211296a1ca0d7f9f2cfebf0daaa575bea9b20e968d81aef4e743d699c6ac4b25',
    ///             value: 1000
    ///         },
    ///         {
    ///             uid: '0xe160ac1bd3606a287b4d53d5d1d6da5895f65b4b4bab6d93aaf5046e48167ade',
    ///             value: 0
    ///         }],
    ///         signatures: [{
    ///             v: 28,
    ///             r: '0x148c...b25b',
    ///             s: '0x5a72...be22'
    ///         },
    ///         {
    ///             v: 28,
    ///             r: '0x487s...67bb',
    ///             s: '0x12ad...2366'
    ///         }],
    ///         revoker: '0x244934dd3e31bE2c81f84ECf0b3E6329F5381992',
    ///         deadline: 1673891048
    ///     }])
    function multiRevokeByDelegation(
        MultiDelegatedRevocationRequest[] calldata multiDelegatedRequests
    ) external payable;

    /// @notice Timestamps the specified bytes32 data.
    /// @param data The data to timestamp.
    /// @return The timestamp the data was timestamped with.
    function timestamp(bytes32 data) external returns (uint64);

    /// @notice Timestamps the specified multiple bytes32 data.
    /// @param data The data to timestamp.
    /// @return The timestamp the data was timestamped with.
    function multiTimestamp(bytes32[] calldata data) external returns (uint64);

    /// @notice Revokes the specified bytes32 data.
    /// @param data The data to timestamp.
    /// @return The timestamp the data was revoked with.
    function revokeOffchain(bytes32 data) external returns (uint64);

    /// @notice Revokes the specified multiple bytes32 data.
    /// @param data The data to timestamp.
    /// @return The timestamp the data was revoked with.
    function multiRevokeOffchain(bytes32[] calldata data) external returns (uint64);

    /// @notice Returns an existing attestation by UID.
    /// @param uid The UID of the attestation to retrieve.
    /// @return The attestation data members.
    function getAttestation(bytes32 uid) external view returns (Attestation memory);

    /// @notice Checks whether an attestation exists.
    /// @param uid The UID of the attestation to retrieve.
    /// @return Whether an attestation exists.
    function isAttestationValid(bytes32 uid) external view returns (bool);

    /// @notice Returns the timestamp that the specified data was timestamped with.
    /// @param data The data to query.
    /// @return The timestamp the data was timestamped with.
    function getTimestamp(bytes32 data) external view returns (uint64);

    /// @notice Returns the timestamp that the specified data was timestamped with.
    /// @param data The data to query.
    /// @return The timestamp the data was timestamped with.
    function getRevokeOffchain(address revoker, bytes32 data) external view returns (uint64);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import {
    IERC20MetadataUpgradeable
} from "openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/IERC20MetadataUpgradeable.sol";

import {IEAS, AttestationRequestData} from "eas-contracts/IEAS.sol";

import {IBaseRegistry} from "src/interfaces/IBaseRegistry.sol";
import {IRoleRegistry} from "src/interfaces/IRoleRegistry.sol";
import {IInstrumentRegistry} from "src/interfaces/IInstrumentRegistry.sol";
import {IDealerRegistry} from "src/interfaces/IDealerRegistry.sol";
import {IOperationsEngine} from "src/interfaces/IOperationsEngine.sol";
import {IUserRegistry} from "src/interfaces/IUserRegistry.sol";
import {IGatewayManager} from "src/interfaces/IGatewayManager.sol";
import {IEternalRegistryStorage} from "src/interfaces/IEternalRegistryStorage.sol";
import {InstrumentAccess} from "src/utils/Types.sol";

/**
 * @title IInvestorRegistry
 * @author Libre
 * @notice Interface for managing investors, including adding new investors, managing credentials, and verifying subscriptions.
 * It integrates with Ethereum Attestation Service (EAS) to ensure security and compliance.
 */
interface IInvestorRegistry is IBaseRegistry, IUserRegistry {
    /// @notice Thrown when an operation is not permitted due to insufficient privileges.
    error IInvestorRegistry_OperationNotPermitted();

    /// @notice Thrown when the sender is not the dealer associated with the specified investor.
    error IInvestorRegistry_NotInvestorsDealer();

    /// @notice Thrown when the caller is not the Operations Engine.
    error IInvestorRegistry_NotOperationsEngine();

    /// @notice Thrown when the caller is not the Jurisdiction Registry.
    error IInvestorRegistry_NotJurisdictionRegistry();

    /// @notice Thrown when attempting to add an investor who is already registered.
    error IInvestorRegistry_AlreadyAnInvestor();

    /// @notice Thrown when the sender does not have the Contract Manager role.
    error IInvestorRegistry_NoContractManagerRole();

    /// @notice Thrown when the Resolver address is empty or invalid.
    error IInvestorRegistry_EmptyResolver();

    /// @notice Thrown when the sender does not have the Attester role.
    error IInvestorRegistry_NoAttesterRole();

    /// @notice Thrown when attempting to access a schema that has not been set.
    error IInvestorRegistry_SchemaNotSet();

    /// @notice Thrown when attempting to access a credential that does not exist.
    error IInvestorRegistry_NonExistingCredential();

    /// @notice Thrown when the credential is found to be invalid or revoked.
    error IInvestorRegistry_CredentialInvalid();

    /// @notice Thrown when the Investor Id doesn't match with the attestation's investor Id.
    error IInvestorRegistry_MismatchInvestorId();

    /// @notice Thrown when a credential does not grant access to the specified instrument.
    error IInvestorRegistry_InstrumentCredentialInvalid();

    /// @notice Thrown when attempting to revoke access to instruments that are not part of the credential.
    error IInvestorRegistry_InstrumentsToRevokeNotInCredential();

    /// @notice Thrown when an invalid dealer ID is provided.
    error IInvestorRegistry_InvalidDealerId();

    // Events
    /**
     * @notice Emitted when a new investor is added to the registry.
     * @param investorId The unique identifier of the new investor.
     * @param wallet The initial wallet address associated with the investor.
     */
    event NewInvestorAdded(bytes32 investorId, address wallet);

    /**
     * @notice Emitted when a credential is set for an investor.
     * @param investorId The unique identifier of the investor.
     * @param instrumentIds The list of instrument IDs included in the credential.
     */
    event InvestorRegistryCredentialSet(bytes32 investorId, bytes32[] instrumentIds);

    /**
     * @notice Emitted when a credential is revoked for an investor.
     * @param investorId The unique identifier of the investor.
     * @param instrumentIds The list of instrument IDs that have been revoked.
     */
    event InvestorRegistryCredentialRevoked(bytes32 investorId, bytes32[] instrumentIds);

    /**
     * @notice Emitted when the Resolver is updated.
     * @param resolverAddress The address of the new Resolver.
     */
    event InvestorRegistryResolverChanged(address resolverAddress);

    /**
     * @notice Initialize the registry
     * @param _roleRegistry Address of the Role Registry
     * @param _instrumentRegistry Address of the Instrument Registry
     * @param _dealerRegistry Address of the Dealer Registry
     * @param _jurisdictionRegistry Address of the Jurisdiction Registry
     * @param _operationsEngine Address of the Operations Engine
     * @param _eas Address of the Ethereum Attestation Service contract
     * @param _gatewayManager Address of the gateway manager contract
     * @param _resolver Address of the EAS Resolver
     */
    function initialize(
        IRoleRegistry _roleRegistry,
        IInstrumentRegistry _instrumentRegistry,
        IDealerRegistry _dealerRegistry,
        IEternalRegistryStorage _jurisdictionRegistry,
        IOperationsEngine _operationsEngine,
        IEAS _eas,
        IGatewayManager _gatewayManager,
        address _resolver
    ) external;

    /**
     * @notice Adds a new investor to the registry
     * @param _senderRole Role the sender claims to have
     * @param _investorId ID of the investor
     * @param _wallet Default address of the investor
     * @param _dealerId ID of the dealer
     */
    function addInvestor(bytes32 _senderRole, bytes32 _investorId, address _wallet, bytes32 _dealerId) external;

    /**
     * @notice Replaces the Resolver contract with a new version, renewing the schema in the process
     * @param _resolver address of the new resolver
     */
    function configureResolver(address _resolver) external;

    /**
     * @notice Creates a new investor credential or updates an existing/revoked one
     * @param _instrumentAccess contains data for setting the credential
     * @dev When updating credential, the lists should contain new instruments to provide access to
     */
    function setCredential(InstrumentAccess calldata _instrumentAccess) external;

    /**
     * @notice Revokes an investor credential, or access to certain instruments, bridge revoked credential via gateway manager
     * @param _investorId Id of the investor
     * @param _instrumentIds Instruments to revoke access from (entire credential revoked if left empty)
     */
    function revokeCredential(bytes32 _investorId, bytes32[] calldata _instrumentIds) external;

    /**
     * @notice Checks if the investor has a valid credential
     * @param _investorId Id of the investor
     */
    function checkCredential(bytes32 _investorId) external view;

    /**
     * @notice Checks if the investor has a valid credential granting access to the instrument
     * @param _investorId Id of the investor
     * @param _instrumentId Id of the instrument
     */
    function checkCredentialInstrument(bytes32 _investorId, bytes32 _instrumentId) external view;

    /**
     * @notice Checks if the investor has a valid credential granting access to the instrument for cross-chain operations
     * @param _investorId Id of the investor
     * @param _instrumentId Id of the instrument
     * @dev Includes 1 day threshold validation for cross-chain operations
     */
    function checkCredentialInstrumentForCrossChain(bytes32 _investorId, bytes32 _instrumentId) external view;

    /**
     * @notice Get dealer id of an investor
     * @param _investorId The ID of the investor
     * @return dealerId_ Id of the dealer associated to the investor
     */
    function getDealer(bytes32 _investorId) external view returns (bytes32 dealerId_);
}

File 5 of 32 : Constant.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

import "src/utils/JurisdictionEncodings.sol";
import "src/utils/Types.sol";

// General Config
bytes32 constant TIMEZONE = keccak256("TIMEZONE");
bytes32 constant MAX_TIMEZONE = keccak256("MAX_TIMEZONE");

// Chain/Gateway Config
uint16 constant NATIVE_CHAIN_SELECTOR = 101;

// NAV
bytes32 constant AUDITED_NAV_PER_SHARE = keccak256("AUDITED_NAV_PER_SHARE");
bytes32 constant INSTRUMENT_ALLOWED_ADDRESSES = keccak256("INSTRUMENT_ALLOWED_ADDRESSES");

// Volatility
bytes32 constant ANNUALIZED_VOLATILITY = keccak256("ANNUALIZED_VOLATILITY");

// Subscription Config
bytes32 constant SUBSCRIPTION_PHASES = keccak256("SUBSCRIPTION_PHASES");
bytes32 constant SUBSCRIPTION_START = keccak256("SUBSCRIPTION_START");
bytes32 constant SUBSCRIPTION_END = keccak256("SUBSCRIPTION_END");
bytes32 constant SUBSCRIPTION_FREQUENCY = keccak256("SUBSCRIPTION_FREQUENCY");
bytes32 constant SUBSCRIPTION_FREQUENCY_TIME_UNIT = keccak256("SUBSCRIPTION_FREQUENCY_TIME_UNIT");

// Redemption Config
bytes32 constant REDEMPTION_START = keccak256("REDEMPTION_START");
bytes32 constant REDEMPTION_END = keccak256("REDEMPTION_END");
bytes32 constant REDEMPTION_FREQUENCY = keccak256("REDEMPTION_FREQUENCY");
bytes32 constant REDEMPTION_FREQUENCY_TIME_UNIT = keccak256("REDEMPTION_FREQUENCY_TIME_UNIT");

// Fund data
bytes32 constant FUND_ID = keccak256("FUND_ID");
bytes32 constant DISCLOSURE_DOCUMENTS = keccak256("DISCLOSURE_DOCUMENTS");
bytes32 constant LISTED_ON_REGULATED_VENUE = keccak256("LISTED_ON_REGULATED_VENUE");
bytes32 constant LOCAL_AIFM = keccak256("LOCAL_AIFM");
bytes32 constant NON_EU_AIFM = keccak256("NON_EU_AIFM");

// Fund Instruments data
bytes32 constant INSTRUMENT_TOKEN = keccak256("INSTRUMENT_TOKEN");
bytes32 constant LOCAL_INVESTOR_MINIMUM_HOLDING = keccak256("LOCAL_INVESTOR_MINIMUM_HOLDING");
bytes32 constant AGGREGATED_MINIMUM_REDEMPTION_VALUE = keccak256("AGGREGATED_MINIMUM_REDEMPTION_VALUE");
bytes32 constant AGGREGATED_MINIMUM_SUBSEQUENT_SUBSCRIPTION = keccak256("AGGREGATED_MINIMUM_SUBSEQUENT_SUBSCRIPTION");
bytes32 constant INVESTOR_MINIMUM_INITIAL_SUBSCRIPTION = keccak256("INVESTOR_MINIMUM_INITIAL_SUBSCRIPTION");
bytes32 constant INVESTOR_MINIMUM_SUBSEQUENT_SUBSCRIPTION = keccak256("INVESTOR_MINIMUM_SUBSEQUENT_SUBSCRIPTION");
bytes32 constant REDEMPTION_BOOK = keccak256("REDEMPTION_BOOK");
bytes32 constant SUBSCRIPTION_BOOK = keccak256("SUBSCRIPTION_BOOK");

// Subscription Cutoff
bytes32 constant SUBSCRIPTION_CUT_OFF_CALENDAR_DAYS = keccak256("SUBSCRIPTION_CUT_OFF_CALENDAR_DAYS");
bytes32 constant SUBSCRIPTION_CUT_OFF_BUSINESS_DAYS = keccak256("SUBSCRIPTION_CUT_OFF_BUSINESS_DAYS");
bytes32 constant SUBSCRIPTION_CUT_OFF_TIME = keccak256("SUBSCRIPTION_CUT_OFF_TIME");
bytes32 constant SUBSCRIPTION_CUT_OFF_PERIOD = keccak256("SUBSCRIPTION_CUT_OFF_PERIOD");

bytes32 constant LOCAL_AGGREGATE_MINIMUM_HOLDING = keccak256("LOCAL_AGGREGATE_MINIMUM_HOLDING");
bytes32 constant SUBSCRIPTION_SIZE_MULTIPLE = keccak256("SUBSCRIPTION_SIZE_MULTIPLE");
bytes32 constant GLOBAL_AGGREGATE_MINIMUM_BREACH = keccak256("GLOBAL_AGGREGATE_MINIMUM_BREACH");
bytes32 constant FORCED_REDEMPTION_TOP_UP_PERIOD = keccak256("FORCED_REDEMPTION_TOP_UP_PERIOD");
bytes32 constant FORCED_REDEMPTION_TOP_UP_PERIOD_UNIT = keccak256("FORCED_REDEMPTION_TOP_UP_PERIOD_UNIT");
bytes32 constant REDEMPTION_VOLUME_LIMIT = keccak256("REDEMPTION_VOLUME_LIMIT");
bytes32 constant REDEMPTION_VOLUME_LIMIT_PER_INVESTOR = keccak256("REDEMPTION_VOLUME_LIMIT_PER_INVESTOR");
bytes32 constant REDEMPTION_VOLUME_LIMIT_PER_INVESTOR_FEE = keccak256("REDEMPTION_VOLUME_LIMIT_PER_INVESTOR_FEE");
bytes32 constant REDEMPTION_LOOKBACK_PERIOD = keccak256("REDEMPTION_LOOKBACK_PERIOD");
bytes32 constant REDEMPTION_LOOKBACK_PERIOD_UNIT = keccak256("REDEMPTION_LOOKBACK_PERIOD_UNIT");
bytes32 constant GATE_CRITERIA_1 = keccak256("GATE_CRITERIA_1");
bytes32 constant REDEMPTION_NOTICE_PERIOD = keccak256("REDEMPTION_NOTICE_PERIOD");
bytes32 constant REDEMPTION_NOTICE_PERIOD_UNIT = keccak256("REDEMPTION_NOTICE_PERIOD_UNIT");
bytes32 constant REDEMPTION_CUT_OFF_PERIOD = keccak256("REDEMPTION_CUT_OFF_PERIOD");
// Calendar
bytes32 constant BUSINESS_DAY_BITMAP_PART_1 = keccak256("BUSINESS_DAY_BITMAP_PART_1");
bytes32 constant BUSINESS_DAY_BITMAP_PART_2 = keccak256("BUSINESS_DAY_BITMAP_PART_2");

// Redemption Cutoff
bytes32 constant REDEMPTION_CUT_OFF_CALENDAR_DAYS = keccak256("REDEMPTION_CUT_OFF_CALENDAR_DAYS");
bytes32 constant REDEMPTION_CUT_OFF_BUSINESS_DAYS = keccak256("REDEMPTION_CUT_OFF_BUSINESS_DAYS");
bytes32 constant REDEMPTION_CUT_OFF_TIME = keccak256("REDEMPTION_CUT_OFF_TIME");

// Execution Date
bytes32 constant SUBSCRIPTION_NOTICE_PERIOD_CALENDAR_MONTHS = keccak256("SUBSCRIPTION_NOTICE_PERIOD_CALENDAR_MONTHS");
bytes32 constant SUBSCRIPTION_NOTICE_PERIOD_CALENDAR_DAYS = keccak256("SUBSCRIPTION_NOTICE_PERIOD_CALENDAR_DAYS");
bytes32 constant SUBSCRIPTION_NOTICE_PERIOD_BUSINESS_DAYS = keccak256("SUBSCRIPTION_NOTICE_PERIOD_BUSINESS_DAYS");
bytes32 constant SUBSCRIPTION_CUTOFF_TIME = keccak256("SUBSCRIPTION_CUTOFF_TIME");
bytes32 constant WAIVE_SUBSCRIPTION_NOTICE_PERIOD = keccak256("WAIVE_SUBSCRIPTION_NOTICE_PERIOD");
bytes32 constant SUBSCRIPTION_PREADJUSTMENT_DEALING_DAY_OF_MONTH =
    keccak256("SUBSCRIPTION_PREADJUSTMENT_DEALING_DAY_OF_MONTH");
bytes32 constant REDEMPTION_NOTICE_PERIOD_CALENDAR_MONTHS = keccak256("REDEMPTION_NOTICE_PERIOD_CALENDAR_MONTHS");
bytes32 constant REDEMPTION_NOTICE_PERIOD_CALENDAR_DAYS = keccak256("REDEMPTION_NOTICE_PERIOD_CALENDAR_DAYS");
bytes32 constant REDEMPTION_NOTICE_PERIOD_BUSINESS_DAYS = keccak256("REDEMPTION_NOTICE_PERIOD_BUSINESS_DAYS");
bytes32 constant REDEMPTION_CUTOFF_TIME = keccak256("REDEMPTION_CUTOFF_TIME");
bytes32 constant WAIVE_REDEMPTION_NOTICE_PERIOD = keccak256("WAIVE_REDEMPTION_NOTICE_PERIOD");
bytes32 constant REDEMPTION_PREADJUSTMENT_DEALING_DAY_OF_MONTH =
    keccak256("REDEMPTION_PREADJUSTMENT_DEALING_DAY_OF_MONTH");

// Redemption Holding Intervals
bytes32 constant REDEMPTION_FIRST_UNLOCK_PERIOD = keccak256("REDEMPTION_FIRST_UNLOCK_PERIOD");
bytes32 constant REDEMPTION_SECOND_UNLOCK_PERIOD = keccak256("REDEMPTION_SECOND_UNLOCK_PERIOD");

bytes32 constant CUMULATIVE_REDEMPTION_PERIOD = keccak256("CUMULATIVE_REDEMPTION_PERIOD");
bytes32 constant CUMULATIVE_REDEMPTION_PERIOD_UNIT = keccak256("CUMULATIVE_REDEMPTION_PERIOD_UNIT");
bytes32 constant CUMULATIVE_REDEMPTION_LIMIT_FEE = keccak256("CUMULATIVE_REDEMPTION_LIMIT_FEE");
bytes32 constant CUMULATIVE_REDEMPTION_LIMIT_ALLOWANCE = keccak256("CUMULATIVE_REDEMPTION_LIMIT_ALLOWANCE");
bytes32 constant CUMULATIVE_REDEMPTION_LIMIT_PER_INVESTOR_ALLOWANCE =
    keccak256("CUMULATIVE_REDEMPTION_LIMIT_PER_INVESTOR_ALLOWANCE");
bytes32 constant CUMULATIVE_REDEMPTION_LIMIT_PER_INVESTOR_FEE =
    keccak256("CUMULATIVE_REDEMPTION_LIMIT_PER_INVESTOR_FEE");
bytes32 constant INITIAL_REDEMPTION_RESTRICTED_PERIOD = keccak256("INITIAL_REDEMPTION_RESTRICTED_PERIOD");
bytes32 constant INITIAL_SUBSCRIPTION_RESTRICTED_PERIOD = keccak256("INITIAL_SUBSCRIPTION_RESTRICTED_PERIOD");
bytes32 constant INITIAL_SUBSCRIPTION_RESTRICTED_PERIOD_UNIT = keccak256("INITIAL_SUBSCRIPTION_RESTRICTED_PERIOD_UNIT");
bytes32 constant INITIAL_SUBSCRIPTION_RESTRICTED_PERIOD_ALLOWANCE =
    keccak256("INITIAL_SUBSCRIPTION_RESTRICTED_PERIOD_ALLOWANCE");
bytes32 constant INITIAL_SUBSCRIPTION_RESTRICTED_PERIOD_FEE = keccak256("INITIAL_SUBSCRIPTION_RESTRICTED_PERIOD_FEE");
bytes32 constant INITIAL_REDEMPTION_RESTRICTED_PERIOD_ALLOWANCE =
    keccak256("INITIAL_REDEMPTION_RESTRICTED_PERIOD_ALLOWANCE");
bytes32 constant POST_SUBSCRIPTION_RESTRICTED_PERIOD_ALLOWANCE =
    keccak256("POST_SUBSCRIPTION_RESTRICTED_PERIOD_ALLOWANCE");
bytes32 constant POST_SUBSCRIPTION_RESTRICTED_PERIOD_FEE = keccak256("POST_SUBSCRIPTION_RESTRICTED_PERIOD_FEE");
bytes32 constant INITIAL_REDEMPTION_RESTRICTED_PERIOD_FEE = keccak256("INITIAL_REDEMPTION_RESTRICTED_PERIOD_FEE");
bytes32 constant POST_REDEMPTION_RESTRICTED_PERIOD_ALLOWANCE = keccak256("POST_REDEMPTION_RESTRICTED_PERIOD_ALLOWANCE");
bytes32 constant POST_REDEMPTION_RESTRICTED_PERIOD_FEE = keccak256("POST_REDEMPTION_RESTRICTED_PERIOD_FEE");
bytes32 constant GLOBAL_AGGREGATE_MINIMUM_HOLDING = keccak256("GLOBAL_AGGREGATE_MINIMUM_HOLDING");
bytes32 constant BLOCKED = keccak256("BLOCKED");
bytes32 constant RESIDENCES_ALLOWED = keccak256("RESIDENCES_ALLOWED");
bytes32 constant NATIONALITIES_ALLOWED = keccak256("NATIONALITIES_ALLOWED");
bytes32 constant INVESTOR_TYPES_ALLOWED = keccak256("INVESTOR_TYPES_ALLOWED");
bytes32 constant ESTIMATED_REDEMPTION_FEE = keccak256("ESTIMATED_REDEMPTION_FEE");
bytes32 constant IS_INVESTOR_ALLOWED = keccak256("IS_INVESTOR_ALLOWED");
bytes32 constant HOLDING_FEE = keccak256("HOLDING_FEE");
bytes32 constant HOLDING_FEE_PERIOD = keccak256("HOLDING_FEE_PERIOD");
bytes32 constant HOLDING_FEE_PERIOD_UNIT = keccak256("HOLDING_FEE_PERIOD_UNIT");

// Investors data
bytes32 constant INVESTOR_TYPE = keccak256("INVESTOR_TYPE");
bytes32 constant MAX_INVESTOR_TYPE = keccak256("MAX_INVESTOR_TYPE");
bytes32 constant NATIONALITIES = keccak256("NATIONALITIES");
bytes32 constant REVIEW_DATE = keccak256("REVIEW_DATE");
bytes32 constant KYC_PASSED = keccak256("KYC_PASSED");
bytes32 constant AML_PASSED = keccak256("AML_PASSED");
bytes32 constant SANCTIONS_PASSED = keccak256("SANCTIONS_PASSED");
bytes32 constant SELF_CERTIFICATION = keccak256("SELF_CERTIFICATION");
bytes32 constant FITNESS_TEST = keccak256("FITNESS_TEST");
bytes32 constant FROZEN = keccak256("FROZEN");
bytes32 constant USA_STATE = keccak256("USA_STATE");
bytes32 constant ALLOWED_TO_INTERACT_DAY = keccak256("ALLOWED_TO_INTERACT_DAY");
bytes32 constant DEALER = keccak256("DEALER");
bytes32 constant REVERSE_ENQUIRY = keccak256("REVERSE_ENQUIRY");

// Jurisdictions data
bytes32 constant JURISDICTION_ALLOWED = keccak256("JURISDICTION_ALLOWED");
bytes32 constant INVESTOR_ALLOWLISTED_REQUIRED = keccak256("INVESTOR_ALLOWLISTED_REQUIRED");
bytes32 constant SELF_CERTIFICATION_REQUIRED = keccak256("SELF_CERTIFICATION_REQUIRED");
bytes32 constant FITNESS_TEST_REQUIRED = keccak256("FITNESS_TEST_REQUIRED");
bytes32 constant DISCLOSURE_DOCUMENTS_REQUIRED = keccak256("DISCLOSURE_DOCUMENTS_REQUIRED");
bytes32 constant LISTED_ON_REGULATED_VENUE_REQUIRED = keccak256("LISTED_ON_REGULATED_VENUE_REQUIRED");
bytes32 constant LOCAL_AIFM_REQUIRED = keccak256("LOCAL_AIFM_REQUIRED");
bytes32 constant NON_EU_AIFM_REQUIRED = keccak256("NON_EU_AIFM_REQUIRED");
bytes32 constant MINIMUM_INVESTMENT_REQUIRED = keccak256("MINIMUM_INVESTMENT_REQUIRED");
bytes32 constant MINIMUM_INVESTMENT = keccak256("MINIMUM_INVESTMENT");
bytes32 constant NO_MINIMUM_INVESTMENT_HOLDER_NUMBER = keccak256("NO_MINIMUM_INVESTMENT_HOLDER_NUMBER");

// Registries
bytes32 constant GENERAL_DATA_ID = bytes32(0);
bytes32 constant ETERNAL_STORAGE_SLOT = keccak256("ETERNAL_STORAGE_SLOT");
bytes32 constant BLACKLISTED_WALLET = keccak256("BLACKLISTED_WALLET");

// Tracking
bytes32 constant CURRENT_SUBSCRIPTIONS_AMOUNT = keccak256("CURRENT_SUBSCRIPTIONS_AMOUNT");
bytes32 constant INVESTOR_SUBSCRIPTION_AMOUNT = keccak256("INVESTOR_SUBSCRIPTION_AMOUNT");
bytes32 constant CURRENT_REDEMPTIONS_AMOUNT = keccak256("CURRENT_REDEMPTIONS_AMOUNT");
bytes32 constant INVESTOR_REDEMPTION_AMOUNT = keccak256("INVESTOR_REDEMPTION_AMOUNT");
bytes32 constant REDEMPTION_RECORDS = keccak256("REDEMPTION_RECORDS");
bytes32 constant REDEMPTION_RECORDS_COUNT = keccak256("REDEMPTION_RECORDS_COUNT");
bytes32 constant REDEMPTION_RECORDS_AGGREGATE = keccak256("REDEMPTION_RECORDS_AGGREGATE");
bytes32 constant REDEMPTION_RECORDS_TIMESTAMP = keccak256("REDEMPTION_RECORDS_TIMESTAMP");
bytes32 constant HOLDING_FEE_EXEMPTION_CREDITS = keccak256("HOLDING_FEE_EXEMPTION_CREDITS");
bytes32 constant HOLDING_FEE_EXEMPTION_CREDITS_START = keccak256("HOLDING_FEE_EXEMPTION_CREDITS_START");
bytes32 constant HOLDING_FEE_EXEMPTION_CREDITS_COUNT = keccak256("HOLDING_FEE_EXEMPTION_CREDITS_COUNT");
bytes32 constant HOLDING_FEE_EXEMPTION_CREDITS_AMOUNT = keccak256("HOLDING_FEE_EXEMPTION_CREDITS_AMOUNT");
bytes32 constant HOLDING_FEE_EXEMPTION_CREDITS_TIMESTAMP = keccak256("HOLDING_FEE_EXEMPTION_CREDITS_TIMESTAMP");

bytes32 constant RTV_HOLDING_FEE_EXEMPTION_CREDITS = keccak256("RTV_HOLDING_FEE_EXEMPTION_CREDITS");
bytes32 constant RTV_HOLDING_FEE_EXEMPTION_CREDITS_START = keccak256("RTV_HOLDING_FEE_EXEMPTION_CREDITS_START");
bytes32 constant RTV_HOLDING_FEE_EXEMPTION_CREDITS_COUNT = keccak256("RTV_HOLDING_FEE_EXEMPTION_CREDITS_COUNT");
bytes32 constant RTV_HOLDING_FEE_EXEMPTION_CREDITS_AMOUNT = keccak256("RTV_HOLDING_FEE_EXEMPTION_CREDITS_AMOUNT");
bytes32 constant RTV_HOLDING_FEE_EXEMPTION_CREDITS_TIMESTAMP = keccak256("RTV_HOLDING_FEE_EXEMPTION_CREDITS_TIMESTAMP");

bytes32 constant AAVE_HORIZON_HOLDING_FEE_EXEMPTION_CREDITS = keccak256("AAVE_HORIZON_HOLDING_FEE_EXEMPTION_CREDITS");
bytes32 constant AAVE_HORIZON_HOLDING_FEE_EXEMPTION_CREDITS_START =
    keccak256("AAVE_HORIZON_HOLDING_FEE_EXEMPTION_CREDITS_START");
bytes32 constant AAVE_HORIZON_HOLDING_FEE_EXEMPTION_CREDITS_COUNT =
    keccak256("AAVE_HORIZON_HOLDING_FEE_EXEMPTION_CREDITS_COUNT");
bytes32 constant AAVE_HORIZON_HOLDING_FEE_EXEMPTION_CREDITS_AMOUNT =
    keccak256("AAVE_HORIZON_HOLDING_FEE_EXEMPTION_CREDITS_AMOUNT");
bytes32 constant AAVE_HORIZON_HOLDING_FEE_EXEMPTION_CREDITS_TIMESTAMP =
    keccak256("AAVE_HORIZON_HOLDING_FEE_EXEMPTION_CREDITS_TIMESTAMP");
bytes32 constant AAVE_HORIZON_ATOKEN_ADDRESS = keccak256("AAVE_HORIZON_ATOKEN_ADDRESS");
bytes32 constant AAVE_HORIZON_ATOKEN_PARKED_BALANCE = keccak256("AAVE_HORIZON_ATOKEN_PARKED_BALANCE");

bytes32 constant GATEWAY_HOLDING_FEE_EXEMPTION_CREDITS = keccak256("GATEWAY_HOLDING_FEE_EXEMPTION_CREDITS");
bytes32 constant GATEWAY_HOLDING_FEE_EXEMPTION_CREDITS_START = keccak256("GATEWAY_HOLDING_FEE_EXEMPTION_CREDITS_START");
bytes32 constant GATEWAY_HOLDING_FEE_EXEMPTION_CREDITS_COUNT = keccak256("GATEWAY_HOLDING_FEE_EXEMPTION_CREDITS_COUNT");
bytes32 constant GATEWAY_HOLDING_FEE_EXEMPTION_CREDITS_AMOUNT =
    keccak256("GATEWAY_HOLDING_FEE_EXEMPTION_CREDITS_AMOUNT");
bytes32 constant GATEWAY_HOLDING_FEE_EXEMPTION_CREDITS_TIMESTAMP =
    keccak256("GATEWAY_HOLDING_FEE_EXEMPTION_CREDITS_TIMESTAMP");

// Tracking (Holding Intervals)
bytes32 constant HOLDING_TIME_CREDITS = keccak256("HOLDING_TIME_CREDITS");
bytes32 constant HOLDING_TIME_CREDITS_START = keccak256("HOLDING_TIME_CREDITS_START");
bytes32 constant HOLDING_TIME_CREDITS_COUNT = keccak256("HOLDING_TIME_CREDITS_COUNT");
bytes32 constant HOLDING_TIME_CREDITS_TIMESTAMP = keccak256("HOLDING_TIME_CREDITS_TIMESTAMP");

bytes32 constant HOLDING_CREDITS_FIRST_PERIOD = keccak256("HOLDING_CREDITS_FIRST_PERIOD"); // related to REDEMPTION_FIRST_UNLOCK_PERIOD
bytes32 constant HOLDING_CREDITS_SECOND_PERIOD = keccak256("HOLDING_CREDITS_SECOND_PERIOD"); // related to REDEMPTION_SECOND_UNLOCK_PERIOD

bytes32 constant HOLDING_PERCENTAGE_FIRST_PERIOD = keccak256("HOLDING_PERCENTAGE_FIRST_PERIOD");
bytes32 constant HOLDING_PERCENTAGE_SECOND_PERIOD = keccak256("HOLDING_PERCENTAGE_SECOND_PERIOD");

// Tracking (NAV Track Module)
bytes32 constant NAV_NUM_CHECKPOINTS = keccak256("NAV_NUM_CHECKPOINTS");
bytes32 constant NAV_CHECKPOINT = keccak256("NAV_CHECKPOINT");
bytes32 constant NAV_CHECKPOINT_TIMESTAMP = keccak256("NAV_CHECKPOINT_TIMESTAMP");

// Security Token Track (Per investor)
bytes32 constant SECURITY_TOKEN_NUM_CHECKPOINTS = keccak256("SECURITY_TOKEN_NUM_CHECKPOINTS");
bytes32 constant SECURITY_TOKEN_CHECKPOINT_TIMESTAMP = keccak256("SECURITY_TOKEN_CHECKPOINT_TIMESTAMP");
bytes32 constant SECURITY_TOKEN_CHECKPOINTS = keccak256("SECURITY_TOKEN_CHECKPOINTS");

// Investor Registry
bytes32 constant IS_INVESTOR = keccak256("IS_INVESTOR");
bytes32 constant LAST_OPERATION_TIMESTAMP = keccak256("LAST_OPERATION_TIMESTAMP");
bytes32 constant CREDENTIAL_ID = keccak256("CREDENTIAL_ID");
bytes32 constant INVESTOR_CROSS_CHAIN_ADDRESSES = keccak256("INVESTOR_CROSS_CHAIN_ADDRESSES");

// Instrument Registry
bytes32 constant IS_INSTRUMENT = keccak256("IS_INSTRUMENT");
bytes32 constant TRADING_DAYS_IN_YEAR = keccak256("TRADING_DAYS_IN_YEAR");
bytes32 constant INSTRUMENT_INITIALIZED = keccak256("INSTRUMENT_INITIALIZED");
bytes32 constant INSTRUMENT_CLOSED = keccak256("INSTRUMENT_CLOSED");
bytes32 constant INSTANT_SETTLEMENT = keccak256("INSTANT_SETTLEMENT");
bytes32 constant CAN_TRANSFER_SETTLEMENT_TOKEN = keccak256("CAN_TRANSFER_SETTLEMENT_TOKEN");
bytes32 constant CAN_TRANSFER_SECURITY_TOKEN = keccak256("CAN_TRANSFER_SECURITY_TOKEN");
bytes32 constant INSTRUMENT_PAUSED = keccak256("INSTRUMENT_PAUSED");

// Dealer Registry
bytes32 constant IS_DEALER = keccak256("IS_DEALER");

// Fund Registry
bytes32 constant IS_FUND = keccak256("IS_FUND");
bytes32 constant DEFAULT_FUND_MODULES = keccak256("DEFAULT_FUND_MODULES");

// Roles
bytes32 constant DEFAULT_ADMIN_ROLE = bytes32(0);
bytes32 constant DEALER_MANAGER_ROLE = keccak256("DEALER_MANAGER_ROLE");
bytes32 constant LIBRE_DEALER_ROLE = keccak256("LIBRE_DEALER_ROLE");
bytes32 constant INSTRUMENT_MANAGER_ROLE = keccak256("INSTRUMENT_MANAGER_ROLE");
bytes32 constant SETTLEMENT_TOKEN_MINTER_ROLE = keccak256("SETTLEMENT_TOKEN_MINTER_ROLE");
bytes32 constant SETTLEMENT_TOKEN_BURNER_ROLE = keccak256("SETTLEMENT_TOKEN_BURNER_ROLE");
bytes32 constant RELAYER_ROLE = keccak256("RELAYER_ROLE");
bytes32 constant CONTRACT_MANAGER_ROLE = keccak256("CONTRACT_MANAGER_ROLE");
bytes32 constant REGISTRY_WRITER_ROLE_ADMIN = keccak256("REGISTRY_WRITER_ROLE_ADMIN");
bytes32 constant REGISTRY_WRITER_ROLE = keccak256("REGISTRY_WRITER_ROLE");
bytes32 constant ATTESTER_ROLE = keccak256("ATTESTER_ROLE");
bytes32 constant GATEWAY_MANAGER_ROLE = keccak256("GATEWAY_MANAGER_ROLE");
bytes32 constant CORE_GATEWAY_ROLE = keccak256("CORE_GATEWAY_ROLE");
bytes32 constant BRIDGE_ROLE = keccak256("BRIDGE_ROLE");
bytes32 constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

// Hash of module names
bytes32 constant TRANSFER_MODULE = keccak256("TransferModule");
bytes32 constant DAILY_EXECUTION_CALCULATOR_MODULE = keccak256("DailyExecutionCalculatorModule");
bytes32 constant EXECUTION_DATE_ENFORCEMENT_MODULE = keccak256("ExecutionDateEnforcementModule");
bytes32 constant HALT_MODULE = keccak256("HaltModule");
bytes32 constant HOLDINGS_MODULE = keccak256("HoldingsModule");
bytes32 constant MONTHLY_EXECUTION_CALCULATOR_MODULE = keccak256("MonthlyExecutionCalculatorModule");
bytes32 constant FORCED_REDEMPTION_MODULE = keccak256("ForcedRedemptionModule");
bytes32 constant GATE_1_MODULE = keccak256("Gate1Module");
bytes32 constant HOLDING_INTERVALS_MODULE = keccak256("HoldingIntervalsModule");
bytes32 constant REDEMPTIONS_AGGREGATION_LIMIT_MODULE = keccak256("RedemptionsAggregationLimitModule");
bytes32 constant SUBSCRIPTIONS_AGGREGATION_LIMIT_MODULE = keccak256("SubscriptionsAggregationLimitModule");
bytes32 constant HOLDING_FEE_MODULE = keccak256("HoldingFeeModule");
bytes32 constant REDEMPTION_VOLUME_LIMIT_PER_INVESTOR_FEE_MODULE =
    keccak256("RedemptionVolumeLimitPerInvestorFeeModule");
bytes32 constant INVESTOR_LAST_OPERATION_TRACKING_MODULE = keccak256("InvestorLastOperationTrackingModule");
bytes32 constant NAV_TRACKING_MODULE = keccak256("NAVTrackingModule");
bytes32 constant REDEMPTIONS_RECORDS_MODULE = keccak256("RedemptionsRecordsModule");
bytes32 constant REDEMPTIONS_TOTAL_AMOUNT_MODULE = keccak256("RedemptionsTotalAmountModule");
bytes32 constant SECURITY_TOKEN_TRACKING_MODULE = keccak256("SecurityTokenTrackingModule");
bytes32 constant SUBSCRIPTIONS_TOTAL_AMOUNT_MODULE = keccak256("SubscriptionsTotalAmountModule");
bytes32 constant HOLDING_FEE_EXEMPTION_MODULE = keccak256("HoldingFeeExemptionModule");
bytes32 constant HOLDING_INTERVALS_TRACKING_MODULE = keccak256("HoldingIntervalsTrackingModule");
bytes32 constant HOLDING_FEE_RECEIPT_TOKEN_VAULT_EXEMPTION_MODULE =
    keccak256("HoldingFeeReceiptTokenVaultExemptionModule");
bytes32 constant HOLDING_FEE_AAVE_HORIZON_EXEMPTION_MODULE = keccak256("HoldingFeeAaveHorizonExemptionModule");
bytes32 constant AAVE_HORIZON_CONFIG_CHECK_MODULE = keccak256("AaveHorizonConfigCheckModule");
bytes32 constant HOLDING_FEE_GATEWAY_EXEMPTION_MODULE = keccak256("HoldingFeeGatewayExemptionModule");

// Percentage calculations
uint256 constant PERCENTAGE_BASE = 100_00;

// Cross-chain threshold time (1 day) for credential expiry validation
uint256 constant CROSS_CHAIN_THRESHOLD_TIME = 1 days;

File 6 of 32 : Common.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

// A representation of an empty/uninitialized UID.
bytes32 constant EMPTY_UID = 0;

// A zero expiration represents an non-expiring attestation.
uint64 constant NO_EXPIRATION_TIME = 0;

error AccessDenied();
error DeadlineExpired();
error InvalidEAS();
error InvalidLength();
error InvalidSignature();
error NotFound();

/// @notice A struct representing ECDSA signature data.
struct Signature {
    uint8 v; // The recovery ID.
    bytes32 r; // The x-coordinate of the nonce R.
    bytes32 s; // The signature data.
}

/// @notice A struct representing a single attestation.
struct Attestation {
    bytes32 uid; // A unique identifier of the attestation.
    bytes32 schema; // The unique identifier of the schema.
    uint64 time; // The time when the attestation was created (Unix timestamp).
    uint64 expirationTime; // The time when the attestation expires (Unix timestamp).
    uint64 revocationTime; // The time when the attestation was revoked (Unix timestamp).
    bytes32 refUID; // The UID of the related attestation.
    address recipient; // The recipient of the attestation.
    address attester; // The attester/sender of the attestation.
    bool revocable; // Whether the attestation is revocable.
    bytes data; // Custom attestation data.
}

/// @notice A helper function to work with unchecked iterators in loops.
function uncheckedInc(uint256 i) pure returns (uint256 j) {
    unchecked {
        j = i + 1;
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

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

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

/// @title Semver
/// @notice A simple contract for managing contract versions.
contract Semver is ISemver {
    // Contract's major version number.
    uint256 private immutable _major;

    // Contract's minor version number.
    uint256 private immutable _minor;

    // Contract's patch version number.
    uint256 private immutable _patch;

    /// @dev Create a new Semver instance.
    /// @param major Major version number.
    /// @param minor Minor version number.
    /// @param patch Patch version number.
    constructor(uint256 major, uint256 minor, uint256 patch) {
        _major = major;
        _minor = minor;
        _patch = patch;
    }

    /// @notice Returns the full semver contract version.
    /// @return Semver contract version as a string.
    function version() external view returns (string memory) {
        return
            string(
                abi.encodePacked(Strings.toString(_major), ".", Strings.toString(_minor), ".", Strings.toString(_patch))
            );
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { Attestation } from "./../Common.sol";
import { ISemver } from "./../ISemver.sol";

/// @title ISchemaResolver
/// @notice The interface of an optional schema resolver.
interface ISchemaResolver is ISemver {
    /// @notice Checks if the resolver can be sent ETH.
    /// @return Whether the resolver supports ETH transfers.
    function isPayable() external pure returns (bool);

    /// @notice Processes an attestation and verifies whether it's valid.
    /// @param attestation The new attestation.
    /// @return Whether the attestation is valid.
    function attest(Attestation calldata attestation) external payable returns (bool);

    /// @notice Processes multiple attestations and verifies whether they are valid.
    /// @param attestations The new attestations.
    /// @param values Explicit ETH amounts which were sent with each attestation.
    /// @return Whether all the attestations are valid.
    function multiAttest(
        Attestation[] calldata attestations,
        uint256[] calldata values
    ) external payable returns (bool);

    /// @notice Processes an attestation revocation and verifies if it can be revoked.
    /// @param attestation The existing attestation to be revoked.
    /// @return Whether the attestation can be revoked.
    function revoke(Attestation calldata attestation) external payable returns (bool);

    /// @notice Processes revocation of multiple attestation and verifies they can be revoked.
    /// @param attestations The existing attestations to be revoked.
    /// @param values Explicit ETH amounts which were sent with each revocation.
    /// @return Whether the attestations can be revoked.
    function multiRevoke(
        Attestation[] calldata attestations,
        uint256[] calldata values
    ) external payable returns (bool);
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

import { ISchemaResolver } from "./resolver/ISchemaResolver.sol";

/// @notice A struct representing a record for a submitted schema.
struct SchemaRecord {
    bytes32 uid; // The unique identifier of the schema.
    ISchemaResolver resolver; // Optional schema resolver.
    bool revocable; // Whether the schema allows revocations explicitly.
    string schema; // Custom specification of the schema (e.g., an ABI).
}

/// @title ISchemaRegistry
/// @notice The interface of global attestation schemas for the Ethereum Attestation Service protocol.
interface ISchemaRegistry is ISemver {
    /// @notice Emitted when a new schema has been registered
    /// @param uid The schema UID.
    /// @param registerer The address of the account used to register the schema.
    /// @param schema The schema data.
    event Registered(bytes32 indexed uid, address indexed registerer, SchemaRecord schema);

    /// @notice Submits and reserves a new schema
    /// @param schema The schema data schema.
    /// @param resolver An optional schema resolver.
    /// @param revocable Whether the schema allows revocations explicitly.
    /// @return The UID of the new schema.
    function register(string calldata schema, ISchemaResolver resolver, bool revocable) external returns (bytes32);

    /// @notice Returns an existing schema by UID
    /// @param uid The UID of the schema to retrieve.
    /// @return The schema data members.
    function getSchema(bytes32 uid) external view returns (SchemaRecord memory);
}

File 10 of 32 : ISemver.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @title ISemver
/// @notice A semver interface.
interface ISemver {
    /// @notice Returns the full semver contract version.
    /// @return Semver contract version as a string.
    function version() external view returns (string memory);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.19;

import "../IERC20Upgradeable.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20MetadataUpgradeable is IERC20Upgradeable {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

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

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

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import {IEternalRegistryStorage} from "src/interfaces/IEternalRegistryStorage.sol";

/**
 * @title IBaseRegistry
 * @notice Interface for the base registry, which provides functionality for tracking and retrieving data
 * specific to modules that interact with the registry.
 * @dev The tracked data can be queried via a delegatecall callback to the relevant module,
 * which implements the query logic.
 */
interface IBaseRegistry is IEternalRegistryStorage {
    /**
     * @notice Thrown when an unauthorized caller tries to access a restricted function or resource.
     */
    error IBaseRegistry_AccessDenied();

    /**
     * @notice Thrown when an action is attempted on a module that is not registered in the registry.
     */
    error IBaseRegistry_NotRegisteredModule();

    /**
     * @notice Thrown when an invalid or non-existent ID is provided for an operation.
     */
    error IBaseRegistry_NotAValidId();

    /**
     * @notice Thrown when the system fails to retrieve the tracked data for a specific operation or ID.
     */
    error IBaseRegistry_FailedToGetTrackedData();

    /**
     * @notice Get the tracked data of the registry
     * @param _data Data to be used by the module to get the tracked data
     * @return bytes memory of the tracked data
     * @dev This function is used to get the tracked data from the registry
     * by doing a delegatecall callback to the module requesting the data
     * which can implement the query logic
     */
    function getTrackedData(bytes memory _data) external returns (bytes memory);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import {
    IAccessControlUpgradeable
} from "openzeppelin-contracts-upgradeable/contracts/access/IAccessControlUpgradeable.sol";

/**
 * @title Roles Registry
 * @author Libre
 * @notice Store all the roles of the system as well as the permissions a user get by having each role.
 * @dev It is meant to be used for roles access validation in all the components of the system.
 */
interface IRoleRegistry is IAccessControlUpgradeable {
    /**
     * @notice Thrown when attempting to create a role that already exists.
     */
    error IRoleRegistry_RoleAlreadyExists();

    /**
     * @notice Thrown when attempting to grant a role that does not exist.
     */
    error IRoleRegistry_RoleDoesNotExist();

    /**
     * @notice Thrown when attempting to use a blacklisted wallet.
     */
    error IRoleRegistry_WalletBlacklisted();

    /**
     * @notice Used when a new role is created
     * @param _role Parent role
     * @param _subRole Newly created Role
     */
    event RoleCreated(bytes32 _role, bytes32 _subRole);

    /**
     * @notice Used when a contract manager is set
     * @param _contract Contract managed
     * @param _roleManager New contract manager role
     */
    event ContractManagerAdded(address _contract, bytes32 _roleManager);

    /**
     * @notice Used when a wallet is blacklisted
     * @param _wallet Wallet blacklisted
     */
    event WalletBlacklisted(address _wallet);

    /**
     * @notice Used when a contract function access is granted
     * @param _contract Contract managed
     * @param _selector Selector of the funtion which access is modified
     * @param _role Role getting access gratend
     */
    event AccessGranted(address _contract, bytes4 _selector, bytes32 _role);

    /**
     * @notice Used when a contract function access is revoked
     * @param _contract Contract managed
     * @param _selector Selector of the funtion which access is modified
     * @param _role Role which access is revoked
     */
    event RevokedAccess(address _contract, bytes4 _selector, bytes32 _role);

    /**
     * @notice Used when a contract access is reset
     * @param _contract Contract managed
     * @param _timestamp Time when the reset is executed
     */
    event ResetedAccess(address _contract, uint256 _timestamp);

    /**
     * @notice Function to create subroles
     * @param _role New role to create
     * @param _adminRole Parent role of the newly created role
     * @dev Caller needs to has role _adminRole
     */
    function createRole(bytes32 _role, bytes32 _adminRole) external;

    /**
     * @notice Set the role going to manage permissions in a specific contract
     * @param _contract Address of the contract to where permissions are needed to be managed
     * @param _roleManager Role in charge of managing the permissions
     */
    function setContractManager(address _contract, bytes32 _roleManager) external;

    /**
     * @notice Get the role that manages permissions in a specific contract
     * @param _contract Address of the contract
     */
    function getContractManager(address _contract) external view returns (bytes32);

    /**
     * @notice Grant a role the right access a functionality of a contract
     * @param _contract Contract where the access is granted
     * @param _selector Functionality granted access for
     * @param _role Role getting the access
     */
    function grantAccess(address _contract, bytes4 _selector, bytes32 _role) external;

    /**
     * @notice Prevent a role for having access to a functionality of a contract
     * @param _contract Contract where the access is revoked
     * @param _selector Functionality revoked access for
     * @param _role Role losing the access
     */
    function revokeAccess(address _contract, bytes4 _selector, bytes32 _role) external;

    /**
     * @notice Revoke access to a functionality of a contract for all roles
     * @param _contract Contract where the access is reset
     * @param _selector Functionality reset access for
     * @dev Cleans all the previously granted roles
     */
    function resetAccess(address _contract, bytes4 _selector) external;

    /**
     * @notice Blacklists a wallet.
     * @param _wallet The wallet to blacklist.
     */
    function blacklistWallet(address _wallet) external;

    /**
     * @notice Check access of a role to a contract functionality
     * @param _contract Contract where accessed
     * @param _selector Functionality accessed
     * @param _requestor Address attempting to access
     * @param _role Role of the address attempting to access
     */
    function hasAccess(address _contract, bytes4 _selector, address _requestor, bytes32 _role)
        external
        view
        returns (bool);

    /**
     * @notice Checks if a wallet is blacklisted.
     * @param _wallet The wallet to check.
     * @return Boolean indicating whether the wallet is blacklisted.
     */
    function isBlacklisted(address _wallet) external view returns (bool);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

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

import {IBaseRegistry} from "src/interfaces/IBaseRegistry.sol";
import {IDealerRegistry} from "src/interfaces/IDealerRegistry.sol";
import {IFundRegistry} from "src/interfaces/IFundRegistry.sol";
import {IRoleRegistry} from "src/interfaces/IRoleRegistry.sol";
import {IInvestorRegistry} from "src/interfaces/IInvestorRegistry.sol";
import {IOperationsEngine} from "src/interfaces/IOperationsEngine.sol";
import {IEternalRegistryStorage} from "src/interfaces/IEternalRegistryStorage.sol";

/**
 * @title IInstrumentRegistry
 * @author Libre
 * @notice Interface for managing instruments within a fund registry system.
 * Provides functionalities to initialize, add, and manage instruments, track their subscription and redemption periods,
 * update Net Asset Value (NAV) per share, and manage investor access.
 */
interface IInstrumentRegistry is IBaseRegistry {
    /**
     * @notice Thrown when attempting to add an instrument that already exists in the registry.
     */
    error IInstrumentRegistry_InstrumentAlreadyExists();

    /**
     * @notice Thrown when the specified instrument does not exist in the registry.
     */
    error IInstrumentRegistry_InstrumentDoesNotExist();

    /**
     * @notice Thrown when attempting to initialize an instrument that has already been initialized.
     */
    error IInstrumentRegistry_InstrumentAlreadyInitialized();

    /**
     * @notice Emited when a new instrument is added
     * @param fundId Id of the fund where the instrument is added
     * @param instrumentId Id of the new instrument
     */
    event NewFundInstrumentAdded(bytes32 indexed fundId, bytes32 indexed instrumentId);

    /**
     * @notice Emited when an instrument is initialized
     * @param instrumentId Id of the new instrument
     * @param settlementToken Address of the settlement token of the instrument
     * @param securityToken Address of the deployed security token
     * @param subscriptionBook Address of the deployed subscription book
     * @param redemptionBook Address of the deployed redemption book
     */
    event FundInstrumentInitialized(
        bytes32 indexed instrumentId,
        address settlementToken,
        address securityToken,
        address subscriptionBook,
        address redemptionBook
    );

    /**
     * @notice Emited when an instrument is closed
     * @param instrumentId Id of the instrument
     */
    event InstrumentClosed(bytes32 indexed instrumentId);

    /**
     * @notice Emited when an instrument is paused
     * @param instrumentId Id of the instrument
     * @param paused Y/N value indicating if the instrument is paused or not
     */
    event InstrumentPaused(bytes32 indexed instrumentId, bool paused);

    /**
     * @notice Emited when an investor's allowlist status is updated for an instrument
     * @param instrumentId Id of the instrument
     * @param investorId Id of the investor
     * @param allowed Y/N value indicating if the investor is allowed or not
     */
    event InvestorAllowedUpdated(bytes32 indexed instrumentId, bytes32 indexed investorId, bool allowed);

    /**
     * @notice Initialize the registry
     * @param _roleRegistry Address of the Role Registry
     * @param _investorRegistry Address of the Investor Registry
     * @param _dealerRegistry Address of the Dealer Registry
     * @param _fundRegistry Address of the Fund Registry
     * @param _jurisdictionRegistry Address of the Jurisdiction Registry
     * @param _operationsEngine Address of the Rules Engine
     */
    function initialize(
        IRoleRegistry _roleRegistry,
        IInvestorRegistry _investorRegistry,
        IDealerRegistry _dealerRegistry,
        IFundRegistry _fundRegistry,
        IEternalRegistryStorage _jurisdictionRegistry,
        IOperationsEngine _operationsEngine
    ) external;

    /**
     * @notice Adds a new fund instrument to the registry
     * @dev _senderRole is ignored if the caller if the Fund Admin of the instrument
     * @param _senderRole Role of the sender which allow the operation execution
     * @param _instrumentId the instrument ID
     * @param _fundId the fund ID
     */
    function addFundInstrument(bytes32 _senderRole, bytes32 _instrumentId, bytes32 _fundId) external;

    /**
     * @notice Adds a new instrument to the registry
     * @dev _senderRole is ignored if the caller if the Fund Admin of the instrument
     * @param _senderRole Role of the sender which allow the operation execution
     * @param _instrumentId the instrument ID
     * @param _settlementToken settlement token address
     * @param _name the instrument token name
     * @param _symbol the instrument token symbol
     * @param _subscriptionBookImp The implementation of subscription book to be cloned
     * @param _redemptionBookImp The implementation of redemption book to be cloned
     * @param _stTokenImpl The implementation of security token implementation to be cloned
     * @param _treasury The address of instrument treasury
     */
    function initializeFundInstrument(
        bytes32 _senderRole,
        bytes32 _fundId,
        bytes32 _instrumentId,
        address _settlementToken,
        string memory _name,
        string memory _symbol,
        address _subscriptionBookImp,
        address _redemptionBookImp,
        address _stTokenImpl,
        address _treasury
    ) external;

    /**
     * @notice Close a fund instrument
     * @param _senderRole Role of the sender which allow the operation execution
     * @param _instrumentId The ID of the instrument
     */
    function closeFundInstrument(bytes32 _senderRole, bytes32 _instrumentId) external;

    /**
     * @notice Return the number of the current subscription period or
     * 0 in case subscription is not started or infinite if subscriptions are done
     * @dev Infinite is represented by type(uint256).max;
     * @param _instrumentId Id of the instrument
     * @return The current period of the subscription, the start & the end of it
     */
    function currentSubscriptionPeriod(bytes32 _instrumentId) external view returns (uint256, uint256, uint256);

    /**
     * @notice Return the number of the current redemption period or
     * 0 in case redemption is not started or infinite if redemptions are done
     * @param _instrumentId Id of the instrument
     * @return The current period of the redemption, the start & the end of it
     */
    function currentRedemptionPeriod(bytes32 _instrumentId) external view returns (uint256, uint256, uint256);

    /**
     * @notice Sets a new value for the audited NAV per share of a fund
     * @param _senderRole Role of the sender which allow the operation execution
     * @param _instrumentId ID of the fund
     * @param _newNavPerShare new value of the nav
     * @dev Expressed with 6 decimals as it is in settlement tokens units
     */
    function updateAuditedNavPerShare(bytes32 _senderRole, bytes32 _instrumentId, uint256 _newNavPerShare) external;

    /**
     * @notice Allow investor for a specific instrument
     * @param _instrumentId The ID of the instrument
     * @param _investorId Id of the investor
     * @param _allowed Y/N value indicating if the investor is allowed or not
     * @param _senderRole Role of the sender
     */
    function allowInvestor(bytes32 _instrumentId, bytes32 _investorId, bool _allowed, bytes32 _senderRole) external;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import {IBaseRegistry} from "src/interfaces/IBaseRegistry.sol";
import {IRoleRegistry} from "src/interfaces/IRoleRegistry.sol";
import {IInvestorRegistry} from "src/interfaces/IInvestorRegistry.sol";
import {IUserRegistry} from "src/interfaces/IUserRegistry.sol";
import {IOperationsEngine} from "src/interfaces/IOperationsEngine.sol";
import {IInstrumentRegistry} from "src/interfaces/IInstrumentRegistry.sol";

/**
 * @title IDealerRegistry
 * @notice Interface for managing dealers within the registry, including adding new dealers,
 * allowing dealers for specific instruments, and checking dealer permissions.
 * @dev Extends both `IBaseRegistry` and `IUserRegistry` for core registry and user management functionality.
 */
interface IDealerRegistry is IBaseRegistry, IUserRegistry {
    /**
     * @notice Thrown when attempting to add a dealer who is already registered.
     */
    error IDealerRegistry_AlreadyADealer();

    /**
     * @notice Thrown when invalid input parameters are provided.
     */
    error IDealerRegistry_InvalidInput();

    /**
     * @notice Thrown when the specified instrument does not exist in the registry.
     */
    error IDealerRegistry_NonExistingInstrument();

    /**
     * @notice Thrown when the dealer is not allowed for the specified instrument.
     */
    error IDealerRegistry_DealerNotAllowed();

    /**
     * @notice Emited when a new dealer is added
     * @param dealerId Id of the new dealer
     * @param wallet Initial wallet of the dealer
     */
    event NewDealerAdded(bytes32 dealerId, address wallet);

    /**
     * @notice Emited when an dealer's allowed status is updated
     * @param dealerId Id of the dealer updated
     * @param allowed Y/N if dealer is allowed for the instrument
     */
    event dealerAllowedUpdated(bytes32 dealerId, bytes32 instrumentId, bool allowed);

    /**
     * @notice Initialize the registry
     * @param _roleRegistry Address of the role registry
     */
    function initialize(
        IRoleRegistry _roleRegistry,
        IOperationsEngine _operationsEngine,
        IInvestorRegistry _investorRegistry,
        IInstrumentRegistry _instrumentRegistry
    ) external;

    /**
     * @notice Adds a new dealer to the registry
     * @param _senderRole Role of the sender which allows the operation execution
     * @param _dealerId ID of the dealer
     * @param _wallet Wallet of the dealer
     */
    function addDealer(bytes32 _senderRole, bytes32 _dealerId, address _wallet) external;

    /**
     * @notice Allows a dealer for a specific instrument
     * @param _senderRole Role of the sender which allows the operation execution
     * @param _dealerId ID of the dealer
     * @param _instrumentId ID of the instrument
     * @param _allowed Y/N if dealer is allowed for the instrument
     */
    function allowDealer(bytes32 _senderRole, bytes32 _dealerId, bytes32 _instrumentId, bool _allowed) external;

    /**
     * @notice Returns if the dealer is allowed for a specific instrument
     * @param _dealerId ID of the dealer
     * @param _instrumentId ID of the instrument
     * @return Bool indicating if dealer if allowed
     */
    function isDealerAllowed(bytes32 _dealerId, bytes32 _instrumentId) external view returns (bool);

    /**
     * @notice Reverts if the dealer is not allowed for a specific instrument
     * @param _dealerId ID of the dealer
     * @param _instrumentId ID of the instrument
     */
    function checkDealerAllowed(bytes32 _dealerId, bytes32 _instrumentId) external view;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import {IInvestorRegistry} from "src/interfaces/IInvestorRegistry.sol";
import {IFundRegistry} from "src/interfaces/IFundRegistry.sol";
import {IDealerRegistry} from "src/interfaces/IDealerRegistry.sol";
import {IInstrumentRegistry} from "src/interfaces/IInstrumentRegistry.sol";
import {IRoleRegistry} from "src/interfaces/IRoleRegistry.sol";
import {IEternalRegistryStorage} from "src/interfaces/IEternalRegistryStorage.sol";
import {OPERATIONS} from "./operations/IOperationModule.sol";

/**
 * @title Operations Engine Interface
 * @author Libre
 * @notice Defines the interface for managing modules, operations, and dependencies in the system.
 * @dev Includes error definitions, events, and function signatures for interacting with the operations engine.
 */
interface IOperationsEngine {
    // Enums
    /**
     * @notice Defines the type of ID for the module check, modules are either tracking ones or check ones.
     */
    enum IdType {
        NULL,
        INSTRUMENT_CHECK,
        FEE,
        TRACKING
    }

    /**
     * @notice Defines the type of check a module performs.
     */
    enum CHECK_TYPE {
        BOOLEAN,
        NUMERIC_MIN,
        NUMERIC_MAX
    }

    // Errors
    /**
     * @dev Revert when the tracking module reverts for any reason
     */
    error IOperationsEngine_TrackingModuleFailure();

    /**
     * @dev Revert when the fee module reverts for any reason
     */
    error IOperationsEngine_FeeModuleFailure();

    /**
     * @dev Revert when the module address provided is invalid.
     */
    error IOperationsEngine_InvalidModuleAddress();

    /**
     * @dev Revert when the module is not of the Libre type.
     */
    error IOperationsEngine_ModuleIsNotLibreType();

    /**
     * @dev Revert when a lending module cannot be disabled.
     */
    error IOperationsEngine_LendingModuleDisable();

    /**
     * @dev Revert when dependencies required for an operation are missing.
     */
    error IOperationsEngine_MissingDependencies();

    /**
     * @dev Revert when an invalid check type is used.
     */
    error IOperationsEngine_InvalidCheckType();

    /**
     * @dev Revert when the caller is not authorized to perform the action.
     */
    error IOperationsEngine_CallerNotAuthorized();

    /**
     * @dev Revert when adding a module that is already been added.
     */
    error IOperationsEngine_ModuleAlreadyAdded();

    /**
     * @dev Revert when module already enabled for the same instrument.
     */
    error IOperationsEngine_ModuleAlreadyEnabled();

    /**
     * @dev Revert when a module to be disabled for an instrument is not enabled
     */
    error IOperationsEngine_ModuleNotEnabled();

    // Events
    /**
     * @notice Emitted when a module is added to the available modules library.
     * @param moduleAddress Address of the module that was added.
     */
    event ModuleAdded(address indexed moduleAddress);

    /**
     * @notice Emitted when a module is removed from the available modules library.
     * @param moduleAddress Address of the module that was removed.
     */
    event ModuleRemoved(address indexed moduleAddress);

    /**
     * @notice Emitted when a module is enabled for a specific instrument.
     * @param moduleAddress Address of the module that was enabled.
     * @param instrumentId Id of the instrument for which the module was enabled.
     */
    event ModuleEnabled(address indexed moduleAddress, bytes32 instrumentId);

    /**
     * @notice Emitted when a module is disabled for a specific instrument.
     * @param moduleAddress Address of the module that was disabled.
     * @param instrumentId Id of the instrument for which the module was disabled.
     */
    event ModuleDisabled(address indexed moduleAddress, bytes32 instrumentId);

    /**
     * @dev Event emitted when a fee is applied.
     * @param instrumentId The ID of the instrument.
     * @param orderId The ID of the order.
     * @param feeModule The address of the fee module.
     * @param feeAmount The amount of the fee applied.
     */
    event FeeApplied(bytes32 indexed instrumentId, uint256 orderId, address feeModule, uint256 feeAmount);

    //functions

    /**
     * @notice Initializes the Rules Engine with necessary registries.
     * @param _investorRegistry Address of the Investor Registry.
     * @param _dealerRegistry Address of the Dealer Registry.
     * @param _fundRegistry Address of the Fund Registry.
     * @param _instrumentRegistry Address of the Instrument Registry.
     * @param _roleRegistry Address of the Role Registry.
     * @param _jurisdictionRegistry Address of the Jurisdiction Registry.
     */
    function initialize(
        IInvestorRegistry _investorRegistry,
        IDealerRegistry _dealerRegistry,
        IFundRegistry _fundRegistry,
        IInstrumentRegistry _instrumentRegistry,
        IRoleRegistry _roleRegistry,
        IEternalRegistryStorage _jurisdictionRegistry
    ) external;

    /**
     * @notice Gets the addresses for registered check modules implementing the given operation.
     * @param _id Id of the dealer or instrument the modules belong to.
     * @param _operation Operation implemented in modules.
     * @return An array of addresses of modules implementing the given operation.
     */
    function getCheckModules(bytes32 _id, OPERATIONS _operation) external view returns (address[] memory);

    /**
     * @notice Gets the addresses for registered fee modules implementing the given operation.
     * @param _id Id of the dealer or instrument the modules belong to.
     * @param _operation Operation implemented in modules.
     * @return An array of addresses of modules implementing the given operation.
     */
    function getFeeModules(bytes32 _id, OPERATIONS _operation) external view returns (address[] memory);

    /**
     * @notice Gets the addresses for registered tracking modules implementing the given operation.
     * @param _id Id of the dealer or instrument the modules belong to.
     * @param _operation Operation implemented in modules.
     * @return An array of addresses of modules implementing the given operation.
     */
    function getTrackingModules(bytes32 _id, OPERATIONS _operation) external view returns (address[] memory);

    /**
     * @notice Adds a module to the available modules library.
     * @param _moduleAddress Address of the module to add.
     * @param _role Role required to add the module.
     * @param _idType Type of the module.
     */
    function addModule(address _moduleAddress, bytes32 _role, IdType _idType) external;

    /**
     * @notice Removes a module from the available modules library.
     * @param _moduleAddress Address of the module to remove.
     * @param _role Role required to remove the module.
     */
    function removeModule(address _moduleAddress, bytes32 _role) external;

    /**
     * @notice Enables a module for a specific instrument.
     * @param _moduleAddress Address of the module to enable.
     * @param _instrumentId Id of the instrument.
     * @param _role Role required to enable the module.
     */
    function enableModule(address _moduleAddress, bytes32 _instrumentId, bytes32 _role) external;

    /**
     * @notice Disables a module for a specific instrument.
     * @param _moduleAddress Address of the module to disable.
     * @param _instrumentId Id of the instrument.
     * @param _role Role required to disable the module.
     * @dev Lending modules cannot be disabled, because at least one module must be enabled at all times.
     * Instead, the lending module can be replaced with another lending module by enabling the new module.
     */
    function disableModule(address _moduleAddress, bytes32 _instrumentId, bytes32 _role) external;

    /**
     * @notice Checks if a module is available in the available modules library.
     * @param _moduleAddress Address of the module to check.
     * @return True if the module is available, false otherwise.
     */
    function isAvailableModule(address _moduleAddress) external view returns (bool);

    /**
     * @notice Checks if a module is enabled for a specific instrument.
     * @param _id Id of the instrument.
     * @param _moduleName Hash of the abi.encodePacked of the module name
     * @return True if the module is enabled, false otherwise.
     */
    function hasModuleEnabled(bytes32 _id, bytes32 _moduleName) external view returns (bool);

    /**
     * @notice Function to be delegatecalled by the registry to perform the check
     * @param _operation The operation to be checked
     * @param _id The id of the operation
     * @param _data Arbitrary data that can be used by the module to perform the check
     * @return result memory Result of the check
     */
    function check(OPERATIONS _operation, bytes32 _id, bytes calldata _data) external returns (bytes memory result);

    /**
     * @notice Tracks the operation
     * @param _operation The operation to track
     * @param _id The id of the instrument or investor
     * @param _data The data to track
     * @return True if the tracking was successful, false otherwise
     */
    function track(OPERATIONS _operation, bytes32 _id, bytes memory _data) external returns (bool);

    /**
     * @notice Calculates the total fee amount to be deducted for a given operation and instrument.
     * @dev Loops through all enabled fee modules for the given operation and instrument, calls them with the input data,
     *      and sums up the returned fee amounts. Also emits `FeeApplied` events for each individual module fee.
     *
     * @param _operation The operation type (e.g., SETTLE_SUBSCRIPTION_PER_ORDER).
     * @param _id The instrument ID for which the fees are being calculated.
     * @param _data ABI-encoded parameters required for fee calculation. Must encode the following values in order:
     *        - `uint256 _orderId`: Unique identifier of the order
     *        - `bytes32 _investorId`: The ID of the investor associated with the order
     *        - `uint256 _amountToSettle`: The current amount to be settled
     *
     * @return totalFee The total fee amount to be deducted across all enabled fee modules.
     */
    function calculateFees(OPERATIONS _operation, bytes32 _id, bytes memory _data) external returns (uint256 totalFee);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

/**
 * @title IUserRegistry
 * @author Libre
 * @notice Interface for managing user wallets and their association with investor/dealer IDs.
 * @dev Provides functions for adding wallets, retrieving wallets, and mapping wallets to user IDs.
 */
interface IUserRegistry {
    /**
     * @notice Thrown when the wallet address being added is already associated with another user ID.
     */
    error IUserRegistry_WalletAlreadyClaimed();

    /**
     * @notice Thrown when the provided signature fails to verify ownership of the wallet address.
     */
    error IUserRegistry_WalletOwnershipVerificationFailure();

    /**
     * @notice Thrown when the provided wallet signature has expired.
     */
    error IUserRegistry_WalletSignatureExpired();

    /**
     * @notice Thrown when the provided wallet address to add is blacklisted.
     */
    error IUserRegistry_WalletBlacklisted();

    /**
     * @notice Emited when secondary wallet is removed
     * @param wallet User wallet address
     */
    event WalletAdded(address indexed wallet);

    /**
     * @notice Emited when secondary wallet is removed
     * @param wallet User wallet address
     */
    event WalletRemoved(address indexed wallet);

    /**
     * @notice Adds a new investor to the registry
     * @param _userId The ID of the user.
     * @param _wallet The wallet address to associate with the user ID.
     * @param _role The role used by the caller.
     */
    function addWallet(bytes32 _userId, address _wallet, bytes32 _role) external;

    /**
     * @notice Removes a wallet from the registry
     * @param _wallet The wallet address to remove.
     * @param _role The role used by the caller.
     * @return The user ID that the wallet was removed from
     */
    function removeWallet(address _wallet, bytes32 _role) external returns (bytes32);

    /**
     * @notice Get Investor/Dealer array of wallets
     * @param _userId Investor/Dealer ID
     */
    function getWallets(bytes32 _userId) external view returns (address[] memory);

    /**
     * @notice Get Investor/Dealer ID
     * @param _wallet Investor/Dealer address
     */
    function getIdFromWallet(address _wallet) external view returns (bytes32);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import {IInvestorRegistry} from "src/interfaces/IInvestorRegistry.sol";
import {IRoleRegistry} from "src/interfaces/IRoleRegistry.sol";
import {ILibreCoreGateway} from "src/interfaces/ILibreCoreGateway.sol";
import {IEAS} from "eas-contracts/IEAS.sol";

/**
 * @title IGatewayManager Interface
 * @notice Interface for managing cross-chain addresses and investor chain preferences
 * @dev Coordinates with LibreCoreGateway for cross-chain operations
 */
interface IGatewayManager {
    /**
     * @notice Emitted when an investor's preferred chains are updated or set
     * @param investorId The unique identifier of the investor
     * @param chains Array of custom chain IDs that the investor prefers
     * @param oldChains Previous array of chain IDs for the investor
     */
    event InvestorChainsUpdated(bytes32 investorId, uint16[] chains, uint16[] oldChains);

    /**
     * @notice Emitted when cross-chain addresses are updated for an investor
     * @dev Each address change is encoded as: [1 byte action][2 bytes chainSelector][n bytes address]
     * @param investorId The unique identifier of the investor
     * @param addressChanges A list of concatenated bytes describing a change in multi-chain address
     */
    event InvestorAddressesUpdated(bytes32 investorId, bytes[] addressChanges);

    /// @notice Thrown when an unsupported or invalid chain ID is provided
    error GatewayManager_InvalidChain();

    /// @notice Thrown when the investor ID doesn't exist or is invalid
    error GatewayManager_InvalidInvestor();

    /// @notice Thrown when input parameters are invalid or missing
    error GatewayManager_InvalidInput();

    /// @notice Thrown when a cross-chain address is already associated with another investor
    error GatewayManager_AddressAlreadyUsed();

    /**
     * @notice Initializes the gateway manager
     * @param _coreGateway Address of the LibreCoreGateway contract
     * @param _eas Address of the Ethereum Attestation Service
     * @param _investorRegistry Address of the InvestorRegistry contract
     * @param _roleRegistry Address of the RoleRegistry contract
     */
    function initialize(
        ILibreCoreGateway _coreGateway,
        IEAS _eas,
        IInvestorRegistry _investorRegistry,
        IRoleRegistry _roleRegistry
    ) external;

    /**
     * @notice Sets the preferred chains for an investor
     * @param _senderRole Role of the transaction sender
     * @param _investorId Unique identifier of the investor
     * @param _chains Array of chain IDs to set as preferred
     */
    function setInvestorChains(bytes32 _senderRole, bytes32 _investorId, uint16[] calldata _chains) external;

    /**
     * @notice Updates investor's cross-chain addresses and propagates changes to other chains
     * @dev Adds or removes addresses and bridges the updates to other chains
     * @param _senderRole Role of the transaction sender
     * @param _investorId Unique identifier of the investor
     * @param _addressChanges A list of concatenated bytes describing a change in multi-chain address
     */
    function manageCrossChainAddresses(bytes32 _senderRole, bytes32 _investorId, bytes[] calldata _addressChanges)
        external;

    /**
     * @notice Bridges a credential to all investor's preferred chains via core gateway.
     * @param _attestationId The unique ID of the attestation to be bridged.
     * @param _senderRole Role of the transaction sender.
     * @param _chains Chains to bridge credential to (all investor chains if empty)
     */
    function bridgeCredential(bytes32 _attestationId, bytes32 _senderRole, uint16[] memory _chains) external;

    /**
     * @notice Validates if an address is a valid cross-chain address for an investor
     * @param _investorId The investor ID to check
     * @param _chainSelector The chain ID to check
     * @param _address The address to validate
     * @return bool True if the address is valid for the investor on the chain
     */
    function isValidCrossChainAddress(bytes32 _investorId, uint16 _chainSelector, bytes calldata _address)
        external
        view
        returns (bool);

    /**
     * @notice Gets an investor's preferred chains
     * @param _investorId The investor's unique identifier
     * @return uint16[] Array of preferred chain IDs
     */
    function getInvestorChains(bytes32 _investorId) external view returns (uint16[] memory);

    /**
     * @notice Gets the owner of a cross-chain address
     * @param _chainSelector The chain ID of the address
     * @param _address The address to get the owner of
     * @return bytes32 The owner of the address
     */
    function getAddressOwner(uint16 _chainSelector, bytes calldata _address) external view returns (bytes32);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

/**
 * @title IEternalRegistryStorage
 * @notice Interface for managing a flexible and extensible storage system, allowing various types of values to be
 * stored and retrieved for specific registry entries. Provides a robust and modular way to interact with registry data.
 * @dev Enables storage and retrieval of multiple data types (e.g., uint256, address, string, bool, bytes32) and
 * supports operations like batch updates, array manipulations, and role-based access control for data modifications.
 */
interface IEternalRegistryStorage {
    /**
     * @notice Thrown when attempting to modify a key that has been restricted.
     */
    error IEternalRegistryStorage_AttemptToSetARestrictedKey();

    /**
     * @notice Thrown when the length of the provided input arrays does not match.
     */
    error IEternalRegistryStorage_BatchLengthMismatch();

    /**
     * @notice Thrown when attempting to access an array index that is out of bounds.
     */
    error IEternalRegistryStorage_IndexShouldBeLessThanLengthOfTheArray();

    /**
     * @notice Emitted when a boolean value is added to the registry.
     * @param id The identifier of the registry entry.
     * @param key The key associated with the boolean value.
     * @param value The boolean value added to the registry.
     */
    event AddedBool(bytes32 indexed id, bytes32 indexed key, bool value);

    /**
     * @notice Emitted when a uint256 value is added to the registry.
     * @param id The identifier of the registry entry.
     * @param key The key associated with the uint256 value.
     * @param value The uint256 value added to the registry.
     */
    event AddedUint(bytes32 indexed id, bytes32 indexed key, uint256 value);

    /**
     * @notice Emitted when an address value is added to the registry.
     * @param id The identifier of the registry entry.
     * @param key The key associated with the address value.
     * @param value The address value added to the registry.
     */
    event AddedAddress(bytes32 indexed id, bytes32 indexed key, address value);

    /**
     * @notice Emitted when a string value is added to the registry.
     * @param id The identifier of the registry entry.
     * @param key The key associated with the string value.
     * @param value The string value added to the registry.
     */
    event AddedString(bytes32 indexed id, bytes32 indexed key, string value);

    /**
     * @notice Emitted when a bytes32 value is added to the registry.
     * @param id The identifier of the registry entry.
     * @param key The key associated with the bytes32 value.
     * @param value The bytes32 value added to the registry.
     */
    event AddedBytes(bytes32 indexed id, bytes32 indexed key, bytes32 value);

    /**
     * @notice Emitted every time a key is restricted from being set
     * for an specific entry
     * @param _id Id of the entry where the key is restricted
     * @param _key Key being restricted
     */
    event KeyRestricted(bytes32 indexed _id, bytes32 indexed _key);

    /**
     * @notice Get a boolean value from the storage
     * @param _id Id of the registry entry to get the value from
     * @param _key Key identifying the value to get
     * @return bool value of the element associated to the key
     * in the requested registry entry
     */
    function getBool(bytes32 _id, bytes32 _key) external view returns (bool);

    /**
     * @notice Get a uint256 value from the storage
     * @param _id Id of the registry entry to get the value from
     * @param _key Key identifying the value to get
     * @return uint256 value of the element associated to the key
     * in the requested registry entry
     */
    function getUint(bytes32 _id, bytes32 _key) external view returns (uint256);

    /**
     * @notice Get an address value from the storage
     * @param _id Id of the registry entry to get the value from
     * @param _key Key identifying the value to get
     * @return address value of the element associated to the key
     * in the requested registry entry
     */
    function getAddress(bytes32 _id, bytes32 _key) external view returns (address);

    /**
     * @notice Get a string value from the storage
     * @param _id Id of the registry entry to get the value from
     * @param _key Key identifying the value to get
     * @return string value of the element associated to the key
     * in the requested registry entry
     */
    function getString(bytes32 _id, bytes32 _key) external view returns (string memory);

    /**
     * @notice Get a bytes value from the storage
     * @param _id Id of the registry entry to get the value from
     * @param _key Key identifying the value to get
     * @return bytes value of the element associated to the key
     * in the requested registry entry
     */
    function getBytes(bytes32 _id, bytes32 _key) external view returns (bytes32);

    /**
     * @notice Set a boolean value on the storage
     * @param _id Id of the registry entry to set the value on
     * @param _key Key identifying the value to set
     * @param _value Value to set
     * @param _senderRole Role of the sender allowing the operation
     */
    function setBool(bytes32 _id, bytes32 _key, bool _value, bytes32 _senderRole) external;

    /**
     * @notice Set a uint256 value on the storage
     * @param _id Id of the registry entry to set the value on
     * @param _key Key identifying the value to set
     * @param _value Value to set
     * @param _senderRole Role of the sender allowing the operation
     */
    function setUint(bytes32 _id, bytes32 _key, uint256 _value, bytes32 _senderRole) external;

    /**
     * @notice Set an address value on the storage
     * @param _id Id of the registry entry to set the value on
     * @param _key Key identifying the value to set
     * @param _value Value to set
     * @param _senderRole Role of the sender allowing the operation
     */
    function setAddress(bytes32 _id, bytes32 _key, address _value, bytes32 _senderRole) external;

    /**
     * @notice Set a string value on the storage
     * @param _id Id of the registry entry to set the value on
     * @param _key Key identifying the value to set
     * @param _value Value to set
     * @param _senderRole Role of the sender allowing the operation
     */
    function setString(bytes32 _id, bytes32 _key, string calldata _value, bytes32 _senderRole) external;

    /**
     * @notice Set a bytes value on the storage
     * @param _id Id of the registry entry to set the value on
     * @param _key Key identifying the value to set
     * @param _value Value to set
     * @param _senderRole Role of the sender allowing the operation
     */
    function setBytes(bytes32 _id, bytes32 _key, bytes32 _value, bytes32 _senderRole) external;

    /**
     * @notice Set multiple values in one transaction
     * @param _ids Ids associated to the values to set
     * @param _keys Key associated to the values to set
     * @param _boolValues Bool values to set
     * @param _uintValues Uint256 values to set
     * @param _addressValues Address values to set
     * @param _stringValues String values to set
     * @param _bytesValues Bytes32 values to set
     * @param _senderRole Role of the sender allowing the operation
     */
    function setBatch(
        bytes32[] memory _ids,
        bytes32[] memory _keys,
        bool[] memory _boolValues,
        uint256[] memory _uintValues,
        address[] memory _addressValues,
        string[] memory _stringValues,
        bytes32[] memory _bytesValues,
        bytes32 _senderRole
    ) external;

    /**
     * @notice Get multiple values in one transaction
     * @param _ids Ids associated to the values to get
     * @param _keys Keys associated to the values to get
     * @param _boolCount Number of bool values to get
     * @param _uintCount Number of uint256 values to get
     * @param _addressCount Number of address values to get
     * @param _stringCount Number of string values to get
     * @param _bytesCount Number of bytes32 values to get
     * @return _boolValues Bool values return array
     * @return _uintValues Uint256 values return array
     * @return _addressValues Address values return array
     * @return _stringValues String values return array
     * @return _bytesValues Bytes32 values return array
     */
    function getBatch(
        bytes32[] memory _ids,
        bytes32[] memory _keys,
        uint256 _boolCount,
        uint256 _uintCount,
        uint256 _addressCount,
        uint256 _stringCount,
        uint256 _bytesCount
    )
        external
        view
        returns (
            bool[] memory _boolValues,
            uint256[] memory _uintValues,
            address[] memory _addressValues,
            string[] memory _stringValues,
            bytes32[] memory _bytesValues
        );
}

File 20 of 32 : Types.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

// @notice The mapping of whitelisted instruments with expirations
struct InstrumentAccess {
    //Id of the investor
    bytes32 investorId;
    //Credential expiry timestamp
    uint64 expiryTimestamp;
    //Ids of instruments the investor is registered for
    bytes32[] instrumentIds;
    //Access expiry timestamps for instruments the investor is registered for
    uint64[] instrumentExpiryTimestamps;
}

File 21 of 32 : JurisdictionEncodings.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

//The encodings used for jurisdictions are based and ordered after the ISO 3166 standard as of 2023/11/28
//(https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes)
//Any new additions after this date will be added at the end as to not modify existing encodings
//allowed values are in the range of: 2^x, 0 ≤ x ≤ 255
uint256 constant AF = 2 ** 0;
uint256 constant AX = 2 ** 1;
uint256 constant AL = 2 ** 2;
uint256 constant DZ = 2 ** 3;
uint256 constant AS = 2 ** 4;
uint256 constant AD = 2 ** 5;
uint256 constant AO = 2 ** 6;
uint256 constant AI = 2 ** 7;
uint256 constant AQ = 2 ** 8;
uint256 constant AG = 2 ** 9;
uint256 constant AR = 2 ** 10;
uint256 constant AM = 2 ** 11;
uint256 constant AW = 2 ** 12;
uint256 constant AU = 2 ** 13;
uint256 constant AT = 2 ** 14;
uint256 constant AZ = 2 ** 15;
uint256 constant BS = 2 ** 16;
uint256 constant BH = 2 ** 17;
uint256 constant BD = 2 ** 18;
uint256 constant BB = 2 ** 19;
uint256 constant BY = 2 ** 20;
uint256 constant BE = 2 ** 21;
uint256 constant BZ = 2 ** 22;
uint256 constant BJ = 2 ** 23;
uint256 constant BM = 2 ** 24;
uint256 constant BT = 2 ** 25;
uint256 constant BO = 2 ** 26;
uint256 constant BQ = 2 ** 27;
uint256 constant BA = 2 ** 28;
uint256 constant BW = 2 ** 29;
uint256 constant BV = 2 ** 30;
uint256 constant BR = 2 ** 31;
uint256 constant IO = 2 ** 32;
uint256 constant BN = 2 ** 33;
uint256 constant BG = 2 ** 34;
uint256 constant BF = 2 ** 35;
uint256 constant BI = 2 ** 36;
uint256 constant CV = 2 ** 37;
uint256 constant KH = 2 ** 38;
uint256 constant CM = 2 ** 39;
uint256 constant CA = 2 ** 40;
uint256 constant KY = 2 ** 41;
uint256 constant CF = 2 ** 42;
uint256 constant TD = 2 ** 43;
uint256 constant CL = 2 ** 44;
uint256 constant CN = 2 ** 45;
uint256 constant CX = 2 ** 46;
uint256 constant CC = 2 ** 47;
uint256 constant CO = 2 ** 48;
uint256 constant KM = 2 ** 49;
uint256 constant CD = 2 ** 50;
uint256 constant CG = 2 ** 51;
uint256 constant CK = 2 ** 52;
uint256 constant CR = 2 ** 53;
uint256 constant CI = 2 ** 54;
uint256 constant HR = 2 ** 55;
uint256 constant CU = 2 ** 56;
uint256 constant CW = 2 ** 57;
uint256 constant CY = 2 ** 58;
uint256 constant CZ = 2 ** 59;
uint256 constant DK = 2 ** 60;
uint256 constant DJ = 2 ** 61;
uint256 constant DM = 2 ** 62;
uint256 constant DO = 2 ** 63;
uint256 constant EC = 2 ** 64;
uint256 constant EG = 2 ** 65;
uint256 constant SV = 2 ** 66;
uint256 constant GQ = 2 ** 67;
uint256 constant ER = 2 ** 68;
uint256 constant EE = 2 ** 69;
uint256 constant SZ = 2 ** 70;
uint256 constant ET = 2 ** 71;
uint256 constant FK = 2 ** 72;
uint256 constant FO = 2 ** 73;
uint256 constant FJ = 2 ** 74;
uint256 constant FI = 2 ** 75;
uint256 constant FR = 2 ** 76;
uint256 constant GF = 2 ** 77;
uint256 constant PF = 2 ** 78;
uint256 constant TF = 2 ** 79;
uint256 constant GA = 2 ** 80;
uint256 constant GM = 2 ** 81;
uint256 constant GE = 2 ** 82;
uint256 constant DE = 2 ** 83;
uint256 constant GH = 2 ** 84;
uint256 constant GI = 2 ** 85;
uint256 constant GR = 2 ** 86;
uint256 constant GL = 2 ** 87;
uint256 constant GD = 2 ** 88;
uint256 constant GP = 2 ** 89;
uint256 constant GU = 2 ** 90;
uint256 constant GT = 2 ** 91;
uint256 constant GG = 2 ** 92;
uint256 constant GN = 2 ** 93;
uint256 constant GW = 2 ** 94;
uint256 constant GY = 2 ** 95;
uint256 constant HT = 2 ** 96;
uint256 constant HM = 2 ** 97;
uint256 constant VA = 2 ** 98;
uint256 constant HN = 2 ** 99;
uint256 constant HK = 2 ** 100;
uint256 constant HU = 2 ** 101;
uint256 constant IS = 2 ** 102;
uint256 constant IN = 2 ** 103;
uint256 constant ID = 2 ** 104;
uint256 constant IR = 2 ** 105;
uint256 constant IQ = 2 ** 106;
uint256 constant IE = 2 ** 107;
uint256 constant IM = 2 ** 108;
uint256 constant IL = 2 ** 109;
uint256 constant IT = 2 ** 110;
uint256 constant JM = 2 ** 111;
uint256 constant JP = 2 ** 112;
uint256 constant JE = 2 ** 113;
uint256 constant JO = 2 ** 114;
uint256 constant KZ = 2 ** 115;
uint256 constant KE = 2 ** 116;
uint256 constant KI = 2 ** 117;
uint256 constant KP = 2 ** 118;
uint256 constant KR = 2 ** 119;
uint256 constant KW = 2 ** 120;
uint256 constant KG = 2 ** 121;
uint256 constant LA = 2 ** 122;
uint256 constant LV = 2 ** 123;
uint256 constant LB = 2 ** 124;
uint256 constant LS = 2 ** 125;
uint256 constant LR = 2 ** 126;
uint256 constant LY = 2 ** 127;
uint256 constant LI = 2 ** 128;
uint256 constant LT = 2 ** 129;
uint256 constant LU = 2 ** 130;
uint256 constant MO = 2 ** 131;
uint256 constant MK = 2 ** 132;
uint256 constant MG = 2 ** 133;
uint256 constant MW = 2 ** 134;
uint256 constant MY = 2 ** 135;
uint256 constant MV = 2 ** 136;
uint256 constant ML = 2 ** 137;
uint256 constant MT = 2 ** 138;
uint256 constant MH = 2 ** 139;
uint256 constant MQ = 2 ** 140;
uint256 constant MR = 2 ** 141;
uint256 constant MU = 2 ** 142;
uint256 constant YT = 2 ** 143;
uint256 constant MX = 2 ** 144;
uint256 constant FM = 2 ** 145;
uint256 constant MD = 2 ** 146;
uint256 constant MC = 2 ** 147;
uint256 constant MN = 2 ** 148;
uint256 constant ME = 2 ** 149;
uint256 constant MS = 2 ** 150;
uint256 constant MA = 2 ** 151;
uint256 constant MZ = 2 ** 152;
uint256 constant MM = 2 ** 153;
uint256 constant NA = 2 ** 154;
uint256 constant NR = 2 ** 155;
uint256 constant NP = 2 ** 156;
uint256 constant NL = 2 ** 157;
uint256 constant NC = 2 ** 158;
uint256 constant NZ = 2 ** 159;
uint256 constant NI = 2 ** 160;
uint256 constant NE = 2 ** 161;
uint256 constant NG = 2 ** 162;
uint256 constant NU = 2 ** 163;
uint256 constant NF = 2 ** 164;
uint256 constant MP = 2 ** 165;
uint256 constant NO = 2 ** 166;
uint256 constant OM = 2 ** 167;
uint256 constant PK = 2 ** 168;
uint256 constant PW = 2 ** 169;
uint256 constant PS = 2 ** 170;
uint256 constant PA = 2 ** 171;
uint256 constant PG = 2 ** 172;
uint256 constant PY = 2 ** 173;
uint256 constant PE = 2 ** 174;
uint256 constant PH = 2 ** 175;
uint256 constant PN = 2 ** 176;
uint256 constant PL = 2 ** 177;
uint256 constant PT = 2 ** 178;
uint256 constant PR = 2 ** 179;
uint256 constant QA = 2 ** 180;
uint256 constant RE = 2 ** 181;
uint256 constant RO = 2 ** 182;
uint256 constant RU = 2 ** 183;
uint256 constant RW = 2 ** 184;
uint256 constant BL = 2 ** 185;
uint256 constant SH = 2 ** 186;
uint256 constant KN = 2 ** 187;
uint256 constant LC = 2 ** 188;
uint256 constant MF = 2 ** 189;
uint256 constant PM = 2 ** 190;
uint256 constant VC = 2 ** 191;
uint256 constant WS = 2 ** 192;
uint256 constant SM = 2 ** 193;
uint256 constant ST = 2 ** 194;
uint256 constant SA = 2 ** 195;
uint256 constant SN = 2 ** 196;
uint256 constant RS = 2 ** 197;
uint256 constant SC = 2 ** 198;
uint256 constant SL = 2 ** 199;
uint256 constant SG = 2 ** 200;
uint256 constant SX = 2 ** 201;
uint256 constant SK = 2 ** 202;
uint256 constant SI = 2 ** 203;
uint256 constant SB = 2 ** 204;
uint256 constant SO = 2 ** 205;
uint256 constant ZA = 2 ** 206;
uint256 constant GS = 2 ** 207;
uint256 constant SS = 2 ** 208;
uint256 constant ES = 2 ** 209;
uint256 constant LK = 2 ** 210;
uint256 constant SD = 2 ** 211;
uint256 constant SR = 2 ** 212;
uint256 constant SJ = 2 ** 213;
uint256 constant SE = 2 ** 214;
uint256 constant CH = 2 ** 215;
uint256 constant SY = 2 ** 216;
uint256 constant TW = 2 ** 217;
uint256 constant TJ = 2 ** 218;
uint256 constant TZ = 2 ** 219;
uint256 constant TH = 2 ** 220;
uint256 constant TL = 2 ** 221;
uint256 constant TG = 2 ** 222;
uint256 constant TK = 2 ** 223;
uint256 constant TO = 2 ** 224;
uint256 constant TT = 2 ** 225;
uint256 constant TN = 2 ** 226;
uint256 constant TR = 2 ** 227;
uint256 constant TM = 2 ** 228;
uint256 constant TC = 2 ** 229;
uint256 constant TV = 2 ** 230;
uint256 constant UG = 2 ** 231;
uint256 constant UA = 2 ** 232;
uint256 constant AE = 2 ** 233;
uint256 constant GB = 2 ** 234;
uint256 constant UM = 2 ** 235;
uint256 constant US = 2 ** 236;
uint256 constant UY = 2 ** 237;
uint256 constant UZ = 2 ** 238;
uint256 constant VU = 2 ** 239;
uint256 constant VE = 2 ** 240;
uint256 constant VN = 2 ** 241;
uint256 constant VG = 2 ** 242;
uint256 constant VI = 2 ** 243;
uint256 constant WF = 2 ** 244;
uint256 constant EH = 2 ** 245;
uint256 constant YE = 2 ** 246;
uint256 constant ZM = 2 ** 247;
uint256 constant ZW = 2 ** 248;
//values 249-255 reserved for future additions

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.19;

import "./math/Math.sol";
import "./math/SignedMath.sol";

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.19;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @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 amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` 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 amount) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.19;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControlUpgradeable {
    /**
     * @dev The `account` is missing a role.
     */
    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);

    /**
     * @dev The caller of a function is not the expected one.
     *
     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
     */
    error AccessControlBadConfirmation();

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     */
    function renounceRole(bytes32 role, address callerConfirmation) external;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.19;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

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

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

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import {IBaseRegistry} from "src/interfaces/IBaseRegistry.sol";
import {IRoleRegistry} from "src/interfaces/IRoleRegistry.sol";
import {IOperationsEngine} from "src/interfaces/IOperationsEngine.sol";

/**
 * @title IFundRegistry
 * @author Libre
 * @notice Interface for managing and storing information related to funds within a registry.
 * Provides functionality to initialize the registry, add new funds, and emit events for key actions.
 */
interface IFundRegistry is IBaseRegistry {
    /// @notice Error raised when attempting to register a fund that is already registered.
    error IFundRegistry_AlreadyRegistered();

    /**
     * @notice Emited when a new Fund is added
     * @param fundId Id of the new fund
     */
    event NewFundAdded(bytes32 indexed fundId);

    /**
     * @notice Initialize the registry
     * @param _roleRegistry Address of the role registry
     */
    function initialize(IRoleRegistry _roleRegistry, IOperationsEngine _operationsEngine) external;

    /**
     * @notice Adds a new fund to the registry
     * @param _senderRole Role of the sender which allow the operation execution
     * @param _fundId The ID of the fund
     */
    function addNewFund(bytes32 _senderRole, bytes32 _fundId) external;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import {IInvestorRegistry} from "src/interfaces/IInvestorRegistry.sol";
import {IFundRegistry} from "src/interfaces/IFundRegistry.sol";
import {IDealerRegistry} from "src/interfaces/IDealerRegistry.sol";
import {IInstrumentRegistry} from "src/interfaces/IInstrumentRegistry.sol";
import {IEternalRegistryStorage} from "src/interfaces/IEternalRegistryStorage.sol";
import {IRoleRegistry} from "src/interfaces/IRoleRegistry.sol";

enum OPERATIONS {
    // Transfer specific operations:
    TRANSFER,
    FORCED_TRANSFER,
    RECEIVE,
    SEND,
    CLAWBACK,
    // Subscription specific operations:
    ADVISED_CREATE_SUBSCRIPTION,
    CREATE_SUBSCRIPTION,
    SUBSCRIPTION_EXECUTION_DATE_CALCULATION,
    SETTLE_SUBSCRIPTIONS,
    SETTLE_SUBSCRIPTION_PER_ORDER,
    SUBSCRIPTION_CANCELLATION,
    // Redemption specific operations:
    ADVISED_CREATE_REDEMPTION,
    CREATE_REDEMPTION,
    REDEMPTION_EXECUTION_DATE_CALCULATION,
    REDEMPTION_CANCELLATION,
    SETTLE_REDEMPTIONS,
    SETTLE_REDEMPTION_PER_ORDER,
    FORCED_REDEMPTION,
    REDEMPTION_FEE,
    // Secondary Market specific operations:
    FILL,
    ORDER,
    TRADE,
    // NAV operations:
    UPDATE_AUDITED_NAV_PER_SHARE
}

interface IOperationModule {
    /**
     * @return The list of operations associated with a module
     * @dev It should contains all the Operation ids of all
     * the IOperations it implements
     */
    function getOperations() external pure returns (OPERATIONS[] memory);

    /**
     * @return Returns the name of the module
     */
    function name() external pure returns (string memory);

    /**
     * @return Returns the hash of the module name
     */
    function nameHash() external pure returns (bytes32);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import {IInvestorRegistry} from "src/interfaces/IInvestorRegistry.sol";
import {IInstrumentRegistry} from "src/interfaces/IInstrumentRegistry.sol";
import {IRoleRegistry} from "src/interfaces/IRoleRegistry.sol";
import {IGatewayManager} from "src/interfaces/IGatewayManager.sol";
import {Attestation} from "eas-contracts/IEAS.sol";
import {UidOp} from "src/lib/OperationId.sol";

/**
 * @notice Structure representing token transfer details across chains
 * @dev Used for both locking and unlocking operations
 */
struct TokenTransfer {
    uint16 chainSelector; // Unique 16-bit Custom Chain ID where the Token is transfered
    bytes32 instrumentId; // Instrument ID associated with the security token
    bytes32 investorId; // Investor ID associated with the sender
    bytes receiver; // Recipient's address for minting/unlocking
    uint256 amount; // Amount of the asset to transfer
}

/**
 * @notice Structure for managing investor addresses across specific chain
 * @dev Each address change is encoded as: [1 byte action][n bytes address]
 */
struct ChainInvestorAddresses {
    // Unique investor ID
    bytes32 investorId;
    // Unique chain ID
    uint16 chainSelector;
    // Address size for that chain
    uint8 addressSize;
    // A list of concatenated bytes describing a change in addresses
    // Format `addressChanges`` := bytes1 action ++ bytes accountAddress
    // where `action`: 0b0 -> Add, 0b1 -> Remove
    bytes[] addressChanges;
}

/// @notice Interface for the Libre Core Gateway.
interface ILibreCoreGateway {
    // Events
    /**
     * @notice Emitted when tokens are locked on source chain
     * @param uid Unique identifier of the originating bridge operation
     * @param encodedTransferDetails ABI-encoded TokenTransfer struct
     */
    event TokenLockedAndBridged(bytes32 indexed uid, bytes encodedTransferDetails);

    /**
     * @notice Emitted when tokens are unlocked on destination chain
     * @param uid Unique identifier of the originating bridge operation
     * @param encodedTransferDetails ABI-encoded TokenTransfer struct
     */
    event TokenUnlocked(bytes32 indexed uid, bytes encodedTransferDetails);

    /**
     * @notice Emitted when a credential is bridged to other chains
     * @param investorId Investor ID of the investor
     * @param chainSelectors Array of destination chains where credential is bridged
     * @param uid Unique identifier of the originating bridge operation
     */
    event CredentialBridged(bytes32 indexed investorId, uint16[] chainSelectors, bytes32 indexed uid);

    /**
     * @notice Emitted when investor chain-specific addresses are bridged
     * @param uid Unique identifier of the originating bridge operation
     * @param investorId Investor ID of the investor
     * @param investorDestChainSelectors Array of destination chain selectors that investor has addresses on
     * @param encodedChainInvestorAddresses ABI-encoded ChainInvestorAddresses struct
     */
    event ChainInvestorAddressesBridged(
        bytes32 indexed uid,
        bytes32 investorId,
        uint16[] investorDestChainSelectors,
        bytes encodedChainInvestorAddresses
    );

    /**
     * @notice Emitted when chain support status is updated
     * @param chainSelector Chain ID whose support status changed
     * @param isSupported New support status
     */
    event ChainSupportUpdated(uint16 chainSelector, bool isSupported);

    /**
     * @notice Emitted when chain variable length address support status is updated
     * @param _chainSelector Chain ID whose support status changed
     * @param _isVariableLengthAddress New support status
     */
    event ChainVariableLengthAddressSupportUpdated(uint16 _chainSelector, bool _isVariableLengthAddress);

    // Custom Errors
    /// @notice Thrown when operation is attempted on an unsupported chain
    /// @param chainSelector The unsupported chain ID
    error CoreGateway_ChainNotSupported(uint16 chainSelector);

    /// @notice Thrown when the chain selector (chain ID) not matches with Native chain ID for unlock requests
    error CoreGateway_InvalidNativeChainSelector();

    /// @notice Thrown when token address is not found or invalid
    /// @param token The invalid token address
    error CoreGateway_TokenAddressNotFound(address token);

    /// @notice Thrown when attempting transfer with zero amount
    error CoreGateway_ZeroAmount();

    /// @notice Thrown when transfer validation fails
    error CoreGateway_InvalidTransfer();

    /// @notice Thrown when provided investor id is not found
    error CoreGateway_InvalidInvestor();

    /// @notice Thrown when wrong inputs are provided
    error CoreGateway_InvalidInput();

    /// @notice Thrown when credential to be bridged are not stored or mismatches with registry
    error CoreGateway_InvalidCredential();

    /// @notice Thrown when an attempted status update results in no actual change.
    error CoreGateway_NoStatusChanged();

    /// @notice Thrown when trying to use a UID that has already been used for a different operation
    error CoreGateway_UidAlreadyUsed();

    /// @notice Thrown when the UID operation state is invalid for the current operation
    error CoreGateway_InvalidUidOperation();

    /// @notice Thrown when trying to set operation to Unset
    error CoreGateway_CannotSetUnsetOperation();

    /// @notice Thrown when credential has already been bridged to a specific chain
    /// @param chainSelector The chain that already has this credential
    error CoreGateway_CredentialAlreadyBridged(uint16 chainSelector);

    /**
     * @notice Initializes the gateway contract
     * @param _instrumentRegistry Address of the Instrument Registry
     * @param _investorRegistry Address of the Investor Registry
     * @param _gatewayManager Address of the Gateway Manager
     * @param _roleRegistry Address of the Role Registry
     * @param _supportedChains Array of supported custom chain Ids
     */
    function initialize(
        IInstrumentRegistry _instrumentRegistry,
        IInvestorRegistry _investorRegistry,
        IGatewayManager _gatewayManager,
        IRoleRegistry _roleRegistry,
        uint16[] memory _supportedChains
    ) external;

    /**
     * @notice Locks tokens for cross-chain transfer and emits event for minting
     * @dev Validates investor credentials and cross-chain addresses before locking
     * @param transferDetails The details of the token transfer.
     */
    function lockAndBridge(TokenTransfer calldata transferDetails) external;

    /**
     * @notice Unlocks tokens on the source chain
     * @dev Called by bridge operators to release tokens to the recipient
     * @param _senderRole The role of the caller
     * @param encodedTransferDetails ABI-encoded transfer details
     * @param uid Unique identifier of the originating bridge operation
     */
    function unlock(bytes32 _senderRole, bytes calldata encodedTransferDetails, bytes32 uid) external;

    /**
     * @notice Bridges a credential to one or more destination chains.
     * @dev Perform below steps:
     *      1. Verifies all destination chains are supported
     *      2. Validates the attestation matches the stored credential for the investor
     *      3. Emits CredentialBridged event for cross-chain communication
     * @param _senderRole The role of the caller
     * @param attestation The attestation details to bridge.
     * @param destinationChainSelectors Array of destination chain IDs.
     */
    function bridgeCredential(
        bytes32 _senderRole,
        Attestation calldata attestation,
        uint16[] calldata destinationChainSelectors
    ) external;

    /**
     * @notice Bridges investor addresses updates.
     * @dev Processes and broadcasts chain specific address changes across destination chains
     * Destination chains are extracted from _addressChanges to avoid parameter redundancy
     * UID is generated internally and nonce is incremented for operation tracking
     * Emits `ChainInvestorAddressesBridged` event for each chain's address updates.
     *
     * @param _senderRole The role of the caller for authorization
     * @param _investorId The unique identifier of the investor whose addresses are being updated
     * @param _addressChanges Array of encoded address changes containing chain and address information
     * @param investorDestChainSelectors Array of destination chain selectors that investor has addresses on
     */
    function bridgeInvestorAddresses(
        bytes32 _senderRole,
        bytes32 _investorId,
        bytes[] calldata _addressChanges,
        uint16[] calldata investorDestChainSelectors
    ) external;

    /**
     * @notice Pauses all gateway operations
     * @param _senderRole The role of the caller
     */
    function pause(bytes32 _senderRole) external;

    /**
     * @notice Unpauses gateway operations
     * @param _senderRole The role of the caller
     */
    function unpause(bytes32 _senderRole) external;

    /**
     * @notice Enables or disables support for a specific chain.
     * @param _senderRole The role of the caller
     * @param chainSelector The Custom chain ID.
     * @param isSupported Whether the chain is supported.
     */
    function setChainSupport(bytes32 _senderRole, uint16 chainSelector, bool isSupported) external;

    /**
     * @notice Enables or disables support for variable length addresses for a specific chain.
     * @param _senderRole The role of the caller
     * @param _chainSelector The Custom chain ID.
     * @param _isVariableLengthAddress Whether variable length addresses for the chain are supported.
     */
    function setChainVariableLengthAddressSupport(
        bytes32 _senderRole,
        uint16 _chainSelector,
        bool _isVariableLengthAddress
    ) external;

    /**
     * @notice Retrieves the total amount of a specific instrument/token locked in the gateway.
     * @param instrumentId The Instrument ID associated with the security token
     * @return The total amount of the token locked in the gateway
     */
    function getLockedBalance(bytes32 instrumentId) external view returns (uint256);

    /**
     * @notice Checks if a chain is supported.
     * @param chainSelector The ID of the chain to check.
     * @return True if the chain is supported, false otherwise.
     */
    function isSupportedChain(uint16 chainSelector) external view returns (bool);

    /**
     * @notice Gets the list of supported chains
     * @return uint16[] Array of supported chain IDs
     */
    function getSupportedChains() external view returns (uint16[] memory);

    /**
     * @notice Gets the last operation performed with a specific UID.
     * @param uid The unique identifier to query.
     * @return The last operation performed with the given UID.
     */
    function getUidLastOp(bytes32 uid) external view returns (UidOp);
}

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

pragma solidity ^0.8.19;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.19;

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

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

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

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

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

pragma solidity ^0.8.19;

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

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

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

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

    /**
     * @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` 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 amount) external returns (bool);
}

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

// Enum representing last operation with UID
enum UidOp {
    Unset, // default value for unset entries
    MintUnlock,
    BurnLock,
    CredentialSync,
    AddressSync
}

/**
 * @title OperationId
 * @notice Library for generating unique identifiers for cross-chain operations
 * @dev Used to create consistent UIDs across source and destination chains
 */
library OperationId {
    /**
     * @notice Generate a new unique id for the bridge operation
     * @param _nonce Current operation nonce
     * @param _sourceChainSelector Chain selector of the source chain
     * @param _destChainSelector Chain selector of the destination chain
     * @param _investorId Investor ID bytes
     * @return bytes32 Unique operation identifier
     */
    function generate(uint128 _nonce, uint16 _sourceChainSelector, uint16 _destChainSelector, bytes32 _investorId)
        internal
        pure
        returns (bytes32)
    {
        return keccak256(abi.encodePacked(_nonce, _sourceChainSelector, _destChainSelector, _investorId));
    }
}

Settings
{
  "remappings": [
    "lib/aave-v3-horizon/lib/solidity-utils/lib/openzeppelin-contracts-upgradeable:@openzeppelin/contracts-upgradeable/=lib/aave-v3-horizon/lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/contracts/",
    "lib/aave-v3-horizon/lib/solidity-utils/lib/openzeppelin-contracts-upgradeable:@openzeppelin/contracts/=lib/aave-v3-horizon/lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/",
    "lib/aave-v3-horizon:solidity-utils/=lib/aave-v3-horizon/lib/solidity-utils/src/",
    "lib/aave-v3-horizon:forge-std/=lib/aave-v3-horizon/lib/forge-std/src/",
    "lib/aave-v3-horizon:ds-test/=lib/aave-v3-horizon/lib/forge-std/lib/ds-test/src/",
    "lib/aave-v3-horizon:openzeppelin-contracts-upgradeable/=lib/aave-v3-horizon/lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/",
    "lib/aave-v3-horizon:openzeppelin-contracts/=lib/aave-v3-horizon/lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin/=lib/openzeppelin-contracts/",
    "@openzeppelin/=lib/openzeppelin-contracts/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "eas-contracts/=lib/eas-contracts/contracts/",
    "safe-contracts/=lib/safe-contracts/contracts/",
    "@aave-v3-horizon/=lib/aave-v3-horizon/",
    "BokkyPooBahsDateTimeLibrary/=lib/BokkyPooBahsDateTimeLibrary/",
    "aave-v3-horizon/=lib/aave-v3-horizon/",
    "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
    "halmos-cheatcodes/=lib/aave-v3-horizon/lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
    "risc0-ethereum/=lib/risc0-ethereum/",
    "solidity-utils/=lib/aave-v3-horizon/lib/solidity-utils/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"contract IEAS","name":"eas","type":"address"},{"internalType":"contract IInvestorRegistry","name":"_investorRegistry","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessDenied","type":"error"},{"inputs":[],"name":"InsufficientValue","type":"error"},{"inputs":[],"name":"InvalidEAS","type":"error"},{"inputs":[],"name":"InvalidLength","type":"error"},{"inputs":[],"name":"NotPayable","type":"error"},{"inputs":[{"components":[{"internalType":"bytes32","name":"uid","type":"bytes32"},{"internalType":"bytes32","name":"schema","type":"bytes32"},{"internalType":"uint64","name":"time","type":"uint64"},{"internalType":"uint64","name":"expirationTime","type":"uint64"},{"internalType":"uint64","name":"revocationTime","type":"uint64"},{"internalType":"bytes32","name":"refUID","type":"bytes32"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"attester","type":"address"},{"internalType":"bool","name":"revocable","type":"bool"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Attestation","name":"attestation","type":"tuple"}],"name":"attest","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"investorRegistry","outputs":[{"internalType":"contract IInvestorRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPayable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"uid","type":"bytes32"},{"internalType":"bytes32","name":"schema","type":"bytes32"},{"internalType":"uint64","name":"time","type":"uint64"},{"internalType":"uint64","name":"expirationTime","type":"uint64"},{"internalType":"uint64","name":"revocationTime","type":"uint64"},{"internalType":"bytes32","name":"refUID","type":"bytes32"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"attester","type":"address"},{"internalType":"bool","name":"revocable","type":"bool"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Attestation[]","name":"attestations","type":"tuple[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"multiAttest","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"uid","type":"bytes32"},{"internalType":"bytes32","name":"schema","type":"bytes32"},{"internalType":"uint64","name":"time","type":"uint64"},{"internalType":"uint64","name":"expirationTime","type":"uint64"},{"internalType":"uint64","name":"revocationTime","type":"uint64"},{"internalType":"bytes32","name":"refUID","type":"bytes32"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"attester","type":"address"},{"internalType":"bool","name":"revocable","type":"bool"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Attestation[]","name":"attestations","type":"tuple[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"multiRevoke","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"uid","type":"bytes32"},{"internalType":"bytes32","name":"schema","type":"bytes32"},{"internalType":"uint64","name":"time","type":"uint64"},{"internalType":"uint64","name":"expirationTime","type":"uint64"},{"internalType":"uint64","name":"revocationTime","type":"uint64"},{"internalType":"bytes32","name":"refUID","type":"bytes32"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"attester","type":"address"},{"internalType":"bool","name":"revocable","type":"bool"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Attestation","name":"attestation","type":"tuple"}],"name":"revoke","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

610120604052348015610010575f5ffd5b50604051610b0e380380610b0e83398101604081905261002f91610095565b6001608052600360a0525f60c052816001600160a01b038116610065576040516341bc07ff60e11b815260040160405180910390fd5b6001600160a01b0390811660e0521661010052506100cd565b6001600160a01b0381168114610092575f5ffd5b50565b5f5f604083850312156100a6575f5ffd5b82516100b18161007e565b60208401519092506100c28161007e565b809150509250929050565b60805160a05160c05160e051610100516109fe6101105f395f818160a401526104a601525f61045b01525f6101cb01525f6101a201525f61017901526109fe5ff3fe608060405260043610610071575f3560e01c806391db0b7e1161004c57806391db0b7e14610127578063ce46e0461461013a578063e49617e11461014c578063e60c35051461015f575f5ffd5b80633734256e1461009357806354fd4d50146100e357806388e5b2d914610104575f5ffd5b3661008f57604051631574f9f360e01b815260040160405180910390fd5b5f5ffd5b34801561009e575f5ffd5b506100c67f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100ee575f5ffd5b506100f7610172565b6040516100da9190610603565b610117610112366004610680565b610215565b60405190151581526020016100da565b610117610135366004610680565b6102e0565b348015610145575f5ffd5b505f610117565b61011761015a3660046106ec565b61039b565b61011761016d3660046106ec565b6103ad565b606061019d7f00000000000000000000000000000000000000000000000000000000000000006103c0565b6101c67f00000000000000000000000000000000000000000000000000000000000000006103c0565b6101ef7f00000000000000000000000000000000000000000000000000000000000000006103c0565b60405160200161020193929190610742565b604051602081830303815290604052905090565b5f61021e610450565b8382811461023f5760405163251f56a160e21b815260040160405180910390fd5b345f5b828110156102d0575f86868381811061025d5761025d610781565b905060200201359050828111156102875760405163044044a560e21b815260040160405180910390fd5b6102b489898481811061029c5761029c610781565b90506020028101906102ae9190610795565b50600190565b6102c4575f9450505050506102d8565b90910390600101610242565b506001925050505b949350505050565b5f6102e9610450565b8382811461030a5760405163251f56a160e21b815260040160405180910390fd5b345f5b828110156102d0575f86868381811061032857610328610781565b905060200201359050828111156103525760405163044044a560e21b815260040160405180910390fd5b61037f89898481811061036757610367610781565b90506020028101906103799190610795565b8261049b565b61038f575f9450505050506102d8565b9091039060010161030d565b5f6103a4610450565b60015b92915050565b5f6103b6610450565b6103a7823461049b565b60605f6103cc8361052c565b60010190505f8167ffffffffffffffff8111156103eb576103eb6107b4565b6040519080825280601f01601f191660200182016040528015610415576020820181803683370190505b5090508181016020015b5f19016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a850494508461041f57509392505050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461049957604051634ca8886760e01b815260040160405180910390fd5b565b5f6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166104d7610100850160e086016107c8565b6001600160a01b0316146104ec57505f6103a7565b5f806104fc6101208601866107ee565b8101906105099190610904565b92509250508051825114610521575f925050506103a7565b506001949350505050565b5f8072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b831061056a5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef81000000008310610596576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc1000083106105b457662386f26fc10000830492506010015b6305f5e10083106105cc576305f5e100830492506008015b61271083106105e057612710830492506004015b606483106105f2576064830492506002015b600a83106103a75760010192915050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f5f83601f840112610648575f5ffd5b50813567ffffffffffffffff81111561065f575f5ffd5b6020830191508360208260051b8501011115610679575f5ffd5b9250929050565b5f5f5f5f60408587031215610693575f5ffd5b843567ffffffffffffffff8111156106a9575f5ffd5b6106b587828801610638565b909550935050602085013567ffffffffffffffff8111156106d4575f5ffd5b6106e087828801610638565b95989497509550505050565b5f602082840312156106fc575f5ffd5b813567ffffffffffffffff811115610712575f5ffd5b82016101408185031215610724575f5ffd5b9392505050565b5f81518060208401855e5f93019283525090919050565b5f61074d828661072b565b601760f91b8152610761600182018661072b565b9050601760f91b8152610777600182018561072b565b9695505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f823561013e198336030181126107aa575f5ffd5b9190910192915050565b634e487b7160e01b5f52604160045260245ffd5b5f602082840312156107d8575f5ffd5b81356001600160a01b0381168114610724575f5ffd5b5f5f8335601e19843603018112610803575f5ffd5b83018035915067ffffffffffffffff82111561081d575f5ffd5b602001915036819003821315610679575f5ffd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561085a5761085a6107b4565b604052919050565b5f67ffffffffffffffff82111561087b5761087b6107b4565b5060051b60200190565b5f82601f830112610894575f5ffd5b81356108a76108a282610862565b610831565b8082825260208201915060208360051b8601019250858311156108c8575f5ffd5b602085015b838110156108fa57803567ffffffffffffffff811681146108ec575f5ffd5b8352602092830192016108cd565b5095945050505050565b5f5f5f60608486031215610916575f5ffd5b83359250602084013567ffffffffffffffff811115610933575f5ffd5b8401601f81018613610943575f5ffd5b80356109516108a282610862565b8082825260208201915060208360051b850101925088831115610972575f5ffd5b6020840193505b82841015610994578335825260209384019390910190610979565b9450505050604084013567ffffffffffffffff8111156109b2575f5ffd5b6109be86828701610885565b915050925092509256fea26469706673582212209da8a9ff874542ed558a1a588833d8b51c195d809e823de93da6542c6ea7e1ff64736f6c634300081c003300000000000000000000000042000000000000000000000000000000000000210000000000000000000000007ed179e1196dfcaa6ae30a96dc697de7a2677f67

Deployed Bytecode

0x608060405260043610610071575f3560e01c806391db0b7e1161004c57806391db0b7e14610127578063ce46e0461461013a578063e49617e11461014c578063e60c35051461015f575f5ffd5b80633734256e1461009357806354fd4d50146100e357806388e5b2d914610104575f5ffd5b3661008f57604051631574f9f360e01b815260040160405180910390fd5b5f5ffd5b34801561009e575f5ffd5b506100c67f0000000000000000000000007ed179e1196dfcaa6ae30a96dc697de7a2677f6781565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100ee575f5ffd5b506100f7610172565b6040516100da9190610603565b610117610112366004610680565b610215565b60405190151581526020016100da565b610117610135366004610680565b6102e0565b348015610145575f5ffd5b505f610117565b61011761015a3660046106ec565b61039b565b61011761016d3660046106ec565b6103ad565b606061019d7f00000000000000000000000000000000000000000000000000000000000000016103c0565b6101c67f00000000000000000000000000000000000000000000000000000000000000036103c0565b6101ef7f00000000000000000000000000000000000000000000000000000000000000006103c0565b60405160200161020193929190610742565b604051602081830303815290604052905090565b5f61021e610450565b8382811461023f5760405163251f56a160e21b815260040160405180910390fd5b345f5b828110156102d0575f86868381811061025d5761025d610781565b905060200201359050828111156102875760405163044044a560e21b815260040160405180910390fd5b6102b489898481811061029c5761029c610781565b90506020028101906102ae9190610795565b50600190565b6102c4575f9450505050506102d8565b90910390600101610242565b506001925050505b949350505050565b5f6102e9610450565b8382811461030a5760405163251f56a160e21b815260040160405180910390fd5b345f5b828110156102d0575f86868381811061032857610328610781565b905060200201359050828111156103525760405163044044a560e21b815260040160405180910390fd5b61037f89898481811061036757610367610781565b90506020028101906103799190610795565b8261049b565b61038f575f9450505050506102d8565b9091039060010161030d565b5f6103a4610450565b60015b92915050565b5f6103b6610450565b6103a7823461049b565b60605f6103cc8361052c565b60010190505f8167ffffffffffffffff8111156103eb576103eb6107b4565b6040519080825280601f01601f191660200182016040528015610415576020820181803683370190505b5090508181016020015b5f19016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a850494508461041f57509392505050565b336001600160a01b037f0000000000000000000000004200000000000000000000000000000000000021161461049957604051634ca8886760e01b815260040160405180910390fd5b565b5f6001600160a01b037f0000000000000000000000007ed179e1196dfcaa6ae30a96dc697de7a2677f67166104d7610100850160e086016107c8565b6001600160a01b0316146104ec57505f6103a7565b5f806104fc6101208601866107ee565b8101906105099190610904565b92509250508051825114610521575f925050506103a7565b506001949350505050565b5f8072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b831061056a5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef81000000008310610596576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc1000083106105b457662386f26fc10000830492506010015b6305f5e10083106105cc576305f5e100830492506008015b61271083106105e057612710830492506004015b606483106105f2576064830492506002015b600a83106103a75760010192915050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f5f83601f840112610648575f5ffd5b50813567ffffffffffffffff81111561065f575f5ffd5b6020830191508360208260051b8501011115610679575f5ffd5b9250929050565b5f5f5f5f60408587031215610693575f5ffd5b843567ffffffffffffffff8111156106a9575f5ffd5b6106b587828801610638565b909550935050602085013567ffffffffffffffff8111156106d4575f5ffd5b6106e087828801610638565b95989497509550505050565b5f602082840312156106fc575f5ffd5b813567ffffffffffffffff811115610712575f5ffd5b82016101408185031215610724575f5ffd5b9392505050565b5f81518060208401855e5f93019283525090919050565b5f61074d828661072b565b601760f91b8152610761600182018661072b565b9050601760f91b8152610777600182018561072b565b9695505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f823561013e198336030181126107aa575f5ffd5b9190910192915050565b634e487b7160e01b5f52604160045260245ffd5b5f602082840312156107d8575f5ffd5b81356001600160a01b0381168114610724575f5ffd5b5f5f8335601e19843603018112610803575f5ffd5b83018035915067ffffffffffffffff82111561081d575f5ffd5b602001915036819003821315610679575f5ffd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561085a5761085a6107b4565b604052919050565b5f67ffffffffffffffff82111561087b5761087b6107b4565b5060051b60200190565b5f82601f830112610894575f5ffd5b81356108a76108a282610862565b610831565b8082825260208201915060208360051b8601019250858311156108c8575f5ffd5b602085015b838110156108fa57803567ffffffffffffffff811681146108ec575f5ffd5b8352602092830192016108cd565b5095945050505050565b5f5f5f60608486031215610916575f5ffd5b83359250602084013567ffffffffffffffff811115610933575f5ffd5b8401601f81018613610943575f5ffd5b80356109516108a282610862565b8082825260208201915060208360051b850101925088831115610972575f5ffd5b6020840193505b82841015610994578335825260209384019390910190610979565b9450505050604084013567ffffffffffffffff8111156109b2575f5ffd5b6109be86828701610885565b915050925092509256fea26469706673582212209da8a9ff874542ed558a1a588833d8b51c195d809e823de93da6542c6ea7e1ff64736f6c634300081c0033

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

00000000000000000000000042000000000000000000000000000000000000210000000000000000000000007ed179e1196dfcaa6ae30a96dc697de7a2677f67

-----Decoded View---------------
Arg [0] : eas (address): 0x4200000000000000000000000000000000000021
Arg [1] : _investorRegistry (address): 0x7Ed179E1196dfcaA6ae30A96DC697dE7A2677f67

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000004200000000000000000000000000000000000021
Arg [1] : 0000000000000000000000007ed179e1196dfcaa6ae30a96dc697de7a2677f67


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

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.