ETH Price: $3,130.60 (-1.56%)
 

Overview

ETH Balance

0 ETH

ETH Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Delete Address314983782025-06-13 3:41:43182 days ago1749786103IN
0x2661710D...8a6369e3a
0 ETH0.000000150.00428698

Parent Transaction Hash Block From To
View All Internal Transactions

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
EthosProfile

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion
File 1 of 34 : EthosProfile.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import { IEthosProfile } from "./interfaces/IEthosProfile.sol";
import { ITargetStatus } from "./interfaces/ITargetStatus.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { ETHOS_REVIEW, ETHOS_ATTESTATION } from "./utils/Constants.sol";
import { AccessControl } from "./utils/AccessControl.sol";
import { ProfileNotFound, ProfileExists, ProfileNotFoundForAddress, AddressCompromised, AddressAlreadyInvited, MaxInvitesReached, MaxAddressesReached, ProfileExistsForAddress, ProfileAccess, AddressAuthorization, InsufficientInvites, AddressNotInvited, InvalidSender, InvalidIndex } from "./errors/ProfileErrors.sol";
import { Common } from "./utils/Common.sol";
import { EthosVouch } from "./EthosVouch.sol";

/**
 * @title EthosProfile
 * @dev The EthosProfile contract serves as the primary profile management system of the Ethos Network protocol.
 * Users who join the network will first interact with EthosProfile to create their profile and receive a unique profile ID.
 * This profile ID serves as the user's identity in the Ethos Network and allows users to interact with all other Ethos Network smart contract systems.
 * All protocol interactions are associated with this profile ID, which also serves a record-keeping purpose.
 *
 * Key Features:
 * - Users can associate multiple addresses with a single profile ID, enabling participation from any wallet they own.
 * - Profiles are fully public and transparent, acting as a tool for others to verify on-chain activities and history.
 * - Implements an invite system for creating new profiles.
 * - Supports the creation of "mock" profiles for tracking reviews and attestations without associated subjects.
 * - Allows users to archive and restore their profiles.
 *
 * Invite System:
 * - To create a profile on Ethos Network, a user's address must first be invited by an existing profile.
 * - The inviting profile must hold sufficient invites (distributed by admin).
 * - Upon receiving an invite, the user may accept it and create a profile.
 * - A user may receive multiple concurrent invites for the same address.
 * - The profile ID of the inviter is forever associated with the newly created profile.
 * - This system promotes responsible growth of the network and creates a chain of accountability among users.
 *
 * Multiple Addresses:
 * - Users are encouraged to register different addresses with their profile ID.
 * - In case of account compromises, a profile can unregister an account and mark it as compromised.
 *
 * Mock Profiles:
 * - In addition to user profiles, EthosProfile is used to track reviews (from EthosReview.sol) and attestations (from EthosAttestation.sol)
 * - When a review or attestation is created without an associated subject profileId, EthosProfile will issue a "mock id" to it.
 * - A mock ID is an empty, non-user profile used to track these activity items. This ensures reviews and attestations for the same
 *   subject are linked together with a distinct ID.
 */
contract EthosProfile is IEthosProfile, ITargetStatus, AccessControl, UUPSUpgradeable, Common {
  /**
   * @dev Constructor that disables initializers when the implementation contract is deployed.
   * This prevents the implementation contract from being initialized, which is important for
   * security since the implementation contract should never be used directly, only through
   * delegatecall from the proxy.
   */
  constructor() {
    _disableInitializers();
  }

  enum AddressClaimStatus {
    Unclaimed,
    Claimed
  }

  // Tracks the total number of profiles created. Initial value is 1 since no profile with ID 0 exists.
  uint256 public profileCount;
  // Default number of invites each profile starts with upon creation. Can be modified by an admin.
  uint256 public defaultNumberOfInvites;

  // Stores profile information, where the key is the profile ID, and the value is the Profile struct.
  mapping(uint256 => Profile) private profiles;
  // Maps a user's address to their profile ID for quick lookups. This includes removed addresses; do not rely on it for verification.
  mapping(address => uint256) public profileIdByAddress;
  // Keeps track of addresses that have been removed from a profile, preventing certain actions from being taken by these addresses.
  mapping(address => bool) public isAddressCompromised;
  // Maps an attestation hash to a profile ID, linking attestations to profiles.
  mapping(bytes32 => uint256) public profileIdByAttestation;
  // Tracks the timestamp at which a specific user was invited by a profile.
  mapping(uint256 => mapping(address => uint256)) public sentAt;

  // Maximum number of addresses that can be associated with a single profile
  uint256 public maxNumberOfAddresses;
  // Maximum number of invites a profile can have (including available, sent, and accepted)
  uint256 public maxNumberOfInvites;
  // Maps profileId -> invitee address -> index in the sent invites array
  // Used for efficient removal of sent invites
  mapping(uint256 => mapping(address => uint256)) private sentInviteIndexByProfileIdAndAddress;
  // Maps inviter profileId -> invited profileId -> index in the acceptedIds array
  // This ended up being unused in the final implementation
  mapping(uint256 => mapping(uint256 => uint256)) private acceptedIdIndexByProfileIdAndId;
  // Maps profileId -> address -> index in the addresses array
  mapping(uint256 => mapping(address => uint256)) private addressIndexByProfileIdAndAddress;

  // Add storage gap as the last storage variable
  // This allows us to add new storage variables in future upgrades
  // by reducing the size of this gap
  uint256[50] private __gap;

  event MockProfileCreated(uint256 indexed mockId);
  event ProfileCreated(uint256 indexed profileId, address indexed addr);
  event ProfileArchived(uint256 indexed profileId);
  event ProfileRestored(uint256 indexed profileId);
  event AddressClaim(
    uint256 indexed profileId,
    address indexed addr,
    AddressClaimStatus indexed claim
  );
  event UserInvited(uint256 inviterID, address inviteeAddress);
  event DefaultInvitesChanged(uint256 defaultInvites);
  event InvitesAdded(uint256 profileId, uint256 amount);
  event Uninvited(
    uint256 inviterId,
    address inviterAddress,
    uint256 remainingInvites,
    address uninvitedUser
  );

  modifier isEthosAttestation() {
    if (msg.sender != contractAddressManager.getContractAddressForName(ETHOS_ATTESTATION)) {
      revert InvalidSender();
    }
    _;
  }

  modifier onlyNonCompromisedAddress(address addr) {
    if (isAddressCompromised[addr]) {
      revert AddressCompromised(addr);
    }
    _;
  }

  /**
   * @dev Initializes the contract.
   * @param owner Owner address.
   * @param admin Admin address.
   * @param expectedSigner ExpectedSigner address.
   * @param signatureVerifier SignatureVerifier address.
   * @param contractAddressManagerAddr ContractAddressManagerAddr address.
   */
  function initialize(
    address owner,
    address admin,
    address expectedSigner,
    address signatureVerifier,
    address contractAddressManagerAddr
  ) external initializer {
    __accessControl_init(
      owner,
      admin,
      expectedSigner,
      signatureVerifier,
      contractAddressManagerAddr
    );
    __UUPSUpgradeable_init();
    maxNumberOfInvites = 2048;
    maxNumberOfAddresses = 128;
    profileCount = 1; // no profiles with id 0
    _createProfile(owner);
    profiles[1].inviteInfo.available = 10;
    // only the origin can invite themself
    profiles[1].inviteInfo.invitedBy = 1;
  }

  /**
   * @notice Restricts upgrading to owner
   * @param newImplementation Address of new implementation contract
   */
  function _authorizeUpgrade(
    address newImplementation
  ) internal override onlyOwner onlyNonZeroAddress(newImplementation) {
    // Intentionally left blank to ensure onlyOwner and zeroCheck modifiers run
  }

  /**
   * @dev Creates a new Ethos profile for the sender.
   * @param inviterId Profile ID of the account that is inviting the new user
   * @notice This function can only be called by an address that has been invited by an existing profile.
   * The inviter must have available invites and their profile must be active.
   */
  function createProfile(
    uint256 inviterId
  ) external whenNotPaused onlyNonCompromisedAddress(msg.sender) {
    (
      bool inviteSenderVerified,
      bool inviteSenderArchived,
      bool inviteSenderIsMock
    ) = profileStatusById(inviterId);
    if (!inviteSenderVerified || inviteSenderArchived || inviteSenderIsMock) {
      revert InvalidSender();
    }
    _inviterProfileAuthorizedSender(inviterId, msg.sender);

    uint256 newID = _createProfile(msg.sender);
    profiles[newID].inviteInfo.invitedBy = inviterId;
    profiles[inviterId].inviteInfo.acceptedIds.push(newID);
  }

  /**
   * @dev Enables user to authorize the address of an invitee to create a profile
   * @param invitee Address of user invited to ETHOS
   * @notice This function checks if the sender has available invites and if the invitee
   * is eligible to receive an invite. It updates the invite information and emits an event.
   */
  function inviteAddress(
    address invitee
  ) public whenNotPaused onlyNonZeroAddress(invitee) onlyNonCompromisedAddress(invitee) {
    (bool verified, bool archived, bool mock, ) = profileStatusByAddress(msg.sender);

    if (!verified || archived || mock) {
      revert InvalidSender();
    }

    // because profileStatusByAddress does not check if the address has been removed,
    // this will prevent invitations being sent to removed addresses
    (bool recipientExists, , bool recipientMock, ) = profileStatusByAddress(invitee);

    if (recipientExists && !recipientMock) {
      revert ProfileExistsForAddress(invitee);
    }

    uint256 profile = profileIdByAddress[msg.sender];

    _profileShouldHaveInvites(profile);

    _isAddressAlreadyInvited(profile, invitee);

    sentInviteIndexByProfileIdAndAddress[profile][invitee] = profiles[profile]
      .inviteInfo
      .sent
      .length;
    profiles[profile].inviteInfo.sent.push(invitee);
    profiles[profile].inviteInfo.available--;
    sentAt[profile][invitee] = block.timestamp;
    emit UserInvited(profile, invitee);
  }

  /**
   * @dev Allows a user to invite multiple addresses in a single transaction.
   * @param invitees An array of addresses to be invited.
   * @notice It is up to the caller to determine the array size and feasible gas costs.
   * All conditions from inviteAddress are still applicable.
   * The gas cost increases linearly with the number of invitees, so be mindful of potential gas limits.
   */
  function bulkInviteAddresses(address[] calldata invitees) external whenNotPaused {
    for (uint256 i = 0; i < invitees.length; i++) {
      inviteAddress(invitees[i]);
    }
  }

  /**
   * @dev Enables existing user to remove a pending invite and restore available invites
   * @param user Address of user invited to ETHOS
   */
  function uninviteUser(address user) external whenNotPaused onlyNonZeroAddress(user) {
    uint256 id = profileIdByAddress[msg.sender];
    Profile storage inviter = profiles[id];
    if (inviter.archived) {
      revert ProfileAccess(id, "Profile is archived");
    }

    uint256 index = sentInviteIndexByProfileIdAndAddress[id][user];
    if (index >= inviter.inviteInfo.sent.length || inviter.inviteInfo.sent[index] != user) {
      revert AddressNotInvited();
    }

    // Get last address before removing from array (needed for updating index mapping)
    address lastAddress = inviter.inviteInfo.sent[inviter.inviteInfo.sent.length - 1];

    _removeFromArray(index, inviter.inviteInfo.sent);

    // Update the index mapping for the moved address
    if (lastAddress != user) {
      sentInviteIndexByProfileIdAndAddress[id][lastAddress] = index;
    }
    delete sentInviteIndexByProfileIdAndAddress[id][user];

    inviter.inviteInfo.available++;
    sentAt[id][user] = 0;
    emit Uninvited(id, msg.sender, inviter.inviteInfo.available, user);
  }

  /**
   * @dev Creates a "mock" (user-less) profile to assist with tracking of reviews
   * @notice Only callable by EthosReview.sol and EthosAttestation.sol
   * @param isAttestation Flag if the mock belongs to an attestation or address
   * @param subject Address of subject if mock belongs to an address. address(0) if isAttestation
   * @param attestation Hash of attestation. Will be blank 0x if mock is for an address.
   * @return profileId The ID of the newly created mock profile
   */
  function incrementProfileCount(
    bool isAttestation,
    address subject,
    bytes32 attestation
  ) external whenNotPaused returns (uint256 profileId) {
    if (
      msg.sender != contractAddressManager.getContractAddressForName(ETHOS_REVIEW) &&
      msg.sender != contractAddressManager.getContractAddressForName(ETHOS_ATTESTATION)
    ) {
      revert InvalidSender();
    }
    profileId = profileCount;
    if (isAttestation) {
      profileIdByAttestation[attestation] = profileId;
    } else {
      profileIdByAddress[subject] = profileId;
    }
    profileCount++;

    emit MockProfileCreated(profileId);
  }

  /**
   * @dev Assigns a profileId to an attestation hash
   * @notice Callable from ethosAttestation
   * @param attestationHash Hash from ethosAttestation
   * @param profileId Profile id to assign to hash
   */
  function assignExistingProfileToAttestation(
    bytes32 attestationHash,
    uint256 profileId
  ) external isEthosAttestation whenNotPaused {
    address ethosVouch = contractAddressManager.getContractAddressForName("ETHOS_VOUCH");
    if (ethosVouch != address(0)) {
      EthosVouch(ethosVouch).handleAttestationClaim(attestationHash, profileId);
    }
    profileIdByAttestation[attestationHash] = profileId;
  }

  /**
   * @dev Archives a profile for the sender.
   * @notice This function allows a user to temporarily disable their profile.
   * An archived profile cannot perform most actions on the Ethos network.
   */
  function archiveProfile() external whenNotPaused {
    (bool verified, bool archived, bool mock, uint256 profileId) = profileStatusByAddress(
      msg.sender
    );
    if (!verified) {
      revert ProfileNotFoundForAddress(msg.sender);
    }
    if (archived || mock) {
      revert ProfileAccess(profileId, "Profile is archived");
    }

    profiles[profileId].archived = true;
    emit ProfileArchived(profileId);
  }

  /**
   * @dev Restores a profile for the sender.
   * @notice This function allows a user to reactivate their previously archived profile.
   */
  function restoreProfile() external whenNotPaused {
    (bool verified, bool archived, bool mock, uint256 profileId) = profileStatusByAddress(
      msg.sender
    );
    if (!verified) {
      revert ProfileNotFoundForAddress(msg.sender);
    }
    if (!archived || mock) {
      revert ProfileAccess(profileId, "Profile is not archived");
    }

    delete profiles[profileId].archived;
    emit ProfileRestored(profileId);
  }

  /**
   * @dev Registers an address for the profile.
   * @param addressStr Address to be registered.
   * @param profileId Profile id to be registered for.
   * @param randValue Random value to be used for signature. Use case: user can register, unregister and register again the same address.
   * @param signature Signature to be verified.
   * @notice This function allows a user to add a new address to their profile.
   * The address must not be compromised or already associated with another profile.
   */
  function registerAddress(
    address addressStr,
    uint256 profileId,
    uint256 randValue,
    bytes calldata signature
  ) external whenNotPaused onlyNonZeroAddress(addressStr) onlyNonCompromisedAddress(addressStr) {
    // the target profile must contain the msg.sender address among the list of valid, non-removed addresses
    if (profileIdByAddress[msg.sender] != profileId) {
      revert ProfileNotFoundForAddress(msg.sender);
    }

    (bool verified, bool archived, bool mock) = profileStatusById(profileId);
    if (!verified) {
      revert ProfileNotFound(profileId);
    }
    if (archived || mock) {
      revert ProfileAccess(profileId, "Profile is archived");
    }
    (bool addressAlreadyRegistered, , , uint256 registeredProfileId) = profileStatusByAddress(
      addressStr
    );
    if (addressAlreadyRegistered && registeredProfileId != profileId) {
      revert ProfileExistsForAddress(addressStr);
    }

    validateAndSaveSignature(
      _keccakForRegisterAddress(addressStr, profileId, randValue),
      signature
    );

    addressIndexByProfileIdAndAddress[profileId][addressStr] = profiles[profileId].addresses.length;
    profiles[profileId].addresses.push(addressStr);
    profileIdByAddress[addressStr] = profileId;

    checkMaxAddresses(profileId);

    emit AddressClaim(profileId, addressStr, AddressClaimStatus.Claimed);
  }

  function deleteAddress(address addressStr, bool markAsCompromised) external whenNotPaused {
    uint256 profileId = profileIdByAddress[msg.sender];
    uint256 index = addressIndexByProfileIdAndAddress[profileId][addressStr];
    deleteAddressAtIndex(index, markAsCompromised);
  }

  /**
   * @dev Deletes an address at index.
   * @notice Deleted addresses can be re-registered to any profile.
   * @notice Compromised addresses cannot be re-registered. Only an admin can revoke compromised addresses.
   * @param addressIndex Index of address to be archived.
   * @param markAsCompromised Whether to mark the address as compromised.
   */
  function deleteAddressAtIndex(uint256 addressIndex, bool markAsCompromised) public whenNotPaused {
    uint256 profileId = profileIdByAddress[msg.sender];
    (bool verified, bool archived, bool mock) = profileStatusById(profileId);
    if (!verified) {
      revert ProfileNotFoundForAddress(msg.sender);
    }
    if (archived || mock) {
      revert ProfileAccess(profileId, "Profile is archived");
    }

    address[] storage addresses = profiles[profileId].addresses;
    if (addresses.length <= addressIndex) {
      revert InvalidIndex();
    }

    address addressStr = addresses[addressIndex];
    _addressShouldDifferFromSender(addressStr);

    if (markAsCompromised) {
      isAddressCompromised[addressStr] = true;
    }

    _deleteAddressAtIndexFromArray(addressIndex, addresses);
    delete profileIdByAddress[addressStr];

    emit AddressClaim(profileId, addressStr, AddressClaimStatus.Unclaimed);
  }

  /**
   * @dev Restores a compromised address.
   * @notice Only callable by an admin.
   * @param addressStr Address to be restored.
   */
  function restoreCompromisedAddress(address addressStr) external onlyAdmin whenNotPaused {
    isAddressCompromised[addressStr] = false;
  }

  /**
   * @dev Retrieves a profile by its ID.
   * @param id The profile ID to retrieve.
   * @return profile The Profile struct associated with the given ID.
   */
  function getProfile(uint256 id) external view returns (Profile memory profile) {
    if (id == 0 || id >= profileCount) revert ProfileNotFound(id);
    profile = profiles[id];
  }

  /**
   * @dev Returns addresses for profile.
   * @param profileId Profile id.
   * @return Addresses for profile.
   */
  function addressesForProfile(uint256 profileId) external view returns (address[] memory) {
    return profiles[profileId].addresses;
  }

  /**
   * @dev Returns array of IDs that accepted an invite
   * @param profileId Profile id.
   * @return inviting profileID
   */
  function invitedIdsForProfile(uint256 profileId) external view returns (uint256[] memory) {
    return profiles[profileId].inviteInfo.acceptedIds;
  }

  /**
   * @dev Returns array of addresses that have pending invites for given profileId
   * @param profileId Profile id.
   * @return array of addresses
   */
  function sentInvitationsForProfile(uint256 profileId) external view returns (address[] memory) {
    return profiles[profileId].inviteInfo.sent;
  }

  /**
   * @dev Returns InviteInfo struct of a given profile
   * @notice does not include array elements
   * @param profileId Profile id.
   * @return InviteInfo for subject profileId
   */
  function inviteInfoForProfileId(uint256 profileId) external view returns (InviteInfo memory) {
    return profiles[profileId].inviteInfo;
  }

  // ITargetStatus implementation
  /**
   * @dev Checks whether profile verified & is allowed to be used.
   * @param targetId Profile id.
   * @return exist Whether profile verified.
   * @return allowed Whether profile is allowed to be used.
   * @notice This is a standard function used across Ethos contracts to validate profiles.
   */
  function targetExistsAndAllowedForId(
    uint256 targetId
  ) external view returns (bool exist, bool allowed) {
    Profile storage profile = profiles[targetId];

    exist = profile.createdAt > 0;
    allowed = exist;
  }

  /**
   * @dev Returns the status of a profile by its ID.
   * @param profileId The ID of the profile to check.
   * @return verified Whether the profile is verified.
   * @return archived Whether the profile is archived.
   * @return mock Whether the profile is a mock profile.
   */
  function profileStatusById(
    uint256 profileId
  ) public view returns (bool verified, bool archived, bool mock) {
    Profile storage profile = profiles[profileId];
    // mock profileIds do not have a profile struct, and so this returns false
    verified = profile.profileId > 0;
    archived = verified && profile.archived;
    mock = profileId > 0 && !verified && profileId < profileCount;
  }

  /**
   * @dev Returns the status of a profile by its associated address.
   * @param addressStr The address to check.
   * @return verified Whether the profile is verified.
   * @return archived Whether the profile is archived.
   * @return mock Whether the profile is a mock profile.
   * @return profileId The ID of the profile associated with the address.
   */
  function profileStatusByAddress(
    address addressStr
  ) public view returns (bool verified, bool archived, bool mock, uint256 profileId) {
    profileId = profileIdByAddress[addressStr];
    (verified, archived, mock) = profileStatusById(profileId);
  }

  // IEthosProfile implementation
  /**
   * @dev Checks whether address belongs to profile.
   * @param addressStr Address to be checked.
   * @param profileId Profile id.
   * @return Whether address belongs to profile.
   */
  function addressBelongsToProfile(
    address addressStr,
    uint256 profileId
  ) external view returns (bool) {
    if (profileIdByAddress[addressStr] == 0) {
      revert ProfileNotFoundForAddress(addressStr);
    }
    return profileIdByAddress[addressStr] == profileId;
  }

  /**
   * LEGACY INTERFACE FUNCTIONS
   *
   * These satisfy the IEthosProfile interface but are more difficult to understand than profileStatus* functions
   * and should be deprecated on the next major upgrade.
   */
  function profileExistsAndArchivedForId(
    uint256 profileId
  ) external view returns (bool verified, bool archived) {
    (bool _verified, bool _archived, bool mock) = profileStatusById(profileId);
    return (_verified && !mock, _archived && !mock);
  }

  function verifiedProfileIdForAddress(address _address) external view returns (uint256) {
    (bool verified, bool archived, bool mock, uint256 profileId) = profileStatusByAddress(_address);
    if (!verified || archived || mock) {
      revert ProfileNotFoundForAddress(_address);
    }
    return profileId;
  }

  // private functions

  /**
   * @dev Deletes address at index.
   * @param index Index of address to be deleted.
   * @param addresses Address array to be modified.
   */
  function _deleteAddressAtIndexFromArray(uint256 index, address[] storage addresses) private {
    address addressToRemove = addresses[index];
    address lastAddress = addresses[addresses.length - 1];

    _removeFromArray(index, addresses);

    if (lastAddress != addressToRemove) {
      uint256 profileId = profileIdByAddress[msg.sender];
      addressIndexByProfileIdAndAddress[profileId][lastAddress] = index;
    }

    delete addressIndexByProfileIdAndAddress[profileIdByAddress[msg.sender]][addressToRemove];
  }

  /**
   * @dev Checks whether address is not the same as sender.
   * @param addressStr Address to be checked.
   */
  function _addressShouldDifferFromSender(address addressStr) private view {
    if (addressStr == msg.sender) {
      revert AddressAuthorization(addressStr, "Address == msg.sender");
    }
  }

  /**
   * @dev Checks whether profile has sufficient invites.
   * @param profileId Profile id to be checked.
   */
  function _profileShouldHaveInvites(uint256 profileId) private view {
    uint256 availableInvites = profiles[profileId].inviteInfo.available;

    if (availableInvites == 0) {
      revert InsufficientInvites(profileId);
    }
  }

  /**
   * @dev Checks if address has already been invited.
   * @param profileId Profile id to be checked.
   * @param invitee address of user being invited
   */
  function _isAddressAlreadyInvited(uint256 profileId, address invitee) private view {
    if (sentAt[profileId][invitee] != 0) {
      revert AddressAlreadyInvited(profileId, invitee);
    }
  }

  /**
   * @dev Internal function for setting up new profile
   * @param user Address of the new user creating profile.
   * @return profileId The ID of the newly created profile
   * @notice This function handles the core logic of profile creation, including
   * assigning a new profile ID and initializing the profile's invite information.
   */
  function _createProfile(address user) internal returns (uint256 profileId) {
    (bool verified, , bool mock, uint256 existingProfileId) = profileStatusByAddress(user);
    if (verified) {
      revert ProfileExists(existingProfileId);
    } else if (mock) {
      profileId = existingProfileId;
    } else {
      profileId = profileCount;
      profileCount++;
    }

    profileIdByAddress[user] = profileId;
    profiles[profileId].profileId = profileId;
    profiles[profileId].createdAt = block.timestamp;
    profiles[profileId].inviteInfo.available = defaultNumberOfInvites;
    profiles[profileId].addresses.push(user);

    emit ProfileCreated(profileId, user);
    return profileId;
  }

  /**
   * @dev Checks if new user has been authorized by inviter for profile creation
   * @param inviterId profile ID of inviting user
   * @param user address of new user attempting to create profile
   */
  function _inviterProfileAuthorizedSender(uint256 inviterId, address user) internal {
    Profile storage inviter = profiles[inviterId];

    uint256 index = sentInviteIndexByProfileIdAndAddress[inviterId][user];
    if (index >= inviter.inviteInfo.sent.length || inviter.inviteInfo.sent[index] != user) {
      revert AddressNotInvited();
    }

    // Update the index mapping for the moved address
    address lastAddress = inviter.inviteInfo.sent[inviter.inviteInfo.sent.length - 1];
    if (lastAddress != user) {
      sentInviteIndexByProfileIdAndAddress[inviterId][lastAddress] = index;
    }

    _removeFromArray(index, inviter.inviteInfo.sent);
    delete sentInviteIndexByProfileIdAndAddress[inviterId][user];
  }

  /**
   * @dev Gets hash for the address registration.
   * @param addressStr Address to be registered.
   * @param profileId Profile id to be registered for.
   * @param randValue Random value.
   * @return Hash.
   */
  function _keccakForRegisterAddress(
    address addressStr,
    uint256 profileId,
    uint256 randValue
  ) private pure returns (bytes32) {
    return keccak256(abi.encodePacked(addressStr, profileId, randValue));
  }

  /**
   * @dev Modifies default number of invites new profiles start with
   * @param defaultInvites new default invite amount
   */
  function setDefaultNumberOfInvites(uint256 defaultInvites) external onlyAdmin whenNotPaused {
    defaultNumberOfInvites = defaultInvites;
    if (defaultInvites > maxNumberOfInvites) {
      revert MaxInvitesReached(0);
    }
    emit DefaultInvitesChanged(defaultInvites);
  }

  /**
   * @dev Adds invites to an individual profile
   * @param user address of profile
   * @param amount quantity of invites to add to the profile
   */
  function addInvites(address user, uint256 amount) public onlyAdmin whenNotPaused {
    (bool verified, bool archived, bool mock, uint256 id) = profileStatusByAddress(user);
    if (!verified || archived || mock) {
      revert ProfileNotFoundForAddress(user);
    }
    profiles[id].inviteInfo.available += amount;
    checkMaxInvites(id);
    emit InvitesAdded(id, amount);
  }

  /**
   * @dev Batch adds invites to many profiles
   * @notice can hit gas limit if list too long
   * @param users array of addresses to add invites
   * @param amount quantity of invites to add to the profiles
   */
  function addInvitesBatch(
    address[] calldata users,
    uint256 amount
  ) external onlyAdmin whenNotPaused {
    for (uint256 i; i < users.length; i++) {
      addInvites(users[i], amount);
    }
  }

  /**
   * @dev Calculates the total number of invites for a profile
   * @param profileId The ID of the profile
   * @return The sum of available, sent, and accepted invites
   */
  function _sumInvites(uint256 profileId) internal view returns (uint256) {
    uint256 sum = profiles[profileId].inviteInfo.available;
    sum += profiles[profileId].inviteInfo.sent.length;
    sum += profiles[profileId].inviteInfo.acceptedIds.length;
    return sum;
  }

  /**
   * @dev Checks if the total number of invites for a profile exceeds the maximum allowed
   * @param profileId The ID of the profile to check
   */
  function checkMaxInvites(uint256 profileId) internal view {
    if (_sumInvites(profileId) > maxNumberOfInvites) {
      revert MaxInvitesReached(profileId);
    }
  }

  /**
   * @dev Checks if the total number of addresses for a profile exceeds the maximum allowed
   * @param profileId The ID of the profile to check
   */
  function checkMaxAddresses(uint256 profileId) internal view {
    uint256 sum = profiles[profileId].addresses.length;
    if (sum > maxNumberOfAddresses) {
      revert MaxAddressesReached(profileId);
    }
  }

  /**
   * @dev Sets the maximum number of addresses allowed per profile
   * @param maxAddresses The new maximum number of addresses
   */
  function setMaxAddresses(uint256 maxAddresses) external onlyAdmin whenNotPaused {
    maxNumberOfAddresses = maxAddresses;
    if (maxAddresses > 2048) {
      revert MaxAddressesReached(0);
    }
  }

  /**
   * @dev Sets the maximum number of invites allowed per profile
   * @param maxInvites The new maximum number of invites
   */
  function setMaxInvites(uint256 maxInvites) external onlyAdmin whenNotPaused {
    maxNumberOfInvites = maxInvites;
    if (maxInvites > 2048) {
      revert MaxInvitesReached(0);
    }
  }
}

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

pragma solidity ^0.8.20;

import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {
    struct RoleData {
        mapping(address account => bool) hasRole;
        bytes32 adminRole;
    }

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;


    /// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
    struct AccessControlStorage {
        mapping(bytes32 role => RoleData) _roles;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;

    function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {
        assembly {
            $.slot := AccessControlStorageLocation
        }
    }

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with an {AccessControlUnauthorizedAccount} error including the required role.
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    function __AccessControl_init() internal onlyInitializing {
    }

    function __AccessControl_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        return $._roles[role].hasRole[account];
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
     * is missing `role`.
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert AccessControlUnauthorizedAccount(account, role);
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        return $._roles[role].adminRole;
    }

    /**
     * @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.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

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

    /**
     * @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 revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address callerConfirmation) public virtual {
        if (callerConfirmation != _msgSender()) {
            revert AccessControlBadConfirmation();
        }

        _revokeRole(role, callerConfirmation);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        AccessControlStorage storage $ = _getAccessControlStorage();
        bytes32 previousAdminRole = getRoleAdmin(role);
        $._roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        if (!hasRole(role, account)) {
            $._roles[role].hasRole[account] = true;
            emit RoleGranted(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        if (hasRole(role, account)) {
            $._roles[role].hasRole[account] = false;
            emit RoleRevoked(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/extensions/AccessControlEnumerable.sol)

pragma solidity ^0.8.20;

import {IAccessControlEnumerable} from "@openzeppelin/contracts/access/extensions/IAccessControlEnumerable.sol";
import {AccessControlUpgradeable} from "../AccessControlUpgradeable.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";

/**
 * @dev Extension of {AccessControl} that allows enumerating the members of each role.
 */
abstract contract AccessControlEnumerableUpgradeable is Initializable, IAccessControlEnumerable, AccessControlUpgradeable {
    using EnumerableSet for EnumerableSet.AddressSet;

    /// @custom:storage-location erc7201:openzeppelin.storage.AccessControlEnumerable
    struct AccessControlEnumerableStorage {
        mapping(bytes32 role => EnumerableSet.AddressSet) _roleMembers;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControlEnumerable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant AccessControlEnumerableStorageLocation = 0xc1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000;

    function _getAccessControlEnumerableStorage() private pure returns (AccessControlEnumerableStorage storage $) {
        assembly {
            $.slot := AccessControlEnumerableStorageLocation
        }
    }

    function __AccessControlEnumerable_init() internal onlyInitializing {
    }

    function __AccessControlEnumerable_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) public view virtual returns (address) {
        AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage();
        return $._roleMembers[role].at(index);
    }

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) public view virtual returns (uint256) {
        AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage();
        return $._roleMembers[role].length();
    }

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

    /**
     * @dev Overload {AccessControl-_grantRole} to track enumerable memberships
     */
    function _grantRole(bytes32 role, address account) internal virtual override returns (bool) {
        AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage();
        bool granted = super._grantRole(role, account);
        if (granted) {
            $._roleMembers[role].add(account);
        }
        return granted;
    }

    /**
     * @dev Overload {AccessControl-_revokeRole} to track enumerable memberships
     */
    function _revokeRole(bytes32 role, address account) internal virtual override returns (bool) {
        AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage();
        bool revoked = super._revokeRole(role, account);
        if (revoked) {
            $._roleMembers[role].remove(account);
        }
        return revoked;
    }
}

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.22;

import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Initializable} from "./Initializable.sol";

/**
 * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
 * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
 *
 * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
 * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
 * `UUPSUpgradeable` with a custom implementation of upgrades.
 *
 * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
 */
abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address private immutable __self = address(this);

    /**
     * @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
     * and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
     * while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
     * If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
     * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
     * during an upgrade.
     */
    string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";

    /**
     * @dev The call is from an unauthorized context.
     */
    error UUPSUnauthorizedCallContext();

    /**
     * @dev The storage `slot` is unsupported as a UUID.
     */
    error UUPSUnsupportedProxiableUUID(bytes32 slot);

    /**
     * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
     * a proxy contract with an implementation (as defined in ERC-1967) pointing to self. This should only be the case
     * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
     * function through ERC-1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
     * fail.
     */
    modifier onlyProxy() {
        _checkProxy();
        _;
    }

    /**
     * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
     * callable on the implementing contract but not through proxies.
     */
    modifier notDelegated() {
        _checkNotDelegated();
        _;
    }

    function __UUPSUpgradeable_init() internal onlyInitializing {
    }

    function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev Implementation of the ERC-1822 {proxiableUUID} function. This returns the storage slot used by the
     * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
     */
    function proxiableUUID() external view virtual notDelegated returns (bytes32) {
        return ERC1967Utils.IMPLEMENTATION_SLOT;
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
     * encoded in `data`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     *
     * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, data);
    }

    /**
     * @dev Reverts if the execution is not performed via delegatecall or the execution
     * context is not of a proxy with an ERC-1967 compliant implementation pointing to self.
     * See {_onlyProxy}.
     */
    function _checkProxy() internal view virtual {
        if (
            address(this) == __self || // Must be called through delegatecall
            ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
        ) {
            revert UUPSUnauthorizedCallContext();
        }
    }

    /**
     * @dev Reverts if the execution is performed via delegatecall.
     * See {notDelegated}.
     */
    function _checkNotDelegated() internal view virtual {
        if (address(this) != __self) {
            // Must not be called through delegatecall
            revert UUPSUnauthorizedCallContext();
        }
    }

    /**
     * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
     * {upgradeToAndCall}.
     *
     * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
     *
     * ```solidity
     * function _authorizeUpgrade(address) internal onlyOwner {}
     * ```
     */
    function _authorizeUpgrade(address newImplementation) internal virtual;

    /**
     * @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
     *
     * As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
     * is expected to be the implementation slot in ERC-1967.
     *
     * Emits an {IERC1967-Upgraded} event.
     */
    function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
        try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
            if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
                revert UUPSUnsupportedProxiableUUID(slot);
            }
            ERC1967Utils.upgradeToAndCall(newImplementation, data);
        } catch {
            // The implementation is not UUPS
            revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
        }
    }
}

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

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

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165Upgradeable is Initializable, IERC165 {
    function __ERC165_init() internal onlyInitializing {
    }

    function __ERC165_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

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

pragma solidity ^0.8.20;

import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

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

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Pausable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300;

    function _getPausableStorage() private pure returns (PausableStorage storage $) {
        assembly {
            $.slot := PausableStorageLocation
        }
    }

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

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

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

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

    /**
     * @dev Initializes the contract in unpaused state.
     */
    function __Pausable_init() internal onlyInitializing {
        __Pausable_init_unchained();
    }

    function __Pausable_init_unchained() internal onlyInitializing {
        PausableStorage storage $ = _getPausableStorage();
        $._paused = false;
    }

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

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

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/extensions/IAccessControlEnumerable.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev External interface of AccessControlEnumerable declared to support ERC-165 detection.
 */
interface IAccessControlEnumerable is IAccessControl {
    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) external view returns (address);

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) external view returns (uint256);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/IAccessControl.sol)

pragma solidity ^0.8.20;

/**
 * @dev External interface of AccessControl declared to support ERC-165 detection.
 */
interface IAccessControl {
    /**
     * @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.
     */
    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. This account bears the admin role (for the granted role).
     * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
     */
    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;
}

File 11 of 34 : draft-IERC1822.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC1822.sol)

pragma solidity ^0.8.20;

/**
 * @dev ERC-1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
 * proxy whose upgrades are fully controlled by the current implementation.
 */
interface IERC1822Proxiable {
    /**
     * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
     * address.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy.
     */
    function proxiableUUID() external view returns (bytes32);
}

File 12 of 34 : IERC1967.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1967.sol)

pragma solidity ^0.8.20;

/**
 * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
 */
interface IERC1967 {
    /**
     * @dev Emitted when the implementation is upgraded.
     */
    event Upgraded(address indexed implementation);

    /**
     * @dev Emitted when the admin account has changed.
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    /**
     * @dev Emitted when the beacon is changed.
     */
    event BeaconUpgraded(address indexed beacon);
}

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

pragma solidity ^0.8.20;

/**
 * @dev This is the interface that {BeaconProxy} expects of its beacon.
 */
interface IBeacon {
    /**
     * @dev Must return an address that can be used as a delegate call target.
     *
     * {UpgradeableBeacon} will check that this address is a contract.
     */
    function implementation() external view returns (address);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (proxy/ERC1967/ERC1967Utils.sol)

pragma solidity ^0.8.22;

import {IBeacon} from "../beacon/IBeacon.sol";
import {IERC1967} from "../../interfaces/IERC1967.sol";
import {Address} from "../../utils/Address.sol";
import {StorageSlot} from "../../utils/StorageSlot.sol";

/**
 * @dev This library provides getters and event emitting update functions for
 * https://eips.ethereum.org/EIPS/eip-1967[ERC-1967] slots.
 */
library ERC1967Utils {
    /**
     * @dev Storage slot with the address of the current implementation.
     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
     */
    // solhint-disable-next-line private-vars-leading-underscore
    bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /**
     * @dev The `implementation` of the proxy is invalid.
     */
    error ERC1967InvalidImplementation(address implementation);

    /**
     * @dev The `admin` of the proxy is invalid.
     */
    error ERC1967InvalidAdmin(address admin);

    /**
     * @dev The `beacon` of the proxy is invalid.
     */
    error ERC1967InvalidBeacon(address beacon);

    /**
     * @dev An upgrade function sees `msg.value > 0` that may be lost.
     */
    error ERC1967NonPayable();

    /**
     * @dev Returns the current implementation address.
     */
    function getImplementation() internal view returns (address) {
        return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
    }

    /**
     * @dev Stores a new address in the ERC-1967 implementation slot.
     */
    function _setImplementation(address newImplementation) private {
        if (newImplementation.code.length == 0) {
            revert ERC1967InvalidImplementation(newImplementation);
        }
        StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
    }

    /**
     * @dev Performs implementation upgrade with additional setup call if data is nonempty.
     * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
     * to avoid stuck value in the contract.
     *
     * Emits an {IERC1967-Upgraded} event.
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) internal {
        _setImplementation(newImplementation);
        emit IERC1967.Upgraded(newImplementation);

        if (data.length > 0) {
            Address.functionDelegateCall(newImplementation, data);
        } else {
            _checkNonPayable();
        }
    }

    /**
     * @dev Storage slot with the admin of the contract.
     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
     */
    // solhint-disable-next-line private-vars-leading-underscore
    bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    /**
     * @dev Returns the current admin.
     *
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by ERC-1967) using
     * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
     */
    function getAdmin() internal view returns (address) {
        return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
    }

    /**
     * @dev Stores a new address in the ERC-1967 admin slot.
     */
    function _setAdmin(address newAdmin) private {
        if (newAdmin == address(0)) {
            revert ERC1967InvalidAdmin(address(0));
        }
        StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {IERC1967-AdminChanged} event.
     */
    function changeAdmin(address newAdmin) internal {
        emit IERC1967.AdminChanged(getAdmin(), newAdmin);
        _setAdmin(newAdmin);
    }

    /**
     * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
     * This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
     */
    // solhint-disable-next-line private-vars-leading-underscore
    bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;

    /**
     * @dev Returns the current beacon.
     */
    function getBeacon() internal view returns (address) {
        return StorageSlot.getAddressSlot(BEACON_SLOT).value;
    }

    /**
     * @dev Stores a new beacon in the ERC-1967 beacon slot.
     */
    function _setBeacon(address newBeacon) private {
        if (newBeacon.code.length == 0) {
            revert ERC1967InvalidBeacon(newBeacon);
        }

        StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;

        address beaconImplementation = IBeacon(newBeacon).implementation();
        if (beaconImplementation.code.length == 0) {
            revert ERC1967InvalidImplementation(beaconImplementation);
        }
    }

    /**
     * @dev Change the beacon and trigger a setup call if data is nonempty.
     * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
     * to avoid stuck value in the contract.
     *
     * Emits an {IERC1967-BeaconUpgraded} event.
     *
     * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
     * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
     * efficiency.
     */
    function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
        _setBeacon(newBeacon);
        emit IERC1967.BeaconUpgraded(newBeacon);

        if (data.length > 0) {
            Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
        } else {
            _checkNonPayable();
        }
    }

    /**
     * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
     * if an upgrade doesn't perform an initialization call.
     */
    function _checkNonPayable() private {
        if (msg.value > 0) {
            revert ERC1967NonPayable();
        }
    }
}

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

pragma solidity ^0.8.20;

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

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert Errors.InsufficientBalance(address(this).balance, amount);
        }

        (bool success, bytes memory returndata) = recipient.call{value: amount}("");
        if (!success) {
            _revert(returndata);
        }
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {Errors.FailedCall} error.
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance(address(this).balance, value);
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
     * of an unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {Errors.FailedCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            assembly ("memory-safe") {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert Errors.FailedCall();
        }
    }
}

File 16 of 34 : Errors.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

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

pragma solidity ^0.8.20;

import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

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

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

    /**
     * @dev Returns the multiplication of two unsigned integers, with an success flag (no overflow).
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        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 success flag (no division by zero).
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

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

    /**
     * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
     *
     * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
     * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
     * one branch when needed, making this function more expensive.
     */
    function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
        unchecked {
            // branchless ternary works because:
            // b ^ (a ^ b) == a
            // b ^ 0 == b
            return b ^ ((a ^ b) * SafeCast.toUint(condition));
        }
    }

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

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

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

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

        // The following calculation ensures accurate ceiling division without overflow.
        // Since a is non-zero, (a - 1) / b will not overflow.
        // The largest possible result occurs when (a - 1) / b is type(uint256).max,
        // but the largest value we can obtain is type(uint256).max - 1, which happens
        // when a = type(uint256).max and b = 1.
        unchecked {
            return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
        }
    }

    /**
     * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     *
     * 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²⁵⁶ and mod 2²⁵⁶ - 1, then use
            // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2²⁵⁶ + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

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

            // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
            if (denominator <= prod1) {
                Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
            }

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

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

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

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

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

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

                // Flip twos such that it is 2²⁵⁶ / 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²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
            // that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv ≡ 1 mod 2⁴.
            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⁸
            inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
            inverse *= 2 - denominator * inverse; // inverse mod 2³²
            inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
            inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
            inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶

            // 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²⁵⁶. Since the preconditions guarantee that the outcome is
            // less than 2²⁵⁶, 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;
        }
    }

    /**
     * @dev 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) {
        return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
    }

    /**
     * @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
     *
     * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
     * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
     *
     * If the input value is not inversible, 0 is returned.
     *
     * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
     * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
     */
    function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
        unchecked {
            if (n == 0) return 0;

            // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
            // Used to compute integers x and y such that: ax + ny = gcd(a, n).
            // When the gcd is 1, then the inverse of a modulo n exists and it's x.
            // ax + ny = 1
            // ax = 1 + (-y)n
            // ax ≡ 1 (mod n) # x is the inverse of a modulo n

            // If the remainder is 0 the gcd is n right away.
            uint256 remainder = a % n;
            uint256 gcd = n;

            // Therefore the initial coefficients are:
            // ax + ny = gcd(a, n) = n
            // 0a + 1n = n
            int256 x = 0;
            int256 y = 1;

            while (remainder != 0) {
                uint256 quotient = gcd / remainder;

                (gcd, remainder) = (
                    // The old remainder is the next gcd to try.
                    remainder,
                    // Compute the next remainder.
                    // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
                    // where gcd is at most n (capped to type(uint256).max)
                    gcd - remainder * quotient
                );

                (x, y) = (
                    // Increment the coefficient of a.
                    y,
                    // Decrement the coefficient of n.
                    // Can overflow, but the result is casted to uint256 so that the
                    // next value of y is "wrapped around" to a value between 0 and n - 1.
                    x - y * int256(quotient)
                );
            }

            if (gcd != 1) return 0; // No inverse exists.
            return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
        }
    }

    /**
     * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
     *
     * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
     * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
     * `a**(p-2)` is the modular multiplicative inverse of a in Fp.
     *
     * NOTE: this function does NOT check that `p` is a prime greater than `2`.
     */
    function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
        unchecked {
            return Math.modExp(a, p - 2, p);
        }
    }

    /**
     * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
     *
     * Requirements:
     * - modulus can't be zero
     * - underlying staticcall to precompile must succeed
     *
     * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
     * sure the chain you're using it on supports the precompiled contract for modular exponentiation
     * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
     * the underlying function will succeed given the lack of a revert, but the result may be incorrectly
     * interpreted as 0.
     */
    function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
        (bool success, uint256 result) = tryModExp(b, e, m);
        if (!success) {
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }
        return result;
    }

    /**
     * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
     * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
     * to operate modulo 0 or if the underlying precompile reverted.
     *
     * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
     * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
     * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
     * of a revert, but the result may be incorrectly interpreted as 0.
     */
    function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
        if (m == 0) return (false, 0);
        assembly ("memory-safe") {
            let ptr := mload(0x40)
            // | Offset    | Content    | Content (Hex)                                                      |
            // |-----------|------------|--------------------------------------------------------------------|
            // | 0x00:0x1f | size of b  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x20:0x3f | size of e  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x40:0x5f | size of m  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x60:0x7f | value of b | 0x<.............................................................b> |
            // | 0x80:0x9f | value of e | 0x<.............................................................e> |
            // | 0xa0:0xbf | value of m | 0x<.............................................................m> |
            mstore(ptr, 0x20)
            mstore(add(ptr, 0x20), 0x20)
            mstore(add(ptr, 0x40), 0x20)
            mstore(add(ptr, 0x60), b)
            mstore(add(ptr, 0x80), e)
            mstore(add(ptr, 0xa0), m)

            // Given the result < m, it's guaranteed to fit in 32 bytes,
            // so we can use the memory scratch space located at offset 0.
            success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
            result := mload(0x00)
        }
    }

    /**
     * @dev Variant of {modExp} that supports inputs of arbitrary length.
     */
    function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
        (bool success, bytes memory result) = tryModExp(b, e, m);
        if (!success) {
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }
        return result;
    }

    /**
     * @dev Variant of {tryModExp} that supports inputs of arbitrary length.
     */
    function tryModExp(
        bytes memory b,
        bytes memory e,
        bytes memory m
    ) internal view returns (bool success, bytes memory result) {
        if (_zeroBytes(m)) return (false, new bytes(0));

        uint256 mLen = m.length;

        // Encode call args in result and move the free memory pointer
        result = abi.encodePacked(b.length, e.length, mLen, b, e, m);

        assembly ("memory-safe") {
            let dataPtr := add(result, 0x20)
            // Write result on top of args to avoid allocating extra memory.
            success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
            // Overwrite the length.
            // result.length > returndatasize() is guaranteed because returndatasize() == m.length
            mstore(result, mLen)
            // Set the memory pointer after the returned data.
            mstore(0x40, add(dataPtr, mLen))
        }
    }

    /**
     * @dev Returns whether the provided byte array is zero.
     */
    function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
        for (uint256 i = 0; i < byteArray.length; ++i) {
            if (byteArray[i] != 0) {
                return false;
            }
        }
        return true;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * This method is based on Newton's method for computing square roots; the algorithm is restricted to only
     * using integer operations.
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        unchecked {
            // Take care of easy edge cases when a == 0 or a == 1
            if (a <= 1) {
                return a;
            }

            // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
            // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
            // the current value as `ε_n = | x_n - sqrt(a) |`.
            //
            // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
            // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
            // bigger than any uint256.
            //
            // By noticing that
            // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
            // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
            // to the msb function.
            uint256 aa = a;
            uint256 xn = 1;

            if (aa >= (1 << 128)) {
                aa >>= 128;
                xn <<= 64;
            }
            if (aa >= (1 << 64)) {
                aa >>= 64;
                xn <<= 32;
            }
            if (aa >= (1 << 32)) {
                aa >>= 32;
                xn <<= 16;
            }
            if (aa >= (1 << 16)) {
                aa >>= 16;
                xn <<= 8;
            }
            if (aa >= (1 << 8)) {
                aa >>= 8;
                xn <<= 4;
            }
            if (aa >= (1 << 4)) {
                aa >>= 4;
                xn <<= 2;
            }
            if (aa >= (1 << 2)) {
                xn <<= 1;
            }

            // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
            //
            // We can refine our estimation by noticing that the middle of that interval minimizes the error.
            // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
            // This is going to be our x_0 (and ε_0)
            xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)

            // From here, Newton's method give us:
            // x_{n+1} = (x_n + a / x_n) / 2
            //
            // One should note that:
            // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
            //              = ((x_n² + a) / (2 * x_n))² - a
            //              = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
            //              = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
            //              = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
            //              = (x_n² - a)² / (2 * x_n)²
            //              = ((x_n² - a) / (2 * x_n))²
            //              ≥ 0
            // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
            //
            // This gives us the proof of quadratic convergence of the sequence:
            // ε_{n+1} = | x_{n+1} - sqrt(a) |
            //         = | (x_n + a / x_n) / 2 - sqrt(a) |
            //         = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
            //         = | (x_n - sqrt(a))² / (2 * x_n) |
            //         = | ε_n² / (2 * x_n) |
            //         = ε_n² / | (2 * x_n) |
            //
            // For the first iteration, we have a special case where x_0 is known:
            // ε_1 = ε_0² / | (2 * x_0) |
            //     ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
            //     ≤ 2**(2*e-4) / (3 * 2**(e-1))
            //     ≤ 2**(e-3) / 3
            //     ≤ 2**(e-3-log2(3))
            //     ≤ 2**(e-4.5)
            //
            // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
            // ε_{n+1} = ε_n² / | (2 * x_n) |
            //         ≤ (2**(e-k))² / (2 * 2**(e-1))
            //         ≤ 2**(2*e-2*k) / 2**e
            //         ≤ 2**(e-2*k)
            xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5)  -- special case, see above
            xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9)    -- general case with k = 4.5
            xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18)   -- general case with k = 9
            xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36)   -- general case with k = 18
            xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72)   -- general case with k = 36
            xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144)  -- general case with k = 72

            // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
            // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
            // sqrt(a) or sqrt(a) + 1.
            return xn - SafeCast.toUint(xn > a / xn);
        }
    }

    /**
     * @dev 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        uint256 exp;
        unchecked {
            exp = 128 * SafeCast.toUint(value > (1 << 128) - 1);
            value >>= exp;
            result += exp;

            exp = 64 * SafeCast.toUint(value > (1 << 64) - 1);
            value >>= exp;
            result += exp;

            exp = 32 * SafeCast.toUint(value > (1 << 32) - 1);
            value >>= exp;
            result += exp;

            exp = 16 * SafeCast.toUint(value > (1 << 16) - 1);
            value >>= exp;
            result += exp;

            exp = 8 * SafeCast.toUint(value > (1 << 8) - 1);
            value >>= exp;
            result += exp;

            exp = 4 * SafeCast.toUint(value > (1 << 4) - 1);
            value >>= exp;
            result += exp;

            exp = 2 * SafeCast.toUint(value > (1 << 2) - 1);
            value >>= exp;
            result += exp;

            result += SafeCast.toUint(value > 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);
        }
    }

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

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

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

            isGt = SafeCast.toUint(value > (1 << 64) - 1);
            value >>= isGt * 64;
            result += isGt * 8;

            isGt = SafeCast.toUint(value > (1 << 32) - 1);
            value >>= isGt * 32;
            result += isGt * 4;

            isGt = SafeCast.toUint(value > (1 << 16) - 1);
            value >>= isGt * 16;
            result += isGt * 2;

            result += SafeCast.toUint(value > (1 << 8) - 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);
        }
    }

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

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    /**
     * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
     */
    function toUint(bool b) internal pure returns (uint256 u) {
        assembly ("memory-safe") {
            u := iszero(iszero(b))
        }
    }
}

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

pragma solidity ^0.8.20;

/**
 * @dev Helper library for emitting standardized panic codes.
 *
 * ```solidity
 * contract Example {
 *      using Panic for uint256;
 *
 *      // Use any of the declared internal constants
 *      function foo() { Panic.GENERIC.panic(); }
 *
 *      // Alternatively
 *      function foo() { Panic.panic(Panic.GENERIC); }
 * }
 * ```
 *
 * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
 *
 * _Available since v5.1._
 */
// slither-disable-next-line unused-state
library Panic {
    /// @dev generic / unspecified error
    uint256 internal constant GENERIC = 0x00;
    /// @dev used by the assert() builtin
    uint256 internal constant ASSERT = 0x01;
    /// @dev arithmetic underflow or overflow
    uint256 internal constant UNDER_OVERFLOW = 0x11;
    /// @dev division or modulo by zero
    uint256 internal constant DIVISION_BY_ZERO = 0x12;
    /// @dev enum conversion error
    uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
    /// @dev invalid encoding in storage
    uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
    /// @dev empty array pop
    uint256 internal constant EMPTY_ARRAY_POP = 0x31;
    /// @dev array out of bounds access
    uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
    /// @dev resource error (too large allocation or too large array)
    uint256 internal constant RESOURCE_ERROR = 0x41;
    /// @dev calling invalid internal function
    uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;

    /// @dev Reverts with a panic code. Recommended to use with
    /// the internal constants with predefined codes.
    function panic(uint256 code) internal pure {
        assembly ("memory-safe") {
            mstore(0x00, 0x4e487b71)
            mstore(0x20, code)
            revert(0x1c, 0x24)
        }
    }
}

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

pragma solidity ^0.8.20;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
 * consider using {ReentrancyGuardTransient} instead.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

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

    constructor() {
        _status = NOT_ENTERED;
    }

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

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

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

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

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

pragma solidity ^0.8.20;

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

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    struct Int256Slot {
        int256 value;
    }

    struct StringSlot {
        string value;
    }

    struct BytesSlot {
        bytes value;
    }

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

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

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

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

    /**
     * @dev Returns a `Int256Slot` with member `value` located at `slot`.
     */
    function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

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

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

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

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

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

            return true;
        } else {
            return false;
        }
    }

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

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

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

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

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

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

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

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

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

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

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

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

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

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

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

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

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

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

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

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

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

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

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

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

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

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

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

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

        return result;
    }
}

File 24 of 34 : ProfileErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

error ProfileNotFound(uint256 profileId);
error ProfileNotMock(uint256 profileId);
error ProfileNotFoundForAddress(address userAddress);
error ProfileExists(uint256 profileId);
error ProfileExistsForAddress(address userAddress);
error ProfileAccess(uint256 profileId, string message);
error AddressAuthorization(address userAddress, string message);
error AddressAlreadyInvited(uint256 profileId, address user);
error ZeroAddress();
error InsufficientInvites(uint256 profileId);
error AddressNotInvited();
error AddressCompromised(address user);
error InvalidSender();
error InvalidIndex();
error MaxInvitesReached(uint256 profileId);
error MaxAddressesReached(uint256 profileId);

File 25 of 34 : EthosVouch.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { ITargetStatus } from "./interfaces/ITargetStatus.sol";
import { IEthosProfile } from "./interfaces/IEthosProfile.sol";
import { ETHOS_PROFILE } from "./utils/Constants.sol";
import { AccessControl } from "./utils/AccessControl.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { ProfileNotFoundForAddress } from "./errors/ProfileErrors.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

/**
 * @notice Represents a trust relationship between profiles backed by staked ETH, where one profile (author)
 * vouches for another profile (subject). The stake amount represents the magnitude of trust, making
 * credibility a function of stake value rather than number of vouchers.
 *
 * Core Philosophy:
 * - Long-standing mutually beneficial relationships are the best indicators of trust
 * - Penalties should be proportional; not all infractions warrant complete reputation loss
 * - Financial stakes force intentional trust allocation decisions
 * - Credibility is based on stake value, not popularity (e.g., 1 vouch of 10,000Ξ = 10,000 vouches of 1Ξ)
 * - Mutual vouching (3,3) magnifies credibility and rewards for both parties
 *
 * Requirements:
 * - Author must have a valid profile
 * - Subject must have a valid profile or be a mock profile created via address/attestation review
 * - Vouch amount must meet minimum configured amount (>= 0.0001 ETH)
 * - Author cannot exceed maximum allowed vouches (max 256)
 * - Subject cannot exceed maximum allowed vouches
 * - Author cannot vouch for the same subject multiple times
 * - Author cannot vouch for an archived profile
 *
 * Features:
 * - Supports vouching by profile ID or address
 * - Allows increasing vouch amount
 * - Supports unvouching (withdrawal) with a limited time period for marking as unhealthy
 * - Handles fee distribution (protocol, donation, vouchers pool)
 * - Manages rewards/incentives for vouchers
 * - Supports slashing up to 10% of total stake for validated unethical behavior
 *
 * Fee Structure:
 * - Protocol Fee: Sent to protocol treasury for operational costs
 * - Donation Fee: Direct reward to subject profile, claimable via rewards system
 * - Vouchers Pool Fee: Distributed proportionally among existing vouchers
 * - Exit Fee: Charged on withdrawals, sent to protocol treasury
 *
 * Slashing Mechanism:
 * - Only another Ethos contract is authorized to handle slashing
 * - Maximum slash is 10% of total staked amount
 * - Only way staked funds can be withdrawn without explicit owner approval
 * - Intended to be rare - negative reviews/unvouching preferred first
 * - Context: https://whitepaper.ethos.network/ethos-mechanisms/slash
 *
 * Access Control:
 * - Only vouch author can unvouch or mark unhealthy
 * - Only authorized slasher contract can slash vouches (up to 10% of author's total stake)
 * - Only admin can configure fees and limits
 *
 * Important Notes:
 * - Subject does not control the staked funds (cannot withdraw/spend/reallocate)
 * - Unvouching requires full withdrawal of staked amount
 * - Vouches that end due to distrust can be marked as "unhealthy" within a limited time period
 *
 * @notice See https://whitepaper.ethos.network/ethos-mechanisms/vouch for original design
 */
contract EthosVouch is AccessControl, UUPSUpgradeable, ITargetStatus, ReentrancyGuard {
  /**
   * @dev Constructor that disables initializers when the implementation contract is deployed.
   * This prevents the implementation contract from being initialized, which is important for
   * security since the implementation contract should never be used directly, only through
   * delegatecall from the proxy.
   */
  constructor() {
    _disableInitializers();
  }

  using Math for uint256;

  /**
   * @dev Tracks timestamps for key vouch activities
   * @param vouchedAt Timestamp when the vouch was created
   * @param unvouchedAt Timestamp when the vouch was withdrawn
   * @param unhealthyAt Timestamp when the vouch was marked unhealthy
   */
  struct ActivityCheckpoints {
    uint256 vouchedAt;
    uint256 unvouchedAt;
    uint256 unhealthyAt;
  }

  /**
   * @dev Represents a single vouch relationship between two profiles
   * @param archived Whether the vouch has been withdrawn
   * @param unhealthy Whether the vouch was marked as unhealthy after withdrawal
   * @param authorProfileId Profile ID of the vouching party
   * @param authorAddress Address of the vouching party
   * @param vouchId Unique identifier for this vouch
   * @param subjectProfileId Profile ID being vouched for
   * @param balance Current staked amount in wei
   * @param comment Optional text comment about the vouch
   * @param metadata Additional structured metadata about the vouch
   * @param activityCheckpoints Timestamps for key vouch activities
   */
  struct Vouch {
    bool archived;
    bool unhealthy;
    uint256 authorProfileId;
    address authorAddress;
    uint256 vouchId;
    uint256 subjectProfileId;
    uint256 balance;
    string comment;
    string metadata;
    ActivityCheckpoints activityCheckpoints;
  }

  // --- Constants ---
  uint256 private constant ABSOLUTE_MINIMUM_VOUCH_AMOUNT = 0.0001 ether;
  // 1000 basis points = 10% max fees
  uint256 public constant MAX_TOTAL_FEES = 1000;
  uint256 public constant BASIS_POINT_SCALE = 10000;
  // 1000 basis points = 10% max slash
  uint256 public constant MAX_SLASH_PERCENTAGE = 1000;

  // --- State Variables ---
  /// @notice Minimum amount that must be staked for a vouch in wei
  uint256 public configuredMinimumVouchAmount;
  /// @notice Total number of vouches ever created
  uint256 public vouchCount;
  /// @notice Time period after unvouching during which a vouch can be marked unhealthy
  uint256 public unhealthyResponsePeriod;
  /// @notice Maximum number of vouches allowed per profile
  uint256 public maximumVouches;
  /// @notice Address where protocol fees are sent
  address public protocolFeeAddress;
  /// @notice Protocol fee in basis points (100 = 1%) charged on entry
  uint256 public entryProtocolFeeBasisPoints;
  /// @notice Donation fee in basis points charged on entry
  uint256 public entryDonationFeeBasisPoints;
  /// @notice Fee in basis points distributed to existing vouchers on entry
  uint256 public entryVouchersPoolFeeBasisPoints;
  /// @notice Fee in basis points charged on exit/withdrawal
  uint256 public exitFeeBasisPoints;

  // --- Mappings ---
  /** @notice Maps vouch IDs to their full vouch data */
  mapping(uint256 => Vouch) public vouches;

  /**
   * @notice Maps author profile IDs to their list of vouch IDs
   * @dev authorProfileId => vouchIds
   */
  mapping(uint256 => uint256[]) public vouchIdsByAuthor;

  /**
   * @notice Maps author profile IDs and vouch IDs to their index in the vouchIdsByAuthor array
   * @dev authorProfileId => vouchId => vouchIdsByAuthor index
   */
  mapping(uint256 => mapping(uint256 => uint256)) public vouchIdsByAuthorIndex;

  /**
   * @notice Maps subject profile IDs to the list of vouch IDs vouching for them
   * @dev subjectProfileId => vouchIds
   */
  mapping(uint256 => uint256[]) public vouchIdsForSubjectProfileId;

  /**
   * @notice Maps subject profile IDs and vouch IDs to their index in the vouchIdsForSubjectProfileId array
   * @dev authorProfileId => subjectProfileId => vouchId
   */
  mapping(uint256 => mapping(uint256 => uint256)) public vouchIdsForSubjectProfileIdIndex;

  /**
   * @notice Maps author profile IDs and subject profile IDs to their active vouch ID
   * @dev authorProfileId => subjectProfileId => vouchId (active vouches between author and subject)
   */
  mapping(uint256 => mapping(uint256 => uint256)) public vouchIdByAuthorForSubjectProfileId;

  /**
   * @notice Maps profile IDs to their rewards balance in ETH
   * @dev Balances are Eth only; no ERC20 support. Maps Ethos profile IDs to their rewards balances.
   */
  mapping(uint256 => uint256) public rewardsByProfileId;
  /**
   * @notice Maps attestation hashes to their rewards balance
   * Only used when vouching for mock profiles
   */
  mapping(bytes32 => uint256) public rewardsByAttestationHash;
  /**
   * @notice Maps address to their rewards balance
   * Only used when vouching for mock profiles
   */
  mapping(address => uint256) public rewardsByAddress;

  /*
   * @notice Maps author profile IDs to their frozen status, preventing withdrawals before slashing
   * @dev authorProfileId => isFrozen
   */
  mapping(uint256 => bool) public frozenAuthors;

  /*
   * @notice Maps vouch IDs to their attestation hash, tracking which vouches were created by attestation
     note: once an attestation is claimed, vouches are then associated with the claiming profile
   * @dev vouchId => attestationHash
   */
  mapping(uint256 => bytes32) public vouchAttestationHash;

  // Add storage gap as the last storage variable
  // This allows us to add new storage variables in future upgrades
  // by reducing the size of this gap
  // NOTE: gap reduced by 1 to allow for the addition of the new vouchAttestationHash mapping
  uint256[49] private __gap;

  // --- Error Codes ---
  error InvalidEthosProfileForVouch(uint256 ethosProfileId);
  error AlreadyVouched(uint256 author, uint256 voucheeEthosProfileId);
  error SelfVouch(uint256 author, uint256 voucheeEthosProfileId);
  error VouchNotFound(uint256 vouchId);
  error NotAuthorForVouch(uint256 vouchId, uint256 user);
  error WrongSubjectProfileIdForVouch(uint256 vouchId, uint256 subjectProfileId);
  error CannotMarkVouchAsUnhealthy(uint256 vouchId);
  error AlreadyUnvouched(uint256 vouchId);
  error InvalidFeeMultiplier(uint256 newFee);
  error MinimumVouchAmount(uint256 amount);
  error AddressNotVouchAuthor(uint256 vouchId, address caller, address author);
  error MaximumVouchesExceeded(uint256 vouches, string message);
  error FeesExceedMaximum(uint256 totalFees, uint256 maxFees);
  error FeeTransferFailed(string message);
  error InsufficientRewardsBalance();
  error InsufficientProtocolFeeBalance();
  error InvalidSlashPercentage();
  error InvalidFeeProtocolAddress();
  error NotSlasher();
  error InvalidAttestationHash(bytes32 attestationHash);
  error PendingSlash(uint256 vouchId, uint256 authorProfileId);
  error InvalidSender();

  // --- Events ---
  event Vouched(
    uint256 indexed vouchId,
    uint256 indexed authorProfileId,
    uint256 indexed subjectProfileId,
    address subjectAddress,
    bytes32 attestationHash,
    uint256 amountStaked,
    uint256 amountDeposited
  );

  event VouchIncreased(
    uint256 indexed vouchId,
    uint256 indexed authorProfileId,
    uint256 indexed subjectProfileId,
    address subjectAddress,
    bytes32 attestationHash,
    uint256 amountStaked,
    uint256 amountDeposited
  );

  event Unvouched(
    uint256 indexed vouchId,
    uint256 indexed authorProfileId,
    uint256 indexed subjectProfileId,
    uint256 amountWithdrawn
  );

  event MarkedUnhealthy(
    uint256 indexed vouchId,
    uint256 indexed authorProfileId,
    uint256 indexed subjectProfileId
  );
  event ProtocolFeeAddressUpdated(address newFeeProtocolAddress);
  event EntryProtocolFeeBasisPointsUpdated(uint256 newProtocolFeeBasisPoints);
  event EntryDonationFeeBasisPointsUpdated(uint256 newDonationFeeBasisPoints);
  event EntryVouchersPoolFeeBasisPointsUpdated(uint256 newVouchersPoolFeeBasisPoints);
  event ExitFeeBasisPointsUpdated(uint256 newExitFeeBasisPoints);
  event DepositedToRewards(uint256 indexed recipientProfileId, uint256 amount);
  event WithdrawnFromRewards(uint256 indexed accountProfileId, uint256 amount);
  event Slashed(uint256 indexed authorProfileId, uint256 slashBasisPoints, uint256 totalSlashed);
  event Frozen(uint256 indexed authorProfileId, bool isFrozen);
  event AttestationVouchMappingUpdated(
    bytes32 indexed attestationHash,
    uint256 indexed formerProfileId,
    uint256 indexed newProfileId
  );

  /**
   * @notice Modifier to restrict access to slasher contract only
   */
  modifier onlySlasher() {
    address slasher = contractAddressManager.getContractAddressForName("SLASHER");
    if (msg.sender != slasher) revert NotSlasher();
    _;
  }
  /**
   * @notice Modifier to restrict access to Ethos Profile contract only
   */
  modifier isEthosProfile() {
    if (msg.sender != contractAddressManager.getContractAddressForName("ETHOS_PROFILE")) {
      revert InvalidSender();
    }
    _;
  }

  /**
   * @dev initializer in place of constructor.
   * @param _owner Owner address.
   * @param _admin Admin address.
   * @param _expectedSigner ExpectedSigner address.
   * @param _signatureVerifier SignatureVerifier address.
   * @param _contractAddressManagerAddr ContractAddressManager address.
   */
  function initialize(
    address _owner,
    address _admin,
    address _expectedSigner,
    address _signatureVerifier,
    address _contractAddressManagerAddr,
    address _feeProtocolAddress,
    uint256 _entryProtocolFeeBasisPoints,
    uint256 _entryDonationFeeBasisPoints,
    uint256 _entryVouchersPoolFeeBasisPoints,
    uint256 _exitFeeBasisPoints
  ) external initializer {
    __accessControl_init(
      _owner,
      _admin,
      _expectedSigner,
      _signatureVerifier,
      _contractAddressManagerAddr
    );

    __UUPSUpgradeable_init();
    if (_feeProtocolAddress == address(0)) revert InvalidFeeProtocolAddress();
    protocolFeeAddress = _feeProtocolAddress;
    entryProtocolFeeBasisPoints = _entryProtocolFeeBasisPoints;
    entryDonationFeeBasisPoints = _entryDonationFeeBasisPoints;
    entryVouchersPoolFeeBasisPoints = _entryVouchersPoolFeeBasisPoints;
    exitFeeBasisPoints = _exitFeeBasisPoints;
    configuredMinimumVouchAmount = ABSOLUTE_MINIMUM_VOUCH_AMOUNT;
    maximumVouches = 256;
    unhealthyResponsePeriod = 24 hours;
  }

  /**
   * @notice restricts upgrading to owner
   * @param newImplementation address of new implementation contract
   */
  function _authorizeUpgrade(
    address newImplementation
  ) internal override onlyOwner onlyNonZeroAddress(newImplementation) {
    // Intentionally left blank to ensure onlyOwner and zeroCheck modifiers run
  }

  // --- Vouch Functions ---

  /**
   * @notice Vouches by address, allowing vouches for addresses that have not yet joined Ethos (mock profiles)
   * @param subjectAddress The address to vouch for
   * @param comment Optional text comment about the vouch
   * @param metadata Additional structured metadata about the vouch
   */
  function vouchByAddress(
    address subjectAddress,
    string calldata comment,
    string calldata metadata
  ) public payable onlyNonZeroAddress(subjectAddress) whenNotPaused nonReentrant {
    (bool verified, bool archived, bool mock, uint256 subjectProfileId) = _ethosProfile()
      .profileStatusByAddress(subjectAddress);

    // you may not vouch for archived profiles
    if (archived) revert InvalidEthosProfileForVouch(subjectProfileId);
    // if verified, use the standard vouchByProfileId
    if (verified) {
      _vouchByProfileIdCommon(subjectProfileId, comment, metadata);
      return;
    }
    // use vouchByAddress or vouchByAttestationHash for mock profiles
    if (!mock) revert InvalidEthosProfileForVouch(subjectProfileId);

    _vouchCommon(subjectProfileId, comment, metadata, bytes32(0), subjectAddress);
  }

  /**
   * @notice Vouches by attestation hash, allowing vouches for social accounts that have not yet joined Ethos (mock profiles)
   * @param attestationHash The attestation hash identifying the subject to vouch for
   * @param comment Optional text comment about the vouch
   * @param metadata Additional structured metadata about the vouch
   */
  function vouchByAttestation(
    bytes32 attestationHash,
    string calldata comment,
    string calldata metadata
  ) public payable whenNotPaused nonReentrant {
    uint256 attestationProfileId = _ethosProfile().profileIdByAttestation(attestationHash);
    // submitted hash must match the mock profile attestation hash
    (bool verified, bool archived, bool mock) = _ethosProfile().profileStatusById(
      attestationProfileId
    );

    // you may not vouch for archived profiles
    if (archived) revert InvalidEthosProfileForVouch(attestationProfileId);
    // this function is only for mock profiles
    if (verified) {
      _vouchByProfileIdCommon(attestationProfileId, comment, metadata);
      return;
    }
    // use vouchByAddress or vouchByAttestationHash for verified profiles
    if (!mock) revert InvalidEthosProfileForVouch(attestationProfileId);

    _vouchCommon(attestationProfileId, comment, metadata, attestationHash, address(0));
  }

  /**
   * @notice Vouches by profile ID, allowing vouches for existing Ethos profiles
   * @param subjectProfileId The profile ID to vouch for
   * @param comment Optional text comment about the vouch
   * @param metadata Additional structured metadata about the vouch
   */
  function vouchByProfileId(
    uint256 subjectProfileId,
    string calldata comment,
    string calldata metadata
  ) external payable whenNotPaused nonReentrant {
    _vouchByProfileIdCommon(subjectProfileId, comment, metadata);
  }

  function _vouchByProfileIdCommon(
    uint256 subjectProfileId,
    string calldata comment,
    string calldata metadata
  ) internal {
    (bool verified, bool archived, bool mock) = _ethosProfile().profileStatusById(subjectProfileId);

    // this function is only for verified profiles
    if (!verified) revert InvalidEthosProfileForVouch(subjectProfileId);
    // you may not vouch for archived profiles
    if (archived) revert InvalidEthosProfileForVouch(subjectProfileId);
    // use vouchByAddress or vouchByAttestationHash for mock profiles
    if (mock) revert InvalidEthosProfileForVouch(subjectProfileId);

    _vouchCommon(subjectProfileId, comment, metadata, bytes32(0), address(0));
  }

  function _vouchCommon(
    uint256 subjectProfileId,
    string calldata comment,
    string calldata metadata,
    bytes32 attestationHash,
    address subjectAddress
  ) internal {
    uint256 authorProfileId = _ethosProfile().verifiedProfileIdForAddress(msg.sender);

    // pls no vouch for yourself
    if (authorProfileId == subjectProfileId) {
      revert SelfVouch(authorProfileId, subjectProfileId);
    }

    // users can't exceed the maximum number of vouches
    if (vouchIdsByAuthor[authorProfileId].length >= maximumVouches) {
      revert MaximumVouchesExceeded(
        vouchIdsByAuthor[authorProfileId].length,
        "Exceeds author vouch limit"
      );
    }

    // validate subject profile
    if (subjectProfileId == 0) revert InvalidEthosProfileForVouch(subjectProfileId);

    // one vouch per profile per author
    _vouchShouldNotExistFor(authorProfileId, subjectProfileId);

    // don't exceed maximum vouches per subject profile
    if (vouchIdsForSubjectProfileId[subjectProfileId].length >= maximumVouches) {
      revert MaximumVouchesExceeded(
        vouchIdsForSubjectProfileId[subjectProfileId].length,
        "Exceeds subject vouch limit"
      );
    }

    // must meet the minimum vouch amount
    if (msg.value < configuredMinimumVouchAmount) {
      revert MinimumVouchAmount(configuredMinimumVouchAmount);
    }

    (uint256 toDeposit, ) = applyEntryFees(
      msg.value,
      subjectProfileId,
      authorProfileId,
      attestationHash,
      subjectAddress
    );

    // store vouch details
    uint256 count = vouchCount;
    vouchIdsByAuthor[authorProfileId].push(count);
    vouchIdsByAuthorIndex[authorProfileId][count] = vouchIdsByAuthor[authorProfileId].length - 1;
    vouchIdsForSubjectProfileId[subjectProfileId].push(count);
    vouchIdsForSubjectProfileIdIndex[subjectProfileId][count] =
      vouchIdsForSubjectProfileId[subjectProfileId].length -
      1;

    vouchIdByAuthorForSubjectProfileId[authorProfileId][subjectProfileId] = count;
    vouches[count] = Vouch({
      archived: false,
      unhealthy: false,
      authorProfileId: authorProfileId,
      authorAddress: msg.sender,
      vouchId: count,
      balance: toDeposit,
      subjectProfileId: subjectProfileId,
      comment: comment,
      metadata: metadata,
      activityCheckpoints: ActivityCheckpoints({
        vouchedAt: block.timestamp,
        unvouchedAt: 0,
        unhealthyAt: 0
      })
    });

    // Store attestation hash if this vouch was created by attestation
    if (attestationHash != bytes32(0)) {
      vouchAttestationHash[count] = attestationHash;
    }

    emit Vouched(
      count,
      authorProfileId,
      subjectProfileId,
      subjectAddress,
      attestationHash,
      msg.value,
      toDeposit
    );
    vouchCount++;
  }

  /**
   * @notice Increases the staked amount for an existing vouch
   * @param vouchId The ID of the vouch to increase
   * @param attestationHash Optional mock profile attestation hash (use bytes32(0) if not needed)
   * @param subjectAddress Optional mock profile address (use address(0) if not needed)
   * @dev For verified profiles, both attestationHash and subjectAddress should be empty
   * @dev For mock profiles, either attestationHash OR subjectAddress must match the original vouch
   */
  function increaseVouch(
    uint256 vouchId,
    bytes32 attestationHash,
    address subjectAddress
  ) public payable nonReentrant {
    // vouch increases much also meet the minimum vouch amount
    if (msg.value < configuredMinimumVouchAmount) {
      revert MinimumVouchAmount(configuredMinimumVouchAmount);
    }
    // get the profile id of the author
    uint256 authorProfileId = _ethosProfile().verifiedProfileIdForAddress(msg.sender);
    _vouchShouldBelongToAuthor(vouchId, authorProfileId);
    // make sure this vouch is active; not unvouched
    _vouchShouldBePossibleUnvouch(vouchId);

    uint256 subjectProfileId = vouches[vouchId].subjectProfileId;

    // If the subject profile is a mock profile, require a subject address or attestation hash
    (, , bool mock) = _ethosProfile().profileStatusById(subjectProfileId);
    if (mock && (subjectAddress == address(0) && attestationHash == bytes32(0)))
      revert InvalidEthosProfileForVouch(subjectProfileId);

    // Validate that either attestationHash or subjectAddress matches the vouch
    if (attestationHash != bytes32(0)) {
      uint256 attestationProfileId = _ethosProfile().profileIdByAttestation(attestationHash);
      if (attestationProfileId != subjectProfileId)
        revert InvalidEthosProfileForVouch(attestationProfileId);
    }

    if (subjectAddress != address(0)) {
      uint256 addressProfileId = _ethosProfile().profileIdByAddress(subjectAddress);
      if (addressProfileId != subjectProfileId)
        revert InvalidEthosProfileForVouch(addressProfileId);
    }

    (uint256 toDeposit, ) = applyEntryFees(
      msg.value,
      subjectProfileId,
      authorProfileId,
      attestationHash,
      subjectAddress
    );
    vouches[vouchId].balance += toDeposit;

    emit VouchIncreased(
      vouchId,
      authorProfileId,
      subjectProfileId,
      subjectAddress,
      attestationHash,
      msg.value,
      toDeposit
    );
  }

  // --- Unvouch Functions ---

  /**
   * @notice Withdraws a vouch, returning staked funds to the author minus exit fees
   * @param vouchId The ID of the vouch to withdraw
   */
  function unvouch(uint256 vouchId) public whenNotPaused nonReentrant {
    Vouch storage v = vouches[vouchId];
    _vouchShouldExist(vouchId);
    _vouchShouldBePossibleUnvouch(vouchId);

    // Prevent withdrawals if the author's profile is frozen
    if (frozenAuthors[v.authorProfileId]) revert PendingSlash(vouchId, v.authorProfileId);

    // because it's $$$, you can only withdraw/unvouch to the same address you used to vouch
    // however, we don't care about the status of the address's profile; funds are always attached
    // to an address, not a profile
    if (vouches[vouchId].authorAddress != msg.sender) {
      revert AddressNotVouchAuthor(vouchId, msg.sender, vouches[vouchId].authorAddress);
    }

    v.archived = true;
    // solhint-disable-next-line not-rely-on-time
    v.activityCheckpoints.unvouchedAt = block.timestamp;
    // remove the vouch from the tracking arrays and index mappings
    _removeVouchFromArrays(v);

    // apply fees and determine how much is left to send back to the author
    (uint256 toWithdraw, ) = applyExitFees(v.balance);
    // set the balance to 0 and save back to storage
    v.balance = 0;
    // send the funds to the author
    // note: it sends it to the same address that vouched; not the one that called unvouch
    (bool success, ) = payable(v.authorAddress).call{ value: toWithdraw }("");
    if (!success) {
      revert FeeTransferFailed("Failed to send ETH to author");
    }

    emit Unvouched(v.vouchId, v.authorProfileId, v.subjectProfileId, toWithdraw);
  }

  /**
   * @dev Convenience function that combines unvouch and mark unhealthy to avoid multiple transactions.
   * @param vouchId Vouch Id.
   */
  function unvouchUnhealthy(uint256 vouchId) external whenNotPaused {
    unvouch(vouchId);
    markUnhealthy(vouchId);
  }

  /**
   * @dev Marks vouch as unhealthy.
   * @param vouchId Vouch Id.
   */
  function markUnhealthy(uint256 vouchId) public whenNotPaused {
    Vouch storage v = vouches[vouchId];
    uint256 profileId = _ethosProfile().verifiedProfileIdForAddress(msg.sender);

    _vouchShouldExist(vouchId);
    _vouchShouldBePossibleUnhealthy(vouchId);
    _vouchShouldBelongToAuthor(vouchId, profileId);
    v.unhealthy = true;
    // solhint-disable-next-line not-rely-on-time
    v.activityCheckpoints.unhealthyAt = block.timestamp;

    emit MarkedUnhealthy(v.vouchId, v.authorProfileId, v.subjectProfileId);
  }

  // --- Slash Functions ---

  /**
   * @notice Freezes all vouches from a specific author, preventing withdrawals until unfrozen or slashed
   * @dev Only callable by the authorized slasher contract
   * @param authorProfileId The profile ID whose vouches will be frozen
   * This is a protective measure to prevent withdrawal of funds during investigation before the slashing process is complete
   */
  function freeze(uint256 authorProfileId) external onlySlasher whenNotPaused nonReentrant {
    frozenAuthors[authorProfileId] = true;
    emit Frozen(authorProfileId, true);
  }

  /**
   * @notice Unfreezes an author's vouches, allowing withdrawals again
   * @dev Only callable by the authorized slasher contract
   * @param authorProfileId The profile ID whose vouches will be unfrozen
   */
  function unfreeze(uint256 authorProfileId) external onlySlasher whenNotPaused nonReentrant {
    frozenAuthors[authorProfileId] = false;
    emit Frozen(authorProfileId, false);
  }

  /**
   * @notice Reduces all vouch balances for a given author by a percentage
   * @param authorProfileId The profile ID whose vouches will be slashed
   * @param slashBasisPoints The percentage to slash in basis points (100 = 1%)
   * @return totalSlashed The total amount slashed across all vouches
   */
  function slash(
    uint256 authorProfileId,
    uint256 slashBasisPoints
  ) external onlySlasher whenNotPaused nonReentrant returns (uint256) {
    if (slashBasisPoints > MAX_SLASH_PERCENTAGE) {
      revert InvalidSlashPercentage();
    }

    uint256 totalSlashed;
    uint256[] storage vouchIds = vouchIdsByAuthor[authorProfileId];

    for (uint256 i = 0; i < vouchIds.length; i++) {
      Vouch storage vouch = vouches[vouchIds[i]];
      // Only slash active vouches
      if (!vouch.archived) {
        uint256 slashAmount = vouch.balance.mulDiv(
          slashBasisPoints,
          BASIS_POINT_SCALE,
          Math.Rounding.Floor
        );
        if (slashAmount > 0) {
          vouch.balance -= slashAmount;
          totalSlashed += slashAmount;
        }
      }
    }

    if (totalSlashed > 0) {
      // Send slashed funds to protocol fee address
      (bool success, ) = protocolFeeAddress.call{ value: totalSlashed }("");
      if (!success) revert FeeTransferFailed("Slash transfer failed");
    }

    emit Slashed(authorProfileId, slashBasisPoints, totalSlashed);
    return totalSlashed;
  }

  // --- Fee Management ---

  /**
   * @notice Updates the protocol fee percentage charged on entry
   * @dev Only callable by admin
   * @param _newEntryProtocolFeeBasisPoints New fee in basis points (100 = 1%)
   * @custom:throws {FeesExceedMaximum} If new total fees would exceed maximum
   * @custom:emits EntryProtocolFeeBasisPointsUpdated
   */
  function setEntryProtocolFeeBasisPoints(
    uint256 _newEntryProtocolFeeBasisPoints
  ) external onlyAdmin {
    checkFeeExceedsMaximum(entryProtocolFeeBasisPoints, _newEntryProtocolFeeBasisPoints);
    entryProtocolFeeBasisPoints = _newEntryProtocolFeeBasisPoints;
    emit EntryProtocolFeeBasisPointsUpdated(_newEntryProtocolFeeBasisPoints);
  }

  /**
   * @notice Updates the donation fee percentage charged on entry
   * @dev Only callable by admin
   * @param _newEntryDonationFeeBasisPoints New fee in basis points (100 = 1%)
   * @custom:throws {FeesExceedMaximum} If new total fees would exceed maximum
   * @custom:emits EntryDonationFeeBasisPointsUpdated
   */
  function setEntryDonationFeeBasisPoints(
    uint256 _newEntryDonationFeeBasisPoints
  ) external onlyAdmin {
    checkFeeExceedsMaximum(entryDonationFeeBasisPoints, _newEntryDonationFeeBasisPoints);
    entryDonationFeeBasisPoints = _newEntryDonationFeeBasisPoints;
    emit EntryDonationFeeBasisPointsUpdated(_newEntryDonationFeeBasisPoints);
  }

  /**
   * @notice Updates the vouchers pool fee percentage charged on entry
   * @dev Only callable by admin
   * @param _newEntryVouchersPoolFeeBasisPoints New fee in basis points (100 = 1%)
   * @custom:throws {FeesExceedMaximum} If new total fees would exceed maximum
   * @custom:emits EntryVouchersPoolFeeBasisPointsUpdated
   */
  function setEntryVouchersPoolFeeBasisPoints(
    uint256 _newEntryVouchersPoolFeeBasisPoints
  ) external onlyAdmin {
    checkFeeExceedsMaximum(entryVouchersPoolFeeBasisPoints, _newEntryVouchersPoolFeeBasisPoints);
    entryVouchersPoolFeeBasisPoints = _newEntryVouchersPoolFeeBasisPoints;
    emit EntryVouchersPoolFeeBasisPointsUpdated(_newEntryVouchersPoolFeeBasisPoints);
  }

  /**
   * @notice Updates the exit fee percentage charged on withdrawals
   * @dev Only callable by admin
   * @param _newExitFeeBasisPoints New fee in basis points (100 = 1%)
   * @custom:throws {FeesExceedMaximum} If new total fees would exceed maximum
   * @custom:emits ExitFeeBasisPointsUpdated
   */
  function setExitFeeBasisPoints(uint256 _newExitFeeBasisPoints) external onlyAdmin {
    checkFeeExceedsMaximum(exitFeeBasisPoints, _newExitFeeBasisPoints);
    exitFeeBasisPoints = _newExitFeeBasisPoints;
    emit ExitFeeBasisPointsUpdated(_newExitFeeBasisPoints);
  }

  /**
   * @notice Updates the address where protocol fees are sent
   * @dev Only callable by owner
   * @param _protocolFeeAddress New fee recipient address
   * @custom:throws {InvalidFeeProtocolAddress} If address is zero
   * @custom:emits ProtocolFeeAddressUpdated
   */
  function setProtocolFeeAddress(address _protocolFeeAddress) external onlyOwner {
    if (_protocolFeeAddress == address(0)) revert InvalidFeeProtocolAddress();
    protocolFeeAddress = _protocolFeeAddress;
    emit ProtocolFeeAddressUpdated(protocolFeeAddress);
  }

  // --- Configuration Functions ---

  /**
   * @dev Sets the minimum vouch amount.
   * @param amount New minimum vouch amount in wei.
   */
  function setMinimumVouchAmount(uint256 amount) external onlyAdmin whenNotPaused {
    if (amount < ABSOLUTE_MINIMUM_VOUCH_AMOUNT) {
      revert MinimumVouchAmount(ABSOLUTE_MINIMUM_VOUCH_AMOUNT);
    }
    configuredMinimumVouchAmount = amount;
  }

  /**
   * @notice Updates the maximum number of vouches allowed
   * @dev Only callable by admin when contract is not paused
   * @param maximumVouches_ The new maximum number of vouches
   */
  function updateMaximumVouches(uint256 maximumVouches_) external onlyAdmin whenNotPaused {
    if (maximumVouches_ > 256) {
      revert MaximumVouchesExceeded(maximumVouches_, "Maximum vouches cannot exceed 256");
    }
    maximumVouches = maximumVouches_;
  }

  /**
   * @dev Updates time period for unhealthy response.
   * @param unhealthyResponsePeriodDuration Time period.
   */
  function updateUnhealthyResponsePeriod(
    uint256 unhealthyResponsePeriodDuration
  ) external onlyAdmin whenNotPaused {
    unhealthyResponsePeriod = unhealthyResponsePeriodDuration;
  }

  // --- Reward Functions ---

  /**
   * @notice Claims accumulated rewards for a verified Ethos profile
   * @dev Combines rewards from both profile ID and address-based rewards
   */
  function claimRewards() external whenNotPaused nonReentrant {
    (bool verified, , bool mock, uint256 callerProfileId) = _ethosProfile().profileStatusByAddress(
      msg.sender
    );

    // Only check that this is a real profile (not mock) and was verified at some point
    if (!verified || mock) revert ProfileNotFoundForAddress(msg.sender);

    // claim rewards by profile id
    uint256 amount = rewardsByProfileId[callerProfileId];

    // claim rewards by address
    uint256 addressRewards = rewardsByAddress[msg.sender];
    amount += addressRewards;

    if (amount == 0) revert InsufficientRewardsBalance();

    rewardsByProfileId[callerProfileId] = 0;
    rewardsByAddress[msg.sender] = 0;
    (bool success, ) = msg.sender.call{ value: amount }("");
    if (!success) revert FeeTransferFailed("Rewards claim failed");

    emit WithdrawnFromRewards(callerProfileId, amount);
  }

  /**
   * @notice Claims accumulated rewards for a profile using their attestation hash
   * @dev Only for verified profiles that registered the attestation hash
   * @param attestationHash The attestation hash used to identify the rewards
   */
  function claimRewardsByAttestation(bytes32 attestationHash) external whenNotPaused nonReentrant {
    (bool verified, , bool mock, uint256 callerProfileId) = _ethosProfile().profileStatusByAddress(
      msg.sender
    );
    if (!verified) revert ProfileNotFoundForAddress(msg.sender);
    if (mock) revert ProfileNotFoundForAddress(msg.sender);

    // ensure this hash belongs to the caller
    if (_ethosProfile().profileIdByAttestation(attestationHash) != callerProfileId)
      revert InvalidAttestationHash(attestationHash);

    uint256 amount = rewardsByAttestationHash[attestationHash];
    if (amount == 0) revert InsufficientRewardsBalance();

    rewardsByAttestationHash[attestationHash] = 0;
    (bool success, ) = msg.sender.call{ value: amount }("");
    if (!success) revert FeeTransferFailed("Rewards claim failed");

    emit WithdrawnFromRewards(callerProfileId, amount);
  }

  function _depositRewards(
    uint256 amount,
    uint256 recipientProfileId,
    bytes32 attestationHash,
    address subjectAddress
  ) internal {
    if (attestationHash != bytes32(0)) {
      rewardsByAttestationHash[attestationHash] += amount;
    } else if (subjectAddress != address(0)) {
      rewardsByAddress[subjectAddress] += amount;
    } else {
      rewardsByProfileId[recipientProfileId] += amount;
    }
    emit DepositedToRewards(recipientProfileId, amount);
  }

  function _previousVouchersBalance(
    uint256 subjectProfileId,
    uint256 excludeAuthorId
  ) internal view returns (uint256 totalBalance) {
    uint256[] storage vouchIds = vouchIdsForSubjectProfileId[subjectProfileId];
    uint256 totalVouches = vouchIds.length;

    // Calculate total balance of all active vouches except author's
    for (uint256 i = 0; i < totalVouches; i++) {
      Vouch storage vouch = vouches[vouchIds[i]];
      // Only include active (not archived) vouches from other authors
      if (!vouch.archived && vouch.authorProfileId != excludeAuthorId) {
        totalBalance += vouch.balance;
      }
    }
  }

  function _rewardPreviousVouchers(
    uint256 amountToDistribute,
    uint256 previousVouchersBalance,
    uint256 subjectProfileId,
    uint256 excludeAuthorId,
    bytes32 attestationHash,
    address subjectAddress
  ) internal {
    uint256[] storage vouchIds = vouchIdsForSubjectProfileId[subjectProfileId];
    uint256 totalVouches = vouchIds.length;
    // Distribute rewards proportionally
    uint256 remainingRewards = amountToDistribute;
    for (uint256 i = 0; i < totalVouches && remainingRewards > 0; i++) {
      Vouch storage vouch = vouches[vouchIds[i]];
      if (!vouch.archived && vouch.authorProfileId != excludeAuthorId) {
        // Calculate this vouch's share of the rewards
        uint256 reward = amountToDistribute.mulDiv(
          vouch.balance,
          previousVouchersBalance,
          Math.Rounding.Floor
        );
        // Avoid rewarding the author more than the amount they vouched
        // this prevents vouchers from vouching everyone for dust, just to try to get the total real vouch rewards
        reward = Math.min(reward, vouch.balance);

        if (reward > 0) {
          vouch.balance += reward;
          remainingRewards -= reward;
        }
      }
    }

    // Send any remaining rewards to the subject reward escrow
    if (remainingRewards > 0) {
      _depositRewards(remainingRewards, subjectProfileId, attestationHash, subjectAddress);
    }
  }

  // --- View Functions ---

  // ITargetStatus implementation
  /**
   * @dev Checks if target exists and is allowed to be used.
   * @param targetId Vouch id.
   * @return exists Whether target exists.
   * @return allowed Whether target is allowed to be used.
   */
  function targetExistsAndAllowedForId(
    uint256 targetId
  ) external view returns (bool exists, bool allowed) {
    Vouch storage vouch = vouches[targetId];

    exists = vouch.activityCheckpoints.vouchedAt > 0;
    allowed = exists;
  }

  /**
   * @dev Gets a verified vouch by author for vouchee profile Id.
   * @param author Author profileId.
   * @param subjectProfileId Subject profile Id.
   * @return Vouch.
   */
  function verifiedVouchByAuthorForSubjectProfileId(
    uint256 author,
    uint256 subjectProfileId
  ) public view returns (Vouch memory) {
    uint256 id = vouchIdByAuthorForSubjectProfileId[author][subjectProfileId];

    _vouchShouldBelongToAuthor(id, author);

    if (vouches[id].subjectProfileId != subjectProfileId) {
      revert WrongSubjectProfileIdForVouch(id, subjectProfileId);
    }

    return vouches[id];
  }

  /**
   * @dev Gets a verified vouch by author for subject address.
   * @param author author profileId.
   * @param subjectAddress subject address.
   * @return Vouch.
   */
  function verifiedVouchByAuthorForSubjectAddress(
    uint256 author,
    address subjectAddress
  ) external view returns (Vouch memory) {
    uint256 profileId = _ethosProfile().verifiedProfileIdForAddress(subjectAddress);

    return verifiedVouchByAuthorForSubjectProfileId(author, profileId);
  }

  /**
   * @dev Checks whether vouch exists for author and subject profile Id.
   * @param author Author profileId.
   * @param subjectProfileId Vouchee profile Id.
   * @return Whether vouch exists.
   */
  function vouchExistsFor(uint256 author, uint256 subjectProfileId) public view returns (bool) {
    uint256 id = vouchIdByAuthorForSubjectProfileId[author][subjectProfileId];
    Vouch storage v = vouches[id];

    return
      v.authorProfileId == author &&
      v.subjectProfileId == subjectProfileId &&
      v.activityCheckpoints.unvouchedAt == 0;
  }

  // --- Internal Helper Functions ---

  /**
   * @notice Fails if vouch does not belong to Author.
   * @dev Checks if vouch belongs to author.
   * @param vouchId Vouch Id.
   * @param author author profileId.
   */
  function _vouchShouldBelongToAuthor(uint256 vouchId, uint256 author) private view {
    if (vouches[vouchId].authorProfileId != author) {
      revert NotAuthorForVouch(vouchId, author);
    }
  }

  /**
   * @notice Fails if vouch does not exist.
   * @dev Checks if vouch exists.
   * @param vouchId Vouch Id.
   */
  function _vouchShouldExist(uint256 vouchId) private view {
    if (vouches[vouchId].activityCheckpoints.vouchedAt == 0) {
      revert VouchNotFound(vouchId);
    }
  }

  /**
   * @notice Fails if vouch should not exist.
   * @dev Checks if vouch does not exist.
   * @param author Author profileId.
   * @param subjectProfileId Subject profile Id.
   */
  function _vouchShouldNotExistFor(uint256 author, uint256 subjectProfileId) private view {
    if (vouchExistsFor(author, subjectProfileId)) {
      revert AlreadyVouched(author, subjectProfileId);
    }
  }

  /**
   * @notice Fails if vouch cannot be set as unhealthy.
   * @dev Checks if vouch can be set as unhealthy.
   * @param vouchId Vouch Id.
   */
  function _vouchShouldBePossibleUnhealthy(uint256 vouchId) private view {
    Vouch storage v = vouches[vouchId];
    bool stillHasTime = block.timestamp <=
      v.activityCheckpoints.unvouchedAt + unhealthyResponsePeriod;

    if (!v.archived || v.unhealthy || !stillHasTime) {
      revert CannotMarkVouchAsUnhealthy(vouchId);
    }
  }

  /**
   * @notice Fails if vouch cannot be unvouched.
   * @dev Checks if vouch can be unvouched.
   * @param vouchId Vouch Id.
   */
  function _vouchShouldBePossibleUnvouch(uint256 vouchId) private view {
    Vouch storage v = vouches[vouchId];

    if (v.archived) {
      revert AlreadyUnvouched(vouchId);
    }
  }

  /**
   * @dev Sends protocol fees to the designated fee address
   * @param amount Amount of ETH to send
   * @custom:throws {FeeTransferFailed} If the ETH transfer fails
   */
  function _depositProtocolFee(uint256 amount) internal {
    (bool success, ) = protocolFeeAddress.call{ value: amount }("");
    if (!success) revert FeeTransferFailed("Protocol fee deposit failed");
  }

  /**
   * @dev Removes an element from an array by swapping with the last element and popping
   * @param index The index of the element to remove
   * @param arr The array to modify
   */
  function _removeFromArray(uint256 index, uint256[] storage arr) private {
    // If this isn't the last element, swap it with the last one
    if (index < arr.length - 1) {
      arr[index] = arr[arr.length - 1];
    }
    // Pop the last element (now duplicated)
    arr.pop();
  }

  /**
   * @dev Updates the index mapping when swapping elements during removal
   * @param index The index where the element is being removed
   * @param arr The array containing the elements
   * @param profileId The profile ID for the index mapping
   * @param indexMapping The mapping to update
   */
  function _updateIndexMappingForSwap(
    uint256 index,
    uint256[] storage arr,
    uint256 profileId,
    mapping(uint256 => mapping(uint256 => uint256)) storage indexMapping
  ) private {
    if (index < arr.length - 1) {
      uint256 lastId = arr[arr.length - 1];
      indexMapping[profileId][lastId] = index;
    }
  }

  function applyEntryFees(
    uint256 amount,
    uint256 subjectProfileId,
    uint256 authorProfileId,
    bytes32 attestationHash,
    address subjectAddress
  ) internal returns (uint256 toDeposit, uint256 totalFees) {
    uint256 previousVouchersBalance = _previousVouchersBalance(subjectProfileId, authorProfileId);
    // Sum all entry fees to calculate a max fee to be split across each fee type
    uint256 totalFeesBasisPoints = entryProtocolFeeBasisPoints + entryDonationFeeBasisPoints;
    if (previousVouchersBalance > 0) totalFeesBasisPoints += entryVouchersPoolFeeBasisPoints;
    if (totalFeesBasisPoints == 0) return (amount, 0);

    // Calculate deposit amount
    toDeposit = amount.mulDiv(
      BASIS_POINT_SCALE,
      (BASIS_POINT_SCALE + totalFeesBasisPoints),
      Math.Rounding.Floor
    );

    // Calculate total fees as the remainder
    totalFees = amount - toDeposit;

    // Distribute total fees proportionally
    uint256 protocolFee = totalFees.mulDiv(
      entryProtocolFeeBasisPoints,
      totalFeesBasisPoints,
      Math.Rounding.Floor
    );
    uint256 donationFee = totalFees.mulDiv(
      entryDonationFeeBasisPoints,
      totalFeesBasisPoints,
      Math.Rounding.Floor
    );
    uint256 vouchersPoolFee = totalFees - protocolFee - donationFee;

    // Distribute each fee portion to its respective destination
    if (protocolFee > 0) _depositProtocolFee(protocolFee);

    if (donationFee > 0)
      _depositRewards(donationFee, subjectProfileId, attestationHash, subjectAddress);

    if (vouchersPoolFee > 0)
      _rewardPreviousVouchers(
        vouchersPoolFee,
        previousVouchersBalance,
        subjectProfileId,
        authorProfileId,
        attestationHash,
        subjectAddress
      );

    // vouchersPoolFee is dynamic and may be less than initially allocated
    // so we need to recalculate totalFees to ensure the correct amount is deposited
    totalFees = protocolFee + donationFee + vouchersPoolFee;
    toDeposit = amount - totalFees;
  }

  function applyExitFees(uint256 amount) internal returns (uint256 toDeposit, uint256 totalFees) {
    // Exit transactions only have a single protocol fee
    totalFees = calcFee(amount, exitFeeBasisPoints);
    if (totalFees > 0) _depositProtocolFee(totalFees);
    toDeposit = amount - totalFees;
  }

  /**
   * @notice Calculates the fee amount based on total and basis points
   * @dev Calculates fee "backwards" from total amount to ensure deposit + fee = total.
   *
   * @param total The total amount sent by user
   * @param feeBasisPoints The fee percentage in basis points (100 = 1%)
   * @return fee The calculated fee amount
   */
  function calcFee(uint256 total, uint256 feeBasisPoints) internal pure returns (uint256 fee) {
    /*
     * Formula derivation:
     * 1. total = deposit + fee
     * 2. fee = deposit * (feeBasisPoints/10000)
     * 3. total = deposit + deposit * (feeBasisPoints/10000)
     * 4. total = deposit * (1 + feeBasisPoints/10000)
     * 5. deposit = total / (1 + feeBasisPoints/10000)
     * 6. fee = total - deposit
     * 7. fee = total - (total * 10000 / (10000 + feeBasisPoints))
     */
    return
      total -
      (total.mulDiv(BASIS_POINT_SCALE, (BASIS_POINT_SCALE + feeBasisPoints), Math.Rounding.Floor));
  }

  /* @notice Checks if the new fee would cause the total fees to exceed the maximum allowed
   * @dev This function is called before updating any fee to ensure the total doesn't exceed MAX_TOTAL_FEES
   * @param currentFee The current value of the fee being updated
   * @param newFee The proposed new value for the fee
   */
  function checkFeeExceedsMaximum(uint256 currentFee, uint256 newFee) internal view {
    uint256 totalFees = entryProtocolFeeBasisPoints +
      exitFeeBasisPoints +
      entryDonationFeeBasisPoints +
      entryVouchersPoolFeeBasisPoints +
      newFee -
      currentFee;
    if (totalFees > MAX_TOTAL_FEES) revert FeesExceedMaximum(totalFees, MAX_TOTAL_FEES);
  }

  /**
   * @notice Removes a vouch from both author and subject arrays and updates mappings
   * @param v The vouch struct containing author and subject profile IDs
   */
  function _removeVouchFromArrays(Vouch storage v) private {
    // Remove from author's array
    uint256 authorIndex = vouchIdsByAuthorIndex[v.authorProfileId][v.vouchId];
    uint256[] storage authorVouches = vouchIdsByAuthor[v.authorProfileId];

    _updateIndexMappingForSwap(
      authorIndex,
      authorVouches,
      v.authorProfileId,
      vouchIdsByAuthorIndex
    );
    _removeFromArray(authorIndex, authorVouches);
    delete vouchIdsByAuthorIndex[v.authorProfileId][v.vouchId];

    // Remove from subject's array
    uint256 subjectIndex = vouchIdsForSubjectProfileIdIndex[v.subjectProfileId][v.vouchId];
    uint256[] storage subjectVouches = vouchIdsForSubjectProfileId[v.subjectProfileId];

    _updateIndexMappingForSwap(
      subjectIndex,
      subjectVouches,
      v.subjectProfileId,
      vouchIdsForSubjectProfileIdIndex
    );
    _removeFromArray(subjectIndex, subjectVouches);
    delete vouchIdsForSubjectProfileIdIndex[v.subjectProfileId][v.vouchId];

    // the author->subject mapping is only for active vouches; remove it
    delete vouchIdByAuthorForSubjectProfileId[v.authorProfileId][v.subjectProfileId];
  }

  function _ethosProfile() internal view returns (IEthosProfile) {
    return IEthosProfile(contractAddressManager.getContractAddressForName(ETHOS_PROFILE));
  }

  /**
   * @notice Updates vouch mappings when an attestation is claimed by a profile
   * @dev Only callable by EthosProfile contract
   * @param attestationHash The hash of the attestation being claimed
   * @param nextProfileId The profile ID that claimed the attestation
   */
  function handleAttestationClaim(
    bytes32 attestationHash,
    uint256 nextProfileId
  ) external isEthosProfile whenNotPaused {
    uint256 formerProfileId = _ethosProfile().profileIdByAttestation(attestationHash);
    if (formerProfileId == 0) return; // no previous vouches

    uint256[] storage oldVouchIds = vouchIdsForSubjectProfileId[formerProfileId];
    // add to any existing vouches for the upcoming profile
    uint256[] storage newVouchIds = vouchIdsForSubjectProfileId[nextProfileId];

    // transfer to new profile
    uint256[] memory vouchIdsToRemove = new uint256[](oldVouchIds.length);
    for (uint256 i = 0; i < oldVouchIds.length; i++) {
      uint256 vouchId = oldVouchIds[i];
      vouchIdsToRemove[i] = vouchId;
      Vouch storage vouch = vouches[vouchId];
      // Only transfer if this vouch was created by this attestation
      if (!vouch.archived && vouchAttestationHash[vouchId] == attestationHash) {
        // Update the vouch's subject profile ID
        vouch.subjectProfileId = nextProfileId;

        // Update the author->subject mapping
        vouchIdByAuthorForSubjectProfileId[vouch.authorProfileId][nextProfileId] = vouchId;
        delete vouchIdByAuthorForSubjectProfileId[vouch.authorProfileId][formerProfileId];

        // Add to new profile's vouch list and update index
        // Note: this can exceed the 256 vouch limit
        newVouchIds.push(vouchId);
        vouchIdsForSubjectProfileIdIndex[nextProfileId][vouchId] = newVouchIds.length - 1;

        // once an attestation is claimed, vouches are then associated with the claiming profile
        // so we no longer track them by the attestation hash
        delete vouchAttestationHash[vouchId];
      }
    }
    // Remove from old profile's vouch list
    for (uint256 i = 0; i < vouchIdsToRemove.length; i++) {
      uint256 vouchId = vouchIdsToRemove[i];
      uint256 oldIndex = vouchIdsForSubjectProfileIdIndex[formerProfileId][vouchId];
      _updateIndexMappingForSwap(
        oldIndex,
        oldVouchIds,
        formerProfileId,
        vouchIdsForSubjectProfileIdIndex
      );
      _removeFromArray(oldIndex, oldVouchIds);
    }

    emit AttestationVouchMappingUpdated(attestationHash, formerProfileId, nextProfileId);
  }

  /**
   * @notice Updates multiple vouch subject profile IDs in a single transaction
   * @param vouchIds Array of vouch IDs to update
   * @param newSubjectProfileIds Array of new subject profile IDs to assign
   */
  function updateVouchSubjectProfileIds(
    uint256[] calldata vouchIds,
    uint256[] calldata newSubjectProfileIds
  ) external onlyAdmin whenNotPaused nonReentrant {
    // Function expires on May, 2025
    require(block.timestamp <= 1746500000, "Function expired");

    // Ensure arrays have the same length
    require(vouchIds.length == newSubjectProfileIds.length, "Array length mismatch");

    // Process each vouch update
    for (uint256 i = 0; i < vouchIds.length; i++) {
      uint256 vouchId = vouchIds[i];
      uint256 newSubjectProfileId = newSubjectProfileIds[i];

      // Skip if vouch doesn't exist
      if (vouches[vouchId].activityCheckpoints.vouchedAt == 0) continue;

      Vouch storage vouch = vouches[vouchId];
      uint256 oldSubjectProfileId = vouch.subjectProfileId;

      // Skip if no change needed
      if (oldSubjectProfileId == newSubjectProfileId) continue;

      // Remove from old subject's array
      uint256 oldIndex = vouchIdsForSubjectProfileIdIndex[oldSubjectProfileId][vouchId];
      uint256[] storage oldSubjectVouches = vouchIdsForSubjectProfileId[oldSubjectProfileId];
      _updateIndexMappingForSwap(
        oldIndex,
        oldSubjectVouches,
        oldSubjectProfileId,
        vouchIdsForSubjectProfileIdIndex
      );
      _removeFromArray(oldIndex, oldSubjectVouches);
      delete vouchIdsForSubjectProfileIdIndex[oldSubjectProfileId][vouchId];

      // Add to new subject's array
      uint256[] storage newSubjectVouches = vouchIdsForSubjectProfileId[newSubjectProfileId];
      newSubjectVouches.push(vouchId);
      vouchIdsForSubjectProfileIdIndex[newSubjectProfileId][vouchId] = newSubjectVouches.length - 1;

      // Update the author->subject mapping
      delete vouchIdByAuthorForSubjectProfileId[vouch.authorProfileId][oldSubjectProfileId];
      vouchIdByAuthorForSubjectProfileId[vouch.authorProfileId][newSubjectProfileId] = vouchId;

      // Update the vouch itself
      vouch.subjectProfileId = newSubjectProfileId;

      // Remove the attestation hash from the vouch
      delete vouchAttestationHash[vouchId];
    }
  }
}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.26;

interface IContractAddressManager {
  function getContractAddressForName(string memory name) external view returns (address);

  function checkIsEthosContract(address targetAddress) external view returns (bool);
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

/*
 * @dev Interface for EthosProfile Smart Contract.
 */
interface IEthosProfile {
  struct Profile {
    bool archived;
    uint256 profileId;
    uint256 createdAt;
    address[] addresses;
    InviteInfo inviteInfo;
  }

  struct InviteInfo {
    address[] sent;
    uint256[] acceptedIds;
    uint256 available;
    uint256 invitedBy;
  }

  function sentAt(uint256 profile, address invitee) external view returns (uint256);

  function profileExistsAndArchivedForId(
    uint256 profileId
  ) external view returns (bool exists, bool archived);

  function addressBelongsToProfile(
    address _address,
    uint256 _profileId
  ) external view returns (bool);

  function verifiedProfileIdForAddress(address _address) external view returns (uint256);

  function profileStatusById(
    uint256 profileId
  ) external view returns (bool verified, bool archived, bool mock);

  function profileStatusByAddress(
    address _address
  ) external view returns (bool verified, bool archived, bool mock, uint256 profileId);

  function profileIdByAddress(address user) external view returns (uint256);

  function profileIdByAttestation(bytes32 attestationHash) external view returns (uint256);

  function incrementProfileCount(
    bool isAttestation,
    address subject,
    bytes32 attestation
  ) external returns (uint256 count);

  function assignExistingProfileToAttestation(bytes32 attestation, uint256 profileId) external;

  function getProfile(uint256 id) external view returns (Profile memory profile);
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

/*
 * @dev Interface for IPausable Smart Contract.
 */

interface IPausable {
  function paused() external view returns (bool);

  function pause() external;

  function unpause() external;
}

File 29 of 34 : ISignatureVerifier.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

/*
 * @dev Interface for SignatureVerifier Smart Contract.
 */
interface ISignatureVerifier {
  function verifySignature(
    address _expectedSigner,
    bytes32 _messageHash,
    bytes memory _signature
  ) external view returns (bool);
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

/*
 * @dev Interface for ITargetStatus Smart Contract.
 */
interface ITargetStatus {
  /**
   * @dev Checks whether target exists and is allowed to be used for given Id. Target can be any entity like Review, Vouch, Attestation etc.
   * @param _targetId Target id.
   * @return exists Whether target exists.
   * @return allowed Whether target is allowed to be used.
   */
  function targetExistsAndAllowedForId(
    uint256 _targetId
  ) external view returns (bool exists, bool allowed);
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import { IPausable } from "../interfaces/IPausable.sol";
import { IContractAddressManager } from "../interfaces/IContractAddressManager.sol";
import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import { AccessControlEnumerableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol";
import { SignatureControl } from "./SignatureControl.sol";
import { ETHOS_INTERACTION_CONTROL } from "./Constants.sol";

/**
 * @dev Contract module that allows children to restrict access to run functions
 * by service account only.
 */
abstract contract AccessControl is
  IPausable,
  PausableUpgradeable,
  AccessControlEnumerableUpgradeable,
  SignatureControl
{
  /**
   * @dev Constructor that disables initializers when the implementation contract is deployed.
   * This prevents the implementation contract from being initialized, which is important for
   * security since the implementation contract should never be used directly, only through
   * delegatecall from the proxy.
   */
  constructor() {
    _disableInitializers();
  }

  bytes32 public constant OWNER_ROLE = keccak256("OWNER_ROLE");
  bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");

  IContractAddressManager public contractAddressManager;

  // Add storage gap as the last storage variable
  // This allows us to add new storage variables in future upgrades
  // by reducing the size of this gap
  uint256[50] private __gap;

  /**
   * @dev Constructor.
   * @param owner Owner address.
   * @param admin Admin address.
   * @param expectedSigner Signer address used for signing methods that should be approved by Ethos.
   * @param signatureVerifier SignatureVerifier contract address.
   * @param contractAddressManagerAddr ContractAddressManager contract address.
   */
  // solhint-disable-next-line func-name-mixedcase
  function __accessControl_init(
    address owner,
    address admin,
    address expectedSigner,
    address signatureVerifier,
    address contractAddressManagerAddr
  ) internal onlyInitializing {
    if (owner == address(0) || admin == address(0) || contractAddressManagerAddr == address(0)) {
      revert ZeroAddress();
    }

    __signatureControl_init(expectedSigner, signatureVerifier);

    contractAddressManager = IContractAddressManager(contractAddressManagerAddr);

    _grantRole(OWNER_ROLE, owner);
    _grantRole(ADMIN_ROLE, admin);

    // allowlistEnabled = false;
  }

  modifier onlyOwner() {
    _checkRole(OWNER_ROLE);
    _;
  }

  modifier onlyAdmin() {
    _checkRole(ADMIN_ROLE);
    _;
  }

  modifier onlyInteractionControl() {
    address interactionsControlAddr = contractAddressManager.getContractAddressForName(
      ETHOS_INTERACTION_CONTROL
    );

    if (interactionsControlAddr != msg.sender) {
      revert AccessControlUnauthorizedAccount(msg.sender, keccak256("ETHOS_INTERACTION_CONTROL"));
    }

    _;
  }

  /**
   * @dev Updates ContractAddressManager address.
   * @param contractAddressesAddr ContractAddresses address.
   */
  function updateContractAddressManager(address contractAddressesAddr) external onlyAdmin {
    contractAddressManager = IContractAddressManager(contractAddressesAddr);
  }

  // Owner
  /**
   * @dev Updates owner address.
   * @param owner Owner address to be used instead of current.
   */
  function updateOwner(address owner) external onlyOwner {
    _revokeRole(OWNER_ROLE, getRoleMember(OWNER_ROLE, 0));
    _grantRole(OWNER_ROLE, owner);
  }

  // Admin
  /**
   * @dev Adds admin address.
   * @param admin Admin address to be added.
   */
  function addAdmin(address admin) external onlyOwner {
    _grantRole(ADMIN_ROLE, admin);
  }

  /**
   * @dev Removes admin address.
   * @param admin Admin address to be removed.
   */
  function removeAdmin(address admin) external onlyOwner {
    _revokeRole(ADMIN_ROLE, admin);
  }

  // Signature verification
  /**
   * @dev Updates expected signer of signatures.
   * @param signer Signer address.
   */
  function updateExpectedSigner(address signer) external onlyAdmin {
    _updateExpectedSigner(signer);
  }

  /**
   * @dev Updates signature verifier contract address.
   * @param sinatureVerifier SignatureVerifier contract address.
   */
  function updateSignatureVerifier(address sinatureVerifier) external onlyAdmin {
    _updateSignatureVerifier(sinatureVerifier);
  }

  // Pausable
  function pause() external onlyInteractionControl {
    super._pause();
  }

  function unpause() external onlyInteractionControl {
    super._unpause();
  }

  // IPausable
  function paused() public view override(IPausable, PausableUpgradeable) returns (bool) {
    return super.paused();
  }
}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.26;
error IndexOutOfBounds();

contract Common {
  /**
   * @dev Returns correct length for an array to be created.
   * @param arrayLength Length of array.
   * @param maxLength Maximum length to be used.
   * @param fromIdx Start index.
   * @return Correct length.
   */
  function _correctLength(
    uint256 arrayLength,
    uint256 maxLength,
    uint256 fromIdx
  ) internal pure returns (uint256) {
    if (arrayLength == 0 || maxLength == 0 || fromIdx >= arrayLength) {
      return 0;
    }

    uint256 availableLength = arrayLength - fromIdx;
    return maxLength > availableLength ? availableLength : maxLength;
  }

  /**
   * @dev Removes an element from an array by copying the last element into the specified index position
   *      and then removing the last element. This maintains array order but is gas efficient.
   * @param index The index of the element to remove
   * @param array The storage array to modify
   * @notice This function will revert if the index is out of bounds
   */

  function _removeFromArray(uint256 index, address[] storage array) internal {
    if (index >= array.length) revert IndexOutOfBounds();

    // If this is not the last element, copy last element to the index position
    if (index != array.length - 1) {
      array[index] = array[array.length - 1];
    }
    array.pop();
  }
}

File 33 of 34 : Constants.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.26;

string constant ETHOS_ATTESTATION = "ETHOS_ATTESTATION";
string constant ETHOS_DISCUSSION = "ETHOS_DISCUSSION";
string constant ETHOS_INTERACTION_CONTROL = "ETHOS_INTERACTION_CONTROL";
string constant ETHOS_PROFILE = "ETHOS_PROFILE";
string constant ETHOS_REVIEW = "ETHOS_REVIEW";
string constant ETHOS_VOTE = "ETHOS_VOTE";
string constant ETHOS_VOUCH = "ETHOS_VOUCH";

// SPDX-License-Identifier: MIT

pragma solidity 0.8.26;
import { ISignatureVerifier } from "../interfaces/ISignatureVerifier.sol";
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

/**
 * @title SignatureControl Smart Contract.
 * @dev Controls signatures used in functions that must be approved by Ethos.
 */
abstract contract SignatureControl is Initializable {
  address public expectedSigner;
  address public signatureVerifier;

  mapping(bytes => bool) public signatureUsed;

  error SignatureWasUsed();
  error InvalidSignature();
  error ZeroAddress();

  /**
   * @dev Constructor.
   * @param expectedSignerAddr Signer address used for signing methods that should be approved by Ethos.
   * @param signatureVerifierAddr SignatureVerifier contract address.
   */
  // solhint-disable-next-line func-name-mixedcase
  function __signatureControl_init(
    address expectedSignerAddr,
    address signatureVerifierAddr
  ) internal onlyInitializing {
    _updateExpectedSigner(expectedSignerAddr);
    _updateSignatureVerifier(signatureVerifierAddr);
  }

  modifier onlyNonZeroAddress(address addr) {
    _addressShouldNotBeZero(addr);
    _;
  }

  modifier onlyUnusedSignature(bytes calldata signature) {
    if (signatureUsed[signature]) {
      revert SignatureWasUsed();
    }
    _;
  }

  /**
   * @notice Fails if signature is invalid.
   * @dev Verifies signature was signed by expected signer.
   * @param messageHash Message hash.
   * @param signature Message signature.
   */
  function validateAndSaveSignature(
    bytes32 messageHash,
    bytes calldata signature
  ) internal onlyUnusedSignature(signature) {
    bool isValid = ISignatureVerifier(signatureVerifier).verifySignature(
      expectedSigner,
      messageHash,
      signature
    );

    if (!isValid) {
      revert InvalidSignature();
    }

    signatureUsed[signature] = true;
  }

  /**
   * @dev Updates expected signer of signatures.
   * @param signer Signer address.
   */
  function _updateExpectedSigner(address signer) internal onlyNonZeroAddress(signer) {
    expectedSigner = signer;
  }

  /**
   * @dev Updates signature verifier contract address.
   * @param signatureVerifierAddr SignatureVerifier contract address.
   */
  function _updateSignatureVerifier(
    address signatureVerifierAddr
  ) internal onlyNonZeroAddress(signatureVerifierAddr) {
    signatureVerifier = signatureVerifierAddr;
  }

  /**
   * @notice Fails if address is zero.
   * @dev Checks if address is not zero.
   * @param addr Address to be checked.
   */
  function _addressShouldNotBeZero(address addr) internal pure {
    if (addr == address(0)) {
      revert ZeroAddress();
    }
  }
}

Settings
{
  "viaIR": true,
  "optimizer": {
    "enabled": true,
    "runs": 200,
    "details": {
      "yulDetails": {
        "optimizerSteps": "u"
      }
    }
  },
  "evmVersion": "paris",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"uint256","name":"profileId","type":"uint256"},{"internalType":"address","name":"user","type":"address"}],"name":"AddressAlreadyInvited","type":"error"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"},{"internalType":"string","name":"message","type":"string"}],"name":"AddressAuthorization","type":"error"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"AddressCompromised","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[],"name":"AddressNotInvited","type":"error"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"name":"ERC1967InvalidImplementation","type":"error"},{"inputs":[],"name":"ERC1967NonPayable","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"FailedCall","type":"error"},{"inputs":[],"name":"IndexOutOfBounds","type":"error"},{"inputs":[{"internalType":"uint256","name":"profileId","type":"uint256"}],"name":"InsufficientInvites","type":"error"},{"inputs":[],"name":"InvalidIndex","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidSender","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"profileId","type":"uint256"}],"name":"MaxAddressesReached","type":"error"},{"inputs":[{"internalType":"uint256","name":"profileId","type":"uint256"}],"name":"MaxInvitesReached","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"uint256","name":"profileId","type":"uint256"},{"internalType":"string","name":"message","type":"string"}],"name":"ProfileAccess","type":"error"},{"inputs":[{"internalType":"uint256","name":"profileId","type":"uint256"}],"name":"ProfileExists","type":"error"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"}],"name":"ProfileExistsForAddress","type":"error"},{"inputs":[{"internalType":"uint256","name":"profileId","type":"uint256"}],"name":"ProfileNotFound","type":"error"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"}],"name":"ProfileNotFoundForAddress","type":"error"},{"inputs":[],"name":"SignatureWasUsed","type":"error"},{"inputs":[],"name":"UUPSUnauthorizedCallContext","type":"error"},{"inputs":[{"internalType":"bytes32","name":"slot","type":"bytes32"}],"name":"UUPSUnsupportedProxiableUUID","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"profileId","type":"uint256"},{"indexed":true,"internalType":"address","name":"addr","type":"address"},{"indexed":true,"internalType":"enum EthosProfile.AddressClaimStatus","name":"claim","type":"uint8"}],"name":"AddressClaim","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"defaultInvites","type":"uint256"}],"name":"DefaultInvitesChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"profileId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"InvitesAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"mockId","type":"uint256"}],"name":"MockProfileCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"profileId","type":"uint256"}],"name":"ProfileArchived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"profileId","type":"uint256"},{"indexed":true,"internalType":"address","name":"addr","type":"address"}],"name":"ProfileCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"profileId","type":"uint256"}],"name":"ProfileRestored","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"inviterId","type":"uint256"},{"indexed":false,"internalType":"address","name":"inviterAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"remainingInvites","type":"uint256"},{"indexed":false,"internalType":"address","name":"uninvitedUser","type":"address"}],"name":"Uninvited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"inviterID","type":"uint256"},{"indexed":false,"internalType":"address","name":"inviteeAddress","type":"address"}],"name":"UserInvited","type":"event"},{"inputs":[],"name":"ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OWNER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UPGRADE_INTERFACE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"admin","type":"address"}],"name":"addAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"addInvites","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"users","type":"address[]"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"addInvitesBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addressStr","type":"address"},{"internalType":"uint256","name":"profileId","type":"uint256"}],"name":"addressBelongsToProfile","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"profileId","type":"uint256"}],"name":"addressesForProfile","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"archiveProfile","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"attestationHash","type":"bytes32"},{"internalType":"uint256","name":"profileId","type":"uint256"}],"name":"assignExistingProfileToAttestation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"invitees","type":"address[]"}],"name":"bulkInviteAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"contractAddressManager","outputs":[{"internalType":"contract IContractAddressManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"inviterId","type":"uint256"}],"name":"createProfile","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"defaultNumberOfInvites","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addressStr","type":"address"},{"internalType":"bool","name":"markAsCompromised","type":"bool"}],"name":"deleteAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"addressIndex","type":"uint256"},{"internalType":"bool","name":"markAsCompromised","type":"bool"}],"name":"deleteAddressAtIndex","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"expectedSigner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getProfile","outputs":[{"components":[{"internalType":"bool","name":"archived","type":"bool"},{"internalType":"uint256","name":"profileId","type":"uint256"},{"internalType":"uint256","name":"createdAt","type":"uint256"},{"internalType":"address[]","name":"addresses","type":"address[]"},{"components":[{"internalType":"address[]","name":"sent","type":"address[]"},{"internalType":"uint256[]","name":"acceptedIds","type":"uint256[]"},{"internalType":"uint256","name":"available","type":"uint256"},{"internalType":"uint256","name":"invitedBy","type":"uint256"}],"internalType":"struct IEthosProfile.InviteInfo","name":"inviteInfo","type":"tuple"}],"internalType":"struct IEthosProfile.Profile","name":"profile","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMembers","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"isAttestation","type":"bool"},{"internalType":"address","name":"subject","type":"address"},{"internalType":"bytes32","name":"attestation","type":"bytes32"}],"name":"incrementProfileCount","outputs":[{"internalType":"uint256","name":"profileId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"address","name":"expectedSigner","type":"address"},{"internalType":"address","name":"signatureVerifier","type":"address"},{"internalType":"address","name":"contractAddressManagerAddr","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"invitee","type":"address"}],"name":"inviteAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"profileId","type":"uint256"}],"name":"inviteInfoForProfileId","outputs":[{"components":[{"internalType":"address[]","name":"sent","type":"address[]"},{"internalType":"uint256[]","name":"acceptedIds","type":"uint256[]"},{"internalType":"uint256","name":"available","type":"uint256"},{"internalType":"uint256","name":"invitedBy","type":"uint256"}],"internalType":"struct IEthosProfile.InviteInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"profileId","type":"uint256"}],"name":"invitedIdsForProfile","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isAddressCompromised","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxNumberOfAddresses","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxNumberOfInvites","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"profileCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"profileId","type":"uint256"}],"name":"profileExistsAndArchivedForId","outputs":[{"internalType":"bool","name":"verified","type":"bool"},{"internalType":"bool","name":"archived","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"profileIdByAddress","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"profileIdByAttestation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addressStr","type":"address"}],"name":"profileStatusByAddress","outputs":[{"internalType":"bool","name":"verified","type":"bool"},{"internalType":"bool","name":"archived","type":"bool"},{"internalType":"bool","name":"mock","type":"bool"},{"internalType":"uint256","name":"profileId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"profileId","type":"uint256"}],"name":"profileStatusById","outputs":[{"internalType":"bool","name":"verified","type":"bool"},{"internalType":"bool","name":"archived","type":"bool"},{"internalType":"bool","name":"mock","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addressStr","type":"address"},{"internalType":"uint256","name":"profileId","type":"uint256"},{"internalType":"uint256","name":"randValue","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"registerAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"admin","type":"address"}],"name":"removeAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addressStr","type":"address"}],"name":"restoreCompromisedAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"restoreProfile","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"sentAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"profileId","type":"uint256"}],"name":"sentInvitationsForProfile","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"defaultInvites","type":"uint256"}],"name":"setDefaultNumberOfInvites","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxAddresses","type":"uint256"}],"name":"setMaxAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxInvites","type":"uint256"}],"name":"setMaxInvites","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"signatureUsed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"signatureVerifier","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"targetId","type":"uint256"}],"name":"targetExistsAndAllowedForId","outputs":[{"internalType":"bool","name":"exist","type":"bool"},{"internalType":"bool","name":"allowed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"uninviteUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"contractAddressesAddr","type":"address"}],"name":"updateContractAddressManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"signer","type":"address"}],"name":"updateExpectedSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"updateOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sinatureVerifier","type":"address"}],"name":"updateSignatureVerifier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"verifiedProfileIdForAddress","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

60a06040523461003257610011610037565b6040516147866101c98239608051818181613b880152613d34015261478690f35b600080fd5b61003f610049565b61004761011a565b565b610047610086565b61006590610068906001600160a01b031682565b90565b6001600160a01b031690565b61006590610051565b61006590610074565b61008e61009c565b6100973061007d565b608052565b61004761003f565b6100659060401c60ff1690565b61006590546100a4565b610065905b6001600160401b031690565b61006590546100bb565b610065906100c0906001600160401b031682565b906100fa610065610116926100d6565b82546001600160401b0319166001600160401b03919091161790565b9055565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610144816100b1565b6101b657610151816100cc565b6001600160401b0391908290811603610168575050565b816101977fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2936101b1936100ea565b604051918291826001600160401b03909116815260200190565b0390a1565b63f92ee8a960e01b6000908152600490fdfe6080604052600436101561001257600080fd5b60003560e01c806301ffc9a71461042257806305c3d5221461041d57806308473f64146104185780630db4a5bb146104135780630fa5a1d41461040e57806310c7080114610409578063123a9e87146104045780631459457a146103ff5780631785f53c146103fa5780631e95c11d146103f557806320d26736146103f0578063248a9ca3146103eb57806325d0b09e146103e65780632bdd87ff146103e15780632cb3e494146103dc5780632f2ff15d146103d757806333bc3ccf146103d257806334ca272c146103cd57806336568abe146103c85780633ba0a718146103c35780633ba836a2146103be5780633eb3ebd8146103b95780633f4ba83a146103b45780634a5b307d146103af5780634f1ef286146103aa57806352d1902d146103a55780635667981b146103a0578063570f85541461039b5780635c76577c146103965780635c975abb146103915780635d649cbe1461038c578063665de3cb14610387578063704802751461038257806375b238fc1461037d57806376f3da32146103785780637ca64266146103735780637dec74d71461036e5780638456cb5914610369578063880cdc311461036457806389a9132b1461035f5780638bcebdb81461035a5780639010d07c1461035557806391d1485414610350578063923160db1461034b578063a12c64b514610346578063a217fddf14610341578063a238f7b41461033c578063a3246ad314610337578063ad3cb1cc14610332578063ade9ae971461032d578063b07b7e7f14610328578063b8861c2314610323578063bb10c8291461031e578063bd7029fd14610319578063c33b84f314610314578063ca15c8731461030f578063d1a445851461030a578063d547741f14610305578063e01290a114610300578063e58378bb146102fb578063eed38b86146102f6578063ef43acef146102f1578063f08f4f64146102ec578063f275cda9146102e75763fde919f60361043a57611480565b611459565b611426565b6113a6565b611382565b61135b565b611342565b61130d565b6112f5565b6112da565b6112bf565b611293565b611255565b611191565b611176565b611143565b6110f7565b61102f565b611017565b610ffc565b610fc5565b610fac565b610f6d565b610f51565b610f38565b610ee7565b610e8f565b610e77565b610e5c565b610e1e565b610dd8565b610d92565b610d7a565b610d5f565b610d47565b610d2c565b610d14565b610cf8565b610ca2565b610c87565b610c73565b610b4b565b610afa565b610ad3565b610a63565b610a47565b6109b0565b610982565b61094f565b610936565b6108e5565b6108a3565b610887565b61086c565b610853565b610809565b6107a2565b610786565b61070e565b6106c9565b6106b1565b610698565b610623565b6105cc565b610469565b6001600160e01b031981165b0361043a57565b600080fd5b9050359061044c82610427565b565b9060208282031261043a576104629161043f565b90565b9052565b3461043a5761049661048461047f36600461044e565b61149b565b60405191829182901515815260200190565b0390f35b80610433565b9050359061044c8261049a565b9060208282031261043a57610462916104a0565b6001600160a01b031690565b0190565b906104f16104ea6104e0845190565b8084529260200190565b9260200190565b9060005b8181106105025750505090565b90919261052861052160019286516001600160a01b0316815260200190565b9460200190565b9291016104f5565b9061053f6104ea6104e0845190565b9060005b8181106105505750505090565b9091926105666105216001928651815260200190565b929101610543565b90610462906060806105a461059260808501600088015186820360008801526104d1565b60208701518582036020870152610530565b946105b460408201516040860152565b0151910152565b60208082526104629291019061056e565b3461043a576104966105e76105e23660046104ad565b61167d565b604051918291826105bb565b6001600160a01b038116610433565b9050359061044c826105f3565b9060208282031261043a5761046291610602565b3461043a5761063b61063636600461060f565b611704565b604051005b909182601f8301121561043a578135916001600160401b03831161043a57602001926020830284011161043a57565b9060208282031261043a5781356001600160401b03811161043a576106949201610640565b9091565b3461043a5761063b6106ab36600461066f565b90611794565b3461043a5761063b6106c43660046104ad565b611872565b3461043a5761063b6106dc3660046104ad565b6118df565b600091031261043a57565b610462916008021c81565b9061046291546106ec565b6104626000603d6106f7565b3461043a5761071e3660046106e1565b610496610729610702565b6040515b9182918290815260200190565b919060a08382031261043a576107508184610602565b9261075e8260208301610602565b9261046261076f8460408501610602565b93608061077f8260608701610602565b9401610602565b3461043a5761063b61079936600461073a565b93929092611b86565b3461043a5761063b6107b536600461060f565b611bc9565b906107c96104ea6104e0845190565b9060005b8181106107da5750505090565b9091926107f06105216001928651815260200190565b9291016107cd565b6020808252610462929101906107ba565b3461043a5761049661082461081f3660046104ad565b611bdb565b604051918291826107f8565b919060408382031261043a5761046290602061084c8286610602565b94016104a0565b3461043a5761063b610866366004610830565b90611d36565b3461043a576104966107296108823660046104ad565b611d40565b3461043a5761049661048461089d366004610830565b90611d6a565b3461043a5761063b6108b636600461060f565b611dee565b610462916008021c6001600160a01b031690565b9061046291546108bb565b6104626000806108cf565b3461043a576108f53660046106e1565b6104966109006108da565b604051918291826001600160a01b03909116815260200190565b919060408382031261043a5761046290602061077f82866104a0565b3461043a5761063b61094936600461091a565b90611e12565b3461043a5761063b6109623660046104ad565b611f7d565b901515815260408101929161044c916020905b019015159052565b3461043a5761099a6109953660046104ad565b611f86565b906104966109a760405190565b92839283610967565b3461043a5761063b6109c336600461091a565b90611fb3565b909182601f8301121561043a578135916001600160401b03831161043a57602001926001830284011161043a57565b9060808282031261043a57610a0d8183610602565b92610a1b82602085016104a0565b92610a2983604083016104a0565b9260608201356001600160401b03811161043a5761069492016109c9565b3461043a5761063b610a5a3660046109f8565b93929092612298565b3461043a57610a733660046106e1565b61063b612352565b90610a8a6104ea6104e0845190565b9060005b818110610a9b5750505090565b909192610aba61052160019286516001600160a01b0316815260200190565b929101610a8e565b602080825261046292910190610a7b565b3461043a57610496610aee610ae93660046104ad565b612363565b60405191829182610ac2565b3461043a57610b0a3660046106e1565b61063b6124f6565b610b4761044c94610b3e606094989795610b35608086019a600087019015159052565b15156020850152565b15156040830152565b0152565b3461043a57610496610b66610b6136600461060f565b6124fe565b90610b7394929460405190565b94859485610b12565b634e487b7160e01b600052604160045260246000fd5b90601f01601f191681019081106001600160401b03821117610bb357604052565b610b7c565b9061044c610bc560405190565b9283610b92565b6001600160401b038111610bb357602090601f01601f19160190565b90826000939282370152565b90929192610c09610c0482610bcc565b610bb8565b938185528183011161043a5761044c916020850190610be8565b9080601f8301121561043a5781602061046293359101610bf4565b91909160408184031261043a57610c558382610602565b9260208201356001600160401b03811161043a576104629201610c23565b61063b610c81366004610c3e565b90612545565b3461043a57610c973660046106e1565b610496610729612592565b3461043a5761063b610cb536600461060f565b6125c0565b801515610433565b9050359061044c82610cba565b909160608284031261043a57610462610ce88484610cc2565b93604061084c8260208701610602565b3461043a57610496610729610d0e366004610ccf565b916127c8565b3461043a5761063b610d2736600461060f565b6129a6565b3461043a57610d3c3660046106e1565b6104966104846129af565b3461043a57610d573660046106e1565b61063b612ace565b3461043a57610496610aee610d753660046104ad565b612ad6565b3461043a5761063b610d8d36600461060f565b612b20565b3461043a57610da23660046106e1565b610496600080516020614731833981519152610729565b9015158152901515602082015260608101929161044c9160409061097a565b3461043a57610496610df3610dee3660046104ad565b612b29565b60405191939193849384610db9565b919060408382031261043a5761046290602061084c82866104a0565b3461043a5761063b610e31366004610e02565b90612d3d565b905b600052602052604060002090565b6000610e5761046292603b610e37565b6106f7565b3461043a57610496610729610e723660046104ad565b610e47565b3461043a57610e873660046106e1565b61063b612daa565b3461043a5761063b610ea236600461060f565b612e02565b610462906104c1906001600160a01b031682565b61046290610ea7565b61046290610ebb565b90610e3990610ec4565b6000610e57610462926039610ecd565b3461043a57610496610729610efd36600461060f565b610ed7565b9160408383031261043a5782356001600160401b03811161043a5782610f2f602094610462938701610640565b949095016104a0565b3461043a5761063b610f4b366004610f02565b91612e6b565b3461043a57610496610900610f67366004610e02565b90612e76565b3461043a57610496610484610f8336600461091a565b90612eb7565b919060408382031261043a57610462906020610fa582866104a0565b9401610cc2565b3461043a5761063b610fbf366004610f89565b90612fd4565b3461043a5761099a610fd83660046104ad565b612fde565b6104626104626104629290565b6104626000610fdd565b610462610fea565b3461043a5761100c3660046106e1565b610496610729610ff4565b3461043a5761063b61102a36600461060f565b6131e7565b3461043a57610496610aee6110453660046104ad565b6131f0565b90611057610c0483610bcc565b918252565b611066600561104a565b640352e302e360dc1b602082015290565b61046261105c565b610462611077565b61046261107f565b60005b8381106110a25750506000910152565b8181015183820152602001611092565b6110d36110dc6020936104cd936110c7815190565b80835293849260200190565b9586910161108f565b601f01601f191690565b6020808252610462929101906110b2565b3461043a576111073660046106e1565b610496611112611087565b604051918291826110e6565b610462600060036108cf565b61046590610ec4565b60208101929161044c919061112a565b3461043a576111533660046106e1565b61049661115e61111e565b60405191829182611133565b6104626000603e6106f7565b3461043a576111863660046106e1565b61049661072961116a565b3461043a576104966107296111a736600461060f565b613207565b9060208282031261043a5781356001600160401b03811161043a576104629201610c23565b6104cd6111e9926020926111e3815190565b94859290565b9384910161108f565b6110576104cd91602094936111d1565b61121761120e60405190565b928392836111f2565b03902090565b61046291611202565b610462916008021c5b60ff1690565b906104629154611226565b600061125061046292600261121d565b611235565b3461043a5761049661048461126b3660046111ac565b611240565b90610e3990610fdd565b610e576104629261128e600093603c611270565b610ecd565b3461043a576104966107296112a936600461091a565b9061127a565b600061125061046292603a610ecd565b3461043a576104966104846112d536600461060f565b6112af565b3461043a576104966107296112f03660046104ad565b613269565b3461043a5761063b61130836600461060f565b6132b7565b3461043a5761063b61132036600461091a565b906132db565b919060408382031261043a57610462906020610fa58286610602565b3461043a5761063b611355366004611326565b9061331a565b3461043a5761136b3660046106e1565b6104966000805160206146f1833981519152610729565b3461043a5761063b6113953660046104ad565b61336b565b610462600060366106f7565b3461043a576113b63660046106e1565b61049661072961139a565b80511515825261046291608061140460a083016113e360208601516020860152565b6113f260408601516040860152565b606085015184820360608601526104d1565b92015190608081840391015261056e565b6020808252610462929101906113c1565b3461043a5761049661144161143c3660046104ad565b613420565b60405191829182611415565b610462600060376106f7565b3461043a576114693660046106e1565b61049661072961144d565b610462600060016108cf565b3461043a576114903660046106e1565b610496610900611474565b635a05180f60e01b6001600160e01b03198216149081156114ba575090565b6104629150613482565b6104626080610bb8565b6114d66114c4565b906060825260208080808501606081520160008152016000905250565b905250565b6104626114ce565b610462906104c1565b6104629054611500565b9061152e6115226104e0845490565b92600052602060002090565b9060005b81811061153f5750505090565b90919261156c61156560019261155487611509565b6001600160a01b0316815260200190565b9460010190565b929101611532565b9061046291611513565b9061044c6115989261158f60405190565b93848092611574565b0383610b92565b6104629081565b610462905461159f565b906115bf6115226104e0845490565b9060005b8181106115d05750505090565b9091926115ed6115656001926115e5876115a6565b815260200190565b9291016115c3565b90610462916115b0565b9061044c6115989261161060405190565b938480926115f5565b9061044c61166d600361162a6114c4565b9461163b6116378261157e565b8752565b61165161164a600183016115ff565b6020880152565b611667611660600283016115a6565b6040880152565b016115a6565b6060840152565b61046290611619565b60046116966104629261168e6114f8565b506038611270565b01611674565b61044c906116bc6000805160206147318339815191526134bc565b6134bc565b6116f1565b906001600160a01b03905b9181191691161790565b906116e66104626116ed92610ec4565b82546116c1565b9055565b6116fd61044c91610ec4565b60036116d6565b61044c9061169c565b9061044c9161171a6134c7565b611754565b634e487b7160e01b600052603260045260246000fd5b9190811015611745576020020190565b61171f565b35610462816105f3565b9190916117616000610fdd565b8381101561178e5780611783610d2761177e611789948887611735565b61174a565b60010190565b611761565b50509050565b9061044c9161170d565b61044c906117b96000805160206147318339815191526134bc565b61044c906117c56134c7565b611804565b90600019906116cc565b906117e46104626116ed92610fdd565b82546117ca565b61046590610fdd565b60208101929161044c91906117eb565b61180f8160376117d4565b61181c610462603e6115a6565b8111611855576118507f15aea1769167f83f5fc9ac1bedf10a18732f8a29fc4f74ba0b927418d12fa4ce9161072d60405190565b0390a1565b635a092a1d60e11b60009081528061186e8160046117f4565b0390fd5b61044c9061179e565b61044c906118966000805160206147318339815191526134bc565b61044c906118a26134c7565b6118ad81603d6117d4565b6118bf6118bb610800610fdd565b9190565b116118c657565b63b315b31d60e01b60009081528061186e8160046117f4565b61044c9061187b565b6104629060401c61122f565b61046290546118e8565b610462905b6001600160401b031690565b61046290546118fe565b6119036104626104629290565b906001600160401b03906116cc565b611903610462610462926001600160401b031690565b9061195b6104626116ed92611935565b8254611926565b9068ff00000000000000009060401b6116cc565b906119866104626116ed92151590565b8254611962565b61046590611919565b60208101929161044c919061198d565b92939091907ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009485926119e86119e26119de866118f4565b1590565b9461190f565b946000966119f588611919565b6001600160401b0388161480611aee575b600197611a22611a158a611919565b916001600160401b031690565b149081611aca575b155b9081611ac1575b50611ab25790611a609493929186611a578a611a4e8b611919565b9c019b8c61194b565b611aa357611af5565b611a6957505050565b611a97611850927fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d294611976565b60405191829182611996565b611aad888b611976565b611af5565b63f92ee8a960e01b8852600488fd5b15905038611a33565b9050611a2c611ad830610ec4565b3b611ae56118bb8c610fdd565b14919050611a2a565b5085611a06565b92611b059261044c9592856135ba565b611b0d6135cf565b611b22611b1b610800610fdd565b603e6117d4565b611b36611b2f6080610fdd565b603d6117d4565b60036004611b786001611b5c611b4b82610fdd565b95611b578760366117d4565b6135d7565b50611b7f611b6a600a610fdd565b600285611b78856038611270565b01016117d4565b6038611270565b9061044c949392916119a6565b61044c90611bae6000805160206146f18339815191526134bc565b611bc6906000805160206147318339815191526136d2565b50565b61044c90611b93565b610462906115ff565b60016004611bee6104629361168e606090565b0101611bd2565b61044c9190611c116000805160206147318339815191526134bc565b9061044c91611c1e6134c7565b611c67565b634e487b7160e01b600052601160045260246000fd5b9190611c44565b9290565b8201809211611c4f57565b611c23565b90815260408101929161044c9160200152565b9190611c7f611c75846124fe565b9390919215151590565b918215611d2e575b508115611d26575b50611d02577fac52b63ac21eb6a0502ba1afddeff39668a3e6c7ceb05367e6563c8bd14cd805929350611ce460026004611cca846038611270565b0101611cde84611cd9836115a6565b611c39565b906117d4565b611ced8161372b565b611850611cf960405190565b92839283611c54565b63529f6aeb60e11b60009081526001600160a01b038516600452602490fd5b036000fd5b905038611c8f565b915038611c87565b9061044c91611bf5565b600161166761046292611d51600090565b5060006000805160206147118339815191525b01610e37565b611d7d611d78826039610ecd565b6115a6565b600090611d8c6118bb83610fdd565b14611daa57506118bb611c40611d78611da6936039610ecd565b1490565b63529f6aeb60e11b81526001600160a01b03919091166004820152602490fd5b61044c90611de56000805160206147318339815191526134bc565b61044c9061377c565b61044c90611dca565b9061044c91611e086116b782611d40565b90611bc691613785565b9061044c91611df7565b61044c90611e286134c7565b611e40565b6104629061122f565b6104629054611e2d565b611e53611e4e33603a610ecd565b611e36565b611e605761044c90611ef4565b630ad2d8d560e31b600090815233600452602490fd5b805482101561174557611e90600191600052602060002090565b91020190600090565b9160001960089290920291821b911b6116cc565b921b90565b9190611ec36104626116ed93610fdd565b908354611e99565b90815491600160401b831015610bb35782611eee91600161044c95018155611e76565b90611eb2565b611f08611f0082612b29565b909290911590565b918215611f75575b508115611f6d575b50611f5b5761044c90611f2b33826137d4565b611f5660016004611f51611f3e336135d7565b94611b7f81600385611b788a6038611270565b010190565b611ecb565b636edaef2f60e11b6000908152600490fd5b905038611f18565b915038611f10565b61044c90611e1c565b611f8f90612b29565b8291939291611faa575b83611fa357509190565b9092501590565b80159150611f99565b90611fbd336104c1565b6001600160a01b03821603611fd557611bc6916136d2565b63334bd91960e11b6000908152600490fd5b9061044c94939291611ff76134c7565b9061044c94939291612008816138a1565b949392919061201b611e4e87603a610ecd565b6120295761044c94956120db565b630ad2d8d560e31b60009081526001600160a01b038716600452602490fd5b916001600160a01b0360089290920291821b911b6116cc565b91906120726104626116ed93610ec4565b908354612048565b90815491600160401b831015610bb3578261209d91600161044c95018155611e76565b90612061565b634e487b7160e01b600052602160045260246000fd5b600211156120c357565b6120a3565b9061044c826120b9565b610462906120c8565b93919290926120ee611d78336039610ecd565b84900361228257612101611f0085612b29565b61226b578115612263575b506122115761211a856124fe565b91505081612206575b506121e7576121a692849261213f612144936121ac97896138ec565b613ac9565b61217061215c6003612157846038611270565b015490565b61216b8661128e856041611270565b6117d4565b6121898461218460036104cd856038611270565b61207a565b6121988161216b866039610ecd565b6121a181613ad4565b610fdd565b91610ec4565b6121b660016120d2565b917fcdc318b415c4f74ecd1a3bbaf637fe42594b23d3e706bdea4031084fed4f6c736121e160405190565b600090a4565b639e83c77d60e01b60009081526001600160a01b038616600452602490fd5b859150141538612123565b61186e8461221e60405190565b630742992760e11b81529182916004830190815260406020820181905260139082015272141c9bd99a5b19481a5cc8185c98da1a5d9959606a1b606082015260800190565b90503861210c565b635595b91560e11b60009081526004879052602490fd5b63529f6aeb60e11b600090815233600452602490fd5b9061044c94939291611fe7565b6122ad6134c7565b61044c6122d5565b9060ff906116cc565b906122ce6104626116ed92151590565b82546122b5565b6122e1611c75336124fe565b61228257811561234a575b5061233d57806121a16001600061230761230d956038611270565b016122be565b7fe2a74f634d4a5de5ebf5fc6985a469779d12830dd517ce1a08acb22847cdc3ee61233760405190565b600090a2565b61186e9061221e60405190565b9050386122ec565b61044c6122a5565b6104629061157e565b600060046123766104629361168e606090565b010161235a565b612387601961104a565b7f4554484f535f494e544552414354494f4e5f434f4e54524f4c00000000000000602082015290565b61046261237d565b6104626123b0565b9050519061044c826105f3565b9060208282031261043a57610462916123c0565b6040513d6000823e3d90fd5b6001600160a01b03909116815260408101929161044c9160200152565b61244e602061242161241c6003611509565b610ec4565b6124296123b8565b9061243360405190565b9384928391829163d57f7aa360e01b5b8352600483016110e6565b03915afa9081156124e9576000916124ba575b50612474335b916001600160a01b031690565b036124815761044c6124ee565b63e2517d3f60e01b6000908152611d217f261fea28325784de45eba41a3fbb84c4196fbab02ee802566edfe26616ba5afb3360046123ed565b6124dc915060203d6020116124e2575b6124d48183610b92565b8101906123cd565b38612461565b503d6124ca565b6123e1565b61044c613b75565b61044c61240a565b611d786125159161250d600090565b506039610ecd565b61251e81612b29565b9193909290565b9061044c91612532613b7d565b9061044c9161254081613c25565b613c4f565b9061044c91612525565b6104629061255b613d1e565b612589565b6104627f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc610fdd565b50610462612560565b610462600061254f565b61044c906125b76000805160206147318339815191526134bc565b61044c90613d78565b61044c9061259c565b906104629392916125d86134c7565b612655565b6125e7600c61104a565b6b4554484f535f52455649455760a01b602082015290565b6104626125dd565b6104626125ff565b612619601161104a565b7022aa2427a9afa0aa2a22a9aa20aa24a7a760791b602082015290565b61046261260f565b610462612636565b6000198114611c4f5760010190565b5092919061266661241c6003611509565b612692602063d57f7aa392612679612607565b9061268360405190565b80809581946124438960e01b90565b03915afa80156124e9576126b5916000916127a9575b506001600160a01b031690565b3314159081612747575b50611f5b57612708916126d260366115a6565b80928196600014612732575061216b6126ec92603b610e37565b6121a16127016126fc60366115a6565b612646565b60366117d4565b7f3fd0c2393f291765470cc17997e368eb7ec635d8966232abd9d2016e8ad721cf61233760405190565b612742925061216b906039610ecd565b6126ec565b61277e915060209061275c61241c6003611509565b61244361276761263e565b9261277160405190565b9586948593849360e01b90565b03915afa80156124e9576127a0916000916127a957506001600160a01b031690565b331415386126bf565b6127c2915060203d6020116124e2576124d48183610b92565b386126a8565b61046292919060006125c9565b61044c906127e16134c7565b61044c906127ee816138a1565b6127fc611e4e82603a610ecd565b6128095761044c90612858565b630ad2d8d560e31b60009081526001600160a01b039091166004526024036000fd5b8015611c4f576000190190565b90815260408101929161044c916020905b01906001600160a01b03169052565b61286d612864336124fe565b50909290911590565b91821561299e575b508115612996575b50611f5b5761288b816124fe565b5090508161298d575b5061296b577f9c6d622696d829eae44c9559a6e96ae07091e9ca797bc02c35a7770dbcf04580906128c9611d78336039610ecd565b6128d281613d81565b6128dc8282613dc7565b61293d6002600461292a8460386129116129026000866128fc8686611270565b01015490565b61216b8a61128e86603f611270565b61292588612184600087611f518787611270565b611270565b0101611cde612938826115a6565b61282b565b61295661294f8361128e84603c611270565b42906117d4565b61185061296260405190565b92839283612838565b639e83c77d60e01b60009081526001600160a01b039091166004526024036000fd5b15905038612894565b90503861287d565b915038612875565b61044c906127d5565b610462613e0d565b6129bf6134c7565b61044c612a00565b919060086116cc910291611ead60ff841b90565b91906129ec6104626116ed93151590565b9083546129c7565b61044c916000916129db565b612a13612a0c336124fe565b9390921590565b6122825715908115612ac6575b50612a6a57806121a1600080612a3a612a40956038611270565b016129f4565b7fad0c07c6e15db6a51fed6c1d42d277ed0fa53f2eef0d1830c060c1cba8638adc61233760405190565b61186e90612a7760405190565b630742992760e11b8152918291600483019081526040602082018190526017908201527f50726f66696c65206973206e6f74206172636869766564000000000000000000606082015260800190565b905038612a20565b61044c6129b7565b6003612ae76104629261168e606090565b0161235a565b61044c90612b086000805160206146f18339815191526134bc565b611bc690600080516020614731833981519152613785565b61044c90612aed565b612b37610462826038611270565b91612b44600184016115a6565b612b516118bb6000610fdd565b1180809481612b99575b5092612b676000610fdd565b81119182612b90575b5081612b7a575090565b9050612b8c6118bb61046260366115a6565b1090565b15915038612b70565b612ba39150611e36565b38612b5b565b90612bc46020612bbc61241c6003611509565b61242961263e565b03915afa80156124e957612be6916000916127a957506001600160a01b031690565b3303611f5b5761044c919061044c91612bfd6134c7565b612c18565b6104c16104626104629290565b61046290612c02565b90612c2661241c6003611509565b6020612c3160405190565b63d57f7aa360e01b815260206004820152600b60248201526a08aa8909ea6beac9eaa86960ab1b604482015291829060649082905afa9081156124e957600091612d1e575b50612c846104c16000612c0f565b6001600160a01b03821603612ca3575b5061216b61044c92603b610e37565b61241c612caf91610ec4565b91823b1561043a576000612cc260405190565b93849063987088f160e01b8252818381612ce0888860048401611c54565b03925af19081156124e95761044c9361216b92612d00575b509250612c94565b612d18906000612d108183610b92565b8101906106e1565b38612cf8565b612d37915060203d6020116124e2576124d48183610b92565b38612c76565b9061044c91612ba9565b612d59602061242161241c6003611509565b03915afa9081156124e957600091612d83575b50612d7633612467565b036124815761044c612da2565b612d9c915060203d6020116124e2576124d48183610b92565b38612d6c565b61044c613e98565b61044c612d47565b61044c90612dcd6000805160206146f18339815191526134bc565b611bc6906000805160206146f1833981519152612dfc612df6612df06000610fdd565b83612e76565b826136d2565b50613785565b61044c90612db2565b61044c929190612e286000805160206147318339815191526134bc565b9061044c9291612e366134c7565b91929160005b82811015612e64578061178386612e5a61177e612e5f958888611735565b611d36565b612e3c565b5050509050565b9061044c9291612e0b565b90612eb261046261046293612e89600090565b5060007fc1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000611d64565b613ea9565b610462916000612ee2611e4e93612ecc600090565b5082600080516020614711833981519152611d64565b01610ecd565b9061044c91612ef56134c7565b612f06565b61044c91600091611eb2565b612f14611d78336039610ecd565b612f20611f0082612b29565b612282578115612fcc575b5061233d57612f4060036104cd836038611270565b90612f49825490565b831015612fba57612f818284612f6e612f68612f96976121a697611e76565b906108cf565b96612f7888613edd565b612fa057613f4d565b6121a16000612f91866039610ecd565b612efa565b6121b660006120d2565b612fb56001612fb089603a610ecd565b6122be565b613f4d565b6363df817160e01b6000908152600490fd5b905038612f2b565b9061044c91612ee8565b6002611667610462612ff29361168e600090565b612fff6118bb6000610fdd565b11908190565b61044c906130116134c7565b61044c9061301e816138a1565b613066565b91908203918211611c4f57565b61284961044c9461305f60609498979561304f608086019a6000870152565b6001600160a01b03166020850152565b6040830152565b613074611d78336039610ecd565b90613083610462836038611270565b9161308d83611e36565b61233d57603f60046130a6611d788561128e8686611270565b940180549094859181101580156131c0575b6131ae577fc99deea7564c4617f54b6c22cd96a31e991cc2f5524a5d19919cbeef7c4d2c639561314f6000612f918861128e89611850998960029961312e613128613124612f686131819f61310e61311e915490565b6131186001610fdd565b90613023565b86611e76565b9390565b8261401c565b6001600160a01b0386166001600160a01b03831603613196575b5050611270565b0161316561315f6126fc836115a6565b826117d4565b611d786131726000610fdd565b61216b8761128e88603c611270565b9261318b60405190565b938493339085613030565b61216b6131a79261128e8686611270565b3880613148565b63914f33db60e01b6000908152600490fd5b506131ce612f688284611e76565b6131e06001600160a01b038716612467565b14156130b8565b61044c90613005565b61320261046261046292612e89606090565b614085565b61321e90613214816124fe565b9490919215151590565b918215613261575b508115613259575b50613237575090565b63529f6aeb60e11b60009081526001600160a01b039091166004526024036000fd5b90503861322e565b915038613226565b61327b61046261046292612e89600090565b614096565b61044c9061329b6000805160206147318339815191526134bc565b61044c906132a76134c7565b6000612fb061044c92603a610ecd565b61044c90613280565b9061044c916132d16116b782611d40565b90611bc6916136d2565b9061044c916132c0565b9061044c916132f26134c7565b90613315611d7861044c9361128e61330e611d78336039610ecd565b6041611270565b612fd4565b9061044c916132e5565b61044c9061333f6000805160206147318339815191526134bc565b61044c9061334b6134c7565b61335681603e6117d4565b6133646118bb610800610fdd565b1161185557565b61044c90613324565b61046260a0610bb8565b613386613374565b906000825260208080808086016000815201600081520160608152016114f36114f8565b61046261337e565b9061044c61341060046133c3613374565b946133d66133d082611e36565b15158752565b6133e561164a600183016115a6565b6133f4611660600283016115a6565b61340a6134036003830161157e565b6060880152565b01611619565b6080840152565b610462906133b2565b6134286133aa565b50600061343481610fdd565b8214801561346c575b6134555750613450610462916038611270565b613417565b635595b91560e11b81526004810191909152602490fd5b5061347a61046260366115a6565b82101561343d565b637965db0b60e01b6001600160e01b03198216149081156134a1575090565b61046291506001600160e01b0319166301ffc9a760e01b1490565b61044c9033906140ac565b6134cf6129af565b6134d557565b63d93c066560e01b6000908152600490fd5b9061044c949392916134f76140db565b92909391600061350681612c0f565b6001600160a01b0381166001600160a01b0387161490811561359d575b8115613581575b5061357257506116fd61355d9361241c611bc69796946135499461411d565b6000805160206146f1833981519152613785565b50600080516020614731833981519152613785565b63d92e233d60e01b8152600490fd5b6001600160a01b031690506001600160a01b038516143861352a565b90506001600160a01b0381166001600160a01b0388161490613523565b9061044c949392916134e7565b61044c6140db565b61044c6135c7565b906135e1826124fe565b9492909150156136025763b62e454560e01b60009081526004859052602490fd5b91929091156136b157905b61361c8261216b836039610ecd565b6136738161218460036104cd86603861364182600161363b8285611270565b016117d4565b61365861364e8383611270565b60024291016117d4565b61292561366560376115a6565b60026004611b788686611270565b61367f6121a683610fdd565b907f9f150021d8fd04eb219a39e3019273bddf99a9a2e434dd2aa8132e32ea9244b06136aa60405190565b600090a390565b506136bc60366115a6565b906136cd6127016126fc60366115a6565b61360d565b907fc1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000906136ff8184614127565b928361370c575b50505090565b6137229261371d9161046291610e37565b6141bd565b50388080613706565b613734816141ea565b6137446118bb610462603e6115a6565b1161374c5750565b635a092a1d60e11b6000908152600491909152602490fd5b61044c90613771816138a1565b61044c9060006116d6565b61044c90613764565b907fc1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000906137b2818461422a565b92836137be5750505090565b613722926137cf9161046291610e37565b61429b565b906137e3610462836038611270565b603f91600060046137fb611d788461128e8989611270565b9301019161380a610462845490565b811015801561387a575b6131ae5761044c9460009461292561128e9361386387613844612f6861383e61310e612f919c5490565b83611e76565b836001600160a01b038a166001600160a01b0383160361386957505090565b9061401c565b61216b6104629261128e8a8a611270565b50613888612f688285611e76565b61389a6001600160a01b038416612467565b1415613814565b6000906138b36124676104c184612c0f565b146135725750565b610465906001600160a01b031660601b90565b602093926138e66014836138e688956104cd976138bb565b01918252565b613917919061390b6138fd60405190565b9485936020850193846138ce565b90810382520382610b92565b613929613922825190565b9160200190565b2090565b90916104cd9083908093610be8565b61105790602094936104cd9361392d565b90916112179061395c60405190565b9384938461393c565b90916104629261394d565b9190613981611e4e83836002613965565b61398e5761044c92613a18565b63858c8a1f60e01b6000908152600490fd5b9050519061044c82610cba565b9060208282031261043a57610462916139a0565b91906110dc816139d8816104cd9560209181520190565b8095610be8565b613a0b6104629593949294613a04606084019660008501906001600160a01b03169052565b6020830152565b60408185039101526139c1565b91602082829394613a2f61241c61241c6001611509565b90613a3a6000611509565b613a5f613a4660405190565b96879586948594622ea33360e31b8652600486016139df565b03915afa80156124e957613a7991600091613aa057501590565b613a8e57612fb060019161044c936002613965565b638baa579f60e01b6000908152600490fd5b6119de915060203d602011613ac2575b613aba8183610b92565b8101906139ad565b503d613ab0565b9061044c9291613970565b613ae46003612157836038611270565b613af46118bb610462603d6115a6565b11613afc5750565b63b315b31d60e01b6000908152600491909152602490fd5b613b1c6142bb565b61044c613b4b6000807fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300612307565b7f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa61185033610900565b61044c613b14565b613b8630610ec4565b7f000000000000000000000000000000000000000000000000000000000000000090613bba6001600160a01b038316612467565b14908115613bdc575b50613bca57565b63703e46dd60e11b6000908152600490fd5b9050613bf9612467613bec6142de565b926001600160a01b031690565b141538613bc3565b61044c90613c1c6000805160206146f18339815191526134bc565b61044c906138a1565b61044c90613c01565b9050519061044c8261049a565b9060208282031261043a5761046291613c2e565b90613c5c61241c83610ec4565b906020613c6860405190565b6352d1902d60e01b815292839060049082905afa60009281613ced575b50613cb75750506001613c955750565b634c9c8ce360e01b60009081526001600160a01b039091166004526024036000fd5b909291613cc5610462612560565b8403613cd65761044c9293506142f4565b632a87526960e21b60009081526004859052602490fd5b613d1091935060203d602011613d17575b613d088183610b92565b810190613c3b565b9138613c85565b503d613cfe565b613d2730610ec4565b613d596001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016612467565b03613bca57565b61044c90613d6d816138a1565b61044c9060016116d6565b61044c90613d60565b613d9a60026004613d93846038611270565b01016115a6565b90600091613daa6118bb84610fdd565b14613db3575050565b6314ac648360e21b82526004820152602490fd5b90613dda611d788261128e85603c611270565b91600092613dea6118bb85610fdd565b03613df457505050565b63330b0ff560e01b8352829161186e9160048401612838565b6104627fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300611e36565b613e3e6134c7565b61044c613e6e600160007fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300612307565b7f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25861185033610900565b61044c613e36565b61046290610fdd565b613ed3613ece61046293613ec96000613ed895613ec4600090565b500190565b61435d565b613ea0565b612c02565b610ebb565b336001600160a01b03821614613ef05750565b61186e90613efd60405190565b6366d2949d60e01b81526001600160a01b03909116600482015260406024820152601560448201527420b2323932b9b9901e9e9036b9b39739b2b73232b960591b60648201529081906084820190565b612f9160009161044c93613f64612f688383611e76565b91613f86613f7f612f68613f7961310e865490565b85611e76565b928261401c565b6001600160a01b0383166001600160a01b03831603613fb3575b505061128e61330e611d78336039610ecd565b61216b613fcb9261128e61330e611d78336039610ecd565b3880613fa0565b634e487b7160e01b600052603160045260246000fd5b61044c91600091612061565b8054801561401757600019019061401461400e8383611e76565b90613fe8565b55565b613fd2565b90614028610462825490565b8210156140735761044c9161404161046261310e845490565b810361404e575b50613ff4565b61406d9061209d614066612f6861311e61310e875490565b9184611e76565b38614048565b634e23d03560e01b6000908152600490fd5b606090614091906143e6565b905090565b6140a7600061046292613ec4600090565b6143fb565b906140ba6119de8284612eb7565b6140c2575050565b63e2517d3f60e01b600090815291611d219160046123ed565b6140e66119de61440f565b6140ec57565b631afcd79f60e31b6000908152600490fd5b9061044c9161410b6140db565b9061411861044c9261377c565b613d78565b9061044c916140fe565b6000805160206147118339815191526141408383612eb7565b156141a3576000612fb08482612ee2868261415b9701610e37565b61416f6121a6614169339390565b93610ec4565b917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b61419a60405190565b600090a4600190565b505050600090565b6104629081906001600160a01b031681565b906141e56118bb6121a16141e06000610462966141d8600090565b500194610ebb565b6141ab565b614473565b6104629061421e600160046128fc60389461422461420e600285613d93858b611270565b61421e6000866128fc868c611270565b90611c39565b95611270565b6000805160206147118339815191526142466119de8484612eb7565b156141a3576001612fb0846000612ee286826142629701610e37565b6142706121a6614169339390565b917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d61419a60405190565b906142b66118bb6121a16141e06000610462966141d8600090565b614542565b6142c66119de6129af565b6142cc57565b638dfc202b60e01b6000908152600490fd5b61046260006142ee610462612560565b01611509565b906142fe82614583565b61430782610ec4565b7fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b61433160405190565b600090a280516143446118bb6000610fdd565b111561435357611bc691614610565b505061044c6145ce565b6104629160006143769261436f600090565b5001611e76565b906106f7565b9061438b6115226104e0845490565b9060005b81811061439c5750505090565b9091926143b16115656001926115e5876115a6565b92910161438f565b906104629161437c565b9061044c611598926143d460405190565b938480926143b9565b610462906143c3565b6000610462916143f4606090565b50016143dd565b600061046291614409600090565b50015490565b6104627ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006118f4565b9190611ec36104626116ed9390565b61044c91600091614438565b8054801561401757600019019061401461446d8383611e76565b90614447565b90614484611d788260018501610e37565b61448e6000610fdd565b81146141a357612f91610462926000926144e195611d6460019788936144bc6144b686610fdd565b82613023565b888501916144d46144cb845490565b61311889610fdd565b8083036144e65750505090565b614453565b61450961450f6145179461450061437661216b9589611e76565b92839188611e76565b90614438565b888801610e37565b388080613706565b90815491600160401b831015610bb3578261450991600161044c95018155611e76565b61454f6119de8383614637565b1561457c576145779161216b9060016145708461456c848261451f565b5490565b9301610e37565b600190565b5050600090565b6000813b6145936118bb83610fdd565b146145ae579061044c916145a8610462612560565b016116d6565b634c9c8ce360e01b81526001600160a01b03919091166004820152602490fd5b60006145d981610fdd565b34116145e25750565b63b398979f60e01b8152600490fd5b3d1561460b576146003d61104a565b903d6000602084013e565b606090565b6000806104629361461f606090565b50602081519101845af46146316145f1565b91614662565b614650916001611d7892614649600090565b5001610e37565b61465d6118bb6000610fdd565b141590565b9061466d57506146c1565b81519060009161467f6118bb84610fdd565b14806146ac575b61468f57505090565b639996b31560e01b82526001600160a01b03166004820152602490fd5b50803b6146bb6118bb84610fdd565b14614686565b80516000906146d26118bb83610fdd565b11156146e15750805190602001fd5b63d6bda27560e01b8152600490fdfeb19546dff01e856fb3f010c267a7b1c60363cf8a4664e21cc89c26224620214e02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800a49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775a26469706673582212205fe48a9c7be93ce6d6d27cc0adede0c3ccb52b1cc97d9e2513ae40d3070d2d3764736f6c634300081a0033

Deployed Bytecode

0x6080604052600436101561001257600080fd5b60003560e01c806301ffc9a71461042257806305c3d5221461041d57806308473f64146104185780630db4a5bb146104135780630fa5a1d41461040e57806310c7080114610409578063123a9e87146104045780631459457a146103ff5780631785f53c146103fa5780631e95c11d146103f557806320d26736146103f0578063248a9ca3146103eb57806325d0b09e146103e65780632bdd87ff146103e15780632cb3e494146103dc5780632f2ff15d146103d757806333bc3ccf146103d257806334ca272c146103cd57806336568abe146103c85780633ba0a718146103c35780633ba836a2146103be5780633eb3ebd8146103b95780633f4ba83a146103b45780634a5b307d146103af5780634f1ef286146103aa57806352d1902d146103a55780635667981b146103a0578063570f85541461039b5780635c76577c146103965780635c975abb146103915780635d649cbe1461038c578063665de3cb14610387578063704802751461038257806375b238fc1461037d57806376f3da32146103785780637ca64266146103735780637dec74d71461036e5780638456cb5914610369578063880cdc311461036457806389a9132b1461035f5780638bcebdb81461035a5780639010d07c1461035557806391d1485414610350578063923160db1461034b578063a12c64b514610346578063a217fddf14610341578063a238f7b41461033c578063a3246ad314610337578063ad3cb1cc14610332578063ade9ae971461032d578063b07b7e7f14610328578063b8861c2314610323578063bb10c8291461031e578063bd7029fd14610319578063c33b84f314610314578063ca15c8731461030f578063d1a445851461030a578063d547741f14610305578063e01290a114610300578063e58378bb146102fb578063eed38b86146102f6578063ef43acef146102f1578063f08f4f64146102ec578063f275cda9146102e75763fde919f60361043a57611480565b611459565b611426565b6113a6565b611382565b61135b565b611342565b61130d565b6112f5565b6112da565b6112bf565b611293565b611255565b611191565b611176565b611143565b6110f7565b61102f565b611017565b610ffc565b610fc5565b610fac565b610f6d565b610f51565b610f38565b610ee7565b610e8f565b610e77565b610e5c565b610e1e565b610dd8565b610d92565b610d7a565b610d5f565b610d47565b610d2c565b610d14565b610cf8565b610ca2565b610c87565b610c73565b610b4b565b610afa565b610ad3565b610a63565b610a47565b6109b0565b610982565b61094f565b610936565b6108e5565b6108a3565b610887565b61086c565b610853565b610809565b6107a2565b610786565b61070e565b6106c9565b6106b1565b610698565b610623565b6105cc565b610469565b6001600160e01b031981165b0361043a57565b600080fd5b9050359061044c82610427565b565b9060208282031261043a576104629161043f565b90565b9052565b3461043a5761049661048461047f36600461044e565b61149b565b60405191829182901515815260200190565b0390f35b80610433565b9050359061044c8261049a565b9060208282031261043a57610462916104a0565b6001600160a01b031690565b0190565b906104f16104ea6104e0845190565b8084529260200190565b9260200190565b9060005b8181106105025750505090565b90919261052861052160019286516001600160a01b0316815260200190565b9460200190565b9291016104f5565b9061053f6104ea6104e0845190565b9060005b8181106105505750505090565b9091926105666105216001928651815260200190565b929101610543565b90610462906060806105a461059260808501600088015186820360008801526104d1565b60208701518582036020870152610530565b946105b460408201516040860152565b0151910152565b60208082526104629291019061056e565b3461043a576104966105e76105e23660046104ad565b61167d565b604051918291826105bb565b6001600160a01b038116610433565b9050359061044c826105f3565b9060208282031261043a5761046291610602565b3461043a5761063b61063636600461060f565b611704565b604051005b909182601f8301121561043a578135916001600160401b03831161043a57602001926020830284011161043a57565b9060208282031261043a5781356001600160401b03811161043a576106949201610640565b9091565b3461043a5761063b6106ab36600461066f565b90611794565b3461043a5761063b6106c43660046104ad565b611872565b3461043a5761063b6106dc3660046104ad565b6118df565b600091031261043a57565b610462916008021c81565b9061046291546106ec565b6104626000603d6106f7565b3461043a5761071e3660046106e1565b610496610729610702565b6040515b9182918290815260200190565b919060a08382031261043a576107508184610602565b9261075e8260208301610602565b9261046261076f8460408501610602565b93608061077f8260608701610602565b9401610602565b3461043a5761063b61079936600461073a565b93929092611b86565b3461043a5761063b6107b536600461060f565b611bc9565b906107c96104ea6104e0845190565b9060005b8181106107da5750505090565b9091926107f06105216001928651815260200190565b9291016107cd565b6020808252610462929101906107ba565b3461043a5761049661082461081f3660046104ad565b611bdb565b604051918291826107f8565b919060408382031261043a5761046290602061084c8286610602565b94016104a0565b3461043a5761063b610866366004610830565b90611d36565b3461043a576104966107296108823660046104ad565b611d40565b3461043a5761049661048461089d366004610830565b90611d6a565b3461043a5761063b6108b636600461060f565b611dee565b610462916008021c6001600160a01b031690565b9061046291546108bb565b6104626000806108cf565b3461043a576108f53660046106e1565b6104966109006108da565b604051918291826001600160a01b03909116815260200190565b919060408382031261043a5761046290602061077f82866104a0565b3461043a5761063b61094936600461091a565b90611e12565b3461043a5761063b6109623660046104ad565b611f7d565b901515815260408101929161044c916020905b019015159052565b3461043a5761099a6109953660046104ad565b611f86565b906104966109a760405190565b92839283610967565b3461043a5761063b6109c336600461091a565b90611fb3565b909182601f8301121561043a578135916001600160401b03831161043a57602001926001830284011161043a57565b9060808282031261043a57610a0d8183610602565b92610a1b82602085016104a0565b92610a2983604083016104a0565b9260608201356001600160401b03811161043a5761069492016109c9565b3461043a5761063b610a5a3660046109f8565b93929092612298565b3461043a57610a733660046106e1565b61063b612352565b90610a8a6104ea6104e0845190565b9060005b818110610a9b5750505090565b909192610aba61052160019286516001600160a01b0316815260200190565b929101610a8e565b602080825261046292910190610a7b565b3461043a57610496610aee610ae93660046104ad565b612363565b60405191829182610ac2565b3461043a57610b0a3660046106e1565b61063b6124f6565b610b4761044c94610b3e606094989795610b35608086019a600087019015159052565b15156020850152565b15156040830152565b0152565b3461043a57610496610b66610b6136600461060f565b6124fe565b90610b7394929460405190565b94859485610b12565b634e487b7160e01b600052604160045260246000fd5b90601f01601f191681019081106001600160401b03821117610bb357604052565b610b7c565b9061044c610bc560405190565b9283610b92565b6001600160401b038111610bb357602090601f01601f19160190565b90826000939282370152565b90929192610c09610c0482610bcc565b610bb8565b938185528183011161043a5761044c916020850190610be8565b9080601f8301121561043a5781602061046293359101610bf4565b91909160408184031261043a57610c558382610602565b9260208201356001600160401b03811161043a576104629201610c23565b61063b610c81366004610c3e565b90612545565b3461043a57610c973660046106e1565b610496610729612592565b3461043a5761063b610cb536600461060f565b6125c0565b801515610433565b9050359061044c82610cba565b909160608284031261043a57610462610ce88484610cc2565b93604061084c8260208701610602565b3461043a57610496610729610d0e366004610ccf565b916127c8565b3461043a5761063b610d2736600461060f565b6129a6565b3461043a57610d3c3660046106e1565b6104966104846129af565b3461043a57610d573660046106e1565b61063b612ace565b3461043a57610496610aee610d753660046104ad565b612ad6565b3461043a5761063b610d8d36600461060f565b612b20565b3461043a57610da23660046106e1565b610496600080516020614731833981519152610729565b9015158152901515602082015260608101929161044c9160409061097a565b3461043a57610496610df3610dee3660046104ad565b612b29565b60405191939193849384610db9565b919060408382031261043a5761046290602061084c82866104a0565b3461043a5761063b610e31366004610e02565b90612d3d565b905b600052602052604060002090565b6000610e5761046292603b610e37565b6106f7565b3461043a57610496610729610e723660046104ad565b610e47565b3461043a57610e873660046106e1565b61063b612daa565b3461043a5761063b610ea236600461060f565b612e02565b610462906104c1906001600160a01b031682565b61046290610ea7565b61046290610ebb565b90610e3990610ec4565b6000610e57610462926039610ecd565b3461043a57610496610729610efd36600461060f565b610ed7565b9160408383031261043a5782356001600160401b03811161043a5782610f2f602094610462938701610640565b949095016104a0565b3461043a5761063b610f4b366004610f02565b91612e6b565b3461043a57610496610900610f67366004610e02565b90612e76565b3461043a57610496610484610f8336600461091a565b90612eb7565b919060408382031261043a57610462906020610fa582866104a0565b9401610cc2565b3461043a5761063b610fbf366004610f89565b90612fd4565b3461043a5761099a610fd83660046104ad565b612fde565b6104626104626104629290565b6104626000610fdd565b610462610fea565b3461043a5761100c3660046106e1565b610496610729610ff4565b3461043a5761063b61102a36600461060f565b6131e7565b3461043a57610496610aee6110453660046104ad565b6131f0565b90611057610c0483610bcc565b918252565b611066600561104a565b640352e302e360dc1b602082015290565b61046261105c565b610462611077565b61046261107f565b60005b8381106110a25750506000910152565b8181015183820152602001611092565b6110d36110dc6020936104cd936110c7815190565b80835293849260200190565b9586910161108f565b601f01601f191690565b6020808252610462929101906110b2565b3461043a576111073660046106e1565b610496611112611087565b604051918291826110e6565b610462600060036108cf565b61046590610ec4565b60208101929161044c919061112a565b3461043a576111533660046106e1565b61049661115e61111e565b60405191829182611133565b6104626000603e6106f7565b3461043a576111863660046106e1565b61049661072961116a565b3461043a576104966107296111a736600461060f565b613207565b9060208282031261043a5781356001600160401b03811161043a576104629201610c23565b6104cd6111e9926020926111e3815190565b94859290565b9384910161108f565b6110576104cd91602094936111d1565b61121761120e60405190565b928392836111f2565b03902090565b61046291611202565b610462916008021c5b60ff1690565b906104629154611226565b600061125061046292600261121d565b611235565b3461043a5761049661048461126b3660046111ac565b611240565b90610e3990610fdd565b610e576104629261128e600093603c611270565b610ecd565b3461043a576104966107296112a936600461091a565b9061127a565b600061125061046292603a610ecd565b3461043a576104966104846112d536600461060f565b6112af565b3461043a576104966107296112f03660046104ad565b613269565b3461043a5761063b61130836600461060f565b6132b7565b3461043a5761063b61132036600461091a565b906132db565b919060408382031261043a57610462906020610fa58286610602565b3461043a5761063b611355366004611326565b9061331a565b3461043a5761136b3660046106e1565b6104966000805160206146f1833981519152610729565b3461043a5761063b6113953660046104ad565b61336b565b610462600060366106f7565b3461043a576113b63660046106e1565b61049661072961139a565b80511515825261046291608061140460a083016113e360208601516020860152565b6113f260408601516040860152565b606085015184820360608601526104d1565b92015190608081840391015261056e565b6020808252610462929101906113c1565b3461043a5761049661144161143c3660046104ad565b613420565b60405191829182611415565b610462600060376106f7565b3461043a576114693660046106e1565b61049661072961144d565b610462600060016108cf565b3461043a576114903660046106e1565b610496610900611474565b635a05180f60e01b6001600160e01b03198216149081156114ba575090565b6104629150613482565b6104626080610bb8565b6114d66114c4565b906060825260208080808501606081520160008152016000905250565b905250565b6104626114ce565b610462906104c1565b6104629054611500565b9061152e6115226104e0845490565b92600052602060002090565b9060005b81811061153f5750505090565b90919261156c61156560019261155487611509565b6001600160a01b0316815260200190565b9460010190565b929101611532565b9061046291611513565b9061044c6115989261158f60405190565b93848092611574565b0383610b92565b6104629081565b610462905461159f565b906115bf6115226104e0845490565b9060005b8181106115d05750505090565b9091926115ed6115656001926115e5876115a6565b815260200190565b9291016115c3565b90610462916115b0565b9061044c6115989261161060405190565b938480926115f5565b9061044c61166d600361162a6114c4565b9461163b6116378261157e565b8752565b61165161164a600183016115ff565b6020880152565b611667611660600283016115a6565b6040880152565b016115a6565b6060840152565b61046290611619565b60046116966104629261168e6114f8565b506038611270565b01611674565b61044c906116bc6000805160206147318339815191526134bc565b6134bc565b6116f1565b906001600160a01b03905b9181191691161790565b906116e66104626116ed92610ec4565b82546116c1565b9055565b6116fd61044c91610ec4565b60036116d6565b61044c9061169c565b9061044c9161171a6134c7565b611754565b634e487b7160e01b600052603260045260246000fd5b9190811015611745576020020190565b61171f565b35610462816105f3565b9190916117616000610fdd565b8381101561178e5780611783610d2761177e611789948887611735565b61174a565b60010190565b611761565b50509050565b9061044c9161170d565b61044c906117b96000805160206147318339815191526134bc565b61044c906117c56134c7565b611804565b90600019906116cc565b906117e46104626116ed92610fdd565b82546117ca565b61046590610fdd565b60208101929161044c91906117eb565b61180f8160376117d4565b61181c610462603e6115a6565b8111611855576118507f15aea1769167f83f5fc9ac1bedf10a18732f8a29fc4f74ba0b927418d12fa4ce9161072d60405190565b0390a1565b635a092a1d60e11b60009081528061186e8160046117f4565b0390fd5b61044c9061179e565b61044c906118966000805160206147318339815191526134bc565b61044c906118a26134c7565b6118ad81603d6117d4565b6118bf6118bb610800610fdd565b9190565b116118c657565b63b315b31d60e01b60009081528061186e8160046117f4565b61044c9061187b565b6104629060401c61122f565b61046290546118e8565b610462905b6001600160401b031690565b61046290546118fe565b6119036104626104629290565b906001600160401b03906116cc565b611903610462610462926001600160401b031690565b9061195b6104626116ed92611935565b8254611926565b9068ff00000000000000009060401b6116cc565b906119866104626116ed92151590565b8254611962565b61046590611919565b60208101929161044c919061198d565b92939091907ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009485926119e86119e26119de866118f4565b1590565b9461190f565b946000966119f588611919565b6001600160401b0388161480611aee575b600197611a22611a158a611919565b916001600160401b031690565b149081611aca575b155b9081611ac1575b50611ab25790611a609493929186611a578a611a4e8b611919565b9c019b8c61194b565b611aa357611af5565b611a6957505050565b611a97611850927fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d294611976565b60405191829182611996565b611aad888b611976565b611af5565b63f92ee8a960e01b8852600488fd5b15905038611a33565b9050611a2c611ad830610ec4565b3b611ae56118bb8c610fdd565b14919050611a2a565b5085611a06565b92611b059261044c9592856135ba565b611b0d6135cf565b611b22611b1b610800610fdd565b603e6117d4565b611b36611b2f6080610fdd565b603d6117d4565b60036004611b786001611b5c611b4b82610fdd565b95611b578760366117d4565b6135d7565b50611b7f611b6a600a610fdd565b600285611b78856038611270565b01016117d4565b6038611270565b9061044c949392916119a6565b61044c90611bae6000805160206146f18339815191526134bc565b611bc6906000805160206147318339815191526136d2565b50565b61044c90611b93565b610462906115ff565b60016004611bee6104629361168e606090565b0101611bd2565b61044c9190611c116000805160206147318339815191526134bc565b9061044c91611c1e6134c7565b611c67565b634e487b7160e01b600052601160045260246000fd5b9190611c44565b9290565b8201809211611c4f57565b611c23565b90815260408101929161044c9160200152565b9190611c7f611c75846124fe565b9390919215151590565b918215611d2e575b508115611d26575b50611d02577fac52b63ac21eb6a0502ba1afddeff39668a3e6c7ceb05367e6563c8bd14cd805929350611ce460026004611cca846038611270565b0101611cde84611cd9836115a6565b611c39565b906117d4565b611ced8161372b565b611850611cf960405190565b92839283611c54565b63529f6aeb60e11b60009081526001600160a01b038516600452602490fd5b036000fd5b905038611c8f565b915038611c87565b9061044c91611bf5565b600161166761046292611d51600090565b5060006000805160206147118339815191525b01610e37565b611d7d611d78826039610ecd565b6115a6565b600090611d8c6118bb83610fdd565b14611daa57506118bb611c40611d78611da6936039610ecd565b1490565b63529f6aeb60e11b81526001600160a01b03919091166004820152602490fd5b61044c90611de56000805160206147318339815191526134bc565b61044c9061377c565b61044c90611dca565b9061044c91611e086116b782611d40565b90611bc691613785565b9061044c91611df7565b61044c90611e286134c7565b611e40565b6104629061122f565b6104629054611e2d565b611e53611e4e33603a610ecd565b611e36565b611e605761044c90611ef4565b630ad2d8d560e31b600090815233600452602490fd5b805482101561174557611e90600191600052602060002090565b91020190600090565b9160001960089290920291821b911b6116cc565b921b90565b9190611ec36104626116ed93610fdd565b908354611e99565b90815491600160401b831015610bb35782611eee91600161044c95018155611e76565b90611eb2565b611f08611f0082612b29565b909290911590565b918215611f75575b508115611f6d575b50611f5b5761044c90611f2b33826137d4565b611f5660016004611f51611f3e336135d7565b94611b7f81600385611b788a6038611270565b010190565b611ecb565b636edaef2f60e11b6000908152600490fd5b905038611f18565b915038611f10565b61044c90611e1c565b611f8f90612b29565b8291939291611faa575b83611fa357509190565b9092501590565b80159150611f99565b90611fbd336104c1565b6001600160a01b03821603611fd557611bc6916136d2565b63334bd91960e11b6000908152600490fd5b9061044c94939291611ff76134c7565b9061044c94939291612008816138a1565b949392919061201b611e4e87603a610ecd565b6120295761044c94956120db565b630ad2d8d560e31b60009081526001600160a01b038716600452602490fd5b916001600160a01b0360089290920291821b911b6116cc565b91906120726104626116ed93610ec4565b908354612048565b90815491600160401b831015610bb3578261209d91600161044c95018155611e76565b90612061565b634e487b7160e01b600052602160045260246000fd5b600211156120c357565b6120a3565b9061044c826120b9565b610462906120c8565b93919290926120ee611d78336039610ecd565b84900361228257612101611f0085612b29565b61226b578115612263575b506122115761211a856124fe565b91505081612206575b506121e7576121a692849261213f612144936121ac97896138ec565b613ac9565b61217061215c6003612157846038611270565b015490565b61216b8661128e856041611270565b6117d4565b6121898461218460036104cd856038611270565b61207a565b6121988161216b866039610ecd565b6121a181613ad4565b610fdd565b91610ec4565b6121b660016120d2565b917fcdc318b415c4f74ecd1a3bbaf637fe42594b23d3e706bdea4031084fed4f6c736121e160405190565b600090a4565b639e83c77d60e01b60009081526001600160a01b038616600452602490fd5b859150141538612123565b61186e8461221e60405190565b630742992760e11b81529182916004830190815260406020820181905260139082015272141c9bd99a5b19481a5cc8185c98da1a5d9959606a1b606082015260800190565b90503861210c565b635595b91560e11b60009081526004879052602490fd5b63529f6aeb60e11b600090815233600452602490fd5b9061044c94939291611fe7565b6122ad6134c7565b61044c6122d5565b9060ff906116cc565b906122ce6104626116ed92151590565b82546122b5565b6122e1611c75336124fe565b61228257811561234a575b5061233d57806121a16001600061230761230d956038611270565b016122be565b7fe2a74f634d4a5de5ebf5fc6985a469779d12830dd517ce1a08acb22847cdc3ee61233760405190565b600090a2565b61186e9061221e60405190565b9050386122ec565b61044c6122a5565b6104629061157e565b600060046123766104629361168e606090565b010161235a565b612387601961104a565b7f4554484f535f494e544552414354494f4e5f434f4e54524f4c00000000000000602082015290565b61046261237d565b6104626123b0565b9050519061044c826105f3565b9060208282031261043a57610462916123c0565b6040513d6000823e3d90fd5b6001600160a01b03909116815260408101929161044c9160200152565b61244e602061242161241c6003611509565b610ec4565b6124296123b8565b9061243360405190565b9384928391829163d57f7aa360e01b5b8352600483016110e6565b03915afa9081156124e9576000916124ba575b50612474335b916001600160a01b031690565b036124815761044c6124ee565b63e2517d3f60e01b6000908152611d217f261fea28325784de45eba41a3fbb84c4196fbab02ee802566edfe26616ba5afb3360046123ed565b6124dc915060203d6020116124e2575b6124d48183610b92565b8101906123cd565b38612461565b503d6124ca565b6123e1565b61044c613b75565b61044c61240a565b611d786125159161250d600090565b506039610ecd565b61251e81612b29565b9193909290565b9061044c91612532613b7d565b9061044c9161254081613c25565b613c4f565b9061044c91612525565b6104629061255b613d1e565b612589565b6104627f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc610fdd565b50610462612560565b610462600061254f565b61044c906125b76000805160206147318339815191526134bc565b61044c90613d78565b61044c9061259c565b906104629392916125d86134c7565b612655565b6125e7600c61104a565b6b4554484f535f52455649455760a01b602082015290565b6104626125dd565b6104626125ff565b612619601161104a565b7022aa2427a9afa0aa2a22a9aa20aa24a7a760791b602082015290565b61046261260f565b610462612636565b6000198114611c4f5760010190565b5092919061266661241c6003611509565b612692602063d57f7aa392612679612607565b9061268360405190565b80809581946124438960e01b90565b03915afa80156124e9576126b5916000916127a9575b506001600160a01b031690565b3314159081612747575b50611f5b57612708916126d260366115a6565b80928196600014612732575061216b6126ec92603b610e37565b6121a16127016126fc60366115a6565b612646565b60366117d4565b7f3fd0c2393f291765470cc17997e368eb7ec635d8966232abd9d2016e8ad721cf61233760405190565b612742925061216b906039610ecd565b6126ec565b61277e915060209061275c61241c6003611509565b61244361276761263e565b9261277160405190565b9586948593849360e01b90565b03915afa80156124e9576127a0916000916127a957506001600160a01b031690565b331415386126bf565b6127c2915060203d6020116124e2576124d48183610b92565b386126a8565b61046292919060006125c9565b61044c906127e16134c7565b61044c906127ee816138a1565b6127fc611e4e82603a610ecd565b6128095761044c90612858565b630ad2d8d560e31b60009081526001600160a01b039091166004526024036000fd5b8015611c4f576000190190565b90815260408101929161044c916020905b01906001600160a01b03169052565b61286d612864336124fe565b50909290911590565b91821561299e575b508115612996575b50611f5b5761288b816124fe565b5090508161298d575b5061296b577f9c6d622696d829eae44c9559a6e96ae07091e9ca797bc02c35a7770dbcf04580906128c9611d78336039610ecd565b6128d281613d81565b6128dc8282613dc7565b61293d6002600461292a8460386129116129026000866128fc8686611270565b01015490565b61216b8a61128e86603f611270565b61292588612184600087611f518787611270565b611270565b0101611cde612938826115a6565b61282b565b61295661294f8361128e84603c611270565b42906117d4565b61185061296260405190565b92839283612838565b639e83c77d60e01b60009081526001600160a01b039091166004526024036000fd5b15905038612894565b90503861287d565b915038612875565b61044c906127d5565b610462613e0d565b6129bf6134c7565b61044c612a00565b919060086116cc910291611ead60ff841b90565b91906129ec6104626116ed93151590565b9083546129c7565b61044c916000916129db565b612a13612a0c336124fe565b9390921590565b6122825715908115612ac6575b50612a6a57806121a1600080612a3a612a40956038611270565b016129f4565b7fad0c07c6e15db6a51fed6c1d42d277ed0fa53f2eef0d1830c060c1cba8638adc61233760405190565b61186e90612a7760405190565b630742992760e11b8152918291600483019081526040602082018190526017908201527f50726f66696c65206973206e6f74206172636869766564000000000000000000606082015260800190565b905038612a20565b61044c6129b7565b6003612ae76104629261168e606090565b0161235a565b61044c90612b086000805160206146f18339815191526134bc565b611bc690600080516020614731833981519152613785565b61044c90612aed565b612b37610462826038611270565b91612b44600184016115a6565b612b516118bb6000610fdd565b1180809481612b99575b5092612b676000610fdd565b81119182612b90575b5081612b7a575090565b9050612b8c6118bb61046260366115a6565b1090565b15915038612b70565b612ba39150611e36565b38612b5b565b90612bc46020612bbc61241c6003611509565b61242961263e565b03915afa80156124e957612be6916000916127a957506001600160a01b031690565b3303611f5b5761044c919061044c91612bfd6134c7565b612c18565b6104c16104626104629290565b61046290612c02565b90612c2661241c6003611509565b6020612c3160405190565b63d57f7aa360e01b815260206004820152600b60248201526a08aa8909ea6beac9eaa86960ab1b604482015291829060649082905afa9081156124e957600091612d1e575b50612c846104c16000612c0f565b6001600160a01b03821603612ca3575b5061216b61044c92603b610e37565b61241c612caf91610ec4565b91823b1561043a576000612cc260405190565b93849063987088f160e01b8252818381612ce0888860048401611c54565b03925af19081156124e95761044c9361216b92612d00575b509250612c94565b612d18906000612d108183610b92565b8101906106e1565b38612cf8565b612d37915060203d6020116124e2576124d48183610b92565b38612c76565b9061044c91612ba9565b612d59602061242161241c6003611509565b03915afa9081156124e957600091612d83575b50612d7633612467565b036124815761044c612da2565b612d9c915060203d6020116124e2576124d48183610b92565b38612d6c565b61044c613e98565b61044c612d47565b61044c90612dcd6000805160206146f18339815191526134bc565b611bc6906000805160206146f1833981519152612dfc612df6612df06000610fdd565b83612e76565b826136d2565b50613785565b61044c90612db2565b61044c929190612e286000805160206147318339815191526134bc565b9061044c9291612e366134c7565b91929160005b82811015612e64578061178386612e5a61177e612e5f958888611735565b611d36565b612e3c565b5050509050565b9061044c9291612e0b565b90612eb261046261046293612e89600090565b5060007fc1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000611d64565b613ea9565b610462916000612ee2611e4e93612ecc600090565b5082600080516020614711833981519152611d64565b01610ecd565b9061044c91612ef56134c7565b612f06565b61044c91600091611eb2565b612f14611d78336039610ecd565b612f20611f0082612b29565b612282578115612fcc575b5061233d57612f4060036104cd836038611270565b90612f49825490565b831015612fba57612f818284612f6e612f68612f96976121a697611e76565b906108cf565b96612f7888613edd565b612fa057613f4d565b6121a16000612f91866039610ecd565b612efa565b6121b660006120d2565b612fb56001612fb089603a610ecd565b6122be565b613f4d565b6363df817160e01b6000908152600490fd5b905038612f2b565b9061044c91612ee8565b6002611667610462612ff29361168e600090565b612fff6118bb6000610fdd565b11908190565b61044c906130116134c7565b61044c9061301e816138a1565b613066565b91908203918211611c4f57565b61284961044c9461305f60609498979561304f608086019a6000870152565b6001600160a01b03166020850152565b6040830152565b613074611d78336039610ecd565b90613083610462836038611270565b9161308d83611e36565b61233d57603f60046130a6611d788561128e8686611270565b940180549094859181101580156131c0575b6131ae577fc99deea7564c4617f54b6c22cd96a31e991cc2f5524a5d19919cbeef7c4d2c639561314f6000612f918861128e89611850998960029961312e613128613124612f686131819f61310e61311e915490565b6131186001610fdd565b90613023565b86611e76565b9390565b8261401c565b6001600160a01b0386166001600160a01b03831603613196575b5050611270565b0161316561315f6126fc836115a6565b826117d4565b611d786131726000610fdd565b61216b8761128e88603c611270565b9261318b60405190565b938493339085613030565b61216b6131a79261128e8686611270565b3880613148565b63914f33db60e01b6000908152600490fd5b506131ce612f688284611e76565b6131e06001600160a01b038716612467565b14156130b8565b61044c90613005565b61320261046261046292612e89606090565b614085565b61321e90613214816124fe565b9490919215151590565b918215613261575b508115613259575b50613237575090565b63529f6aeb60e11b60009081526001600160a01b039091166004526024036000fd5b90503861322e565b915038613226565b61327b61046261046292612e89600090565b614096565b61044c9061329b6000805160206147318339815191526134bc565b61044c906132a76134c7565b6000612fb061044c92603a610ecd565b61044c90613280565b9061044c916132d16116b782611d40565b90611bc6916136d2565b9061044c916132c0565b9061044c916132f26134c7565b90613315611d7861044c9361128e61330e611d78336039610ecd565b6041611270565b612fd4565b9061044c916132e5565b61044c9061333f6000805160206147318339815191526134bc565b61044c9061334b6134c7565b61335681603e6117d4565b6133646118bb610800610fdd565b1161185557565b61044c90613324565b61046260a0610bb8565b613386613374565b906000825260208080808086016000815201600081520160608152016114f36114f8565b61046261337e565b9061044c61341060046133c3613374565b946133d66133d082611e36565b15158752565b6133e561164a600183016115a6565b6133f4611660600283016115a6565b61340a6134036003830161157e565b6060880152565b01611619565b6080840152565b610462906133b2565b6134286133aa565b50600061343481610fdd565b8214801561346c575b6134555750613450610462916038611270565b613417565b635595b91560e11b81526004810191909152602490fd5b5061347a61046260366115a6565b82101561343d565b637965db0b60e01b6001600160e01b03198216149081156134a1575090565b61046291506001600160e01b0319166301ffc9a760e01b1490565b61044c9033906140ac565b6134cf6129af565b6134d557565b63d93c066560e01b6000908152600490fd5b9061044c949392916134f76140db565b92909391600061350681612c0f565b6001600160a01b0381166001600160a01b0387161490811561359d575b8115613581575b5061357257506116fd61355d9361241c611bc69796946135499461411d565b6000805160206146f1833981519152613785565b50600080516020614731833981519152613785565b63d92e233d60e01b8152600490fd5b6001600160a01b031690506001600160a01b038516143861352a565b90506001600160a01b0381166001600160a01b0388161490613523565b9061044c949392916134e7565b61044c6140db565b61044c6135c7565b906135e1826124fe565b9492909150156136025763b62e454560e01b60009081526004859052602490fd5b91929091156136b157905b61361c8261216b836039610ecd565b6136738161218460036104cd86603861364182600161363b8285611270565b016117d4565b61365861364e8383611270565b60024291016117d4565b61292561366560376115a6565b60026004611b788686611270565b61367f6121a683610fdd565b907f9f150021d8fd04eb219a39e3019273bddf99a9a2e434dd2aa8132e32ea9244b06136aa60405190565b600090a390565b506136bc60366115a6565b906136cd6127016126fc60366115a6565b61360d565b907fc1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000906136ff8184614127565b928361370c575b50505090565b6137229261371d9161046291610e37565b6141bd565b50388080613706565b613734816141ea565b6137446118bb610462603e6115a6565b1161374c5750565b635a092a1d60e11b6000908152600491909152602490fd5b61044c90613771816138a1565b61044c9060006116d6565b61044c90613764565b907fc1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000906137b2818461422a565b92836137be5750505090565b613722926137cf9161046291610e37565b61429b565b906137e3610462836038611270565b603f91600060046137fb611d788461128e8989611270565b9301019161380a610462845490565b811015801561387a575b6131ae5761044c9460009461292561128e9361386387613844612f6861383e61310e612f919c5490565b83611e76565b836001600160a01b038a166001600160a01b0383160361386957505090565b9061401c565b61216b6104629261128e8a8a611270565b50613888612f688285611e76565b61389a6001600160a01b038416612467565b1415613814565b6000906138b36124676104c184612c0f565b146135725750565b610465906001600160a01b031660601b90565b602093926138e66014836138e688956104cd976138bb565b01918252565b613917919061390b6138fd60405190565b9485936020850193846138ce565b90810382520382610b92565b613929613922825190565b9160200190565b2090565b90916104cd9083908093610be8565b61105790602094936104cd9361392d565b90916112179061395c60405190565b9384938461393c565b90916104629261394d565b9190613981611e4e83836002613965565b61398e5761044c92613a18565b63858c8a1f60e01b6000908152600490fd5b9050519061044c82610cba565b9060208282031261043a57610462916139a0565b91906110dc816139d8816104cd9560209181520190565b8095610be8565b613a0b6104629593949294613a04606084019660008501906001600160a01b03169052565b6020830152565b60408185039101526139c1565b91602082829394613a2f61241c61241c6001611509565b90613a3a6000611509565b613a5f613a4660405190565b96879586948594622ea33360e31b8652600486016139df565b03915afa80156124e957613a7991600091613aa057501590565b613a8e57612fb060019161044c936002613965565b638baa579f60e01b6000908152600490fd5b6119de915060203d602011613ac2575b613aba8183610b92565b8101906139ad565b503d613ab0565b9061044c9291613970565b613ae46003612157836038611270565b613af46118bb610462603d6115a6565b11613afc5750565b63b315b31d60e01b6000908152600491909152602490fd5b613b1c6142bb565b61044c613b4b6000807fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300612307565b7f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa61185033610900565b61044c613b14565b613b8630610ec4565b7f0000000000000000000000002661710d76c872681efc25300de84138a6369e3a90613bba6001600160a01b038316612467565b14908115613bdc575b50613bca57565b63703e46dd60e11b6000908152600490fd5b9050613bf9612467613bec6142de565b926001600160a01b031690565b141538613bc3565b61044c90613c1c6000805160206146f18339815191526134bc565b61044c906138a1565b61044c90613c01565b9050519061044c8261049a565b9060208282031261043a5761046291613c2e565b90613c5c61241c83610ec4565b906020613c6860405190565b6352d1902d60e01b815292839060049082905afa60009281613ced575b50613cb75750506001613c955750565b634c9c8ce360e01b60009081526001600160a01b039091166004526024036000fd5b909291613cc5610462612560565b8403613cd65761044c9293506142f4565b632a87526960e21b60009081526004859052602490fd5b613d1091935060203d602011613d17575b613d088183610b92565b810190613c3b565b9138613c85565b503d613cfe565b613d2730610ec4565b613d596001600160a01b037f0000000000000000000000002661710d76c872681efc25300de84138a6369e3a16612467565b03613bca57565b61044c90613d6d816138a1565b61044c9060016116d6565b61044c90613d60565b613d9a60026004613d93846038611270565b01016115a6565b90600091613daa6118bb84610fdd565b14613db3575050565b6314ac648360e21b82526004820152602490fd5b90613dda611d788261128e85603c611270565b91600092613dea6118bb85610fdd565b03613df457505050565b63330b0ff560e01b8352829161186e9160048401612838565b6104627fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300611e36565b613e3e6134c7565b61044c613e6e600160007fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300612307565b7f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25861185033610900565b61044c613e36565b61046290610fdd565b613ed3613ece61046293613ec96000613ed895613ec4600090565b500190565b61435d565b613ea0565b612c02565b610ebb565b336001600160a01b03821614613ef05750565b61186e90613efd60405190565b6366d2949d60e01b81526001600160a01b03909116600482015260406024820152601560448201527420b2323932b9b9901e9e9036b9b39739b2b73232b960591b60648201529081906084820190565b612f9160009161044c93613f64612f688383611e76565b91613f86613f7f612f68613f7961310e865490565b85611e76565b928261401c565b6001600160a01b0383166001600160a01b03831603613fb3575b505061128e61330e611d78336039610ecd565b61216b613fcb9261128e61330e611d78336039610ecd565b3880613fa0565b634e487b7160e01b600052603160045260246000fd5b61044c91600091612061565b8054801561401757600019019061401461400e8383611e76565b90613fe8565b55565b613fd2565b90614028610462825490565b8210156140735761044c9161404161046261310e845490565b810361404e575b50613ff4565b61406d9061209d614066612f6861311e61310e875490565b9184611e76565b38614048565b634e23d03560e01b6000908152600490fd5b606090614091906143e6565b905090565b6140a7600061046292613ec4600090565b6143fb565b906140ba6119de8284612eb7565b6140c2575050565b63e2517d3f60e01b600090815291611d219160046123ed565b6140e66119de61440f565b6140ec57565b631afcd79f60e31b6000908152600490fd5b9061044c9161410b6140db565b9061411861044c9261377c565b613d78565b9061044c916140fe565b6000805160206147118339815191526141408383612eb7565b156141a3576000612fb08482612ee2868261415b9701610e37565b61416f6121a6614169339390565b93610ec4565b917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b61419a60405190565b600090a4600190565b505050600090565b6104629081906001600160a01b031681565b906141e56118bb6121a16141e06000610462966141d8600090565b500194610ebb565b6141ab565b614473565b6104629061421e600160046128fc60389461422461420e600285613d93858b611270565b61421e6000866128fc868c611270565b90611c39565b95611270565b6000805160206147118339815191526142466119de8484612eb7565b156141a3576001612fb0846000612ee286826142629701610e37565b6142706121a6614169339390565b917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d61419a60405190565b906142b66118bb6121a16141e06000610462966141d8600090565b614542565b6142c66119de6129af565b6142cc57565b638dfc202b60e01b6000908152600490fd5b61046260006142ee610462612560565b01611509565b906142fe82614583565b61430782610ec4565b7fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b61433160405190565b600090a280516143446118bb6000610fdd565b111561435357611bc691614610565b505061044c6145ce565b6104629160006143769261436f600090565b5001611e76565b906106f7565b9061438b6115226104e0845490565b9060005b81811061439c5750505090565b9091926143b16115656001926115e5876115a6565b92910161438f565b906104629161437c565b9061044c611598926143d460405190565b938480926143b9565b610462906143c3565b6000610462916143f4606090565b50016143dd565b600061046291614409600090565b50015490565b6104627ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006118f4565b9190611ec36104626116ed9390565b61044c91600091614438565b8054801561401757600019019061401461446d8383611e76565b90614447565b90614484611d788260018501610e37565b61448e6000610fdd565b81146141a357612f91610462926000926144e195611d6460019788936144bc6144b686610fdd565b82613023565b888501916144d46144cb845490565b61311889610fdd565b8083036144e65750505090565b614453565b61450961450f6145179461450061437661216b9589611e76565b92839188611e76565b90614438565b888801610e37565b388080613706565b90815491600160401b831015610bb3578261450991600161044c95018155611e76565b61454f6119de8383614637565b1561457c576145779161216b9060016145708461456c848261451f565b5490565b9301610e37565b600190565b5050600090565b6000813b6145936118bb83610fdd565b146145ae579061044c916145a8610462612560565b016116d6565b634c9c8ce360e01b81526001600160a01b03919091166004820152602490fd5b60006145d981610fdd565b34116145e25750565b63b398979f60e01b8152600490fd5b3d1561460b576146003d61104a565b903d6000602084013e565b606090565b6000806104629361461f606090565b50602081519101845af46146316145f1565b91614662565b614650916001611d7892614649600090565b5001610e37565b61465d6118bb6000610fdd565b141590565b9061466d57506146c1565b81519060009161467f6118bb84610fdd565b14806146ac575b61468f57505090565b639996b31560e01b82526001600160a01b03166004820152602490fd5b50803b6146bb6118bb84610fdd565b14614686565b80516000906146d26118bb83610fdd565b11156146e15750805190602001fd5b63d6bda27560e01b8152600490fdfeb19546dff01e856fb3f010c267a7b1c60363cf8a4664e21cc89c26224620214e02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800a49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775a26469706673582212205fe48a9c7be93ce6d6d27cc0adede0c3ccb52b1cc97d9e2513ae40d3070d2d3764736f6c634300081a0033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.