Source Code
Latest 1 from a total of 1 transactions
| Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Delete Address | 31498378 | 182 days ago | IN | 0 ETH | 0.00000015 |
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
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
Contract Source Code (Solidity Standard Json-Input format)
// 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;
}// 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);
}// 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();
}
}
}// 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;
}
}// 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;
}
}// 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);
// 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;
}// 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();
}
}// 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();
}
}
}{
"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
- No Contract Security Audit Submitted- Submit Audit Here
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"}]Contract Creation Code
60a06040523461003257610011610037565b6040516147866101c98239608051818181613b880152613d34015261478690f35b600080fd5b61003f610049565b61004761011a565b565b610047610086565b61006590610068906001600160a01b031682565b90565b6001600160a01b031690565b61006590610051565b61006590610074565b61008e61009c565b6100973061007d565b608052565b61004761003f565b6100659060401c60ff1690565b61006590546100a4565b610065905b6001600160401b031690565b61006590546100bb565b610065906100c0906001600160401b031682565b906100fa610065610116926100d6565b82546001600160401b0319166001600160401b03919091161790565b9055565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610144816100b1565b6101b657610151816100cc565b6001600160401b0391908290811603610168575050565b816101977fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2936101b1936100ea565b604051918291826001600160401b03909116815260200190565b0390a1565b63f92ee8a960e01b6000908152600490fdfe6080604052600436101561001257600080fd5b60003560e01c806301ffc9a71461042257806305c3d5221461041d57806308473f64146104185780630db4a5bb146104135780630fa5a1d41461040e57806310c7080114610409578063123a9e87146104045780631459457a146103ff5780631785f53c146103fa5780631e95c11d146103f557806320d26736146103f0578063248a9ca3146103eb57806325d0b09e146103e65780632bdd87ff146103e15780632cb3e494146103dc5780632f2ff15d146103d757806333bc3ccf146103d257806334ca272c146103cd57806336568abe146103c85780633ba0a718146103c35780633ba836a2146103be5780633eb3ebd8146103b95780633f4ba83a146103b45780634a5b307d146103af5780634f1ef286146103aa57806352d1902d146103a55780635667981b146103a0578063570f85541461039b5780635c76577c146103965780635c975abb146103915780635d649cbe1461038c578063665de3cb14610387578063704802751461038257806375b238fc1461037d57806376f3da32146103785780637ca64266146103735780637dec74d71461036e5780638456cb5914610369578063880cdc311461036457806389a9132b1461035f5780638bcebdb81461035a5780639010d07c1461035557806391d1485414610350578063923160db1461034b578063a12c64b514610346578063a217fddf14610341578063a238f7b41461033c578063a3246ad314610337578063ad3cb1cc14610332578063ade9ae971461032d578063b07b7e7f14610328578063b8861c2314610323578063bb10c8291461031e578063bd7029fd14610319578063c33b84f314610314578063ca15c8731461030f578063d1a445851461030a578063d547741f14610305578063e01290a114610300578063e58378bb146102fb578063eed38b86146102f6578063ef43acef146102f1578063f08f4f64146102ec578063f275cda9146102e75763fde919f60361043a57611480565b611459565b611426565b6113a6565b611382565b61135b565b611342565b61130d565b6112f5565b6112da565b6112bf565b611293565b611255565b611191565b611176565b611143565b6110f7565b61102f565b611017565b610ffc565b610fc5565b610fac565b610f6d565b610f51565b610f38565b610ee7565b610e8f565b610e77565b610e5c565b610e1e565b610dd8565b610d92565b610d7a565b610d5f565b610d47565b610d2c565b610d14565b610cf8565b610ca2565b610c87565b610c73565b610b4b565b610afa565b610ad3565b610a63565b610a47565b6109b0565b610982565b61094f565b610936565b6108e5565b6108a3565b610887565b61086c565b610853565b610809565b6107a2565b610786565b61070e565b6106c9565b6106b1565b610698565b610623565b6105cc565b610469565b6001600160e01b031981165b0361043a57565b600080fd5b9050359061044c82610427565b565b9060208282031261043a576104629161043f565b90565b9052565b3461043a5761049661048461047f36600461044e565b61149b565b60405191829182901515815260200190565b0390f35b80610433565b9050359061044c8261049a565b9060208282031261043a57610462916104a0565b6001600160a01b031690565b0190565b906104f16104ea6104e0845190565b8084529260200190565b9260200190565b9060005b8181106105025750505090565b90919261052861052160019286516001600160a01b0316815260200190565b9460200190565b9291016104f5565b9061053f6104ea6104e0845190565b9060005b8181106105505750505090565b9091926105666105216001928651815260200190565b929101610543565b90610462906060806105a461059260808501600088015186820360008801526104d1565b60208701518582036020870152610530565b946105b460408201516040860152565b0151910152565b60208082526104629291019061056e565b3461043a576104966105e76105e23660046104ad565b61167d565b604051918291826105bb565b6001600160a01b038116610433565b9050359061044c826105f3565b9060208282031261043a5761046291610602565b3461043a5761063b61063636600461060f565b611704565b604051005b909182601f8301121561043a578135916001600160401b03831161043a57602001926020830284011161043a57565b9060208282031261043a5781356001600160401b03811161043a576106949201610640565b9091565b3461043a5761063b6106ab36600461066f565b90611794565b3461043a5761063b6106c43660046104ad565b611872565b3461043a5761063b6106dc3660046104ad565b6118df565b600091031261043a57565b610462916008021c81565b9061046291546106ec565b6104626000603d6106f7565b3461043a5761071e3660046106e1565b610496610729610702565b6040515b9182918290815260200190565b919060a08382031261043a576107508184610602565b9261075e8260208301610602565b9261046261076f8460408501610602565b93608061077f8260608701610602565b9401610602565b3461043a5761063b61079936600461073a565b93929092611b86565b3461043a5761063b6107b536600461060f565b611bc9565b906107c96104ea6104e0845190565b9060005b8181106107da5750505090565b9091926107f06105216001928651815260200190565b9291016107cd565b6020808252610462929101906107ba565b3461043a5761049661082461081f3660046104ad565b611bdb565b604051918291826107f8565b919060408382031261043a5761046290602061084c8286610602565b94016104a0565b3461043a5761063b610866366004610830565b90611d36565b3461043a576104966107296108823660046104ad565b611d40565b3461043a5761049661048461089d366004610830565b90611d6a565b3461043a5761063b6108b636600461060f565b611dee565b610462916008021c6001600160a01b031690565b9061046291546108bb565b6104626000806108cf565b3461043a576108f53660046106e1565b6104966109006108da565b604051918291826001600160a01b03909116815260200190565b919060408382031261043a5761046290602061077f82866104a0565b3461043a5761063b61094936600461091a565b90611e12565b3461043a5761063b6109623660046104ad565b611f7d565b901515815260408101929161044c916020905b019015159052565b3461043a5761099a6109953660046104ad565b611f86565b906104966109a760405190565b92839283610967565b3461043a5761063b6109c336600461091a565b90611fb3565b909182601f8301121561043a578135916001600160401b03831161043a57602001926001830284011161043a57565b9060808282031261043a57610a0d8183610602565b92610a1b82602085016104a0565b92610a2983604083016104a0565b9260608201356001600160401b03811161043a5761069492016109c9565b3461043a5761063b610a5a3660046109f8565b93929092612298565b3461043a57610a733660046106e1565b61063b612352565b90610a8a6104ea6104e0845190565b9060005b818110610a9b5750505090565b909192610aba61052160019286516001600160a01b0316815260200190565b929101610a8e565b602080825261046292910190610a7b565b3461043a57610496610aee610ae93660046104ad565b612363565b60405191829182610ac2565b3461043a57610b0a3660046106e1565b61063b6124f6565b610b4761044c94610b3e606094989795610b35608086019a600087019015159052565b15156020850152565b15156040830152565b0152565b3461043a57610496610b66610b6136600461060f565b6124fe565b90610b7394929460405190565b94859485610b12565b634e487b7160e01b600052604160045260246000fd5b90601f01601f191681019081106001600160401b03821117610bb357604052565b610b7c565b9061044c610bc560405190565b9283610b92565b6001600160401b038111610bb357602090601f01601f19160190565b90826000939282370152565b90929192610c09610c0482610bcc565b610bb8565b938185528183011161043a5761044c916020850190610be8565b9080601f8301121561043a5781602061046293359101610bf4565b91909160408184031261043a57610c558382610602565b9260208201356001600160401b03811161043a576104629201610c23565b61063b610c81366004610c3e565b90612545565b3461043a57610c973660046106e1565b610496610729612592565b3461043a5761063b610cb536600461060f565b6125c0565b801515610433565b9050359061044c82610cba565b909160608284031261043a57610462610ce88484610cc2565b93604061084c8260208701610602565b3461043a57610496610729610d0e366004610ccf565b916127c8565b3461043a5761063b610d2736600461060f565b6129a6565b3461043a57610d3c3660046106e1565b6104966104846129af565b3461043a57610d573660046106e1565b61063b612ace565b3461043a57610496610aee610d753660046104ad565b612ad6565b3461043a5761063b610d8d36600461060f565b612b20565b3461043a57610da23660046106e1565b610496600080516020614731833981519152610729565b9015158152901515602082015260608101929161044c9160409061097a565b3461043a57610496610df3610dee3660046104ad565b612b29565b60405191939193849384610db9565b919060408382031261043a5761046290602061084c82866104a0565b3461043a5761063b610e31366004610e02565b90612d3d565b905b600052602052604060002090565b6000610e5761046292603b610e37565b6106f7565b3461043a57610496610729610e723660046104ad565b610e47565b3461043a57610e873660046106e1565b61063b612daa565b3461043a5761063b610ea236600461060f565b612e02565b610462906104c1906001600160a01b031682565b61046290610ea7565b61046290610ebb565b90610e3990610ec4565b6000610e57610462926039610ecd565b3461043a57610496610729610efd36600461060f565b610ed7565b9160408383031261043a5782356001600160401b03811161043a5782610f2f602094610462938701610640565b949095016104a0565b3461043a5761063b610f4b366004610f02565b91612e6b565b3461043a57610496610900610f67366004610e02565b90612e76565b3461043a57610496610484610f8336600461091a565b90612eb7565b919060408382031261043a57610462906020610fa582866104a0565b9401610cc2565b3461043a5761063b610fbf366004610f89565b90612fd4565b3461043a5761099a610fd83660046104ad565b612fde565b6104626104626104629290565b6104626000610fdd565b610462610fea565b3461043a5761100c3660046106e1565b610496610729610ff4565b3461043a5761063b61102a36600461060f565b6131e7565b3461043a57610496610aee6110453660046104ad565b6131f0565b90611057610c0483610bcc565b918252565b611066600561104a565b640352e302e360dc1b602082015290565b61046261105c565b610462611077565b61046261107f565b60005b8381106110a25750506000910152565b8181015183820152602001611092565b6110d36110dc6020936104cd936110c7815190565b80835293849260200190565b9586910161108f565b601f01601f191690565b6020808252610462929101906110b2565b3461043a576111073660046106e1565b610496611112611087565b604051918291826110e6565b610462600060036108cf565b61046590610ec4565b60208101929161044c919061112a565b3461043a576111533660046106e1565b61049661115e61111e565b60405191829182611133565b6104626000603e6106f7565b3461043a576111863660046106e1565b61049661072961116a565b3461043a576104966107296111a736600461060f565b613207565b9060208282031261043a5781356001600160401b03811161043a576104629201610c23565b6104cd6111e9926020926111e3815190565b94859290565b9384910161108f565b6110576104cd91602094936111d1565b61121761120e60405190565b928392836111f2565b03902090565b61046291611202565b610462916008021c5b60ff1690565b906104629154611226565b600061125061046292600261121d565b611235565b3461043a5761049661048461126b3660046111ac565b611240565b90610e3990610fdd565b610e576104629261128e600093603c611270565b610ecd565b3461043a576104966107296112a936600461091a565b9061127a565b600061125061046292603a610ecd565b3461043a576104966104846112d536600461060f565b6112af565b3461043a576104966107296112f03660046104ad565b613269565b3461043a5761063b61130836600461060f565b6132b7565b3461043a5761063b61132036600461091a565b906132db565b919060408382031261043a57610462906020610fa58286610602565b3461043a5761063b611355366004611326565b9061331a565b3461043a5761136b3660046106e1565b6104966000805160206146f1833981519152610729565b3461043a5761063b6113953660046104ad565b61336b565b610462600060366106f7565b3461043a576113b63660046106e1565b61049661072961139a565b80511515825261046291608061140460a083016113e360208601516020860152565b6113f260408601516040860152565b606085015184820360608601526104d1565b92015190608081840391015261056e565b6020808252610462929101906113c1565b3461043a5761049661144161143c3660046104ad565b613420565b60405191829182611415565b610462600060376106f7565b3461043a576114693660046106e1565b61049661072961144d565b610462600060016108cf565b3461043a576114903660046106e1565b610496610900611474565b635a05180f60e01b6001600160e01b03198216149081156114ba575090565b6104629150613482565b6104626080610bb8565b6114d66114c4565b906060825260208080808501606081520160008152016000905250565b905250565b6104626114ce565b610462906104c1565b6104629054611500565b9061152e6115226104e0845490565b92600052602060002090565b9060005b81811061153f5750505090565b90919261156c61156560019261155487611509565b6001600160a01b0316815260200190565b9460010190565b929101611532565b9061046291611513565b9061044c6115989261158f60405190565b93848092611574565b0383610b92565b6104629081565b610462905461159f565b906115bf6115226104e0845490565b9060005b8181106115d05750505090565b9091926115ed6115656001926115e5876115a6565b815260200190565b9291016115c3565b90610462916115b0565b9061044c6115989261161060405190565b938480926115f5565b9061044c61166d600361162a6114c4565b9461163b6116378261157e565b8752565b61165161164a600183016115ff565b6020880152565b611667611660600283016115a6565b6040880152565b016115a6565b6060840152565b61046290611619565b60046116966104629261168e6114f8565b506038611270565b01611674565b61044c906116bc6000805160206147318339815191526134bc565b6134bc565b6116f1565b906001600160a01b03905b9181191691161790565b906116e66104626116ed92610ec4565b82546116c1565b9055565b6116fd61044c91610ec4565b60036116d6565b61044c9061169c565b9061044c9161171a6134c7565b611754565b634e487b7160e01b600052603260045260246000fd5b9190811015611745576020020190565b61171f565b35610462816105f3565b9190916117616000610fdd565b8381101561178e5780611783610d2761177e611789948887611735565b61174a565b60010190565b611761565b50509050565b9061044c9161170d565b61044c906117b96000805160206147318339815191526134bc565b61044c906117c56134c7565b611804565b90600019906116cc565b906117e46104626116ed92610fdd565b82546117ca565b61046590610fdd565b60208101929161044c91906117eb565b61180f8160376117d4565b61181c610462603e6115a6565b8111611855576118507f15aea1769167f83f5fc9ac1bedf10a18732f8a29fc4f74ba0b927418d12fa4ce9161072d60405190565b0390a1565b635a092a1d60e11b60009081528061186e8160046117f4565b0390fd5b61044c9061179e565b61044c906118966000805160206147318339815191526134bc565b61044c906118a26134c7565b6118ad81603d6117d4565b6118bf6118bb610800610fdd565b9190565b116118c657565b63b315b31d60e01b60009081528061186e8160046117f4565b61044c9061187b565b6104629060401c61122f565b61046290546118e8565b610462905b6001600160401b031690565b61046290546118fe565b6119036104626104629290565b906001600160401b03906116cc565b611903610462610462926001600160401b031690565b9061195b6104626116ed92611935565b8254611926565b9068ff00000000000000009060401b6116cc565b906119866104626116ed92151590565b8254611962565b61046590611919565b60208101929161044c919061198d565b92939091907ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009485926119e86119e26119de866118f4565b1590565b9461190f565b946000966119f588611919565b6001600160401b0388161480611aee575b600197611a22611a158a611919565b916001600160401b031690565b149081611aca575b155b9081611ac1575b50611ab25790611a609493929186611a578a611a4e8b611919565b9c019b8c61194b565b611aa357611af5565b611a6957505050565b611a97611850927fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d294611976565b60405191829182611996565b611aad888b611976565b611af5565b63f92ee8a960e01b8852600488fd5b15905038611a33565b9050611a2c611ad830610ec4565b3b611ae56118bb8c610fdd565b14919050611a2a565b5085611a06565b92611b059261044c9592856135ba565b611b0d6135cf565b611b22611b1b610800610fdd565b603e6117d4565b611b36611b2f6080610fdd565b603d6117d4565b60036004611b786001611b5c611b4b82610fdd565b95611b578760366117d4565b6135d7565b50611b7f611b6a600a610fdd565b600285611b78856038611270565b01016117d4565b6038611270565b9061044c949392916119a6565b61044c90611bae6000805160206146f18339815191526134bc565b611bc6906000805160206147318339815191526136d2565b50565b61044c90611b93565b610462906115ff565b60016004611bee6104629361168e606090565b0101611bd2565b61044c9190611c116000805160206147318339815191526134bc565b9061044c91611c1e6134c7565b611c67565b634e487b7160e01b600052601160045260246000fd5b9190611c44565b9290565b8201809211611c4f57565b611c23565b90815260408101929161044c9160200152565b9190611c7f611c75846124fe565b9390919215151590565b918215611d2e575b508115611d26575b50611d02577fac52b63ac21eb6a0502ba1afddeff39668a3e6c7ceb05367e6563c8bd14cd805929350611ce460026004611cca846038611270565b0101611cde84611cd9836115a6565b611c39565b906117d4565b611ced8161372b565b611850611cf960405190565b92839283611c54565b63529f6aeb60e11b60009081526001600160a01b038516600452602490fd5b036000fd5b905038611c8f565b915038611c87565b9061044c91611bf5565b600161166761046292611d51600090565b5060006000805160206147118339815191525b01610e37565b611d7d611d78826039610ecd565b6115a6565b600090611d8c6118bb83610fdd565b14611daa57506118bb611c40611d78611da6936039610ecd565b1490565b63529f6aeb60e11b81526001600160a01b03919091166004820152602490fd5b61044c90611de56000805160206147318339815191526134bc565b61044c9061377c565b61044c90611dca565b9061044c91611e086116b782611d40565b90611bc691613785565b9061044c91611df7565b61044c90611e286134c7565b611e40565b6104629061122f565b6104629054611e2d565b611e53611e4e33603a610ecd565b611e36565b611e605761044c90611ef4565b630ad2d8d560e31b600090815233600452602490fd5b805482101561174557611e90600191600052602060002090565b91020190600090565b9160001960089290920291821b911b6116cc565b921b90565b9190611ec36104626116ed93610fdd565b908354611e99565b90815491600160401b831015610bb35782611eee91600161044c95018155611e76565b90611eb2565b611f08611f0082612b29565b909290911590565b918215611f75575b508115611f6d575b50611f5b5761044c90611f2b33826137d4565b611f5660016004611f51611f3e336135d7565b94611b7f81600385611b788a6038611270565b010190565b611ecb565b636edaef2f60e11b6000908152600490fd5b905038611f18565b915038611f10565b61044c90611e1c565b611f8f90612b29565b8291939291611faa575b83611fa357509190565b9092501590565b80159150611f99565b90611fbd336104c1565b6001600160a01b03821603611fd557611bc6916136d2565b63334bd91960e11b6000908152600490fd5b9061044c94939291611ff76134c7565b9061044c94939291612008816138a1565b949392919061201b611e4e87603a610ecd565b6120295761044c94956120db565b630ad2d8d560e31b60009081526001600160a01b038716600452602490fd5b916001600160a01b0360089290920291821b911b6116cc565b91906120726104626116ed93610ec4565b908354612048565b90815491600160401b831015610bb3578261209d91600161044c95018155611e76565b90612061565b634e487b7160e01b600052602160045260246000fd5b600211156120c357565b6120a3565b9061044c826120b9565b610462906120c8565b93919290926120ee611d78336039610ecd565b84900361228257612101611f0085612b29565b61226b578115612263575b506122115761211a856124fe565b91505081612206575b506121e7576121a692849261213f612144936121ac97896138ec565b613ac9565b61217061215c6003612157846038611270565b015490565b61216b8661128e856041611270565b6117d4565b6121898461218460036104cd856038611270565b61207a565b6121988161216b866039610ecd565b6121a181613ad4565b610fdd565b91610ec4565b6121b660016120d2565b917fcdc318b415c4f74ecd1a3bbaf637fe42594b23d3e706bdea4031084fed4f6c736121e160405190565b600090a4565b639e83c77d60e01b60009081526001600160a01b038616600452602490fd5b859150141538612123565b61186e8461221e60405190565b630742992760e11b81529182916004830190815260406020820181905260139082015272141c9bd99a5b19481a5cc8185c98da1a5d9959606a1b606082015260800190565b90503861210c565b635595b91560e11b60009081526004879052602490fd5b63529f6aeb60e11b600090815233600452602490fd5b9061044c94939291611fe7565b6122ad6134c7565b61044c6122d5565b9060ff906116cc565b906122ce6104626116ed92151590565b82546122b5565b6122e1611c75336124fe565b61228257811561234a575b5061233d57806121a16001600061230761230d956038611270565b016122be565b7fe2a74f634d4a5de5ebf5fc6985a469779d12830dd517ce1a08acb22847cdc3ee61233760405190565b600090a2565b61186e9061221e60405190565b9050386122ec565b61044c6122a5565b6104629061157e565b600060046123766104629361168e606090565b010161235a565b612387601961104a565b7f4554484f535f494e544552414354494f4e5f434f4e54524f4c00000000000000602082015290565b61046261237d565b6104626123b0565b9050519061044c826105f3565b9060208282031261043a57610462916123c0565b6040513d6000823e3d90fd5b6001600160a01b03909116815260408101929161044c9160200152565b61244e602061242161241c6003611509565b610ec4565b6124296123b8565b9061243360405190565b9384928391829163d57f7aa360e01b5b8352600483016110e6565b03915afa9081156124e9576000916124ba575b50612474335b916001600160a01b031690565b036124815761044c6124ee565b63e2517d3f60e01b6000908152611d217f261fea28325784de45eba41a3fbb84c4196fbab02ee802566edfe26616ba5afb3360046123ed565b6124dc915060203d6020116124e2575b6124d48183610b92565b8101906123cd565b38612461565b503d6124ca565b6123e1565b61044c613b75565b61044c61240a565b611d786125159161250d600090565b506039610ecd565b61251e81612b29565b9193909290565b9061044c91612532613b7d565b9061044c9161254081613c25565b613c4f565b9061044c91612525565b6104629061255b613d1e565b612589565b6104627f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc610fdd565b50610462612560565b610462600061254f565b61044c906125b76000805160206147318339815191526134bc565b61044c90613d78565b61044c9061259c565b906104629392916125d86134c7565b612655565b6125e7600c61104a565b6b4554484f535f52455649455760a01b602082015290565b6104626125dd565b6104626125ff565b612619601161104a565b7022aa2427a9afa0aa2a22a9aa20aa24a7a760791b602082015290565b61046261260f565b610462612636565b6000198114611c4f5760010190565b5092919061266661241c6003611509565b612692602063d57f7aa392612679612607565b9061268360405190565b80809581946124438960e01b90565b03915afa80156124e9576126b5916000916127a9575b506001600160a01b031690565b3314159081612747575b50611f5b57612708916126d260366115a6565b80928196600014612732575061216b6126ec92603b610e37565b6121a16127016126fc60366115a6565b612646565b60366117d4565b7f3fd0c2393f291765470cc17997e368eb7ec635d8966232abd9d2016e8ad721cf61233760405190565b612742925061216b906039610ecd565b6126ec565b61277e915060209061275c61241c6003611509565b61244361276761263e565b9261277160405190565b9586948593849360e01b90565b03915afa80156124e9576127a0916000916127a957506001600160a01b031690565b331415386126bf565b6127c2915060203d6020116124e2576124d48183610b92565b386126a8565b61046292919060006125c9565b61044c906127e16134c7565b61044c906127ee816138a1565b6127fc611e4e82603a610ecd565b6128095761044c90612858565b630ad2d8d560e31b60009081526001600160a01b039091166004526024036000fd5b8015611c4f576000190190565b90815260408101929161044c916020905b01906001600160a01b03169052565b61286d612864336124fe565b50909290911590565b91821561299e575b508115612996575b50611f5b5761288b816124fe565b5090508161298d575b5061296b577f9c6d622696d829eae44c9559a6e96ae07091e9ca797bc02c35a7770dbcf04580906128c9611d78336039610ecd565b6128d281613d81565b6128dc8282613dc7565b61293d6002600461292a8460386129116129026000866128fc8686611270565b01015490565b61216b8a61128e86603f611270565b61292588612184600087611f518787611270565b611270565b0101611cde612938826115a6565b61282b565b61295661294f8361128e84603c611270565b42906117d4565b61185061296260405190565b92839283612838565b639e83c77d60e01b60009081526001600160a01b039091166004526024036000fd5b15905038612894565b90503861287d565b915038612875565b61044c906127d5565b610462613e0d565b6129bf6134c7565b61044c612a00565b919060086116cc910291611ead60ff841b90565b91906129ec6104626116ed93151590565b9083546129c7565b61044c916000916129db565b612a13612a0c336124fe565b9390921590565b6122825715908115612ac6575b50612a6a57806121a1600080612a3a612a40956038611270565b016129f4565b7fad0c07c6e15db6a51fed6c1d42d277ed0fa53f2eef0d1830c060c1cba8638adc61233760405190565b61186e90612a7760405190565b630742992760e11b8152918291600483019081526040602082018190526017908201527f50726f66696c65206973206e6f74206172636869766564000000000000000000606082015260800190565b905038612a20565b61044c6129b7565b6003612ae76104629261168e606090565b0161235a565b61044c90612b086000805160206146f18339815191526134bc565b611bc690600080516020614731833981519152613785565b61044c90612aed565b612b37610462826038611270565b91612b44600184016115a6565b612b516118bb6000610fdd565b1180809481612b99575b5092612b676000610fdd565b81119182612b90575b5081612b7a575090565b9050612b8c6118bb61046260366115a6565b1090565b15915038612b70565b612ba39150611e36565b38612b5b565b90612bc46020612bbc61241c6003611509565b61242961263e565b03915afa80156124e957612be6916000916127a957506001600160a01b031690565b3303611f5b5761044c919061044c91612bfd6134c7565b612c18565b6104c16104626104629290565b61046290612c02565b90612c2661241c6003611509565b6020612c3160405190565b63d57f7aa360e01b815260206004820152600b60248201526a08aa8909ea6beac9eaa86960ab1b604482015291829060649082905afa9081156124e957600091612d1e575b50612c846104c16000612c0f565b6001600160a01b03821603612ca3575b5061216b61044c92603b610e37565b61241c612caf91610ec4565b91823b1561043a576000612cc260405190565b93849063987088f160e01b8252818381612ce0888860048401611c54565b03925af19081156124e95761044c9361216b92612d00575b509250612c94565b612d18906000612d108183610b92565b8101906106e1565b38612cf8565b612d37915060203d6020116124e2576124d48183610b92565b38612c76565b9061044c91612ba9565b612d59602061242161241c6003611509565b03915afa9081156124e957600091612d83575b50612d7633612467565b036124815761044c612da2565b612d9c915060203d6020116124e2576124d48183610b92565b38612d6c565b61044c613e98565b61044c612d47565b61044c90612dcd6000805160206146f18339815191526134bc565b611bc6906000805160206146f1833981519152612dfc612df6612df06000610fdd565b83612e76565b826136d2565b50613785565b61044c90612db2565b61044c929190612e286000805160206147318339815191526134bc565b9061044c9291612e366134c7565b91929160005b82811015612e64578061178386612e5a61177e612e5f958888611735565b611d36565b612e3c565b5050509050565b9061044c9291612e0b565b90612eb261046261046293612e89600090565b5060007fc1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000611d64565b613ea9565b610462916000612ee2611e4e93612ecc600090565b5082600080516020614711833981519152611d64565b01610ecd565b9061044c91612ef56134c7565b612f06565b61044c91600091611eb2565b612f14611d78336039610ecd565b612f20611f0082612b29565b612282578115612fcc575b5061233d57612f4060036104cd836038611270565b90612f49825490565b831015612fba57612f818284612f6e612f68612f96976121a697611e76565b906108cf565b96612f7888613edd565b612fa057613f4d565b6121a16000612f91866039610ecd565b612efa565b6121b660006120d2565b612fb56001612fb089603a610ecd565b6122be565b613f4d565b6363df817160e01b6000908152600490fd5b905038612f2b565b9061044c91612ee8565b6002611667610462612ff29361168e600090565b612fff6118bb6000610fdd565b11908190565b61044c906130116134c7565b61044c9061301e816138a1565b613066565b91908203918211611c4f57565b61284961044c9461305f60609498979561304f608086019a6000870152565b6001600160a01b03166020850152565b6040830152565b613074611d78336039610ecd565b90613083610462836038611270565b9161308d83611e36565b61233d57603f60046130a6611d788561128e8686611270565b940180549094859181101580156131c0575b6131ae577fc99deea7564c4617f54b6c22cd96a31e991cc2f5524a5d19919cbeef7c4d2c639561314f6000612f918861128e89611850998960029961312e613128613124612f686131819f61310e61311e915490565b6131186001610fdd565b90613023565b86611e76565b9390565b8261401c565b6001600160a01b0386166001600160a01b03831603613196575b5050611270565b0161316561315f6126fc836115a6565b826117d4565b611d786131726000610fdd565b61216b8761128e88603c611270565b9261318b60405190565b938493339085613030565b61216b6131a79261128e8686611270565b3880613148565b63914f33db60e01b6000908152600490fd5b506131ce612f688284611e76565b6131e06001600160a01b038716612467565b14156130b8565b61044c90613005565b61320261046261046292612e89606090565b614085565b61321e90613214816124fe565b9490919215151590565b918215613261575b508115613259575b50613237575090565b63529f6aeb60e11b60009081526001600160a01b039091166004526024036000fd5b90503861322e565b915038613226565b61327b61046261046292612e89600090565b614096565b61044c9061329b6000805160206147318339815191526134bc565b61044c906132a76134c7565b6000612fb061044c92603a610ecd565b61044c90613280565b9061044c916132d16116b782611d40565b90611bc6916136d2565b9061044c916132c0565b9061044c916132f26134c7565b90613315611d7861044c9361128e61330e611d78336039610ecd565b6041611270565b612fd4565b9061044c916132e5565b61044c9061333f6000805160206147318339815191526134bc565b61044c9061334b6134c7565b61335681603e6117d4565b6133646118bb610800610fdd565b1161185557565b61044c90613324565b61046260a0610bb8565b613386613374565b906000825260208080808086016000815201600081520160608152016114f36114f8565b61046261337e565b9061044c61341060046133c3613374565b946133d66133d082611e36565b15158752565b6133e561164a600183016115a6565b6133f4611660600283016115a6565b61340a6134036003830161157e565b6060880152565b01611619565b6080840152565b610462906133b2565b6134286133aa565b50600061343481610fdd565b8214801561346c575b6134555750613450610462916038611270565b613417565b635595b91560e11b81526004810191909152602490fd5b5061347a61046260366115a6565b82101561343d565b637965db0b60e01b6001600160e01b03198216149081156134a1575090565b61046291506001600160e01b0319166301ffc9a760e01b1490565b61044c9033906140ac565b6134cf6129af565b6134d557565b63d93c066560e01b6000908152600490fd5b9061044c949392916134f76140db565b92909391600061350681612c0f565b6001600160a01b0381166001600160a01b0387161490811561359d575b8115613581575b5061357257506116fd61355d9361241c611bc69796946135499461411d565b6000805160206146f1833981519152613785565b50600080516020614731833981519152613785565b63d92e233d60e01b8152600490fd5b6001600160a01b031690506001600160a01b038516143861352a565b90506001600160a01b0381166001600160a01b0388161490613523565b9061044c949392916134e7565b61044c6140db565b61044c6135c7565b906135e1826124fe565b9492909150156136025763b62e454560e01b60009081526004859052602490fd5b91929091156136b157905b61361c8261216b836039610ecd565b6136738161218460036104cd86603861364182600161363b8285611270565b016117d4565b61365861364e8383611270565b60024291016117d4565b61292561366560376115a6565b60026004611b788686611270565b61367f6121a683610fdd565b907f9f150021d8fd04eb219a39e3019273bddf99a9a2e434dd2aa8132e32ea9244b06136aa60405190565b600090a390565b506136bc60366115a6565b906136cd6127016126fc60366115a6565b61360d565b907fc1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000906136ff8184614127565b928361370c575b50505090565b6137229261371d9161046291610e37565b6141bd565b50388080613706565b613734816141ea565b6137446118bb610462603e6115a6565b1161374c5750565b635a092a1d60e11b6000908152600491909152602490fd5b61044c90613771816138a1565b61044c9060006116d6565b61044c90613764565b907fc1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000906137b2818461422a565b92836137be5750505090565b613722926137cf9161046291610e37565b61429b565b906137e3610462836038611270565b603f91600060046137fb611d788461128e8989611270565b9301019161380a610462845490565b811015801561387a575b6131ae5761044c9460009461292561128e9361386387613844612f6861383e61310e612f919c5490565b83611e76565b836001600160a01b038a166001600160a01b0383160361386957505090565b9061401c565b61216b6104629261128e8a8a611270565b50613888612f688285611e76565b61389a6001600160a01b038416612467565b1415613814565b6000906138b36124676104c184612c0f565b146135725750565b610465906001600160a01b031660601b90565b602093926138e66014836138e688956104cd976138bb565b01918252565b613917919061390b6138fd60405190565b9485936020850193846138ce565b90810382520382610b92565b613929613922825190565b9160200190565b2090565b90916104cd9083908093610be8565b61105790602094936104cd9361392d565b90916112179061395c60405190565b9384938461393c565b90916104629261394d565b9190613981611e4e83836002613965565b61398e5761044c92613a18565b63858c8a1f60e01b6000908152600490fd5b9050519061044c82610cba565b9060208282031261043a57610462916139a0565b91906110dc816139d8816104cd9560209181520190565b8095610be8565b613a0b6104629593949294613a04606084019660008501906001600160a01b03169052565b6020830152565b60408185039101526139c1565b91602082829394613a2f61241c61241c6001611509565b90613a3a6000611509565b613a5f613a4660405190565b96879586948594622ea33360e31b8652600486016139df565b03915afa80156124e957613a7991600091613aa057501590565b613a8e57612fb060019161044c936002613965565b638baa579f60e01b6000908152600490fd5b6119de915060203d602011613ac2575b613aba8183610b92565b8101906139ad565b503d613ab0565b9061044c9291613970565b613ae46003612157836038611270565b613af46118bb610462603d6115a6565b11613afc5750565b63b315b31d60e01b6000908152600491909152602490fd5b613b1c6142bb565b61044c613b4b6000807fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300612307565b7f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa61185033610900565b61044c613b14565b613b8630610ec4565b7f000000000000000000000000000000000000000000000000000000000000000090613bba6001600160a01b038316612467565b14908115613bdc575b50613bca57565b63703e46dd60e11b6000908152600490fd5b9050613bf9612467613bec6142de565b926001600160a01b031690565b141538613bc3565b61044c90613c1c6000805160206146f18339815191526134bc565b61044c906138a1565b61044c90613c01565b9050519061044c8261049a565b9060208282031261043a5761046291613c2e565b90613c5c61241c83610ec4565b906020613c6860405190565b6352d1902d60e01b815292839060049082905afa60009281613ced575b50613cb75750506001613c955750565b634c9c8ce360e01b60009081526001600160a01b039091166004526024036000fd5b909291613cc5610462612560565b8403613cd65761044c9293506142f4565b632a87526960e21b60009081526004859052602490fd5b613d1091935060203d602011613d17575b613d088183610b92565b810190613c3b565b9138613c85565b503d613cfe565b613d2730610ec4565b613d596001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016612467565b03613bca57565b61044c90613d6d816138a1565b61044c9060016116d6565b61044c90613d60565b613d9a60026004613d93846038611270565b01016115a6565b90600091613daa6118bb84610fdd565b14613db3575050565b6314ac648360e21b82526004820152602490fd5b90613dda611d788261128e85603c611270565b91600092613dea6118bb85610fdd565b03613df457505050565b63330b0ff560e01b8352829161186e9160048401612838565b6104627fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300611e36565b613e3e6134c7565b61044c613e6e600160007fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300612307565b7f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25861185033610900565b61044c613e36565b61046290610fdd565b613ed3613ece61046293613ec96000613ed895613ec4600090565b500190565b61435d565b613ea0565b612c02565b610ebb565b336001600160a01b03821614613ef05750565b61186e90613efd60405190565b6366d2949d60e01b81526001600160a01b03909116600482015260406024820152601560448201527420b2323932b9b9901e9e9036b9b39739b2b73232b960591b60648201529081906084820190565b612f9160009161044c93613f64612f688383611e76565b91613f86613f7f612f68613f7961310e865490565b85611e76565b928261401c565b6001600160a01b0383166001600160a01b03831603613fb3575b505061128e61330e611d78336039610ecd565b61216b613fcb9261128e61330e611d78336039610ecd565b3880613fa0565b634e487b7160e01b600052603160045260246000fd5b61044c91600091612061565b8054801561401757600019019061401461400e8383611e76565b90613fe8565b55565b613fd2565b90614028610462825490565b8210156140735761044c9161404161046261310e845490565b810361404e575b50613ff4565b61406d9061209d614066612f6861311e61310e875490565b9184611e76565b38614048565b634e23d03560e01b6000908152600490fd5b606090614091906143e6565b905090565b6140a7600061046292613ec4600090565b6143fb565b906140ba6119de8284612eb7565b6140c2575050565b63e2517d3f60e01b600090815291611d219160046123ed565b6140e66119de61440f565b6140ec57565b631afcd79f60e31b6000908152600490fd5b9061044c9161410b6140db565b9061411861044c9261377c565b613d78565b9061044c916140fe565b6000805160206147118339815191526141408383612eb7565b156141a3576000612fb08482612ee2868261415b9701610e37565b61416f6121a6614169339390565b93610ec4565b917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b61419a60405190565b600090a4600190565b505050600090565b6104629081906001600160a01b031681565b906141e56118bb6121a16141e06000610462966141d8600090565b500194610ebb565b6141ab565b614473565b6104629061421e600160046128fc60389461422461420e600285613d93858b611270565b61421e6000866128fc868c611270565b90611c39565b95611270565b6000805160206147118339815191526142466119de8484612eb7565b156141a3576001612fb0846000612ee286826142629701610e37565b6142706121a6614169339390565b917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d61419a60405190565b906142b66118bb6121a16141e06000610462966141d8600090565b614542565b6142c66119de6129af565b6142cc57565b638dfc202b60e01b6000908152600490fd5b61046260006142ee610462612560565b01611509565b906142fe82614583565b61430782610ec4565b7fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b61433160405190565b600090a280516143446118bb6000610fdd565b111561435357611bc691614610565b505061044c6145ce565b6104629160006143769261436f600090565b5001611e76565b906106f7565b9061438b6115226104e0845490565b9060005b81811061439c5750505090565b9091926143b16115656001926115e5876115a6565b92910161438f565b906104629161437c565b9061044c611598926143d460405190565b938480926143b9565b610462906143c3565b6000610462916143f4606090565b50016143dd565b600061046291614409600090565b50015490565b6104627ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006118f4565b9190611ec36104626116ed9390565b61044c91600091614438565b8054801561401757600019019061401461446d8383611e76565b90614447565b90614484611d788260018501610e37565b61448e6000610fdd565b81146141a357612f91610462926000926144e195611d6460019788936144bc6144b686610fdd565b82613023565b888501916144d46144cb845490565b61311889610fdd565b8083036144e65750505090565b614453565b61450961450f6145179461450061437661216b9589611e76565b92839188611e76565b90614438565b888801610e37565b388080613706565b90815491600160401b831015610bb3578261450991600161044c95018155611e76565b61454f6119de8383614637565b1561457c576145779161216b9060016145708461456c848261451f565b5490565b9301610e37565b600190565b5050600090565b6000813b6145936118bb83610fdd565b146145ae579061044c916145a8610462612560565b016116d6565b634c9c8ce360e01b81526001600160a01b03919091166004820152602490fd5b60006145d981610fdd565b34116145e25750565b63b398979f60e01b8152600490fd5b3d1561460b576146003d61104a565b903d6000602084013e565b606090565b6000806104629361461f606090565b50602081519101845af46146316145f1565b91614662565b614650916001611d7892614649600090565b5001610e37565b61465d6118bb6000610fdd565b141590565b9061466d57506146c1565b81519060009161467f6118bb84610fdd565b14806146ac575b61468f57505090565b639996b31560e01b82526001600160a01b03166004820152602490fd5b50803b6146bb6118bb84610fdd565b14614686565b80516000906146d26118bb83610fdd565b11156146e15750805190602001fd5b63d6bda27560e01b8152600490fdfeb19546dff01e856fb3f010c267a7b1c60363cf8a4664e21cc89c26224620214e02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800a49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775a26469706673582212205fe48a9c7be93ce6d6d27cc0adede0c3ccb52b1cc97d9e2513ae40d3070d2d3764736f6c634300081a0033
Deployed Bytecode
0x6080604052600436101561001257600080fd5b60003560e01c806301ffc9a71461042257806305c3d5221461041d57806308473f64146104185780630db4a5bb146104135780630fa5a1d41461040e57806310c7080114610409578063123a9e87146104045780631459457a146103ff5780631785f53c146103fa5780631e95c11d146103f557806320d26736146103f0578063248a9ca3146103eb57806325d0b09e146103e65780632bdd87ff146103e15780632cb3e494146103dc5780632f2ff15d146103d757806333bc3ccf146103d257806334ca272c146103cd57806336568abe146103c85780633ba0a718146103c35780633ba836a2146103be5780633eb3ebd8146103b95780633f4ba83a146103b45780634a5b307d146103af5780634f1ef286146103aa57806352d1902d146103a55780635667981b146103a0578063570f85541461039b5780635c76577c146103965780635c975abb146103915780635d649cbe1461038c578063665de3cb14610387578063704802751461038257806375b238fc1461037d57806376f3da32146103785780637ca64266146103735780637dec74d71461036e5780638456cb5914610369578063880cdc311461036457806389a9132b1461035f5780638bcebdb81461035a5780639010d07c1461035557806391d1485414610350578063923160db1461034b578063a12c64b514610346578063a217fddf14610341578063a238f7b41461033c578063a3246ad314610337578063ad3cb1cc14610332578063ade9ae971461032d578063b07b7e7f14610328578063b8861c2314610323578063bb10c8291461031e578063bd7029fd14610319578063c33b84f314610314578063ca15c8731461030f578063d1a445851461030a578063d547741f14610305578063e01290a114610300578063e58378bb146102fb578063eed38b86146102f6578063ef43acef146102f1578063f08f4f64146102ec578063f275cda9146102e75763fde919f60361043a57611480565b611459565b611426565b6113a6565b611382565b61135b565b611342565b61130d565b6112f5565b6112da565b6112bf565b611293565b611255565b611191565b611176565b611143565b6110f7565b61102f565b611017565b610ffc565b610fc5565b610fac565b610f6d565b610f51565b610f38565b610ee7565b610e8f565b610e77565b610e5c565b610e1e565b610dd8565b610d92565b610d7a565b610d5f565b610d47565b610d2c565b610d14565b610cf8565b610ca2565b610c87565b610c73565b610b4b565b610afa565b610ad3565b610a63565b610a47565b6109b0565b610982565b61094f565b610936565b6108e5565b6108a3565b610887565b61086c565b610853565b610809565b6107a2565b610786565b61070e565b6106c9565b6106b1565b610698565b610623565b6105cc565b610469565b6001600160e01b031981165b0361043a57565b600080fd5b9050359061044c82610427565b565b9060208282031261043a576104629161043f565b90565b9052565b3461043a5761049661048461047f36600461044e565b61149b565b60405191829182901515815260200190565b0390f35b80610433565b9050359061044c8261049a565b9060208282031261043a57610462916104a0565b6001600160a01b031690565b0190565b906104f16104ea6104e0845190565b8084529260200190565b9260200190565b9060005b8181106105025750505090565b90919261052861052160019286516001600160a01b0316815260200190565b9460200190565b9291016104f5565b9061053f6104ea6104e0845190565b9060005b8181106105505750505090565b9091926105666105216001928651815260200190565b929101610543565b90610462906060806105a461059260808501600088015186820360008801526104d1565b60208701518582036020870152610530565b946105b460408201516040860152565b0151910152565b60208082526104629291019061056e565b3461043a576104966105e76105e23660046104ad565b61167d565b604051918291826105bb565b6001600160a01b038116610433565b9050359061044c826105f3565b9060208282031261043a5761046291610602565b3461043a5761063b61063636600461060f565b611704565b604051005b909182601f8301121561043a578135916001600160401b03831161043a57602001926020830284011161043a57565b9060208282031261043a5781356001600160401b03811161043a576106949201610640565b9091565b3461043a5761063b6106ab36600461066f565b90611794565b3461043a5761063b6106c43660046104ad565b611872565b3461043a5761063b6106dc3660046104ad565b6118df565b600091031261043a57565b610462916008021c81565b9061046291546106ec565b6104626000603d6106f7565b3461043a5761071e3660046106e1565b610496610729610702565b6040515b9182918290815260200190565b919060a08382031261043a576107508184610602565b9261075e8260208301610602565b9261046261076f8460408501610602565b93608061077f8260608701610602565b9401610602565b3461043a5761063b61079936600461073a565b93929092611b86565b3461043a5761063b6107b536600461060f565b611bc9565b906107c96104ea6104e0845190565b9060005b8181106107da5750505090565b9091926107f06105216001928651815260200190565b9291016107cd565b6020808252610462929101906107ba565b3461043a5761049661082461081f3660046104ad565b611bdb565b604051918291826107f8565b919060408382031261043a5761046290602061084c8286610602565b94016104a0565b3461043a5761063b610866366004610830565b90611d36565b3461043a576104966107296108823660046104ad565b611d40565b3461043a5761049661048461089d366004610830565b90611d6a565b3461043a5761063b6108b636600461060f565b611dee565b610462916008021c6001600160a01b031690565b9061046291546108bb565b6104626000806108cf565b3461043a576108f53660046106e1565b6104966109006108da565b604051918291826001600160a01b03909116815260200190565b919060408382031261043a5761046290602061077f82866104a0565b3461043a5761063b61094936600461091a565b90611e12565b3461043a5761063b6109623660046104ad565b611f7d565b901515815260408101929161044c916020905b019015159052565b3461043a5761099a6109953660046104ad565b611f86565b906104966109a760405190565b92839283610967565b3461043a5761063b6109c336600461091a565b90611fb3565b909182601f8301121561043a578135916001600160401b03831161043a57602001926001830284011161043a57565b9060808282031261043a57610a0d8183610602565b92610a1b82602085016104a0565b92610a2983604083016104a0565b9260608201356001600160401b03811161043a5761069492016109c9565b3461043a5761063b610a5a3660046109f8565b93929092612298565b3461043a57610a733660046106e1565b61063b612352565b90610a8a6104ea6104e0845190565b9060005b818110610a9b5750505090565b909192610aba61052160019286516001600160a01b0316815260200190565b929101610a8e565b602080825261046292910190610a7b565b3461043a57610496610aee610ae93660046104ad565b612363565b60405191829182610ac2565b3461043a57610b0a3660046106e1565b61063b6124f6565b610b4761044c94610b3e606094989795610b35608086019a600087019015159052565b15156020850152565b15156040830152565b0152565b3461043a57610496610b66610b6136600461060f565b6124fe565b90610b7394929460405190565b94859485610b12565b634e487b7160e01b600052604160045260246000fd5b90601f01601f191681019081106001600160401b03821117610bb357604052565b610b7c565b9061044c610bc560405190565b9283610b92565b6001600160401b038111610bb357602090601f01601f19160190565b90826000939282370152565b90929192610c09610c0482610bcc565b610bb8565b938185528183011161043a5761044c916020850190610be8565b9080601f8301121561043a5781602061046293359101610bf4565b91909160408184031261043a57610c558382610602565b9260208201356001600160401b03811161043a576104629201610c23565b61063b610c81366004610c3e565b90612545565b3461043a57610c973660046106e1565b610496610729612592565b3461043a5761063b610cb536600461060f565b6125c0565b801515610433565b9050359061044c82610cba565b909160608284031261043a57610462610ce88484610cc2565b93604061084c8260208701610602565b3461043a57610496610729610d0e366004610ccf565b916127c8565b3461043a5761063b610d2736600461060f565b6129a6565b3461043a57610d3c3660046106e1565b6104966104846129af565b3461043a57610d573660046106e1565b61063b612ace565b3461043a57610496610aee610d753660046104ad565b612ad6565b3461043a5761063b610d8d36600461060f565b612b20565b3461043a57610da23660046106e1565b610496600080516020614731833981519152610729565b9015158152901515602082015260608101929161044c9160409061097a565b3461043a57610496610df3610dee3660046104ad565b612b29565b60405191939193849384610db9565b919060408382031261043a5761046290602061084c82866104a0565b3461043a5761063b610e31366004610e02565b90612d3d565b905b600052602052604060002090565b6000610e5761046292603b610e37565b6106f7565b3461043a57610496610729610e723660046104ad565b610e47565b3461043a57610e873660046106e1565b61063b612daa565b3461043a5761063b610ea236600461060f565b612e02565b610462906104c1906001600160a01b031682565b61046290610ea7565b61046290610ebb565b90610e3990610ec4565b6000610e57610462926039610ecd565b3461043a57610496610729610efd36600461060f565b610ed7565b9160408383031261043a5782356001600160401b03811161043a5782610f2f602094610462938701610640565b949095016104a0565b3461043a5761063b610f4b366004610f02565b91612e6b565b3461043a57610496610900610f67366004610e02565b90612e76565b3461043a57610496610484610f8336600461091a565b90612eb7565b919060408382031261043a57610462906020610fa582866104a0565b9401610cc2565b3461043a5761063b610fbf366004610f89565b90612fd4565b3461043a5761099a610fd83660046104ad565b612fde565b6104626104626104629290565b6104626000610fdd565b610462610fea565b3461043a5761100c3660046106e1565b610496610729610ff4565b3461043a5761063b61102a36600461060f565b6131e7565b3461043a57610496610aee6110453660046104ad565b6131f0565b90611057610c0483610bcc565b918252565b611066600561104a565b640352e302e360dc1b602082015290565b61046261105c565b610462611077565b61046261107f565b60005b8381106110a25750506000910152565b8181015183820152602001611092565b6110d36110dc6020936104cd936110c7815190565b80835293849260200190565b9586910161108f565b601f01601f191690565b6020808252610462929101906110b2565b3461043a576111073660046106e1565b610496611112611087565b604051918291826110e6565b610462600060036108cf565b61046590610ec4565b60208101929161044c919061112a565b3461043a576111533660046106e1565b61049661115e61111e565b60405191829182611133565b6104626000603e6106f7565b3461043a576111863660046106e1565b61049661072961116a565b3461043a576104966107296111a736600461060f565b613207565b9060208282031261043a5781356001600160401b03811161043a576104629201610c23565b6104cd6111e9926020926111e3815190565b94859290565b9384910161108f565b6110576104cd91602094936111d1565b61121761120e60405190565b928392836111f2565b03902090565b61046291611202565b610462916008021c5b60ff1690565b906104629154611226565b600061125061046292600261121d565b611235565b3461043a5761049661048461126b3660046111ac565b611240565b90610e3990610fdd565b610e576104629261128e600093603c611270565b610ecd565b3461043a576104966107296112a936600461091a565b9061127a565b600061125061046292603a610ecd565b3461043a576104966104846112d536600461060f565b6112af565b3461043a576104966107296112f03660046104ad565b613269565b3461043a5761063b61130836600461060f565b6132b7565b3461043a5761063b61132036600461091a565b906132db565b919060408382031261043a57610462906020610fa58286610602565b3461043a5761063b611355366004611326565b9061331a565b3461043a5761136b3660046106e1565b6104966000805160206146f1833981519152610729565b3461043a5761063b6113953660046104ad565b61336b565b610462600060366106f7565b3461043a576113b63660046106e1565b61049661072961139a565b80511515825261046291608061140460a083016113e360208601516020860152565b6113f260408601516040860152565b606085015184820360608601526104d1565b92015190608081840391015261056e565b6020808252610462929101906113c1565b3461043a5761049661144161143c3660046104ad565b613420565b60405191829182611415565b610462600060376106f7565b3461043a576114693660046106e1565b61049661072961144d565b610462600060016108cf565b3461043a576114903660046106e1565b610496610900611474565b635a05180f60e01b6001600160e01b03198216149081156114ba575090565b6104629150613482565b6104626080610bb8565b6114d66114c4565b906060825260208080808501606081520160008152016000905250565b905250565b6104626114ce565b610462906104c1565b6104629054611500565b9061152e6115226104e0845490565b92600052602060002090565b9060005b81811061153f5750505090565b90919261156c61156560019261155487611509565b6001600160a01b0316815260200190565b9460010190565b929101611532565b9061046291611513565b9061044c6115989261158f60405190565b93848092611574565b0383610b92565b6104629081565b610462905461159f565b906115bf6115226104e0845490565b9060005b8181106115d05750505090565b9091926115ed6115656001926115e5876115a6565b815260200190565b9291016115c3565b90610462916115b0565b9061044c6115989261161060405190565b938480926115f5565b9061044c61166d600361162a6114c4565b9461163b6116378261157e565b8752565b61165161164a600183016115ff565b6020880152565b611667611660600283016115a6565b6040880152565b016115a6565b6060840152565b61046290611619565b60046116966104629261168e6114f8565b506038611270565b01611674565b61044c906116bc6000805160206147318339815191526134bc565b6134bc565b6116f1565b906001600160a01b03905b9181191691161790565b906116e66104626116ed92610ec4565b82546116c1565b9055565b6116fd61044c91610ec4565b60036116d6565b61044c9061169c565b9061044c9161171a6134c7565b611754565b634e487b7160e01b600052603260045260246000fd5b9190811015611745576020020190565b61171f565b35610462816105f3565b9190916117616000610fdd565b8381101561178e5780611783610d2761177e611789948887611735565b61174a565b60010190565b611761565b50509050565b9061044c9161170d565b61044c906117b96000805160206147318339815191526134bc565b61044c906117c56134c7565b611804565b90600019906116cc565b906117e46104626116ed92610fdd565b82546117ca565b61046590610fdd565b60208101929161044c91906117eb565b61180f8160376117d4565b61181c610462603e6115a6565b8111611855576118507f15aea1769167f83f5fc9ac1bedf10a18732f8a29fc4f74ba0b927418d12fa4ce9161072d60405190565b0390a1565b635a092a1d60e11b60009081528061186e8160046117f4565b0390fd5b61044c9061179e565b61044c906118966000805160206147318339815191526134bc565b61044c906118a26134c7565b6118ad81603d6117d4565b6118bf6118bb610800610fdd565b9190565b116118c657565b63b315b31d60e01b60009081528061186e8160046117f4565b61044c9061187b565b6104629060401c61122f565b61046290546118e8565b610462905b6001600160401b031690565b61046290546118fe565b6119036104626104629290565b906001600160401b03906116cc565b611903610462610462926001600160401b031690565b9061195b6104626116ed92611935565b8254611926565b9068ff00000000000000009060401b6116cc565b906119866104626116ed92151590565b8254611962565b61046590611919565b60208101929161044c919061198d565b92939091907ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009485926119e86119e26119de866118f4565b1590565b9461190f565b946000966119f588611919565b6001600160401b0388161480611aee575b600197611a22611a158a611919565b916001600160401b031690565b149081611aca575b155b9081611ac1575b50611ab25790611a609493929186611a578a611a4e8b611919565b9c019b8c61194b565b611aa357611af5565b611a6957505050565b611a97611850927fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d294611976565b60405191829182611996565b611aad888b611976565b611af5565b63f92ee8a960e01b8852600488fd5b15905038611a33565b9050611a2c611ad830610ec4565b3b611ae56118bb8c610fdd565b14919050611a2a565b5085611a06565b92611b059261044c9592856135ba565b611b0d6135cf565b611b22611b1b610800610fdd565b603e6117d4565b611b36611b2f6080610fdd565b603d6117d4565b60036004611b786001611b5c611b4b82610fdd565b95611b578760366117d4565b6135d7565b50611b7f611b6a600a610fdd565b600285611b78856038611270565b01016117d4565b6038611270565b9061044c949392916119a6565b61044c90611bae6000805160206146f18339815191526134bc565b611bc6906000805160206147318339815191526136d2565b50565b61044c90611b93565b610462906115ff565b60016004611bee6104629361168e606090565b0101611bd2565b61044c9190611c116000805160206147318339815191526134bc565b9061044c91611c1e6134c7565b611c67565b634e487b7160e01b600052601160045260246000fd5b9190611c44565b9290565b8201809211611c4f57565b611c23565b90815260408101929161044c9160200152565b9190611c7f611c75846124fe565b9390919215151590565b918215611d2e575b508115611d26575b50611d02577fac52b63ac21eb6a0502ba1afddeff39668a3e6c7ceb05367e6563c8bd14cd805929350611ce460026004611cca846038611270565b0101611cde84611cd9836115a6565b611c39565b906117d4565b611ced8161372b565b611850611cf960405190565b92839283611c54565b63529f6aeb60e11b60009081526001600160a01b038516600452602490fd5b036000fd5b905038611c8f565b915038611c87565b9061044c91611bf5565b600161166761046292611d51600090565b5060006000805160206147118339815191525b01610e37565b611d7d611d78826039610ecd565b6115a6565b600090611d8c6118bb83610fdd565b14611daa57506118bb611c40611d78611da6936039610ecd565b1490565b63529f6aeb60e11b81526001600160a01b03919091166004820152602490fd5b61044c90611de56000805160206147318339815191526134bc565b61044c9061377c565b61044c90611dca565b9061044c91611e086116b782611d40565b90611bc691613785565b9061044c91611df7565b61044c90611e286134c7565b611e40565b6104629061122f565b6104629054611e2d565b611e53611e4e33603a610ecd565b611e36565b611e605761044c90611ef4565b630ad2d8d560e31b600090815233600452602490fd5b805482101561174557611e90600191600052602060002090565b91020190600090565b9160001960089290920291821b911b6116cc565b921b90565b9190611ec36104626116ed93610fdd565b908354611e99565b90815491600160401b831015610bb35782611eee91600161044c95018155611e76565b90611eb2565b611f08611f0082612b29565b909290911590565b918215611f75575b508115611f6d575b50611f5b5761044c90611f2b33826137d4565b611f5660016004611f51611f3e336135d7565b94611b7f81600385611b788a6038611270565b010190565b611ecb565b636edaef2f60e11b6000908152600490fd5b905038611f18565b915038611f10565b61044c90611e1c565b611f8f90612b29565b8291939291611faa575b83611fa357509190565b9092501590565b80159150611f99565b90611fbd336104c1565b6001600160a01b03821603611fd557611bc6916136d2565b63334bd91960e11b6000908152600490fd5b9061044c94939291611ff76134c7565b9061044c94939291612008816138a1565b949392919061201b611e4e87603a610ecd565b6120295761044c94956120db565b630ad2d8d560e31b60009081526001600160a01b038716600452602490fd5b916001600160a01b0360089290920291821b911b6116cc565b91906120726104626116ed93610ec4565b908354612048565b90815491600160401b831015610bb3578261209d91600161044c95018155611e76565b90612061565b634e487b7160e01b600052602160045260246000fd5b600211156120c357565b6120a3565b9061044c826120b9565b610462906120c8565b93919290926120ee611d78336039610ecd565b84900361228257612101611f0085612b29565b61226b578115612263575b506122115761211a856124fe565b91505081612206575b506121e7576121a692849261213f612144936121ac97896138ec565b613ac9565b61217061215c6003612157846038611270565b015490565b61216b8661128e856041611270565b6117d4565b6121898461218460036104cd856038611270565b61207a565b6121988161216b866039610ecd565b6121a181613ad4565b610fdd565b91610ec4565b6121b660016120d2565b917fcdc318b415c4f74ecd1a3bbaf637fe42594b23d3e706bdea4031084fed4f6c736121e160405190565b600090a4565b639e83c77d60e01b60009081526001600160a01b038616600452602490fd5b859150141538612123565b61186e8461221e60405190565b630742992760e11b81529182916004830190815260406020820181905260139082015272141c9bd99a5b19481a5cc8185c98da1a5d9959606a1b606082015260800190565b90503861210c565b635595b91560e11b60009081526004879052602490fd5b63529f6aeb60e11b600090815233600452602490fd5b9061044c94939291611fe7565b6122ad6134c7565b61044c6122d5565b9060ff906116cc565b906122ce6104626116ed92151590565b82546122b5565b6122e1611c75336124fe565b61228257811561234a575b5061233d57806121a16001600061230761230d956038611270565b016122be565b7fe2a74f634d4a5de5ebf5fc6985a469779d12830dd517ce1a08acb22847cdc3ee61233760405190565b600090a2565b61186e9061221e60405190565b9050386122ec565b61044c6122a5565b6104629061157e565b600060046123766104629361168e606090565b010161235a565b612387601961104a565b7f4554484f535f494e544552414354494f4e5f434f4e54524f4c00000000000000602082015290565b61046261237d565b6104626123b0565b9050519061044c826105f3565b9060208282031261043a57610462916123c0565b6040513d6000823e3d90fd5b6001600160a01b03909116815260408101929161044c9160200152565b61244e602061242161241c6003611509565b610ec4565b6124296123b8565b9061243360405190565b9384928391829163d57f7aa360e01b5b8352600483016110e6565b03915afa9081156124e9576000916124ba575b50612474335b916001600160a01b031690565b036124815761044c6124ee565b63e2517d3f60e01b6000908152611d217f261fea28325784de45eba41a3fbb84c4196fbab02ee802566edfe26616ba5afb3360046123ed565b6124dc915060203d6020116124e2575b6124d48183610b92565b8101906123cd565b38612461565b503d6124ca565b6123e1565b61044c613b75565b61044c61240a565b611d786125159161250d600090565b506039610ecd565b61251e81612b29565b9193909290565b9061044c91612532613b7d565b9061044c9161254081613c25565b613c4f565b9061044c91612525565b6104629061255b613d1e565b612589565b6104627f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc610fdd565b50610462612560565b610462600061254f565b61044c906125b76000805160206147318339815191526134bc565b61044c90613d78565b61044c9061259c565b906104629392916125d86134c7565b612655565b6125e7600c61104a565b6b4554484f535f52455649455760a01b602082015290565b6104626125dd565b6104626125ff565b612619601161104a565b7022aa2427a9afa0aa2a22a9aa20aa24a7a760791b602082015290565b61046261260f565b610462612636565b6000198114611c4f5760010190565b5092919061266661241c6003611509565b612692602063d57f7aa392612679612607565b9061268360405190565b80809581946124438960e01b90565b03915afa80156124e9576126b5916000916127a9575b506001600160a01b031690565b3314159081612747575b50611f5b57612708916126d260366115a6565b80928196600014612732575061216b6126ec92603b610e37565b6121a16127016126fc60366115a6565b612646565b60366117d4565b7f3fd0c2393f291765470cc17997e368eb7ec635d8966232abd9d2016e8ad721cf61233760405190565b612742925061216b906039610ecd565b6126ec565b61277e915060209061275c61241c6003611509565b61244361276761263e565b9261277160405190565b9586948593849360e01b90565b03915afa80156124e9576127a0916000916127a957506001600160a01b031690565b331415386126bf565b6127c2915060203d6020116124e2576124d48183610b92565b386126a8565b61046292919060006125c9565b61044c906127e16134c7565b61044c906127ee816138a1565b6127fc611e4e82603a610ecd565b6128095761044c90612858565b630ad2d8d560e31b60009081526001600160a01b039091166004526024036000fd5b8015611c4f576000190190565b90815260408101929161044c916020905b01906001600160a01b03169052565b61286d612864336124fe565b50909290911590565b91821561299e575b508115612996575b50611f5b5761288b816124fe565b5090508161298d575b5061296b577f9c6d622696d829eae44c9559a6e96ae07091e9ca797bc02c35a7770dbcf04580906128c9611d78336039610ecd565b6128d281613d81565b6128dc8282613dc7565b61293d6002600461292a8460386129116129026000866128fc8686611270565b01015490565b61216b8a61128e86603f611270565b61292588612184600087611f518787611270565b611270565b0101611cde612938826115a6565b61282b565b61295661294f8361128e84603c611270565b42906117d4565b61185061296260405190565b92839283612838565b639e83c77d60e01b60009081526001600160a01b039091166004526024036000fd5b15905038612894565b90503861287d565b915038612875565b61044c906127d5565b610462613e0d565b6129bf6134c7565b61044c612a00565b919060086116cc910291611ead60ff841b90565b91906129ec6104626116ed93151590565b9083546129c7565b61044c916000916129db565b612a13612a0c336124fe565b9390921590565b6122825715908115612ac6575b50612a6a57806121a1600080612a3a612a40956038611270565b016129f4565b7fad0c07c6e15db6a51fed6c1d42d277ed0fa53f2eef0d1830c060c1cba8638adc61233760405190565b61186e90612a7760405190565b630742992760e11b8152918291600483019081526040602082018190526017908201527f50726f66696c65206973206e6f74206172636869766564000000000000000000606082015260800190565b905038612a20565b61044c6129b7565b6003612ae76104629261168e606090565b0161235a565b61044c90612b086000805160206146f18339815191526134bc565b611bc690600080516020614731833981519152613785565b61044c90612aed565b612b37610462826038611270565b91612b44600184016115a6565b612b516118bb6000610fdd565b1180809481612b99575b5092612b676000610fdd565b81119182612b90575b5081612b7a575090565b9050612b8c6118bb61046260366115a6565b1090565b15915038612b70565b612ba39150611e36565b38612b5b565b90612bc46020612bbc61241c6003611509565b61242961263e565b03915afa80156124e957612be6916000916127a957506001600160a01b031690565b3303611f5b5761044c919061044c91612bfd6134c7565b612c18565b6104c16104626104629290565b61046290612c02565b90612c2661241c6003611509565b6020612c3160405190565b63d57f7aa360e01b815260206004820152600b60248201526a08aa8909ea6beac9eaa86960ab1b604482015291829060649082905afa9081156124e957600091612d1e575b50612c846104c16000612c0f565b6001600160a01b03821603612ca3575b5061216b61044c92603b610e37565b61241c612caf91610ec4565b91823b1561043a576000612cc260405190565b93849063987088f160e01b8252818381612ce0888860048401611c54565b03925af19081156124e95761044c9361216b92612d00575b509250612c94565b612d18906000612d108183610b92565b8101906106e1565b38612cf8565b612d37915060203d6020116124e2576124d48183610b92565b38612c76565b9061044c91612ba9565b612d59602061242161241c6003611509565b03915afa9081156124e957600091612d83575b50612d7633612467565b036124815761044c612da2565b612d9c915060203d6020116124e2576124d48183610b92565b38612d6c565b61044c613e98565b61044c612d47565b61044c90612dcd6000805160206146f18339815191526134bc565b611bc6906000805160206146f1833981519152612dfc612df6612df06000610fdd565b83612e76565b826136d2565b50613785565b61044c90612db2565b61044c929190612e286000805160206147318339815191526134bc565b9061044c9291612e366134c7565b91929160005b82811015612e64578061178386612e5a61177e612e5f958888611735565b611d36565b612e3c565b5050509050565b9061044c9291612e0b565b90612eb261046261046293612e89600090565b5060007fc1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000611d64565b613ea9565b610462916000612ee2611e4e93612ecc600090565b5082600080516020614711833981519152611d64565b01610ecd565b9061044c91612ef56134c7565b612f06565b61044c91600091611eb2565b612f14611d78336039610ecd565b612f20611f0082612b29565b612282578115612fcc575b5061233d57612f4060036104cd836038611270565b90612f49825490565b831015612fba57612f818284612f6e612f68612f96976121a697611e76565b906108cf565b96612f7888613edd565b612fa057613f4d565b6121a16000612f91866039610ecd565b612efa565b6121b660006120d2565b612fb56001612fb089603a610ecd565b6122be565b613f4d565b6363df817160e01b6000908152600490fd5b905038612f2b565b9061044c91612ee8565b6002611667610462612ff29361168e600090565b612fff6118bb6000610fdd565b11908190565b61044c906130116134c7565b61044c9061301e816138a1565b613066565b91908203918211611c4f57565b61284961044c9461305f60609498979561304f608086019a6000870152565b6001600160a01b03166020850152565b6040830152565b613074611d78336039610ecd565b90613083610462836038611270565b9161308d83611e36565b61233d57603f60046130a6611d788561128e8686611270565b940180549094859181101580156131c0575b6131ae577fc99deea7564c4617f54b6c22cd96a31e991cc2f5524a5d19919cbeef7c4d2c639561314f6000612f918861128e89611850998960029961312e613128613124612f686131819f61310e61311e915490565b6131186001610fdd565b90613023565b86611e76565b9390565b8261401c565b6001600160a01b0386166001600160a01b03831603613196575b5050611270565b0161316561315f6126fc836115a6565b826117d4565b611d786131726000610fdd565b61216b8761128e88603c611270565b9261318b60405190565b938493339085613030565b61216b6131a79261128e8686611270565b3880613148565b63914f33db60e01b6000908152600490fd5b506131ce612f688284611e76565b6131e06001600160a01b038716612467565b14156130b8565b61044c90613005565b61320261046261046292612e89606090565b614085565b61321e90613214816124fe565b9490919215151590565b918215613261575b508115613259575b50613237575090565b63529f6aeb60e11b60009081526001600160a01b039091166004526024036000fd5b90503861322e565b915038613226565b61327b61046261046292612e89600090565b614096565b61044c9061329b6000805160206147318339815191526134bc565b61044c906132a76134c7565b6000612fb061044c92603a610ecd565b61044c90613280565b9061044c916132d16116b782611d40565b90611bc6916136d2565b9061044c916132c0565b9061044c916132f26134c7565b90613315611d7861044c9361128e61330e611d78336039610ecd565b6041611270565b612fd4565b9061044c916132e5565b61044c9061333f6000805160206147318339815191526134bc565b61044c9061334b6134c7565b61335681603e6117d4565b6133646118bb610800610fdd565b1161185557565b61044c90613324565b61046260a0610bb8565b613386613374565b906000825260208080808086016000815201600081520160608152016114f36114f8565b61046261337e565b9061044c61341060046133c3613374565b946133d66133d082611e36565b15158752565b6133e561164a600183016115a6565b6133f4611660600283016115a6565b61340a6134036003830161157e565b6060880152565b01611619565b6080840152565b610462906133b2565b6134286133aa565b50600061343481610fdd565b8214801561346c575b6134555750613450610462916038611270565b613417565b635595b91560e11b81526004810191909152602490fd5b5061347a61046260366115a6565b82101561343d565b637965db0b60e01b6001600160e01b03198216149081156134a1575090565b61046291506001600160e01b0319166301ffc9a760e01b1490565b61044c9033906140ac565b6134cf6129af565b6134d557565b63d93c066560e01b6000908152600490fd5b9061044c949392916134f76140db565b92909391600061350681612c0f565b6001600160a01b0381166001600160a01b0387161490811561359d575b8115613581575b5061357257506116fd61355d9361241c611bc69796946135499461411d565b6000805160206146f1833981519152613785565b50600080516020614731833981519152613785565b63d92e233d60e01b8152600490fd5b6001600160a01b031690506001600160a01b038516143861352a565b90506001600160a01b0381166001600160a01b0388161490613523565b9061044c949392916134e7565b61044c6140db565b61044c6135c7565b906135e1826124fe565b9492909150156136025763b62e454560e01b60009081526004859052602490fd5b91929091156136b157905b61361c8261216b836039610ecd565b6136738161218460036104cd86603861364182600161363b8285611270565b016117d4565b61365861364e8383611270565b60024291016117d4565b61292561366560376115a6565b60026004611b788686611270565b61367f6121a683610fdd565b907f9f150021d8fd04eb219a39e3019273bddf99a9a2e434dd2aa8132e32ea9244b06136aa60405190565b600090a390565b506136bc60366115a6565b906136cd6127016126fc60366115a6565b61360d565b907fc1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000906136ff8184614127565b928361370c575b50505090565b6137229261371d9161046291610e37565b6141bd565b50388080613706565b613734816141ea565b6137446118bb610462603e6115a6565b1161374c5750565b635a092a1d60e11b6000908152600491909152602490fd5b61044c90613771816138a1565b61044c9060006116d6565b61044c90613764565b907fc1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000906137b2818461422a565b92836137be5750505090565b613722926137cf9161046291610e37565b61429b565b906137e3610462836038611270565b603f91600060046137fb611d788461128e8989611270565b9301019161380a610462845490565b811015801561387a575b6131ae5761044c9460009461292561128e9361386387613844612f6861383e61310e612f919c5490565b83611e76565b836001600160a01b038a166001600160a01b0383160361386957505090565b9061401c565b61216b6104629261128e8a8a611270565b50613888612f688285611e76565b61389a6001600160a01b038416612467565b1415613814565b6000906138b36124676104c184612c0f565b146135725750565b610465906001600160a01b031660601b90565b602093926138e66014836138e688956104cd976138bb565b01918252565b613917919061390b6138fd60405190565b9485936020850193846138ce565b90810382520382610b92565b613929613922825190565b9160200190565b2090565b90916104cd9083908093610be8565b61105790602094936104cd9361392d565b90916112179061395c60405190565b9384938461393c565b90916104629261394d565b9190613981611e4e83836002613965565b61398e5761044c92613a18565b63858c8a1f60e01b6000908152600490fd5b9050519061044c82610cba565b9060208282031261043a57610462916139a0565b91906110dc816139d8816104cd9560209181520190565b8095610be8565b613a0b6104629593949294613a04606084019660008501906001600160a01b03169052565b6020830152565b60408185039101526139c1565b91602082829394613a2f61241c61241c6001611509565b90613a3a6000611509565b613a5f613a4660405190565b96879586948594622ea33360e31b8652600486016139df565b03915afa80156124e957613a7991600091613aa057501590565b613a8e57612fb060019161044c936002613965565b638baa579f60e01b6000908152600490fd5b6119de915060203d602011613ac2575b613aba8183610b92565b8101906139ad565b503d613ab0565b9061044c9291613970565b613ae46003612157836038611270565b613af46118bb610462603d6115a6565b11613afc5750565b63b315b31d60e01b6000908152600491909152602490fd5b613b1c6142bb565b61044c613b4b6000807fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300612307565b7f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa61185033610900565b61044c613b14565b613b8630610ec4565b7f0000000000000000000000002661710d76c872681efc25300de84138a6369e3a90613bba6001600160a01b038316612467565b14908115613bdc575b50613bca57565b63703e46dd60e11b6000908152600490fd5b9050613bf9612467613bec6142de565b926001600160a01b031690565b141538613bc3565b61044c90613c1c6000805160206146f18339815191526134bc565b61044c906138a1565b61044c90613c01565b9050519061044c8261049a565b9060208282031261043a5761046291613c2e565b90613c5c61241c83610ec4565b906020613c6860405190565b6352d1902d60e01b815292839060049082905afa60009281613ced575b50613cb75750506001613c955750565b634c9c8ce360e01b60009081526001600160a01b039091166004526024036000fd5b909291613cc5610462612560565b8403613cd65761044c9293506142f4565b632a87526960e21b60009081526004859052602490fd5b613d1091935060203d602011613d17575b613d088183610b92565b810190613c3b565b9138613c85565b503d613cfe565b613d2730610ec4565b613d596001600160a01b037f0000000000000000000000002661710d76c872681efc25300de84138a6369e3a16612467565b03613bca57565b61044c90613d6d816138a1565b61044c9060016116d6565b61044c90613d60565b613d9a60026004613d93846038611270565b01016115a6565b90600091613daa6118bb84610fdd565b14613db3575050565b6314ac648360e21b82526004820152602490fd5b90613dda611d788261128e85603c611270565b91600092613dea6118bb85610fdd565b03613df457505050565b63330b0ff560e01b8352829161186e9160048401612838565b6104627fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300611e36565b613e3e6134c7565b61044c613e6e600160007fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300612307565b7f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25861185033610900565b61044c613e36565b61046290610fdd565b613ed3613ece61046293613ec96000613ed895613ec4600090565b500190565b61435d565b613ea0565b612c02565b610ebb565b336001600160a01b03821614613ef05750565b61186e90613efd60405190565b6366d2949d60e01b81526001600160a01b03909116600482015260406024820152601560448201527420b2323932b9b9901e9e9036b9b39739b2b73232b960591b60648201529081906084820190565b612f9160009161044c93613f64612f688383611e76565b91613f86613f7f612f68613f7961310e865490565b85611e76565b928261401c565b6001600160a01b0383166001600160a01b03831603613fb3575b505061128e61330e611d78336039610ecd565b61216b613fcb9261128e61330e611d78336039610ecd565b3880613fa0565b634e487b7160e01b600052603160045260246000fd5b61044c91600091612061565b8054801561401757600019019061401461400e8383611e76565b90613fe8565b55565b613fd2565b90614028610462825490565b8210156140735761044c9161404161046261310e845490565b810361404e575b50613ff4565b61406d9061209d614066612f6861311e61310e875490565b9184611e76565b38614048565b634e23d03560e01b6000908152600490fd5b606090614091906143e6565b905090565b6140a7600061046292613ec4600090565b6143fb565b906140ba6119de8284612eb7565b6140c2575050565b63e2517d3f60e01b600090815291611d219160046123ed565b6140e66119de61440f565b6140ec57565b631afcd79f60e31b6000908152600490fd5b9061044c9161410b6140db565b9061411861044c9261377c565b613d78565b9061044c916140fe565b6000805160206147118339815191526141408383612eb7565b156141a3576000612fb08482612ee2868261415b9701610e37565b61416f6121a6614169339390565b93610ec4565b917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b61419a60405190565b600090a4600190565b505050600090565b6104629081906001600160a01b031681565b906141e56118bb6121a16141e06000610462966141d8600090565b500194610ebb565b6141ab565b614473565b6104629061421e600160046128fc60389461422461420e600285613d93858b611270565b61421e6000866128fc868c611270565b90611c39565b95611270565b6000805160206147118339815191526142466119de8484612eb7565b156141a3576001612fb0846000612ee286826142629701610e37565b6142706121a6614169339390565b917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d61419a60405190565b906142b66118bb6121a16141e06000610462966141d8600090565b614542565b6142c66119de6129af565b6142cc57565b638dfc202b60e01b6000908152600490fd5b61046260006142ee610462612560565b01611509565b906142fe82614583565b61430782610ec4565b7fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b61433160405190565b600090a280516143446118bb6000610fdd565b111561435357611bc691614610565b505061044c6145ce565b6104629160006143769261436f600090565b5001611e76565b906106f7565b9061438b6115226104e0845490565b9060005b81811061439c5750505090565b9091926143b16115656001926115e5876115a6565b92910161438f565b906104629161437c565b9061044c611598926143d460405190565b938480926143b9565b610462906143c3565b6000610462916143f4606090565b50016143dd565b600061046291614409600090565b50015490565b6104627ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006118f4565b9190611ec36104626116ed9390565b61044c91600091614438565b8054801561401757600019019061401461446d8383611e76565b90614447565b90614484611d788260018501610e37565b61448e6000610fdd565b81146141a357612f91610462926000926144e195611d6460019788936144bc6144b686610fdd565b82613023565b888501916144d46144cb845490565b61311889610fdd565b8083036144e65750505090565b614453565b61450961450f6145179461450061437661216b9589611e76565b92839188611e76565b90614438565b888801610e37565b388080613706565b90815491600160401b831015610bb3578261450991600161044c95018155611e76565b61454f6119de8383614637565b1561457c576145779161216b9060016145708461456c848261451f565b5490565b9301610e37565b600190565b5050600090565b6000813b6145936118bb83610fdd565b146145ae579061044c916145a8610462612560565b016116d6565b634c9c8ce360e01b81526001600160a01b03919091166004820152602490fd5b60006145d981610fdd565b34116145e25750565b63b398979f60e01b8152600490fd5b3d1561460b576146003d61104a565b903d6000602084013e565b606090565b6000806104629361461f606090565b50602081519101845af46146316145f1565b91614662565b614650916001611d7892614649600090565b5001610e37565b61465d6118bb6000610fdd565b141590565b9061466d57506146c1565b81519060009161467f6118bb84610fdd565b14806146ac575b61468f57505090565b639996b31560e01b82526001600160a01b03166004820152602490fd5b50803b6146bb6118bb84610fdd565b14614686565b80516000906146d26118bb83610fdd565b11156146e15750805190602001fd5b63d6bda27560e01b8152600490fdfeb19546dff01e856fb3f010c267a7b1c60363cf8a4664e21cc89c26224620214e02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800a49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775a26469706673582212205fe48a9c7be93ce6d6d27cc0adede0c3ccb52b1cc97d9e2513ae40d3070d2d3764736f6c634300081a0033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
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.