Source Code
Overview
APE Balance
APE Value
$0.00Multichain Info
N/A
Cross-Chain Transactions
Loading...
Loading
Contract Name:
ProxyDomaRecordUserFacet
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 200 runs
Other Settings:
istanbul EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
import { LibProxyDomaRecord } from "../libraries/LibProxyDomaRecord.sol";
import { EIP712Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol";
import { IDomaRecord } from "../../interfaces/IDomaRecord.sol";
import { CAIPUtils } from "../../utils/CAIPUtils.sol";
import { NameUtils } from "../../utils/NameUtils.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import { SyntheticToken } from "../../SyntheticToken.sol";
import {
CAPABILITY_REGISTRY_RECORDS_MANAGEMENT,
CAPABILITY_DNS_RECORDS_MANAGEMENT
} from "../../doma-record/libraries/LibDoma.sol";
/**
* @title ProxyDomaRecordUserFacet
* @notice User-facing facet for the proxy DOMA record contract on tokenization chains.
* @dev This facet handles user interactions including tokenization requests, ownership claims,
* cross-chain bridging, and domain record management. It validates vouchers, manages fees,
* and integrates with EIP-712 for signature verification. Supports both ownership tokens
* and capability tokens with appropriate access controls.
*/
contract ProxyDomaRecordUserFacet is EIP712Upgradeable {
using Strings for address;
using Strings for uint256;
using LibProxyDomaRecord for *;
using ECDSA for bytes32;
/**
* @notice Tokenization voucher, obtained from a Registrar.
* @param names List of names to tokenize.
* @param nonce Unique nonce to prevent voucher reuse (replay attacks).
* @param expiresAt Expiration date of the voucher (UNIX seconds).
* @param ownerAddress Minted Ownership Token owner address. Must be equal to transaction sender.
*/
struct TokenizationVoucher {
IDomaRecord.NameInfo[] names;
uint256 nonce;
uint256 expiresAt;
address ownerAddress;
}
/**
* @notice Proof of Contacts voucher, obtained from a Registrar or Doma-provided storage.
* @param registrantHandle Handle of a registrant in an off-chain storage.
* @param proofSource Source of the proof-of-contacts voucher. 1 - Registrar, 2 - Doma.
* @param nonce Unique nonce to prevent voucher reuse (replay attacks).
* @param expiresAt Expiration date of the voucher (UNIX seconds).
*/
struct ProofOfContactsVoucher {
uint256 registrantHandle;
IDomaRecord.ProofOfContactsSource proofSource;
uint256 nonce;
uint256 expiresAt;
}
string private constant _NAME_INFO_TYPE = "NameInfo(string sld,string tld)";
bytes32 private constant _NAME_INFO_TYPE_HASH = keccak256(abi.encodePacked(_NAME_INFO_TYPE));
string private constant _PROOF_OF_CONTACTS_VOUCHER_TYPE =
"ProofOfContactsVoucher(uint256 registrantHandle,uint8 proofSource,uint256 nonce,uint256 expiresAt)";
bytes32 private constant _PROOF_OF_CONTACTS_VOUCHER_TYPE_HASH =
keccak256(abi.encodePacked(_PROOF_OF_CONTACTS_VOUCHER_TYPE));
string private constant _TOKENIZATION_VOUCHER_TYPE =
"TokenizationVoucher(NameInfo[] names,uint256 nonce,uint256 expiresAt,address ownerAddress)";
bytes32 private constant _TOKENIZATION_VOUCHER_TYPE_HASH =
keccak256(abi.encodePacked(_TOKENIZATION_VOUCHER_TYPE, _NAME_INFO_TYPE));
/**
* @notice Thrown when attempting to tokenize a name that is already tokenized.
* @param sld The second-level domain of the name
* @param tld The top-level domain of the name
*/
error NameAlreadyTokenized(string sld, string tld);
/**
* @notice Thrown when a registrar ID doesn't match the expected registrar for an operation.
* @param ianaId The provided registrar IANA ID
* @param expectedIanaId The expected registrar IANA ID
*/
error InvalidRegistrar(uint256 ianaId, uint256 expectedIanaId);
/**
* @notice Thrown when attempting to use an operation that is not supported for synthetic tokens.
* @param tokenId The ID of the synthetic token
*/
error SyntheticTokenNotSupported(uint256 tokenId);
/**
* @notice Thrown when attempting to set DNS records for a host that has an operational subdomain.
* @param host The subdomain host label that is in use
*/
error SubdomainHostInUse(string host);
/**
* @notice Thrown when a subdomain token is provided where a parent token is required.
* @param tokenId The ID of the subdomain token
*/
error InvalidTokenId(uint256 tokenId);
/**
* @notice Thrown when attempting to bridge to an unsupported target chain.
* @param chainId The unsupported chain ID in CAIP-2 format
*/
error UnsupportedTargetChain(string chainId);
/**
* @notice Thrown when a voucher signature is invalid or from an unauthorized signer.
* @param signer The address that was recovered from the signature
*/
error InvalidSigner(address signer);
/**
* @notice Thrown when a voucher has expired and cannot be used.
* @param expiresAt The expiration timestamp of the voucher
* @param currentTime The current block timestamp
*/
error VoucherExpired(uint256 expiresAt, uint256 currentTime);
/**
* @notice Thrown when the price feed data is stale and cannot be trusted.
* @param roundId The round ID from the price feed
* @param updatedAt The timestamp when the price was last updated
*/
error PriceFeedStalePrice(uint80 roundId, uint256 updatedAt);
/**
* @notice Thrown when the price feed returns an invalid price value.
* @param price The invalid price value returned
*/
error InvalidPrice(int256 price);
/**
* @notice Thrown when a native currency transfer fails.
*/
error TransferFailed();
/**
* @notice Thrown when the provided fee doesn't match the expected fee for an operation.
* @param expectedFee The expected fee amount in wei
* @param providedFee The fee amount actually provided
*/
error InvalidFee(uint256 expectedFee, uint256 providedFee);
/**
* @notice Thrown when a fee is provided for an operation that doesn't require payment.
* @param operation The operation identifier
* @param nameCount The number of names being processed
*/
error FeeNotRequired(bytes32 operation, uint256 nameCount);
/**
* @notice Thrown when attempting to reuse a nonce that has already been consumed.
* @param nonce The nonce that was already used
*/
error NonceAlreadyUsed(uint256 nonce);
/**
* @notice Emitted when a fee is collected for an operation.
* @param feeWei The fee amount collected in wei
* @param feeUsdCents The equivalent fee amount in USD cents
* @param correlationId The correlation ID for tracking the operation
*/
event FeeCollected(uint256 feeWei, uint256 feeUsdCents, string correlationId);
/**
* @notice Emitted when a synthetic subdomain token is renounced by its owner.
* @param tokenId The ID of the renounced token.
* @param owner The address who renounced the token.
* @param parentTokenId The ID of the parent token.
* @param groupId The ID of the group the token belonged to.
*/
event SyntheticTokenRenounced(
uint256 indexed tokenId,
address indexed owner,
uint256 indexed parentTokenId,
uint256 groupId
);
/**
* @notice Emitted when a synthetic subdomain token is revoked by parent owner.
* @param tokenId The ID of the revoked token.
* @param revokedFrom The address whose token was revoked.
* @param revokedBy The parent token owner who revoked it.
* @param parentTokenId The ID of the parent token.
* @param groupId The ID of the group the token belonged to.
*/
event SyntheticTokenRevoked(
uint256 indexed tokenId,
address indexed revokedFrom,
address indexed revokedBy,
uint256 parentTokenId,
uint256 groupId
);
/**
* @notice Modifier to prevent operations on synthetic tokens that are not yet supported.
* @dev Reverts with SyntheticTokenNotSupported error for synthetic tokens.
* @param tokenId The ID of the token being operated on
*/
modifier notSynthetic(uint256 tokenId) {
if (LibProxyDomaRecord.isSyntheticToken(tokenId)) {
revert SyntheticTokenNotSupported(tokenId);
}
_;
}
/**
* @notice Modifier to verify that the caller is the owner of the specified token.
* @dev Uses LibProxyDomaRecord to verify ownership before allowing operations.
* @param tokenId The ID of the token to verify ownership for
*/
modifier onlyTokenOwner(uint256 tokenId) {
LibProxyDomaRecord._verifyTokenOwnership(tokenId, msg.sender);
_;
}
/**
* @notice Request tokenization of domain names using a signed voucher from a registrar.
* @dev Validates the voucher signature, collects fees, and initiates cross-chain tokenization.
* The voucher must be signed by an authorized registrar and not expired. Names are validated
* for proper format and checked for existing tokenization on this chain.
* @param voucher The tokenization voucher containing names and authorization details
* @param signature The registrar's signature over the voucher data
*/
function requestTokenization(
TokenizationVoucher calldata voucher,
bytes calldata signature
) external payable {
_verifyNotExpiredVoucher(voucher.expiresAt);
_verifyAndUpdateNonce(voucher.nonce);
if (msg.sender != voucher.ownerAddress) {
revert LibProxyDomaRecord.InvalidOwnerAddress(msg.sender, voucher.ownerAddress);
}
uint256 nameCount = voucher.names.length;
string memory correlationId = LibProxyDomaRecord._useCorrelationId();
_collectFee(LibProxyDomaRecord.REQUEST_TOKENIZATION_OPERATION, nameCount, correlationId);
bytes32 voucherHash = _hashTokenizationVoucher(voucher);
bytes32 digest = _hashTypedDataV4(voucherHash);
uint256 registrarIanaId = _verifyRegistrarSignature(digest, signature);
for (uint256 i = 0; i < nameCount; i++) {
IDomaRecord.NameInfo memory name = voucher.names[i];
// Validate SLD label format
NameUtils.ensureValidLabel(name.sld);
// Validate TLD format (can be eTLD with dots, but must be valid domain name)
NameUtils.ensureValidName(name.tld);
// Optimistic check that name isn't already tokenized
// It's not a foolproof, since name might be tokenized on another chain, or be in progress
// However, this would help to reject repeated tokenization requests on the same chain
uint256 nameId = NameUtils.namehash(string(abi.encodePacked(name.sld, ".", name.tld)));
if (LibProxyDomaRecord.proxyDomaRecordStorage().ownershipToken.exists(nameId)) {
revert NameAlreadyTokenized(name.sld, name.tld);
}
}
bytes memory initiateTokenizationCalldata = abi.encodeCall(
IDomaRecord.initiateTokenization,
(
registrarIanaId,
voucher.names,
CAIPUtils.caip2Local(),
voucher.ownerAddress.toChecksumHexString(),
correlationId
)
);
// Generate a unique nonce key to prevent replays of the same message
uint256 nonceKey = uint256(
keccak256(abi.encodePacked(initiateTokenizationCalldata, block.number))
);
LibProxyDomaRecord._relayMessage(initiateTokenizationCalldata, correlationId, nonceKey);
}
/**
* @notice Claim ownership of a domain name using proof of contacts voucher.
* @dev Allows token owners to establish claim over their domain by providing signed proof
* from either the registrar or DOMA system. Validates voucher signature, collects fees,
* and initiates cross-chain ownership claim process.
* @param tokenId The ID of the ownership token being claimed
* @param proofOfContactsVoucher The voucher containing proof of contact information
* @param signature The signature over the voucher (from registrar or DOMA)
*/
function claimOwnership(
uint256 tokenId,
ProofOfContactsVoucher calldata proofOfContactsVoucher,
bytes calldata signature
) public payable notSynthetic(tokenId) onlyTokenOwner(tokenId) {
_verifyNotExpiredVoucher(proofOfContactsVoucher.expiresAt);
_verifyAndUpdateNonce(proofOfContactsVoucher.nonce);
bytes32 voucherHash = _hashProofOfContactsVoucher(proofOfContactsVoucher);
bytes32 digest = _hashTypedDataV4(voucherHash);
if (proofOfContactsVoucher.proofSource == IDomaRecord.ProofOfContactsSource.REGISTRAR) {
uint256 registrarIanaId = _verifyRegistrarSignature(digest, signature);
uint256 tokenRegistrar = LibProxyDomaRecord
.proxyDomaRecordStorage()
.ownershipToken
.registrarOf(tokenId);
if (tokenRegistrar != registrarIanaId) {
revert InvalidRegistrar(registrarIanaId, tokenRegistrar);
}
} else {
_verifyDomaSignature(digest, signature);
}
string memory correlationId = LibProxyDomaRecord._useCorrelationId();
_collectFee(LibProxyDomaRecord.CLAIM_OWNERSHIP_OPERATION, 1, correlationId);
bytes memory claimOwnershipCalldata = abi.encodeCall(
IDomaRecord.claimOwnership,
(
tokenId.toString(),
CAIPUtils.caip2Local(),
msg.sender.toChecksumHexString(),
proofOfContactsVoucher.proofSource,
proofOfContactsVoucher.registrantHandle,
correlationId
)
);
LibProxyDomaRecord._relayMessage(claimOwnershipCalldata, correlationId, tokenId);
}
/**
* @notice Deprecated overload with isSynthetic parameter - this parameter is ignored and will be removed in a future version.
* @dev This overload is maintained for backward compatibility. Use claimOwnership(tokenId, proofOfContactsVoucher, signature) instead.
*/
function claimOwnership(
uint256 tokenId,
bool, // isSynthetic?
ProofOfContactsVoucher calldata proofOfContactsVoucher,
bytes calldata signature
) external payable notSynthetic(tokenId) onlyTokenOwner(tokenId) {
claimOwnership(tokenId, proofOfContactsVoucher, signature);
}
/**
* @notice Deprecated overload with isSynthetic parameter - this parameter is ignored and will be removed in a future version.
* @dev This overload is maintained for backward compatibility. Use bridge(tokenId, targetChainId, targetOwnerAddress) instead.
*/
function bridge(
uint256 tokenId,
bool, // isSynthetic
string calldata targetChainId,
string calldata targetOwnerAddress
) external payable notSynthetic(tokenId) onlyTokenOwner(tokenId) {
bridge(tokenId, targetChainId, targetOwnerAddress);
}
/**
* @notice Bridge a domain token to another supported chain.
* @dev Burns the token on this chain and initiates minting on the target chain.
* Validates target chain support, transfer lock status, and token expiration.
* Collects fees and relays the bridge request to the DOMA chain.
* @param tokenId The ID of the ownership token to bridge
* @param targetChainId The CAIP-2 chain ID of the destination chain
* @param targetOwnerAddress The owner address on the target chain
*/
function bridge(
uint256 tokenId,
string calldata targetChainId,
string calldata targetOwnerAddress
) public payable notSynthetic(tokenId) onlyTokenOwner(tokenId) {
// Verify the target chain ID is supported
if (!isTargetChainSupported(targetChainId)) {
revert UnsupportedTargetChain(targetChainId);
}
string memory correlationId = LibProxyDomaRecord._useCorrelationId();
_collectFee(LibProxyDomaRecord.BRIDGE_OPERATION, 1, correlationId);
bool isLocked = LibProxyDomaRecord.proxyDomaRecordStorage().ownershipToken.lockStatusOf(
tokenId
);
if (isLocked) {
revert LibProxyDomaRecord.TransferLocked(tokenId);
}
uint256 expiresAt = LibProxyDomaRecord.proxyDomaRecordStorage().ownershipToken.expirationOf(
tokenId
);
if (expiresAt < block.timestamp) {
revert LibProxyDomaRecord.NameTokenHasExpired(tokenId, expiresAt);
}
LibProxyDomaRecord._burnOwnershipToken(tokenId, correlationId);
bytes memory bridgeCalldata = abi.encodeCall(
IDomaRecord.bridge,
(tokenId.toString(), targetChainId, targetOwnerAddress, correlationId)
);
LibProxyDomaRecord._relayMessage(bridgeCalldata, correlationId, tokenId);
}
/**
* @notice Convert an ownership token to a synthetic ownership token.
* @dev Burns the ownership token on this chain and sends a cross-chain message to DOMA chain
* to set the synthetic flag. Once confirmed, a synthetic token will be minted to the owner.
* The synthetic token maintains the same tokenId, expiration, and capabilities as the original.
* @param tokenId The ID of the ownership token to convert
*/
function convertToSynthetic(uint256 tokenId) external payable onlyTokenOwner(tokenId) {
LibProxyDomaRecord.ProxyDomaRecordStorage storage s = LibProxyDomaRecord
.proxyDomaRecordStorage();
string memory correlationId = LibProxyDomaRecord._useCorrelationId();
_collectFee(LibProxyDomaRecord.CONVERT_TO_SYNTHETIC_OPERATION, 1, correlationId);
LibProxyDomaRecord._validateOwnershipTokenForConversion(tokenId, msg.sender);
address tokenOwner = s.ownershipToken.ownerOf(tokenId);
bytes memory convertToSyntheticCalldata = abi.encodeCall(
IDomaRecord.convertToSynthetic,
(tokenId, CAIPUtils.caip2Local(), tokenOwner.toChecksumHexString(), correlationId)
);
LibProxyDomaRecord._relayMessage(convertToSyntheticCalldata, correlationId, tokenId);
}
/**
* @notice Convert a synthetic ownership token back to a regular ownership token.
* @dev Burns the synthetic token on this chain and sends a cross-chain message to DOMA chain
* to update the synthetic flag. Once confirmed, a regular ownership token will be minted.
* Requires that the synthetic token has no active subdomains (subdomainCount == 0).
* @param tokenId The ID of the synthetic token to convert
*/
function convertToOwnership(uint256 tokenId) external payable onlyTokenOwner(tokenId) {
LibProxyDomaRecord.ProxyDomaRecordStorage storage s = LibProxyDomaRecord
.proxyDomaRecordStorage();
string memory correlationId = LibProxyDomaRecord._useCorrelationId();
_collectFee(LibProxyDomaRecord.CONVERT_TO_OWNERSHIP_OPERATION, 1, correlationId);
LibProxyDomaRecord._validateSyntheticTokenForConversion(tokenId, msg.sender);
address tokenOwner = s.syntheticToken.ownerOf(tokenId);
bytes memory convertToOwnershipCalldata = abi.encodeCall(
IDomaRecord.convertToOwnership,
(tokenId, CAIPUtils.caip2Local(), tokenOwner.toChecksumHexString(), correlationId)
);
LibProxyDomaRecord._relayMessage(convertToOwnershipCalldata, correlationId, tokenId);
}
/**
* @notice Deprecated overload with isSynthetic parameter - this parameter is ignored and will be removed in a future version.
* @dev This overload is maintained for backward compatibility. Use requestDetokenization(tokenId) instead.
*/
function requestDetokenization(
uint256 tokenId,
bool
) external notSynthetic(tokenId) onlyTokenOwner(tokenId) {
requestDetokenization(tokenId);
}
/**
* @notice Request detokenization of a domain name by the owner.
* @dev Initiates the detokenization process on the DOMA chain, which will remove
* the domain from the protocol and return it to traditional DNS management.
* Only the token owner can request detokenization.
* @param tokenId The ID of the ownership token to detokenize
*/
function requestDetokenization(
uint256 tokenId
) public notSynthetic(tokenId) onlyTokenOwner(tokenId) {
string memory correlationId = LibProxyDomaRecord._useCorrelationId();
bytes memory ownerDetokenizeCalldata = abi.encodeCall(
IDomaRecord.ownerDetokenize,
(
tokenId.toString(),
CAIPUtils.caip2Local(),
msg.sender.toChecksumHexString(),
correlationId
)
);
LibProxyDomaRecord._relayMessage(ownerDetokenizeCalldata, correlationId, tokenId);
}
/**
* @notice Deprecated overload with isSynthetic parameter - this parameter is ignored and will be removed in a future version.
* @dev This overload is maintained for backward compatibility. Use setNameservers(tokenId, nameservers) instead.
*/
function setNameservers(
uint256 tokenId,
string[] calldata nameservers,
bool
) external payable notSynthetic(tokenId) onlyTokenOwner(tokenId) {
setNameservers(tokenId, nameservers);
}
/**
* @notice Update nameservers for a domain.
* @dev Requires CAPABILITY_REGISTRY_RECORDS_MANAGEMENT. Relays change to Doma Chain.
* @param tokenId The ownership token ID.
* @param nameservers List of nameserver hostnames.
*/
function setNameservers(
uint256 tokenId,
string[] calldata nameservers
) public payable notSynthetic(tokenId) onlyTokenOwner(tokenId) {
_validateCapability(tokenId, CAPABILITY_REGISTRY_RECORDS_MANAGEMENT);
string memory correlationId = LibProxyDomaRecord._useCorrelationId();
_collectFee(LibProxyDomaRecord.SET_NAMESERVERS_OPERATION, 1, correlationId);
bytes memory setNameserversCalldata = abi.encodeCall(
IDomaRecord.setNameservers,
(tokenId, nameservers, correlationId)
);
LibProxyDomaRecord._relayMessage(setNameserversCalldata, correlationId, tokenId);
}
/**
* @notice Deprecated overload with isSynthetic parameter - this parameter is ignored and will be removed in a future version.
* @dev This overload is maintained for backward compatibility. Use setDSKeys(tokenId, dsKeys) instead.
*/
function setDSKeys(
uint256 tokenId,
IDomaRecord.DSKey[] calldata dsKeys,
bool
) external payable notSynthetic(tokenId) onlyTokenOwner(tokenId) {
setDSKeys(tokenId, dsKeys);
}
/**
* @notice Update DNSSEC DS keys for a domain.
* @dev Requires CAPABILITY_REGISTRY_RECORDS_MANAGEMENT. Relays change to Doma Chain.
* @param tokenId The ownership token ID.
* @param dsKeys Array of DS key records.
*/
function setDSKeys(
uint256 tokenId,
IDomaRecord.DSKey[] calldata dsKeys
) public payable notSynthetic(tokenId) onlyTokenOwner(tokenId) {
_validateCapability(tokenId, CAPABILITY_REGISTRY_RECORDS_MANAGEMENT);
string memory correlationId = LibProxyDomaRecord._useCorrelationId();
_collectFee(LibProxyDomaRecord.SET_DS_KEYS_OPERATION, 1, correlationId);
bytes memory setDSKeysCalldata = abi.encodeCall(
IDomaRecord.setDSKeys,
(tokenId, dsKeys, correlationId)
);
LibProxyDomaRecord._relayMessage(setDSKeysCalldata, correlationId, tokenId);
}
/**
* @notice Deprecated overload with isSynthetic parameter - this parameter is ignored and will be removed in a future version.
* @dev This overload is maintained for backward compatibility. Use setDNSRRSet(tokenId, host, recordType, ttl, records) instead.
*/
function setDNSRRSet(
uint256 tokenId,
string calldata host,
string calldata recordType,
uint32 ttl,
string[] calldata records,
bool
) external payable onlyTokenOwner(tokenId) {
setDNSRRSet(tokenId, host, recordType, ttl, records);
}
/**
* @notice Set DNS resource record set for a domain.
* @dev Requires CAPABILITY_DNS_RECORDS_MANAGEMENT. Relays change to Doma Chain.
* Works for both ownership and synthetic tokens (including subdomain tokens).
* Host must be lowercase (enforced by validation).
* @param tokenId The token ID (ownership, synthetic root, or synthetic subdomain).
* @param host The hostname relative to the token (e.g., "www" or "" for apex).
* @param recordType DNS record type (e.g., "A", "AAAA", "CNAME").
* @param ttl Time-to-live in seconds.
* @param records Array of record values. Empty array deletes the RRSet.
*/
function setDNSRRSet(
uint256 tokenId,
string calldata host,
string calldata recordType,
uint32 ttl,
string[] calldata records
) public payable {
LibProxyDomaRecord.ProxyDomaRecordStorage storage s = LibProxyDomaRecord
.proxyDomaRecordStorage();
// Empty string is allowed for subdomain apex
if (bytes(host).length > 0) {
NameUtils.ensureValidHost(host);
}
bool isSynthetic = LibProxyDomaRecord.isSyntheticToken(tokenId);
uint256 parentTokenId = isSynthetic ? s.syntheticToken.getParentTokenId(tokenId) : 0;
// If using a parent token, check if a subdomain exists for this host
// If subdomain exists and is owned by someone else, reject the operation
if (isSynthetic && parentTokenId == 0) {
// This is a parent synthetic token - check for subdomain conflicts
// Extract the last label (immediate child) from the host
string memory immediateChild = _getLastLabel(host);
// If an operational subdomain exists for this label, caller must use the subdomain token
if (s.syntheticToken.isSubdomainOperational(tokenId, immediateChild)) {
revert SubdomainHostInUse(immediateChild);
}
}
// Verify caller owns the token
LibProxyDomaRecord._verifyTokenOwnership(tokenId, msg.sender);
// Validate capability on the token
_validateCapability(tokenId, CAPABILITY_DNS_RECORDS_MANAGEMENT);
string memory correlationId = LibProxyDomaRecord._useCorrelationId();
_collectFee(LibProxyDomaRecord.SET_DNS_RRSET_OPERATION, 1, correlationId);
// Determine parent tokenId and full host for DOMA chain
uint256 parentTokenIdForDoma;
string memory fullHost;
if (isSynthetic && parentTokenId != 0) {
// This is a subdomain token
string memory subdomainHost = s.syntheticToken.getSyntheticData(tokenId).host;
parentTokenIdForDoma = parentTokenId;
if (bytes(host).length == 0) {
// Setting DNS for the subdomain apex
fullHost = subdomainHost;
} else {
// Setting DNS for a nested record under the subdomain
fullHost = string(abi.encodePacked(host, ".", subdomainHost));
}
} else {
// This is either an ownership token or a synthetic root token
parentTokenIdForDoma = tokenId;
fullHost = host;
}
bytes memory setDNSRRSetCalldata = abi.encodeCall(
IDomaRecord.setDNSRRSet,
(parentTokenIdForDoma, fullHost, recordType, ttl, records, correlationId)
);
LibProxyDomaRecord._relayMessage(setDNSRRSetCalldata, correlationId, parentTokenIdForDoma);
}
// View functions
/**
* @notice Get the fee in USD cents for a specific operation.
* @param operation The operation identifier to query
* @return The fee amount in USD cents
*/
function feesUSDCents(bytes32 operation) public view returns (uint256) {
return LibProxyDomaRecord.proxyDomaRecordStorage().feesUSDCents[operation];
}
/**
* @notice Check if a target chain is supported for bridging operations.
* @param targetChainId The CAIP-2 chain identifier to check
* @return Whether the target chain is supported
*/
function isTargetChainSupported(string calldata targetChainId) public view returns (bool) {
return LibProxyDomaRecord.proxyDomaRecordStorage().supportedTargetChains[targetChainId];
}
/**
* @notice Convert a USD cent amount to the equivalent native token amount.
* @dev Uses Chainlink price feed to get current exchange rate. Validates price feed data.
* @param feeUSDCents The fee amount in USD cents
* @return nativeFee The equivalent fee in native token wei
*/
function getNativePrice(uint256 feeUSDCents) public view returns (uint256 nativeFee) {
LibProxyDomaRecord.ProxyDomaRecordStorage storage _storage = LibProxyDomaRecord
.proxyDomaRecordStorage();
(uint80 roundId, int256 price, , uint256 updatedAt, ) = _storage
.priceFeed
.latestRoundData();
_validatePriceFeedResponse(roundId, price, updatedAt);
return _getNativePrice(feeUSDCents, price);
}
/**
* @notice Get the fee in native tokens for a specific operation.
* @dev Combines USD cent fee lookup with current price conversion.
* @param operation The operation identifier to query
* @return The fee amount in native token wei (0 if operation has no fee)
*/
function getOperationFeeInNative(bytes32 operation) public view returns (uint256) {
if (!LibProxyDomaRecord._isValidOperation(operation)) {
revert LibProxyDomaRecord.InvalidOperation(operation);
}
uint256 feeUSDCents = feesUSDCents(operation);
if (feeUSDCents == 0) {
return 0;
}
// Fetch the native token fee for the operation
uint256 nativeFee = getNativePrice(feeUSDCents);
return nativeFee;
}
function _hashTokenizationVoucher(
TokenizationVoucher calldata voucher
) internal pure returns (bytes32) {
bytes32[] memory namesHashes = new bytes32[](voucher.names.length);
for (uint256 i = 0; i < voucher.names.length; i++) {
IDomaRecord.NameInfo memory name = voucher.names[i];
namesHashes[i] = keccak256(
abi.encode(
_NAME_INFO_TYPE_HASH,
keccak256(bytes(name.sld)),
keccak256(bytes(name.tld))
)
);
}
return
keccak256(
abi.encode(
_TOKENIZATION_VOUCHER_TYPE_HASH,
keccak256(abi.encodePacked(namesHashes)),
voucher.nonce,
voucher.expiresAt,
voucher.ownerAddress
)
);
}
function _hashProofOfContactsVoucher(
ProofOfContactsVoucher calldata voucher
) internal pure returns (bytes32) {
uint8 proofSource = uint8(voucher.proofSource);
return
keccak256(
abi.encode(
_PROOF_OF_CONTACTS_VOUCHER_TYPE_HASH,
voucher.registrantHandle,
proofSource,
voucher.nonce,
voucher.expiresAt
)
);
}
function _verifyNotExpiredVoucher(uint256 expiresAt) internal view {
if (expiresAt < block.timestamp) {
revert VoucherExpired(expiresAt, block.timestamp);
}
}
function _verifyAndUpdateNonce(uint256 nonce) internal {
LibProxyDomaRecord.ProxyDomaRecordStorage storage _storage = LibProxyDomaRecord
.proxyDomaRecordStorage();
if (_storage.nonces[nonce] == true) {
revert NonceAlreadyUsed(nonce);
}
_storage.nonces[nonce] = true;
}
function _verifyRegistrarSignature(
bytes32 digest,
bytes calldata signature
) internal view returns (uint256) {
address signatureSigner = digest.recover(signature);
LibProxyDomaRecord.ProxyDomaRecordStorage storage _storage = LibProxyDomaRecord
.proxyDomaRecordStorage();
uint256 ianaId = _storage.registrarSigners[signatureSigner];
if (ianaId == 0) {
revert InvalidSigner(signatureSigner);
}
return ianaId;
}
function _verifyDomaSignature(bytes32 digest, bytes calldata signature) internal view {
address signatureSigner = digest.recover(signature);
LibProxyDomaRecord.ProxyDomaRecordStorage storage _storage = LibProxyDomaRecord
.proxyDomaRecordStorage();
if (!_storage.domaSigners[signatureSigner]) {
revert InvalidSigner(signatureSigner);
}
}
function _collectFee(
bytes32 operation,
uint256 nameCount,
string memory correlationId
) internal {
LibProxyDomaRecord.ProxyDomaRecordStorage storage _storage = LibProxyDomaRecord
.proxyDomaRecordStorage();
uint256 feeUSDCents = _storage.feesUSDCents[operation] * nameCount;
if (feeUSDCents == 0) {
if (msg.value > 0) {
revert FeeNotRequired(operation, nameCount);
}
return;
}
(uint80 latestRoundId, int256 price, , uint256 updatedAt, ) = _storage
.priceFeed
.latestRoundData();
_validatePriceFeedResponse(latestRoundId, price, updatedAt);
uint256 nativeFee = _getNativePrice(feeUSDCents, price);
if (msg.value != nativeFee) {
// Try to use previous round data to calculate the fee
// This could happen if transaction spent to long in the mempool, or round data change while transaction was being constructed/signed
uint80 previousRoundId = _getPreviousRoundId(latestRoundId);
(, int256 previousRoundPrice, , uint256 previousRoundUpdatedAt, ) = _storage
.priceFeed
.getRoundData(previousRoundId);
_validatePriceFeedResponse(previousRoundId, previousRoundPrice, previousRoundUpdatedAt);
nativeFee = _getNativePrice(feeUSDCents, previousRoundPrice);
if (msg.value != nativeFee) {
revert InvalidFee(nativeFee, msg.value);
}
}
if (_storage.treasury == address(0)) revert LibProxyDomaRecord.ZeroAddress();
(bool success, ) = payable(_storage.treasury).call{ value: msg.value }("");
if (!success) revert TransferFailed();
emit FeeCollected(msg.value, feeUSDCents, correlationId);
}
function _getNativePrice(
uint256 feeUSDCents,
int256 price
) internal view returns (uint256 nativeFee) {
LibProxyDomaRecord.ProxyDomaRecordStorage storage _storage = LibProxyDomaRecord
.proxyDomaRecordStorage();
uint8 decimals = _storage.priceFeed.decimals();
if (price <= 0) revert InvalidPrice(price);
uint256 currentPriceInUSD = uint256(price);
// feeUSDCents is in cents (2 decimal places)
// We need to normalize based on the price feed decimals
if (decimals >= 2) {
// If price feed has more decimals than our fee (which is in cents with 2 decimals)
// Multiply fee by 10^(decimals-2) to match price feed precision
uint256 normalizedFee = feeUSDCents * (10 ** (decimals - 2));
nativeFee = (normalizedFee * 1e18) / currentPriceInUSD;
} else {
// If price feed has fewer decimals than our fee
// Multiply price by 10^(2-decimals) to match fee precision
uint256 normalizedPrice = currentPriceInUSD * (10 ** (2 - decimals));
nativeFee = (feeUSDCents * 1e18) / normalizedPrice;
}
return nativeFee;
}
function _validatePriceFeedResponse(
uint80 roundId,
int256 price,
uint256 updatedAt
) internal view {
if (price <= 0) revert InvalidPrice(price);
if (updatedAt < block.timestamp - 1 days) {
revert PriceFeedStalePrice(roundId, updatedAt);
}
}
function _getPreviousRoundId(uint80 roundId) internal pure returns (uint80) {
uint80 phaseId = roundId >> 64;
uint64 aggregatorRoundId = uint64(roundId);
// If it's the first round on a phase, there's no previous round
// Unlikely to happen in actual deployments, but could happen in testnets
// Since we don't know last round of the previous phase, we return the current round as a fallback
if (aggregatorRoundId <= 1) {
return roundId;
}
return (phaseId << 64) | (aggregatorRoundId - 1);
}
/**
* @notice Get the contract version.
* @return The version string of this contract
*/
function version() external pure returns (string memory) {
return "1.1.0";
}
/**
* @notice Override EIP712 name to avoid initialization requirement.
* @dev Returns the domain name used for EIP-712 signature verification.
* @return The EIP-712 domain name
*/
// solhint-disable-next-line func-name-mixedcase
function _EIP712Name() internal pure override returns (string memory) {
return "DOMA";
}
/**
* @notice Override EIP712 version to avoid initialization requirement.
* @dev Returns the domain version used for EIP-712 signature verification.
* @return The EIP-712 domain version
*/
// solhint-disable-next-line func-name-mixedcase
function _EIP712Version() internal pure override returns (string memory) {
return "1";
}
/**
* @notice Override eip712Domain to work without initialization (EIP-5267 compliance).
* @dev Returns the EIP-712 domain parameters for signature verification.
* @return fields The fields that are set in the domain
* @return name The EIP-712 domain name
* @return domainVersion The EIP-712 domain version
* @return chainId The chain ID for the domain
* @return verifyingContract The address of this contract
* @return salt The domain salt (unused)
* @return extensions The domain extensions (unused)
*/
function eip712Domain()
public
view
virtual
override
returns (
bytes1 fields,
string memory name,
string memory domainVersion,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
return (
hex"0f", // 01111
_EIP712Name(),
_EIP712Version(),
block.chainid,
address(this),
bytes32(0),
new uint256[](0)
);
}
/**
* @notice Get the REQUEST_TOKENIZATION_OPERATION constant
* @return The operation hash for request tokenization
*/
// solhint-disable-next-line func-name-mixedcase
function REQUEST_TOKENIZATION_OPERATION() external pure returns (bytes32) {
return LibProxyDomaRecord.REQUEST_TOKENIZATION_OPERATION;
}
/**
* @notice Get the CLAIM_OWNERSHIP_OPERATION constant
* @return The operation hash for claim ownership
*/
// solhint-disable-next-line func-name-mixedcase
function CLAIM_OWNERSHIP_OPERATION() external pure returns (bytes32) {
return LibProxyDomaRecord.CLAIM_OWNERSHIP_OPERATION;
}
/**
* @notice Get the BRIDGE_OPERATION constant
* @return The operation hash for bridge
*/
// solhint-disable-next-line func-name-mixedcase
function BRIDGE_OPERATION() external pure returns (bytes32) {
return LibProxyDomaRecord.BRIDGE_OPERATION;
}
/**
* @notice Get the SET_NAMESERVERS_OPERATION constant
* @return The operation hash for set nameservers
*/
// solhint-disable-next-line func-name-mixedcase
function SET_NAMESERVERS_OPERATION() external pure returns (bytes32) {
return LibProxyDomaRecord.SET_NAMESERVERS_OPERATION;
}
/**
* @notice Get the SET_DS_KEYS_OPERATION constant
* @return The operation hash for set DS keys
*/
// solhint-disable-next-line func-name-mixedcase
function SET_DS_KEYS_OPERATION() external pure returns (bytes32) {
return LibProxyDomaRecord.SET_DS_KEYS_OPERATION;
}
/**
* @notice Get the SET_DNS_RRSET_OPERATION constant
* @return The operation hash for set DNS RRSet
*/
// solhint-disable-next-line func-name-mixedcase
function SET_DNS_RRSET_OPERATION() external pure returns (bytes32) {
return LibProxyDomaRecord.SET_DNS_RRSET_OPERATION;
}
/**
* @notice Get the CONVERT_TO_SYNTHETIC_OPERATION constant
* @return The operation hash for convert to synthetic
*/
// solhint-disable-next-line func-name-mixedcase
function CONVERT_TO_SYNTHETIC_OPERATION() external pure returns (bytes32) {
return LibProxyDomaRecord.CONVERT_TO_SYNTHETIC_OPERATION;
}
/**
* @notice Get the CONVERT_TO_OWNERSHIP_OPERATION constant
* @return The operation hash for convert to ownership
*/
// solhint-disable-next-line func-name-mixedcase
function CONVERT_TO_OWNERSHIP_OPERATION() external pure returns (bytes32) {
return LibProxyDomaRecord.CONVERT_TO_OWNERSHIP_OPERATION;
}
/**
* @notice Validates that a token has the required capability
* @dev For REGISTRY_RECORDS_MANAGEMENT: checks registrar capabilities
* For DNS_RECORDS_MANAGEMENT: checks domain capabilities
* @param tokenId The token ID to check
* @param requiredCapability The required capability bit mask
*/
function _validateCapability(uint256 tokenId, uint256 requiredCapability) private view {
LibProxyDomaRecord.ProxyDomaRecordStorage storage ds = LibProxyDomaRecord
.proxyDomaRecordStorage();
uint256 capabilities;
if (requiredCapability == CAPABILITY_REGISTRY_RECORDS_MANAGEMENT) {
// For registry records (nameservers, DS keys): check registrar capabilities
uint256 registrarIanaId;
if (LibProxyDomaRecord.isSyntheticToken(tokenId)) {
registrarIanaId = ds.syntheticToken.registrarOf(tokenId);
} else {
registrarIanaId = ds.ownershipToken.registrarOf(tokenId);
}
capabilities = ds.registrarCapabilities[registrarIanaId];
} else if (requiredCapability == CAPABILITY_DNS_RECORDS_MANAGEMENT) {
// For DNS records: check domain capabilities
capabilities = LibProxyDomaRecord._getDomainCapabilities(tokenId);
}
if ((capabilities & requiredCapability) != requiredCapability) {
revert LibProxyDomaRecord.InsufficientCapabilities(tokenId, requiredCapability);
}
}
/**
* @notice Mint a synthetic subdomain token directly without cross-chain call.
* @dev Mints subdomain token on this chain, bypassing DomaRecord on DOMA chain.
* Validates parent ownership, depth limits, expiration.
* Only CAPABILITY_DNS_RECORDS_MANAGEMENT (4) is allowed for subdomains.
* Uses new SyntheticSubdomainMinted event without sld/tld (use parentTokenId to lookup).
* @param parentTokenId The ID of the parent synthetic token
* @param host The subdomain host label (e.g., "www" or "api")
* @param capabilities Capability flags (must be CAPABILITY_DNS_RECORDS_MANAGEMENT = 4)
* @param expiresAt Expiration timestamp for the subdomain token (0 to inherit from parent)
* @param revocable Whether the parent can revoke this subdomain
* @param groupId Group identifier for batch operations
* @param receiver The address to receive the minted subdomain token
* @return The newly minted synthetic subdomain token ID
*/
function mintSyntheticSubdomain(
uint256 parentTokenId,
string calldata host,
uint256 capabilities,
uint256 expiresAt,
bool revocable,
uint256 groupId,
address receiver
) external payable onlyTokenOwner(parentTokenId) returns (uint256) {
// Validate subdomain extraction parameters
LibProxyDomaRecord._validateSubdomainExtraction(parentTokenId, host, capabilities);
LibProxyDomaRecord.ProxyDomaRecordStorage storage s = LibProxyDomaRecord
.proxyDomaRecordStorage();
string memory correlationId = LibProxyDomaRecord._useCorrelationId();
// Mint subdomain token directly (no cross-chain call)
return
s.syntheticToken.mintSyntheticSubdomain(
SyntheticToken.SyntheticSubdomainMintInfo({
receiver: receiver,
revocable: revocable,
expiresAt: expiresAt,
capabilities: capabilities,
host: host,
parentTokenId: parentTokenId,
groupId: groupId
}),
correlationId
);
}
/**
* @notice Renew a synthetic subdomain token.
* @dev Can only be called by the parent token owner.
* Validates that new expiry extends current expiry and doesn't exceed parent expiry.
* Only works for subdomains (tokens with parentTokenId != 0).
* @param tokenId The ID of the subdomain token to renew.
* @param expiresAt The new expiration timestamp (must be > current expiry and <= parent expiry).
*/
function renewSubdomain(uint256 tokenId, uint256 expiresAt) external payable {
LibProxyDomaRecord.ProxyDomaRecordStorage storage s = LibProxyDomaRecord
.proxyDomaRecordStorage();
// Get subdomain's parent tokenId
uint256 parentTokenId = s.syntheticToken.getParentTokenId(tokenId);
// Verify this is a subdomain (not a root token)
if (parentTokenId == 0) {
revert LibProxyDomaRecord.NotSubdomain(tokenId);
}
// Verify caller is the parent owner
LibProxyDomaRecord._verifyTokenOwnership(parentTokenId, msg.sender);
string memory correlationId = LibProxyDomaRecord._useCorrelationId();
// Renew the subdomain (SyntheticToken.renew will validate expiry rules)
s.syntheticToken.renew(tokenId, expiresAt, correlationId);
}
/**
* @notice Renounce ownership of a synthetic subdomain token.
* @dev Burns the subdomain token and makes it available for re-minting.
* Can only be called by the subdomain token owner.
* The subdomain must not be in a locked group.
* @param tokenId The ID of the subdomain token to renounce.
*/
function renounceSynthetic(uint256 tokenId) external onlyTokenOwner(tokenId) {
LibProxyDomaRecord.ProxyDomaRecordStorage storage s = LibProxyDomaRecord
.proxyDomaRecordStorage();
// Get token data
SyntheticToken.SyntheticData memory data = s.syntheticToken.getSyntheticData(tokenId);
// Verify this is a subdomain (not a root token)
if (data.parentTokenId == 0) {
revert LibProxyDomaRecord.NotSubdomain(tokenId);
}
// Verify group not revoked
if (s.syntheticToken.isGroupRevoked(data.parentTokenId, data.groupId)) {
revert LibProxyDomaRecord.GroupRevoked(data.parentTokenId, data.groupId);
}
// Generate correlation ID and burn
string memory correlationId = LibProxyDomaRecord._useCorrelationId();
s.syntheticToken.burnSynthetic(tokenId, correlationId);
emit SyntheticTokenRenounced(tokenId, msg.sender, data.parentTokenId, data.groupId);
}
/**
* @notice Revoke a specific synthetic subdomain token.
* @dev Burns the subdomain token and makes it available for re-minting.
* Can only be called by the parent token owner.
* The subdomain must be revocable and not in a locked group.
* @param tokenId The ID of the subdomain token to revoke.
*/
function revokeSynthetic(uint256 tokenId) external {
LibProxyDomaRecord.ProxyDomaRecordStorage storage s = LibProxyDomaRecord
.proxyDomaRecordStorage();
// Get token data
SyntheticToken.SyntheticData memory data = s.syntheticToken.getSyntheticData(tokenId);
// Verify this is a subdomain (not a root token)
if (data.parentTokenId == 0) {
revert LibProxyDomaRecord.NotSubdomain(tokenId);
}
// Only parent token owner can revoke
LibProxyDomaRecord._verifyTokenOwnership(data.parentTokenId, msg.sender);
// Verify token is revocable
if (!data.revocable) {
revert LibProxyDomaRecord.TokenNotRevocable(tokenId);
}
// Verify group not revoked
if (s.syntheticToken.isGroupRevoked(data.parentTokenId, data.groupId)) {
revert LibProxyDomaRecord.GroupRevoked(data.parentTokenId, data.groupId);
}
// Get token owner before burning
address tokenOwner = s.syntheticToken.ownerOf(tokenId);
// Generate correlation ID and burn
string memory correlationId = LibProxyDomaRecord._useCorrelationId();
s.syntheticToken.burnSynthetic(tokenId, correlationId);
emit SyntheticTokenRevoked(
tokenId,
tokenOwner,
msg.sender,
data.parentTokenId,
data.groupId
);
}
/**
* @notice Revoke a group of subdomains, preventing transfers and marking them for bulk operations.
* @dev Can only be called by the parent token owner.
* Revokes all subdomain tokens in the specified group.
* Reduces the parent's subdomain count by the number of tokens in the group.
* @param parentTokenId The ID of the parent synthetic token.
* @param groupId The group identifier to revoke.
*/
function revokeGroup(
uint256 parentTokenId,
uint256 groupId
) external onlyTokenOwner(parentTokenId) {
LibProxyDomaRecord.ProxyDomaRecordStorage storage s = LibProxyDomaRecord
.proxyDomaRecordStorage();
// Verify this is a synthetic token (ownership tokens don't support subdomains)
if (!LibProxyDomaRecord.isSyntheticToken(parentTokenId)) {
revert LibProxyDomaRecord.TokenNotSynthetic(parentTokenId);
}
// Verify this is a root synthetic token (not a subdomain)
uint256 grandparentTokenId = s.syntheticToken.getParentTokenId(parentTokenId);
if (grandparentTokenId != 0) {
revert InvalidTokenId(parentTokenId);
}
// Revoke the group (marks all tokens in group as revoked)
s.syntheticToken.revokeGroup(parentTokenId, groupId);
}
/**
* @dev Extract the last label from a host string (eg. "app" from "api.app").
* If no dot exists, returns the entire string.
* @param host The full host string.
* @return The last label after the final dot.
*/
function _getLastLabel(string memory host) private pure returns (string memory) {
bytes memory hostBytes = bytes(host);
if (hostBytes.length == 0) {
return host;
}
// Find the last occurrence of '.' by searching backwards
for (uint256 i = hostBytes.length; i > 0; i--) {
if (hostBytes[i - 1] == 0x2E) {
// 0x2E is '.'
// Found dot, extract substring after it
bytes memory result = new bytes(hostBytes.length - i);
for (uint256 j = 0; j < result.length; j++) {
result[j] = hostBytes[i + j];
}
return string(result);
}
}
// No dot found, return the entire string
return host;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// solhint-disable-next-line interface-starts-with-i
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(
uint80 _roundId
) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
function latestRoundData()
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)
pragma solidity ^0.8.0;
import "./IAccessControlUpgradeable.sol";
import "../utils/ContextUpgradeable.sol";
import "../utils/StringsUpgradeable.sol";
import "../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, IAccessControlUpgradeable, ERC165Upgradeable {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with a standardized message including the required role.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*
* _Available since v4.1._
*/
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(IAccessControlUpgradeable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
return _roles[role].members[account];
}
/**
* @dev Revert with a standard message if `_msgSender()` is missing `role`.
* Overriding this function changes the behavior of the {onlyRole} modifier.
*
* Format of the revert message is described in {_checkRole}.
*
* _Available since v4.6._
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Revert with a standard message if `account` is missing `role`.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
StringsUpgradeable.toHexString(account),
" is missing role ",
StringsUpgradeable.toHexString(uint256(role), 32)
)
)
);
}
}
/**
* @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 override returns (bytes32) {
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 override 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 override 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 `account`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* May emit a {RoleGranted} event.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*
* NOTE: This function is deprecated in favor of {_grantRole}.
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Grants `role` to `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
/**
* @dev Revokes `role` from `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControlUpgradeable {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
pragma solidity ^0.8.0;
/**
* @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
* proxy whose upgrades are fully controlled by the current implementation.
*/
interface IERC1822ProxiableUpgradeable {
/**
* @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 v4.9.0) (interfaces/IERC1967.sol)
pragma solidity ^0.8.0;
/**
* @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
*
* _Available since v4.8.3._
*/
interface IERC1967Upgradeable {
/**
* @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 v4.4.1 (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.0;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeaconUpgradeable {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {BeaconProxy} will check that this address is a contract.
*/
function implementation() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/ERC1967/ERC1967Upgrade.sol)
pragma solidity ^0.8.2;
import "../beacon/IBeaconUpgradeable.sol";
import "../../interfaces/IERC1967Upgradeable.sol";
import "../../interfaces/draft-IERC1822Upgradeable.sol";
import "../../utils/AddressUpgradeable.sol";
import "../../utils/StorageSlotUpgradeable.sol";
import {Initializable} from "../utils/Initializable.sol";
/**
* @dev This abstract contract provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
*
* _Available since v4.1._
*/
abstract contract ERC1967UpgradeUpgradeable is Initializable, IERC1967Upgradeable {
// This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
function __ERC1967Upgrade_init() internal onlyInitializing {
}
function __ERC1967Upgrade_init_unchained() internal onlyInitializing {
}
/**
* @dev Returns the current implementation address.
*/
function _getImplementation() internal view returns (address) {
return StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
require(AddressUpgradeable.isContract(newImplementation), "ERC1967: new implementation is not a contract");
StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Perform implementation upgrade
*
* Emits an {Upgraded} event.
*/
function _upgradeTo(address newImplementation) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
}
/**
* @dev Perform implementation upgrade with additional setup call.
*
* Emits an {Upgraded} event.
*/
function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
_upgradeTo(newImplementation);
if (data.length > 0 || forceCall) {
AddressUpgradeable.functionDelegateCall(newImplementation, data);
}
}
/**
* @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
*
* Emits an {Upgraded} event.
*/
function _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal {
// Upgrades from old implementations will perform a rollback test. This test requires the new
// implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
// this special case will break upgrade paths from old UUPS implementation to new ones.
if (StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT).value) {
_setImplementation(newImplementation);
} else {
try IERC1822ProxiableUpgradeable(newImplementation).proxiableUUID() returns (bytes32 slot) {
require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
} catch {
revert("ERC1967Upgrade: new implementation is not UUPS");
}
_upgradeToAndCall(newImplementation, data, forceCall);
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Returns the current admin.
*/
function _getAdmin() internal view returns (address) {
return StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
require(newAdmin != address(0), "ERC1967: new admin is the zero address");
StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {AdminChanged} event.
*/
function _changeAdmin(address newAdmin) internal {
emit AdminChanged(_getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
*/
bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Returns the current beacon.
*/
function _getBeacon() internal view returns (address) {
return StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the EIP1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
require(AddressUpgradeable.isContract(newBeacon), "ERC1967: new beacon is not a contract");
require(
AddressUpgradeable.isContract(IBeaconUpgradeable(newBeacon).implementation()),
"ERC1967: beacon implementation is not a contract"
);
StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value = newBeacon;
}
/**
* @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
* not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
*
* Emits a {BeaconUpgraded} event.
*/
function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length > 0 || forceCall) {
AddressUpgradeable.functionDelegateCall(IBeaconUpgradeable(newBeacon).implementation(), data);
}
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
/**
* @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 Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 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 functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_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 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_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() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @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 {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.0;
import "../../interfaces/draft-IERC1822Upgradeable.sol";
import "../ERC1967/ERC1967UpgradeUpgradeable.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.
*
* _Available since v4.1._
*/
abstract contract UUPSUpgradeable is Initializable, IERC1822ProxiableUpgradeable, ERC1967UpgradeUpgradeable {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
address private immutable __self = address(this);
/**
* @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 ERC1967) 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 ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
* fail.
*/
modifier onlyProxy() {
require(address(this) != __self, "Function must be called through delegatecall");
require(_getImplementation() == __self, "Function must be called through active proxy");
_;
}
/**
* @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() {
require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall");
_;
}
function __UUPSUpgradeable_init() internal onlyInitializing {
}
function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
}
/**
* @dev Implementation of the ERC1822 {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 override notDelegated returns (bytes32) {
return _IMPLEMENTATION_SLOT;
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function upgradeTo(address newImplementation) public virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
}
/**
* @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, true);
}
/**
* @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
* {upgradeTo} and {upgradeToAndCall}.
*
* Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
*
* ```solidity
* function _authorizeUpgrade(address) internal override onlyOwner {}
* ```
*/
function _authorizeUpgrade(address newImplementation) internal virtual;
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/ERC721.sol)
pragma solidity ^0.8.0;
import "./IERC721Upgradeable.sol";
import "./IERC721ReceiverUpgradeable.sol";
import "./extensions/IERC721MetadataUpgradeable.sol";
import "../../utils/AddressUpgradeable.sol";
import "../../utils/ContextUpgradeable.sol";
import "../../utils/StringsUpgradeable.sol";
import "../../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
* the Metadata extension, but not including the Enumerable extension, which is available separately as
* {ERC721Enumerable}.
*/
contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721Upgradeable, IERC721MetadataUpgradeable {
using AddressUpgradeable for address;
using StringsUpgradeable for uint256;
// Token name
string private _name;
// Token symbol
string private _symbol;
// Mapping from token ID to owner address
mapping(uint256 => address) private _owners;
// Mapping owner address to token count
mapping(address => uint256) private _balances;
// Mapping from token ID to approved address
mapping(uint256 => address) private _tokenApprovals;
// Mapping from owner to operator approvals
mapping(address => mapping(address => bool)) private _operatorApprovals;
/**
* @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
*/
function __ERC721_init(string memory name_, string memory symbol_) internal onlyInitializing {
__ERC721_init_unchained(name_, symbol_);
}
function __ERC721_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
_name = name_;
_symbol = symbol_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) {
return
interfaceId == type(IERC721Upgradeable).interfaceId ||
interfaceId == type(IERC721MetadataUpgradeable).interfaceId ||
super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC721-balanceOf}.
*/
function balanceOf(address owner) public view virtual override returns (uint256) {
require(owner != address(0), "ERC721: address zero is not a valid owner");
return _balances[owner];
}
/**
* @dev See {IERC721-ownerOf}.
*/
function ownerOf(uint256 tokenId) public view virtual override returns (address) {
address owner = _ownerOf(tokenId);
require(owner != address(0), "ERC721: invalid token ID");
return owner;
}
/**
* @dev See {IERC721Metadata-name}.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev See {IERC721Metadata-symbol}.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev See {IERC721Metadata-tokenURI}.
*/
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
_requireMinted(tokenId);
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
}
/**
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, can be overridden in child contracts.
*/
function _baseURI() internal view virtual returns (string memory) {
return "";
}
/**
* @dev See {IERC721-approve}.
*/
function approve(address to, uint256 tokenId) public virtual override {
address owner = ERC721Upgradeable.ownerOf(tokenId);
require(to != owner, "ERC721: approval to current owner");
require(
_msgSender() == owner || isApprovedForAll(owner, _msgSender()),
"ERC721: approve caller is not token owner or approved for all"
);
_approve(to, tokenId);
}
/**
* @dev See {IERC721-getApproved}.
*/
function getApproved(uint256 tokenId) public view virtual override returns (address) {
_requireMinted(tokenId);
return _tokenApprovals[tokenId];
}
/**
* @dev See {IERC721-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual override {
_setApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC721-isApprovedForAll}.
*/
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev See {IERC721-transferFrom}.
*/
function transferFrom(address from, address to, uint256 tokenId) public virtual override {
//solhint-disable-next-line max-line-length
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
_transfer(from, to, tokenId);
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual override {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
_safeTransfer(from, to, tokenId, data);
}
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* `data` is additional data, it has no specified format and it is sent in call to `to`.
*
* This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
* implement alternative mechanisms to perform token transfer, such as signature-based.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
_transfer(from, to, tokenId);
require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
}
/**
* @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
*/
function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
return _owners[tokenId];
}
/**
* @dev Returns whether `tokenId` exists.
*
* Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
*
* Tokens start existing when they are minted (`_mint`),
* and stop existing when they are burned (`_burn`).
*/
function _exists(uint256 tokenId) internal view virtual returns (bool) {
return _ownerOf(tokenId) != address(0);
}
/**
* @dev Returns whether `spender` is allowed to manage `tokenId`.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
address owner = ERC721Upgradeable.ownerOf(tokenId);
return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
}
/**
* @dev Safely mints `tokenId` and transfers it to `to`.
*
* Requirements:
*
* - `tokenId` must not exist.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeMint(address to, uint256 tokenId) internal virtual {
_safeMint(to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/
function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
_mint(to, tokenId);
require(
_checkOnERC721Received(address(0), to, tokenId, data),
"ERC721: transfer to non ERC721Receiver implementer"
);
}
/**
* @dev Mints `tokenId` and transfers it to `to`.
*
* WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
*
* Requirements:
*
* - `tokenId` must not exist.
* - `to` cannot be the zero address.
*
* Emits a {Transfer} event.
*/
function _mint(address to, uint256 tokenId) internal virtual {
require(to != address(0), "ERC721: mint to the zero address");
require(!_exists(tokenId), "ERC721: token already minted");
_beforeTokenTransfer(address(0), to, tokenId, 1);
// Check that tokenId was not minted by `_beforeTokenTransfer` hook
require(!_exists(tokenId), "ERC721: token already minted");
unchecked {
// Will not overflow unless all 2**256 token ids are minted to the same owner.
// Given that tokens are minted one by one, it is impossible in practice that
// this ever happens. Might change if we allow batch minting.
// The ERC fails to describe this case.
_balances[to] += 1;
}
_owners[tokenId] = to;
emit Transfer(address(0), to, tokenId);
_afterTokenTransfer(address(0), to, tokenId, 1);
}
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
* This is an internal function that does not check if the sender is authorized to operate on the token.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/
function _burn(uint256 tokenId) internal virtual {
address owner = ERC721Upgradeable.ownerOf(tokenId);
_beforeTokenTransfer(owner, address(0), tokenId, 1);
// Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook
owner = ERC721Upgradeable.ownerOf(tokenId);
// Clear approvals
delete _tokenApprovals[tokenId];
unchecked {
// Cannot overflow, as that would require more tokens to be burned/transferred
// out than the owner initially received through minting and transferring in.
_balances[owner] -= 1;
}
delete _owners[tokenId];
emit Transfer(owner, address(0), tokenId);
_afterTokenTransfer(owner, address(0), tokenId, 1);
}
/**
* @dev Transfers `tokenId` from `from` to `to`.
* As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
*
* Emits a {Transfer} event.
*/
function _transfer(address from, address to, uint256 tokenId) internal virtual {
require(ERC721Upgradeable.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
require(to != address(0), "ERC721: transfer to the zero address");
_beforeTokenTransfer(from, to, tokenId, 1);
// Check that tokenId was not transferred by `_beforeTokenTransfer` hook
require(ERC721Upgradeable.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
// Clear approvals from the previous owner
delete _tokenApprovals[tokenId];
unchecked {
// `_balances[from]` cannot overflow for the same reason as described in `_burn`:
// `from`'s balance is the number of token held, which is at least one before the current
// transfer.
// `_balances[to]` could overflow in the conditions described in `_mint`. That would require
// all 2**256 token ids to be minted, which in practice is impossible.
_balances[from] -= 1;
_balances[to] += 1;
}
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
_afterTokenTransfer(from, to, tokenId, 1);
}
/**
* @dev Approve `to` to operate on `tokenId`
*
* Emits an {Approval} event.
*/
function _approve(address to, uint256 tokenId) internal virtual {
_tokenApprovals[tokenId] = to;
emit Approval(ERC721Upgradeable.ownerOf(tokenId), to, tokenId);
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Emits an {ApprovalForAll} event.
*/
function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
require(owner != operator, "ERC721: approve to caller");
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
/**
* @dev Reverts if the `tokenId` has not been minted yet.
*/
function _requireMinted(uint256 tokenId) internal view virtual {
require(_exists(tokenId), "ERC721: invalid token ID");
}
/**
* @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
* The call is not executed if the target address is not a contract.
*
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param data bytes optional data to send along with the call
* @return bool whether the call correctly returned the expected magic value
*/
function _checkOnERC721Received(
address from,
address to,
uint256 tokenId,
bytes memory data
) private returns (bool) {
if (to.isContract()) {
try IERC721ReceiverUpgradeable(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
return retval == IERC721ReceiverUpgradeable.onERC721Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
revert("ERC721: transfer to non ERC721Receiver implementer");
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
} else {
return true;
}
}
/**
* @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is
* used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`.
* - When `from` is zero, the tokens will be minted for `to`.
* - When `to` is zero, ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
* - `batchSize` is non-zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}
/**
* @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is
* used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`.
* - When `from` is zero, the tokens were minted for `to`.
* - When `to` is zero, ``from``'s tokens were burned.
* - `from` and `to` are never both zero.
* - `batchSize` is non-zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}
/**
* @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
*
* WARNING: Anyone calling this MUST ensure that the balances remain consistent with the ownership. The invariant
* being that for any address `a` the value returned by `balanceOf(a)` must be equal to the number of tokens such
* that `ownerOf(tokenId)` is `a`.
*/
// solhint-disable-next-line func-name-mixedcase
function __unsafe_increaseBalance(address account, uint256 amount) internal {
_balances[account] += amount;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[44] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/ERC721Burnable.sol)
pragma solidity ^0.8.0;
import "../ERC721Upgradeable.sol";
import "../../../utils/ContextUpgradeable.sol";
import {Initializable} from "../../../proxy/utils/Initializable.sol";
/**
* @title ERC721 Burnable Token
* @dev ERC721 Token that can be burned (destroyed).
*/
abstract contract ERC721BurnableUpgradeable is Initializable, ContextUpgradeable, ERC721Upgradeable {
function __ERC721Burnable_init() internal onlyInitializing {
}
function __ERC721Burnable_init_unchained() internal onlyInitializing {
}
/**
* @dev Burns `tokenId`. See {ERC721-_burn}.
*
* Requirements:
*
* - The caller must own `tokenId` or be an approved operator.
*/
function burn(uint256 tokenId) public virtual {
//solhint-disable-next-line max-line-length
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
_burn(tokenId);
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC721Upgradeable.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721MetadataUpgradeable is IERC721Upgradeable {
/**
* @dev Returns the token collection name.
*/
function name() external view returns (string memory);
/**
* @dev Returns the token collection symbol.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) external view returns (string memory);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.0;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
interface IERC721ReceiverUpgradeable {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165Upgradeable.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721Upgradeable is IERC165Upgradeable {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @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.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @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, it is bubbled up by this
* function (like regular Solidity function calls).
*
* 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.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @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`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
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;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165Upgradeable.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 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);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
function __ERC165_init() internal onlyInitializing {
}
function __ERC165_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165Upgradeable).interfaceId;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165Upgradeable {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library MathUpgradeable {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMathUpgradeable {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.0;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
* _Available since v4.9 for `string`, `bytes`._
*/
library StorageSlotUpgradeable {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/MathUpgradeable.sol";
import "./math/SignedMathUpgradeable.sol";
/**
* @dev String operations.
*/
library StringsUpgradeable {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = MathUpgradeable.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMathUpgradeable.abs(value))));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, MathUpgradeable.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.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 reinitialization) 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 Pointer to storage slot. Allows integrators to override it with a custom storage location.
*
* NOTE: Consider following the ERC-7201 formula to derive storage locations.
*/
function _initializableStorageSlot() internal pure virtual returns (bytes32) {
return INITIALIZABLE_STORAGE;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
bytes32 slot = _initializableStorageSlot();
assembly {
$.slot := slot
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/cryptography/EIP712.sol)
pragma solidity ^0.8.20;
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP-712] is a standard for hashing and signing of typed structured data.
*
* The encoding scheme specified in the EIP requires a domain separator and a hash of the typed structured data, whose
* encoding is very generic and therefore its implementation in Solidity is not feasible, thus this contract
* does not implement the encoding itself. Protocols need to implement the type-specific encoding they need in order to
* produce the hash of their typed data using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP-712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* NOTE: The upgradeable version of this contract does not use an immutable cache and recomputes the domain separator
* each time {_domainSeparatorV4} is called. That is cheaper than accessing a cached version in cold storage.
*/
abstract contract EIP712Upgradeable is Initializable, IERC5267 {
bytes32 private constant TYPE_HASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
/// @custom:storage-location erc7201:openzeppelin.storage.EIP712
struct EIP712Storage {
/// @custom:oz-renamed-from _HASHED_NAME
bytes32 _hashedName;
/// @custom:oz-renamed-from _HASHED_VERSION
bytes32 _hashedVersion;
string _name;
string _version;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.EIP712")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant EIP712StorageLocation = 0xa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d100;
function _getEIP712Storage() private pure returns (EIP712Storage storage $) {
assembly {
$.slot := EIP712StorageLocation
}
}
/**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP-712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
__EIP712_init_unchained(name, version);
}
function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {
EIP712Storage storage $ = _getEIP712Storage();
$._name = name;
$._version = version;
// Reset prior values in storage if upgrading
$._hashedName = 0;
$._hashedVersion = 0;
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
return _buildDomainSeparator();
}
function _buildDomainSeparator() private view returns (bytes32) {
return keccak256(abi.encode(TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash(), block.chainid, address(this)));
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*
* ```solidity
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
* keccak256("Mail(address to,string contents)"),
* mailTo,
* keccak256(bytes(mailContents))
* )));
* address signer = ECDSA.recover(digest, signature);
* ```
*/
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash);
}
/// @inheritdoc IERC5267
function eip712Domain()
public
view
virtual
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
EIP712Storage storage $ = _getEIP712Storage();
// If the hashed name and version in storage are non-zero, the contract hasn't been properly initialized
// and the EIP712 domain is not reliable, as it will be missing name and version.
require($._hashedName == 0 && $._hashedVersion == 0, "EIP712: Uninitialized");
return (
hex"0f", // 01111
_EIP712Name(),
_EIP712Version(),
block.chainid,
address(this),
bytes32(0),
new uint256[](0)
);
}
/**
* @dev The name parameter for the EIP712 domain.
*
* NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
* are a concern.
*/
function _EIP712Name() internal view virtual returns (string memory) {
EIP712Storage storage $ = _getEIP712Storage();
return $._name;
}
/**
* @dev The version parameter for the EIP712 domain.
*
* NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
* are a concern.
*/
function _EIP712Version() internal view virtual returns (string memory) {
EIP712Storage storage $ = _getEIP712Storage();
return $._version;
}
/**
* @dev The hash of the name parameter for the EIP712 domain.
*
* NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Name` instead.
*/
function _EIP712NameHash() internal view returns (bytes32) {
EIP712Storage storage $ = _getEIP712Storage();
string memory name = _EIP712Name();
if (bytes(name).length > 0) {
return keccak256(bytes(name));
} else {
// If the name is empty, the contract may have been upgraded without initializing the new storage.
// We return the name hash in storage if non-zero, otherwise we assume the name is empty by design.
bytes32 hashedName = $._hashedName;
if (hashedName != 0) {
return hashedName;
} else {
return keccak256("");
}
}
}
/**
* @dev The hash of the version parameter for the EIP712 domain.
*
* NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Version` instead.
*/
function _EIP712VersionHash() internal view returns (bytes32) {
EIP712Storage storage $ = _getEIP712Storage();
string memory version = _EIP712Version();
if (bytes(version).length > 0) {
return keccak256(bytes(version));
} else {
// If the version is empty, the contract may have been upgraded without initializing the new storage.
// We return the version hash in storage if non-zero, otherwise we assume the version is empty by design.
bytes32 hashedVersion = $._hashedVersion;
if (hashedVersion != 0) {
return hashedVersion;
} else {
return keccak256("");
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC165.sol)
pragma solidity >=0.4.16;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC2981.sol)
pragma solidity >=0.6.2;
import {IERC165} from "../utils/introspection/IERC165.sol";
/**
* @dev Interface for the NFT Royalty Standard.
*
* A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
* support for royalty payments across all NFT marketplaces and ecosystem participants.
*/
interface IERC2981 is IERC165 {
/**
* @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
* exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
*
* NOTE: ERC-2981 allows setting the royalty to 100% of the price. In that case all the price would be sent to the
* royalty receiver and 0 tokens to the seller. Contracts dealing with royalty should consider empty transfers.
*/
function royaltyInfo(
uint256 tokenId,
uint256 salePrice
) external view returns (address receiver, uint256 royaltyAmount);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC5267.sol)
pragma solidity >=0.4.16;
interface IERC5267 {
/**
* @dev MAY be emitted to signal that the domain could have changed.
*/
event EIP712DomainChanged();
/**
* @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
* signature.
*/
function eip712Domain()
external
view
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/Arrays.sol)
// This file was procedurally generated from scripts/generate/templates/Arrays.js.
pragma solidity ^0.8.20;
import {Comparators} from "./Comparators.sol";
import {SlotDerivation} from "./SlotDerivation.sol";
import {StorageSlot} from "./StorageSlot.sol";
import {Math} from "./math/Math.sol";
/**
* @dev Collection of functions related to array types.
*/
library Arrays {
using SlotDerivation for bytes32;
using StorageSlot for bytes32;
/**
* @dev Sort an array of uint256 (in memory) following the provided comparator function.
*
* This function does the sorting "in place", meaning that it overrides the input. The object is returned for
* convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
*
* NOTE: this function's cost is `O(n · log(n))` in average and `O(n²)` in the worst case, with n the length of the
* array. Using it in view functions that are executed through `eth_call` is safe, but one should be very careful
* when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
* consume more gas than is available in a block, leading to potential DoS.
*
* IMPORTANT: Consider memory side-effects when using custom comparator functions that access memory in an unsafe way.
*/
function sort(
uint256[] memory array,
function(uint256, uint256) pure returns (bool) comp
) internal pure returns (uint256[] memory) {
_quickSort(_begin(array), _end(array), comp);
return array;
}
/**
* @dev Variant of {sort} that sorts an array of uint256 in increasing order.
*/
function sort(uint256[] memory array) internal pure returns (uint256[] memory) {
sort(array, Comparators.lt);
return array;
}
/**
* @dev Sort an array of address (in memory) following the provided comparator function.
*
* This function does the sorting "in place", meaning that it overrides the input. The object is returned for
* convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
*
* NOTE: this function's cost is `O(n · log(n))` in average and `O(n²)` in the worst case, with n the length of the
* array. Using it in view functions that are executed through `eth_call` is safe, but one should be very careful
* when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
* consume more gas than is available in a block, leading to potential DoS.
*
* IMPORTANT: Consider memory side-effects when using custom comparator functions that access memory in an unsafe way.
*/
function sort(
address[] memory array,
function(address, address) pure returns (bool) comp
) internal pure returns (address[] memory) {
sort(_castToUint256Array(array), _castToUint256Comp(comp));
return array;
}
/**
* @dev Variant of {sort} that sorts an array of address in increasing order.
*/
function sort(address[] memory array) internal pure returns (address[] memory) {
sort(_castToUint256Array(array), Comparators.lt);
return array;
}
/**
* @dev Sort an array of bytes32 (in memory) following the provided comparator function.
*
* This function does the sorting "in place", meaning that it overrides the input. The object is returned for
* convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
*
* NOTE: this function's cost is `O(n · log(n))` in average and `O(n²)` in the worst case, with n the length of the
* array. Using it in view functions that are executed through `eth_call` is safe, but one should be very careful
* when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
* consume more gas than is available in a block, leading to potential DoS.
*
* IMPORTANT: Consider memory side-effects when using custom comparator functions that access memory in an unsafe way.
*/
function sort(
bytes32[] memory array,
function(bytes32, bytes32) pure returns (bool) comp
) internal pure returns (bytes32[] memory) {
sort(_castToUint256Array(array), _castToUint256Comp(comp));
return array;
}
/**
* @dev Variant of {sort} that sorts an array of bytes32 in increasing order.
*/
function sort(bytes32[] memory array) internal pure returns (bytes32[] memory) {
sort(_castToUint256Array(array), Comparators.lt);
return array;
}
/**
* @dev Performs a quick sort of a segment of memory. The segment sorted starts at `begin` (inclusive), and stops
* at end (exclusive). Sorting follows the `comp` comparator.
*
* Invariant: `begin <= end`. This is the case when initially called by {sort} and is preserved in subcalls.
*
* IMPORTANT: Memory locations between `begin` and `end` are not validated/zeroed. This function should
* be used only if the limits are within a memory array.
*/
function _quickSort(uint256 begin, uint256 end, function(uint256, uint256) pure returns (bool) comp) private pure {
unchecked {
if (end - begin < 0x40) return;
// Use first element as pivot
uint256 pivot = _mload(begin);
// Position where the pivot should be at the end of the loop
uint256 pos = begin;
for (uint256 it = begin + 0x20; it < end; it += 0x20) {
if (comp(_mload(it), pivot)) {
// If the value stored at the iterator's position comes before the pivot, we increment the
// position of the pivot and move the value there.
pos += 0x20;
_swap(pos, it);
}
}
_swap(begin, pos); // Swap pivot into place
_quickSort(begin, pos, comp); // Sort the left side of the pivot
_quickSort(pos + 0x20, end, comp); // Sort the right side of the pivot
}
}
/**
* @dev Pointer to the memory location of the first element of `array`.
*/
function _begin(uint256[] memory array) private pure returns (uint256 ptr) {
assembly ("memory-safe") {
ptr := add(array, 0x20)
}
}
/**
* @dev Pointer to the memory location of the first memory word (32bytes) after `array`. This is the memory word
* that comes just after the last element of the array.
*/
function _end(uint256[] memory array) private pure returns (uint256 ptr) {
unchecked {
return _begin(array) + array.length * 0x20;
}
}
/**
* @dev Load memory word (as a uint256) at location `ptr`.
*/
function _mload(uint256 ptr) private pure returns (uint256 value) {
assembly {
value := mload(ptr)
}
}
/**
* @dev Swaps the elements memory location `ptr1` and `ptr2`.
*/
function _swap(uint256 ptr1, uint256 ptr2) private pure {
assembly {
let value1 := mload(ptr1)
let value2 := mload(ptr2)
mstore(ptr1, value2)
mstore(ptr2, value1)
}
}
/// @dev Helper: low level cast address memory array to uint256 memory array
function _castToUint256Array(address[] memory input) private pure returns (uint256[] memory output) {
assembly {
output := input
}
}
/// @dev Helper: low level cast bytes32 memory array to uint256 memory array
function _castToUint256Array(bytes32[] memory input) private pure returns (uint256[] memory output) {
assembly {
output := input
}
}
/// @dev Helper: low level cast address comp function to uint256 comp function
function _castToUint256Comp(
function(address, address) pure returns (bool) input
) private pure returns (function(uint256, uint256) pure returns (bool) output) {
assembly {
output := input
}
}
/// @dev Helper: low level cast bytes32 comp function to uint256 comp function
function _castToUint256Comp(
function(bytes32, bytes32) pure returns (bool) input
) private pure returns (function(uint256, uint256) pure returns (bool) output) {
assembly {
output := input
}
}
/**
* @dev Searches a sorted `array` and returns the first index that contains
* a value greater or equal to `element`. If no such index exists (i.e. all
* values in the array are strictly less than `element`), the array length is
* returned. Time complexity O(log n).
*
* NOTE: The `array` is expected to be sorted in ascending order, and to
* contain no repeated elements.
*
* IMPORTANT: Deprecated. This implementation behaves as {lowerBound} but lacks
* support for repeated elements in the array. The {lowerBound} function should
* be used instead.
*/
function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds towards zero (it does integer division with truncation).
if (unsafeAccess(array, mid).value > element) {
high = mid;
} else {
low = mid + 1;
}
}
// At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
if (low > 0 && unsafeAccess(array, low - 1).value == element) {
return low - 1;
} else {
return low;
}
}
/**
* @dev Searches an `array` sorted in ascending order and returns the first
* index that contains a value greater or equal than `element`. If no such index
* exists (i.e. all values in the array are strictly less than `element`), the array
* length is returned. Time complexity O(log n).
*
* See C++'s https://en.cppreference.com/w/cpp/algorithm/lower_bound[lower_bound].
*/
function lowerBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds towards zero (it does integer division with truncation).
if (unsafeAccess(array, mid).value < element) {
// this cannot overflow because mid < high
unchecked {
low = mid + 1;
}
} else {
high = mid;
}
}
return low;
}
/**
* @dev Searches an `array` sorted in ascending order and returns the first
* index that contains a value strictly greater than `element`. If no such index
* exists (i.e. all values in the array are strictly less than `element`), the array
* length is returned. Time complexity O(log n).
*
* See C++'s https://en.cppreference.com/w/cpp/algorithm/upper_bound[upper_bound].
*/
function upperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds towards zero (it does integer division with truncation).
if (unsafeAccess(array, mid).value > element) {
high = mid;
} else {
// this cannot overflow because mid < high
unchecked {
low = mid + 1;
}
}
}
return low;
}
/**
* @dev Same as {lowerBound}, but with an array in memory.
*/
function lowerBoundMemory(uint256[] memory array, uint256 element) internal pure returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds towards zero (it does integer division with truncation).
if (unsafeMemoryAccess(array, mid) < element) {
// this cannot overflow because mid < high
unchecked {
low = mid + 1;
}
} else {
high = mid;
}
}
return low;
}
/**
* @dev Same as {upperBound}, but with an array in memory.
*/
function upperBoundMemory(uint256[] memory array, uint256 element) internal pure returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds towards zero (it does integer division with truncation).
if (unsafeMemoryAccess(array, mid) > element) {
high = mid;
} else {
// this cannot overflow because mid < high
unchecked {
low = mid + 1;
}
}
}
return low;
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(address[] storage arr, uint256 pos) internal pure returns (StorageSlot.AddressSlot storage) {
bytes32 slot;
assembly ("memory-safe") {
slot := arr.slot
}
return slot.deriveArray().offset(pos).getAddressSlot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(bytes32[] storage arr, uint256 pos) internal pure returns (StorageSlot.Bytes32Slot storage) {
bytes32 slot;
assembly ("memory-safe") {
slot := arr.slot
}
return slot.deriveArray().offset(pos).getBytes32Slot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(uint256[] storage arr, uint256 pos) internal pure returns (StorageSlot.Uint256Slot storage) {
bytes32 slot;
assembly ("memory-safe") {
slot := arr.slot
}
return slot.deriveArray().offset(pos).getUint256Slot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(bytes[] storage arr, uint256 pos) internal pure returns (StorageSlot.BytesSlot storage) {
bytes32 slot;
assembly ("memory-safe") {
slot := arr.slot
}
return slot.deriveArray().offset(pos).getBytesSlot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(string[] storage arr, uint256 pos) internal pure returns (StorageSlot.StringSlot storage) {
bytes32 slot;
assembly ("memory-safe") {
slot := arr.slot
}
return slot.deriveArray().offset(pos).getStringSlot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(address[] memory arr, uint256 pos) internal pure returns (address res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(bytes32[] memory arr, uint256 pos) internal pure returns (bytes32 res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(uint256[] memory arr, uint256 pos) internal pure returns (uint256 res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(bytes[] memory arr, uint256 pos) internal pure returns (bytes memory res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(string[] memory arr, uint256 pos) internal pure returns (string memory res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
*
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/
function unsafeSetLength(address[] storage array, uint256 len) internal {
assembly ("memory-safe") {
sstore(array.slot, len)
}
}
/**
* @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
*
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/
function unsafeSetLength(bytes32[] storage array, uint256 len) internal {
assembly ("memory-safe") {
sstore(array.slot, len)
}
}
/**
* @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
*
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/
function unsafeSetLength(uint256[] storage array, uint256 len) internal {
assembly ("memory-safe") {
sstore(array.slot, len)
}
}
/**
* @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
*
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/
function unsafeSetLength(bytes[] storage array, uint256 len) internal {
assembly ("memory-safe") {
sstore(array.slot, len)
}
}
/**
* @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
*
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/
function unsafeSetLength(string[] storage array, uint256 len) internal {
assembly ("memory-safe") {
sstore(array.slot, len)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Comparators.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides a set of functions to compare values.
*
* _Available since v5.1._
*/
library Comparators {
function lt(uint256 a, uint256 b) internal pure returns (bool) {
return a < b;
}
function gt(uint256 a, uint256 b) internal pure returns (bool) {
return a > b;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.20;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
/**
* @dev The signature derives the `address(0)`.
*/
error ECDSAInvalidSignature();
/**
* @dev The signature has an invalid length.
*/
error ECDSAInvalidSignatureLength(uint256 length);
/**
* @dev The signature has an S value that is in the upper half order.
*/
error ECDSAInvalidSignatureS(bytes32 s);
/**
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
* and a bytes32 providing additional information about the error.
*
* If no error is returned, then the address can be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*/
function tryRecover(
bytes32 hash,
bytes memory signature
) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
assembly ("memory-safe") {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[ERC-2098 short signatures]
*/
function tryRecover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
// We do not check for an overflow here since the shift operation results in 0 or 1.
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
*/
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/cryptography/MessageHashUtils.sol)
pragma solidity ^0.8.20;
import {Strings} from "../Strings.sol";
/**
* @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
*
* The library provides methods for generating a hash of a message that conforms to the
* https://eips.ethereum.org/EIPS/eip-191[ERC-191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
* specifications.
*/
library MessageHashUtils {
/**
* @dev Returns the keccak256 digest of an ERC-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing a bytes32 `messageHash` with
* `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
* hash signed when using the https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign[`eth_sign`] JSON-RPC method.
*
* NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
* keccak256, although any bytes32 value can be safely used because the final digest will
* be re-hashed.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
assembly ("memory-safe") {
mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
}
}
/**
* @dev Returns the keccak256 digest of an ERC-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing an arbitrary `message` with
* `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
* hash signed when using the https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign[`eth_sign`] JSON-RPC method.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
return
keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
}
/**
* @dev Returns the keccak256 digest of an ERC-191 signed data with version
* `0x00` (data with intended validator).
*
* The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
* `validator` address. Then hashing the result.
*
* See {ECDSA-recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(hex"19_00", validator, data));
}
/**
* @dev Variant of {toDataWithIntendedValidatorHash-address-bytes} optimized for cases where `data` is a bytes32.
*/
function toDataWithIntendedValidatorHash(
address validator,
bytes32 messageHash
) internal pure returns (bytes32 digest) {
assembly ("memory-safe") {
mstore(0x00, hex"19_00")
mstore(0x02, shl(96, validator))
mstore(0x16, messageHash)
digest := keccak256(0x00, 0x36)
}
}
/**
* @dev Returns the keccak256 digest of an EIP-712 typed data (ERC-191 version `0x01`).
*
* The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
* `\x19\x01` and hashing the result. It corresponds to the hash signed by the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
*
* See {ECDSA-recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(ptr, hex"19_01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
digest := keccak256(ptr, 0x42)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)
pragma solidity >=0.4.16;
/**
* @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.3.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 Return the 512-bit addition of two uint256.
*
* The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.
*/
function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
assembly ("memory-safe") {
low := add(a, b)
high := lt(low, a)
}
}
/**
* @dev Return the 512-bit multiplication of two uint256.
*
* The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.
*/
function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
// 512-bit multiply [high low] = 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 = high * 2²⁵⁶ + low.
assembly ("memory-safe") {
let mm := mulmod(a, b, not(0))
low := mul(a, b)
high := sub(sub(mm, low), lt(mm, low))
}
}
/**
* @dev Returns the addition of two unsigned integers, with a success flag (no overflow).
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a + b;
success = c >= a;
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a - b;
success = c <= a;
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a * b;
assembly ("memory-safe") {
// Only true when the multiplication doesn't overflow
// (c / a == b) || (a == 0)
success := or(eq(div(c, a), b), iszero(a))
}
// equivalent to: success ? c : 0
result = c * SafeCast.toUint(success);
}
}
/**
* @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 {
success = b > 0;
assembly ("memory-safe") {
// The `DIV` opcode returns zero when the denominator is 0.
result := div(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 {
success = b > 0;
assembly ("memory-safe") {
// The `MOD` opcode returns zero when the denominator is 0.
result := mod(a, b)
}
}
}
/**
* @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.
*/
function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {
(bool success, uint256 result) = tryAdd(a, b);
return ternary(success, result, type(uint256).max);
}
/**
* @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.
*/
function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {
(, uint256 result) = trySub(a, b);
return result;
}
/**
* @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.
*/
function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {
(bool success, uint256 result) = tryMul(a, b);
return ternary(success, result, type(uint256).max);
}
/**
* @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 {
(uint256 high, uint256 low) = mul512(x, y);
// Handle non-overflow cases, 256 by 256 division.
if (high == 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 low / denominator;
}
// Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
if (denominator <= high) {
Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [high low].
uint256 remainder;
assembly ("memory-safe") {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
high := sub(high, gt(remainder, low))
low := sub(low, 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 ("memory-safe") {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [high low] by twos.
low := div(low, 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 high into low.
low |= high * 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 high
// is no longer required.
result = low * 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 Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.
*/
function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {
unchecked {
(uint256 high, uint256 low) = mul512(x, y);
if (high >= 1 << n) {
Panic.panic(Panic.UNDER_OVERFLOW);
}
return (high << (256 - n)) | (low >> n);
}
}
/**
* @dev Calculates x * y >> n with full precision, following the selected rounding direction.
*/
function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {
return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 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 x) internal pure returns (uint256 r) {
// If value has upper 128 bits set, log2 result is at least 128
r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
// If upper 64 bits of 128-bit half set, add 64 to result
r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
// If upper 32 bits of 64-bit half set, add 32 to result
r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
// If upper 16 bits of 32-bit half set, add 16 to result
r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
// If upper 8 bits of 16-bit half set, add 8 to result
r |= SafeCast.toUint((x >> r) > 0xff) << 3;
// If upper 4 bits of 8-bit half set, add 4 to result
r |= SafeCast.toUint((x >> r) > 0xf) << 2;
// Shifts value right by the current result and use it as an index into this lookup table:
//
// | x (4 bits) | index | table[index] = MSB position |
// |------------|---------|-----------------------------|
// | 0000 | 0 | table[0] = 0 |
// | 0001 | 1 | table[1] = 0 |
// | 0010 | 2 | table[2] = 1 |
// | 0011 | 3 | table[3] = 1 |
// | 0100 | 4 | table[4] = 2 |
// | 0101 | 5 | table[5] = 2 |
// | 0110 | 6 | table[6] = 2 |
// | 0111 | 7 | table[7] = 2 |
// | 1000 | 8 | table[8] = 3 |
// | 1001 | 9 | table[9] = 3 |
// | 1010 | 10 | table[10] = 3 |
// | 1011 | 11 | table[11] = 3 |
// | 1100 | 12 | table[12] = 3 |
// | 1101 | 13 | table[13] = 3 |
// | 1110 | 14 | table[14] = 3 |
// | 1111 | 15 | table[15] = 3 |
//
// The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.
assembly ("memory-safe") {
r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))
}
}
/**
* @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 x) internal pure returns (uint256 r) {
// If value has upper 128 bits set, log2 result is at least 128
r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
// If upper 64 bits of 128-bit half set, add 64 to result
r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
// If upper 32 bits of 64-bit half set, add 32 to result
r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
// If upper 16 bits of 32-bit half set, add 16 to result
r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
// Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8
return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);
}
/**
* @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/math/SignedMath.sol)
pragma solidity ^0.8.20;
import {SafeCast} from "./SafeCast.sol";
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @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, int256 a, int256 b) internal pure returns (int256) {
unchecked {
// branchless ternary works because:
// b ^ (a ^ b) == a
// b ^ 0 == b
return b ^ ((a ^ b) * int256(SafeCast.toUint(condition)));
}
}
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return ternary(a > b, a, b);
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return ternary(a < b, a, b);
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// Formula from the "Bit Twiddling Hacks" by Sean Eron Anderson.
// Since `n` is a signed integer, the generated bytecode will use the SAR opcode to perform the right shift,
// taking advantage of the most significant (or "sign" bit) in two's complement representation.
// This opcode adds new most significant bits set to the value of the previous most significant bit. As a result,
// the mask will either be `bytes32(0)` (if n is positive) or `~bytes32(0)` (if n is negative).
int256 mask = n >> 255;
// A `bytes32(0)` mask leaves the input unchanged, while a `~bytes32(0)` mask complements it.
return uint256((n + mask) ^ mask);
}
}
}// 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.3.0) (utils/SlotDerivation.sol)
// This file was procedurally generated from scripts/generate/templates/SlotDerivation.js.
pragma solidity ^0.8.20;
/**
* @dev Library for computing storage (and transient storage) locations from namespaces and deriving slots
* corresponding to standard patterns. The derivation method for array and mapping matches the storage layout used by
* the solidity language / compiler.
*
* See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.].
*
* Example usage:
* ```solidity
* contract Example {
* // Add the library methods
* using StorageSlot for bytes32;
* using SlotDerivation for bytes32;
*
* // Declare a namespace
* string private constant _NAMESPACE = "<namespace>"; // eg. OpenZeppelin.Slot
*
* function setValueInNamespace(uint256 key, address newValue) internal {
* _NAMESPACE.erc7201Slot().deriveMapping(key).getAddressSlot().value = newValue;
* }
*
* function getValueInNamespace(uint256 key) internal view returns (address) {
* return _NAMESPACE.erc7201Slot().deriveMapping(key).getAddressSlot().value;
* }
* }
* ```
*
* TIP: Consider using this library along with {StorageSlot}.
*
* NOTE: This library provides a way to manipulate storage locations in a non-standard way. Tooling for checking
* upgrade safety will ignore the slots accessed through this library.
*
* _Available since v5.1._
*/
library SlotDerivation {
/**
* @dev Derive an ERC-7201 slot from a string (namespace).
*/
function erc7201Slot(string memory namespace) internal pure returns (bytes32 slot) {
assembly ("memory-safe") {
mstore(0x00, sub(keccak256(add(namespace, 0x20), mload(namespace)), 1))
slot := and(keccak256(0x00, 0x20), not(0xff))
}
}
/**
* @dev Add an offset to a slot to get the n-th element of a structure or an array.
*/
function offset(bytes32 slot, uint256 pos) internal pure returns (bytes32 result) {
unchecked {
return bytes32(uint256(slot) + pos);
}
}
/**
* @dev Derive the location of the first element in an array from the slot where the length is stored.
*/
function deriveArray(bytes32 slot) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, slot)
result := keccak256(0x00, 0x20)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, address key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, and(key, shr(96, not(0))))
mstore(0x20, slot)
result := keccak256(0x00, 0x40)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, bool key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, iszero(iszero(key)))
mstore(0x20, slot)
result := keccak256(0x00, 0x40)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, bytes32 key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, key)
mstore(0x20, slot)
result := keccak256(0x00, 0x40)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, uint256 key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, key)
mstore(0x20, slot)
result := keccak256(0x00, 0x40)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, int256 key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, key)
mstore(0x20, slot)
result := keccak256(0x00, 0x40)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, string memory key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
let length := mload(key)
let begin := add(key, 0x20)
let end := add(begin, length)
let cache := mload(end)
mstore(end, slot)
result := keccak256(begin, add(length, 0x20))
mstore(end, cache)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, bytes memory key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
let length := mload(key)
let begin := add(key, 0x20)
let end := add(begin, length)
let cache := mload(end)
mstore(end, slot)
result := keccak256(begin, add(length, 0x20))
mstore(end, cache)
}
}
}// 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.4.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SafeCast} from "./math/SafeCast.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
using SafeCast for *;
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
uint256 private constant SPECIAL_CHARS_LOOKUP =
(1 << 0x08) | // backspace
(1 << 0x09) | // tab
(1 << 0x0a) | // newline
(1 << 0x0c) | // form feed
(1 << 0x0d) | // carriage return
(1 << 0x22) | // double quote
(1 << 0x5c); // backslash
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev The string being parsed contains characters that are not in scope of the given base.
*/
error StringsInvalidChar();
/**
* @dev The string being parsed is not a properly formatted address.
*/
error StringsInvalidAddressFormat();
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
assembly ("memory-safe") {
ptr := add(add(buffer, 0x20), length)
}
while (true) {
ptr--;
assembly ("memory-safe") {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its checksummed ASCII `string` hexadecimal
* representation, according to EIP-55.
*/
function toChecksumHexString(address addr) internal pure returns (string memory) {
bytes memory buffer = bytes(toHexString(addr));
// hash the hex part of buffer (skip length + 2 bytes, length 40)
uint256 hashValue;
assembly ("memory-safe") {
hashValue := shr(96, keccak256(add(buffer, 0x22), 40))
}
for (uint256 i = 41; i > 1; --i) {
// possible values for buffer[i] are 48 (0) to 57 (9) and 97 (a) to 102 (f)
if (hashValue & 0xf > 7 && uint8(buffer[i]) > 96) {
// case shift by xoring with 0x20
buffer[i] ^= 0x20;
}
hashValue >>= 4;
}
return string(buffer);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
/**
* @dev Parse a decimal string and returns the value as a `uint256`.
*
* Requirements:
* - The string must be formatted as `[0-9]*`
* - The result must fit into an `uint256` type
*/
function parseUint(string memory input) internal pure returns (uint256) {
return parseUint(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseUint-string} that parses a substring of `input` located between position `begin` (included) and
* `end` (excluded).
*
* Requirements:
* - The substring must be formatted as `[0-9]*`
* - The result must fit into an `uint256` type
*/
function parseUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {
(bool success, uint256 value) = tryParseUint(input, begin, end);
if (!success) revert StringsInvalidChar();
return value;
}
/**
* @dev Variant of {parseUint-string} that returns false if the parsing fails because of an invalid character.
*
* NOTE: This function will revert if the result does not fit in a `uint256`.
*/
function tryParseUint(string memory input) internal pure returns (bool success, uint256 value) {
return _tryParseUintUncheckedBounds(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseUint-string-uint256-uint256} that returns false if the parsing fails because of an invalid
* character.
*
* NOTE: This function will revert if the result does not fit in a `uint256`.
*/
function tryParseUint(
string memory input,
uint256 begin,
uint256 end
) internal pure returns (bool success, uint256 value) {
if (end > bytes(input).length || begin > end) return (false, 0);
return _tryParseUintUncheckedBounds(input, begin, end);
}
/**
* @dev Implementation of {tryParseUint-string-uint256-uint256} that does not check bounds. Caller should make sure that
* `begin <= end <= input.length`. Other inputs would result in undefined behavior.
*/
function _tryParseUintUncheckedBounds(
string memory input,
uint256 begin,
uint256 end
) private pure returns (bool success, uint256 value) {
bytes memory buffer = bytes(input);
uint256 result = 0;
for (uint256 i = begin; i < end; ++i) {
uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));
if (chr > 9) return (false, 0);
result *= 10;
result += chr;
}
return (true, result);
}
/**
* @dev Parse a decimal string and returns the value as a `int256`.
*
* Requirements:
* - The string must be formatted as `[-+]?[0-9]*`
* - The result must fit in an `int256` type.
*/
function parseInt(string memory input) internal pure returns (int256) {
return parseInt(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseInt-string} that parses a substring of `input` located between position `begin` (included) and
* `end` (excluded).
*
* Requirements:
* - The substring must be formatted as `[-+]?[0-9]*`
* - The result must fit in an `int256` type.
*/
function parseInt(string memory input, uint256 begin, uint256 end) internal pure returns (int256) {
(bool success, int256 value) = tryParseInt(input, begin, end);
if (!success) revert StringsInvalidChar();
return value;
}
/**
* @dev Variant of {parseInt-string} that returns false if the parsing fails because of an invalid character or if
* the result does not fit in a `int256`.
*
* NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.
*/
function tryParseInt(string memory input) internal pure returns (bool success, int256 value) {
return _tryParseIntUncheckedBounds(input, 0, bytes(input).length);
}
uint256 private constant ABS_MIN_INT256 = 2 ** 255;
/**
* @dev Variant of {parseInt-string-uint256-uint256} that returns false if the parsing fails because of an invalid
* character or if the result does not fit in a `int256`.
*
* NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.
*/
function tryParseInt(
string memory input,
uint256 begin,
uint256 end
) internal pure returns (bool success, int256 value) {
if (end > bytes(input).length || begin > end) return (false, 0);
return _tryParseIntUncheckedBounds(input, begin, end);
}
/**
* @dev Implementation of {tryParseInt-string-uint256-uint256} that does not check bounds. Caller should make sure that
* `begin <= end <= input.length`. Other inputs would result in undefined behavior.
*/
function _tryParseIntUncheckedBounds(
string memory input,
uint256 begin,
uint256 end
) private pure returns (bool success, int256 value) {
bytes memory buffer = bytes(input);
// Check presence of a negative sign.
bytes1 sign = begin == end ? bytes1(0) : bytes1(_unsafeReadBytesOffset(buffer, begin)); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
bool positiveSign = sign == bytes1("+");
bool negativeSign = sign == bytes1("-");
uint256 offset = (positiveSign || negativeSign).toUint();
(bool absSuccess, uint256 absValue) = tryParseUint(input, begin + offset, end);
if (absSuccess && absValue < ABS_MIN_INT256) {
return (true, negativeSign ? -int256(absValue) : int256(absValue));
} else if (absSuccess && negativeSign && absValue == ABS_MIN_INT256) {
return (true, type(int256).min);
} else return (false, 0);
}
/**
* @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as a `uint256`.
*
* Requirements:
* - The string must be formatted as `(0x)?[0-9a-fA-F]*`
* - The result must fit in an `uint256` type.
*/
function parseHexUint(string memory input) internal pure returns (uint256) {
return parseHexUint(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseHexUint-string} that parses a substring of `input` located between position `begin` (included) and
* `end` (excluded).
*
* Requirements:
* - The substring must be formatted as `(0x)?[0-9a-fA-F]*`
* - The result must fit in an `uint256` type.
*/
function parseHexUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {
(bool success, uint256 value) = tryParseHexUint(input, begin, end);
if (!success) revert StringsInvalidChar();
return value;
}
/**
* @dev Variant of {parseHexUint-string} that returns false if the parsing fails because of an invalid character.
*
* NOTE: This function will revert if the result does not fit in a `uint256`.
*/
function tryParseHexUint(string memory input) internal pure returns (bool success, uint256 value) {
return _tryParseHexUintUncheckedBounds(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseHexUint-string-uint256-uint256} that returns false if the parsing fails because of an
* invalid character.
*
* NOTE: This function will revert if the result does not fit in a `uint256`.
*/
function tryParseHexUint(
string memory input,
uint256 begin,
uint256 end
) internal pure returns (bool success, uint256 value) {
if (end > bytes(input).length || begin > end) return (false, 0);
return _tryParseHexUintUncheckedBounds(input, begin, end);
}
/**
* @dev Implementation of {tryParseHexUint-string-uint256-uint256} that does not check bounds. Caller should make sure that
* `begin <= end <= input.length`. Other inputs would result in undefined behavior.
*/
function _tryParseHexUintUncheckedBounds(
string memory input,
uint256 begin,
uint256 end
) private pure returns (bool success, uint256 value) {
bytes memory buffer = bytes(input);
// skip 0x prefix if present
bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(buffer, begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
uint256 offset = hasPrefix.toUint() * 2;
uint256 result = 0;
for (uint256 i = begin + offset; i < end; ++i) {
uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));
if (chr > 15) return (false, 0);
result *= 16;
unchecked {
// Multiplying by 16 is equivalent to a shift of 4 bits (with additional overflow check).
// This guarantees that adding a value < 16 will not cause an overflow, hence the unchecked.
result += chr;
}
}
return (true, result);
}
/**
* @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as an `address`.
*
* Requirements:
* - The string must be formatted as `(0x)?[0-9a-fA-F]{40}`
*/
function parseAddress(string memory input) internal pure returns (address) {
return parseAddress(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseAddress-string} that parses a substring of `input` located between position `begin` (included) and
* `end` (excluded).
*
* Requirements:
* - The substring must be formatted as `(0x)?[0-9a-fA-F]{40}`
*/
function parseAddress(string memory input, uint256 begin, uint256 end) internal pure returns (address) {
(bool success, address value) = tryParseAddress(input, begin, end);
if (!success) revert StringsInvalidAddressFormat();
return value;
}
/**
* @dev Variant of {parseAddress-string} that returns false if the parsing fails because the input is not a properly
* formatted address. See {parseAddress-string} requirements.
*/
function tryParseAddress(string memory input) internal pure returns (bool success, address value) {
return tryParseAddress(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseAddress-string-uint256-uint256} that returns false if the parsing fails because input is not a properly
* formatted address. See {parseAddress-string-uint256-uint256} requirements.
*/
function tryParseAddress(
string memory input,
uint256 begin,
uint256 end
) internal pure returns (bool success, address value) {
if (end > bytes(input).length || begin > end) return (false, address(0));
bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(bytes(input), begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
uint256 expectedLength = 40 + hasPrefix.toUint() * 2;
// check that input is the correct length
if (end - begin == expectedLength) {
// length guarantees that this does not overflow, and value is at most type(uint160).max
(bool s, uint256 v) = _tryParseHexUintUncheckedBounds(input, begin, end);
return (s, address(uint160(v)));
} else {
return (false, address(0));
}
}
function _tryParseChr(bytes1 chr) private pure returns (uint8) {
uint8 value = uint8(chr);
// Try to parse `chr`:
// - Case 1: [0-9]
// - Case 2: [a-f]
// - Case 3: [A-F]
// - otherwise not supported
unchecked {
if (value > 47 && value < 58) value -= 48;
else if (value > 96 && value < 103) value -= 87;
else if (value > 64 && value < 71) value -= 55;
else return type(uint8).max;
}
return value;
}
/**
* @dev Escape special characters in JSON strings. This can be useful to prevent JSON injection in NFT metadata.
*
* WARNING: This function should only be used in double quoted JSON strings. Single quotes are not escaped.
*
* NOTE: This function escapes all unicode characters, and not just the ones in ranges defined in section 2.5 of
* RFC-4627 (U+0000 to U+001F, U+0022 and U+005C). ECMAScript's `JSON.parse` does recover escaped unicode
* characters that are not in this range, but other tooling may provide different results.
*/
function escapeJSON(string memory input) internal pure returns (string memory) {
bytes memory buffer = bytes(input);
bytes memory output = new bytes(2 * buffer.length); // worst case scenario
uint256 outputLength = 0;
for (uint256 i; i < buffer.length; ++i) {
bytes1 char = bytes1(_unsafeReadBytesOffset(buffer, i));
if (((SPECIAL_CHARS_LOOKUP & (1 << uint8(char))) != 0)) {
output[outputLength++] = "\\";
if (char == 0x08) output[outputLength++] = "b";
else if (char == 0x09) output[outputLength++] = "t";
else if (char == 0x0a) output[outputLength++] = "n";
else if (char == 0x0c) output[outputLength++] = "f";
else if (char == 0x0d) output[outputLength++] = "r";
else if (char == 0x5c) output[outputLength++] = "\\";
else if (char == 0x22) {
// solhint-disable-next-line quotes
output[outputLength++] = '"';
}
} else {
output[outputLength++] = char;
}
}
// write the actual length and deallocate unused memory
assembly ("memory-safe") {
mstore(output, outputLength)
mstore(0x40, add(output, shl(5, shr(5, add(outputLength, 63)))))
}
return string(output);
}
/**
* @dev Reads a bytes32 from a bytes array without bounds checking.
*
* NOTE: making this function internal would mean it could be used with memory unsafe offset, and marking the
* assembly block as such would prevent some optimizations.
*/
function _unsafeReadBytesOffset(bytes memory buffer, uint256 offset) private pure returns (bytes32 value) {
// This is not memory safe in the general case, but all calls to this private function are within bounds.
assembly ("memory-safe") {
value := mload(add(add(buffer, 0x20), offset))
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
import {Arrays} from "../Arrays.sol";
import {Math} from "../math/Math.sol";
/**
* @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.
* - Set can be cleared (all elements removed) in O(n).
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* The following types are supported:
*
* - `bytes32` (`Bytes32Set`) since v3.3.0
* - `address` (`AddressSet`) since v3.3.0
* - `uint256` (`UintSet`) since v3.3.0
* - `string` (`StringSet`) since v5.4.0
* - `bytes` (`BytesSet`) since v5.4.0
*
* [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 Removes all the values from a set. O(n).
*
* WARNING: This function has an unbounded cost that scales with set size. Developers should keep in mind that
* using it may render the function uncallable if the set grows to the point where clearing it consumes too much
* gas to fit in a block.
*/
function _clear(Set storage set) private {
uint256 len = _length(set);
for (uint256 i = 0; i < len; ++i) {
delete set._positions[set._values[i]];
}
Arrays.unsafeSetLength(set._values, 0);
}
/**
* @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;
}
/**
* @dev Return a slice of the 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, uint256 start, uint256 end) private view returns (bytes32[] memory) {
unchecked {
end = Math.min(end, _length(set));
start = Math.min(start, end);
uint256 len = end - start;
bytes32[] memory result = new bytes32[](len);
for (uint256 i = 0; i < len; ++i) {
result[i] = Arrays.unsafeAccess(set._values, start + i).value;
}
return result;
}
}
// 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 Removes all the values from a set. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(Bytes32Set storage set) internal {
_clear(set._inner);
}
/**
* @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;
}
/**
* @dev Return a slice of the 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, uint256 start, uint256 end) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner, start, end);
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 Removes all the values from a set. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(AddressSet storage set) internal {
_clear(set._inner);
}
/**
* @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;
}
/**
* @dev Return a slice of the 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, uint256 start, uint256 end) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner, start, end);
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 Removes all the values from a set. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(UintSet storage set) internal {
_clear(set._inner);
}
/**
* @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;
}
/**
* @dev Return a slice of the 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, uint256 start, uint256 end) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner, start, end);
uint256[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
struct StringSet {
// Storage of set values
string[] _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(string 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(StringSet storage set, string memory value) internal 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(StringSet storage set, string memory value) internal 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) {
string memory 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 Removes all the values from a set. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(StringSet storage set) internal {
uint256 len = length(set);
for (uint256 i = 0; i < len; ++i) {
delete set._positions[set._values[i]];
}
Arrays.unsafeSetLength(set._values, 0);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(StringSet storage set, string memory value) internal view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function length(StringSet storage set) internal 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(StringSet storage set, uint256 index) internal view returns (string memory) {
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(StringSet storage set) internal view returns (string[] memory) {
return set._values;
}
/**
* @dev Return a slice of the 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(StringSet storage set, uint256 start, uint256 end) internal view returns (string[] memory) {
unchecked {
end = Math.min(end, length(set));
start = Math.min(start, end);
uint256 len = end - start;
string[] memory result = new string[](len);
for (uint256 i = 0; i < len; ++i) {
result[i] = Arrays.unsafeAccess(set._values, start + i).value;
}
return result;
}
}
struct BytesSet {
// Storage of set values
bytes[] _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(bytes 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(BytesSet storage set, bytes memory value) internal 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(BytesSet storage set, bytes memory value) internal 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) {
bytes memory 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 Removes all the values from a set. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(BytesSet storage set) internal {
uint256 len = length(set);
for (uint256 i = 0; i < len; ++i) {
delete set._positions[set._values[i]];
}
Arrays.unsafeSetLength(set._values, 0);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(BytesSet storage set, bytes memory value) internal view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function length(BytesSet storage set) internal 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(BytesSet storage set, uint256 index) internal view returns (bytes memory) {
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(BytesSet storage set) internal view returns (bytes[] memory) {
return set._values;
}
/**
* @dev Return a slice of the 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(BytesSet storage set, uint256 start, uint256 end) internal view returns (bytes[] memory) {
unchecked {
end = Math.min(end, length(set));
start = Math.min(start, end);
uint256 len = end - start;
bytes[] memory result = new bytes[](len);
for (uint256 i = 0; i < len; ++i) {
result[i] = Arrays.unsafeAccess(set._values, start + i).value;
}
return result;
}
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { IDomaRecord } from "../../interfaces/IDomaRecord.sol";
import { IERC7786GatewaySource } from "../../interfaces/IERC7786.sol";
import { CAIPUtils } from "../../utils/CAIPUtils.sol";
import { IProxyDomaRecord } from "../../interfaces/IProxyDomaRecord.sol";
import { NameUtils } from "../../utils/NameUtils.sol";
import { GatewayUtils } from "../../utils/GatewayUtils.sol";
// Capability bit masks
uint256 constant CAPABILITY_REGISTRY_RECORDS_MANAGEMENT = 1; // 1 << 0
uint256 constant CAPABILITY_DNS_RECORDS_MANAGEMENT = 1 << 1; // 2
library LibDoma {
using Strings for uint256;
error ChainNotSupported(string chainId);
error DetokenizationForbidden(string sld, string tld);
error NameIsNotClaimed(string sld, string tld);
error NameNotFound(string sld, string tld);
error TldNotSupported(string tld);
struct Registrar {
uint256 ianaId;
address[] operators;
address[] complianceOperators;
uint256 supportedCapabilities;
}
/**
* @notice Tokenization request information.
* @param registrarIanaId The IANA ID of a sponsoring registrar.
* @param sld The second-level domain of the name. E.g. "example" in "example.com".
* @param tld The top-level domain of the name. E.g. "com" in "example.com".
* @param ownershipTokenChainId The chain ID on which ownership token should be minted, CAIP-2 format.
* @param ownershipTokenOwnerAddress The address that would own the token on the target chain.
*/
struct TokenizationRequest {
uint256 registrarIanaId;
string sld;
string tld;
string ownershipTokenChainId;
string ownershipTokenOwnerAddress;
}
/**
* @notice Claim request information.
* @param claimedBy Token owner that performed a name claim.
* @param proofSource Registrant handle source location. For now, only DOMA value is supported.
* @param registrantHandle Opaque handle of a registrant. Used to fetch contacts information from the proof source.
*/
struct ClaimRequest {
string claimedBy;
IDomaRecord.ProofOfContactsSource proofSource;
uint256 registrantHandle;
}
/* solhint-disable-next-line gas-struct-packing */
struct Name {
/**
* @dev IANA Id of a sponsoring registrar.
*/
uint256 registrarIanaId;
/**
* @dev Second-Level domain. E.g. "example" in "example.com".
*/
string sld;
/**
* @dev Top-Level domain. E.g. "com" in "example.com".
*/
string tld;
/**
* @dev Registrar Expiration Date of a Name.
*/
uint256 expiresAt;
/**
* @dev Indicates whether it's an Expression of Interest (EOI) name, or a full ICANN Domain.
*/
bool eoi;
/**
* @dev List of nameservers configured for a domain.
* Only used for ICANN domains.
*/
string[] nameservers;
/**
* @dev List of DNSSEC DSKeys configured for a domain.
* Only used for ICANN domains.
*/
IDomaRecord.DSKey[] dsKeys;
/**
* @dev Current owner (claimer) of a name (CAIP-10 format).
* For EOIs, always equals to the `ownedBy` of an ownership token.
*/
string claimedBy;
/**
* @dev Registrar-supported capabilities for a current domain.
*/
uint256 supportedCapabilities;
/**
* @dev List of issued name token ids.
* Always contains at least one token (ownership token).
*/
uint256[] nameTokens;
/**
* @dev Whether compliance detokenization was requested for this name.
* Use to prevent some operations, like bridging, if forced detokenization is requested.
*/
bool complianceDetokenizationRequested;
}
struct NameToken {
/**
* @dev Globally-unique token ID.
*/
uint256 tokenId;
/**
* @dev CAIP-2 identifier of a target chain.
*/
string chainId;
/**
* @dev Local-chain address of a last-known owner on a target chain.
* Value is updated by relaying cross-chain messages.
*/
string ownedBy;
/**
* @dev Whether it's an Ownership or Synthetic token type.
*/
bool isSynthetic;
/**
* @dev Whether it's an Ownership token (that gives a claim right to a domain).
*/
bool ownership;
/**
* @dev Capabilities included into this token.
* Only used for Permissioned tokens.
*/
uint256 capabilities;
/**
* @dev Start date when included capabilities can be used
* Only used for Permissioned tokens.
* 0 value means token can be used immediately after mint.
*/
uint256 startsAt;
/**
* @dev End date after which included capabilities can no longer be used.
* Only used for Permissioned tokens.
* 0 value means unbound end date (until domain is deleted).
*/
uint256 expiresAt;
}
struct ChainPolicy {
/**
* @dev Whether this chain only supports EOI names.
*/
bool eoiOnly;
/**
* @dev List of EOI TLDs supported on this chain.
*/
string[] eoiTlds;
}
struct DomaState {
/**
* @dev Address of a cross-chain gateway to send cross-chain messages.
*/
IERC7786GatewaySource crossChainGateway;
/**
* @dev Maps CAIP-2 chain ids to Doma Proxy Contracts Address on a target chain.
*/
mapping(string => string) proxyContracts;
/**
* @dev Mapping to retrieve registrar info by IANA ID.
*/
mapping(uint256 => Registrar) registrarByIanaId;
/**
* @dev Maps registrar operator address to registrar IANA ID.
* Used for efficient access control checks.
*/
mapping(address => uint256) registrarByOperator;
/**
* @dev Maps registrar compliance operator address to registrar IANA ID.
* Used for efficient access control checks.
*/
mapping(address => uint256) registrarByComplianceOperator;
/**
* @dev List of accounts with protocol admin role.
*/
mapping(address => bool) protocolAdmins;
/**
* @dev List of accounts with cross-chain sender role.
*/
mapping(address => bool) crossChainSenders;
/**
* @dev Trusted forwarder contract address.
*/
address trustedForwarder;
/**
* @dev Maps tokenization request ID to tokenization request.
* Tokenization request ID is a namehash of a name (which is also used as ownership token id).
*/
mapping(uint256 => TokenizationRequest) tokenizationRequests;
/**
* @dev Maps name id (namehash) to name info.
*/
mapping(uint256 => Name) names;
/**
* @dev Maps name token id to name token info.
*/
mapping(uint256 => NameToken) nameTokens;
/**
* @dev Maps name token id to name id.
* Useful for reverse lookup of a name by token id.
*/
mapping(uint256 => uint256) nameTokensToName;
/**
* @dev Maps EOI tlds to registrar IANA ID.
* This is used to check if Registrar is allowed to issue EOI names.
*/
mapping(string => uint256) eoiRegistrars;
/**
* @dev List of supported TLDs, that have been whitelisted on DOMA Protocol.
* TLDs on this list are mutually exclusive with EOI tlds (from `eoiRegistrars`).
*/
mapping(string => bool) tlds;
/**
* @dev Maps claim request ID to claim request.
* Claim request ID is a namehash of a name (which is also used as ownership token id).
*/
mapping(uint256 => ClaimRequest) claimRequests;
/**
* @dev List of precomputed TLDs namehashes.
* Used as a performance optimization to better support eTLDs.
*/
mapping(string => uint256) tldHashes;
/**
* @dev Doma-controlled nameservers. Implicitly grants CAPABILITY_DNS_RECORDS_MANAGEMENT.
*/
mapping(string => bool) domaNameservers;
/**
* @dev Maps CAIP-2 chain ids to chain policies.
* Defines which types of names (EOI/ICANN) can be tokenized on each chain.
*/
mapping(string => ChainPolicy) chainPolicies;
}
function domaState() internal pure returns (DomaState storage ds) {
// By convention, doma state is the first storage slot
bytes32 position = 0;
// solhint-disable-next-line no-inline-assembly
assembly {
ds.slot := position
}
}
function relayMessage(
bytes memory data,
string memory targetChainId,
uint256 nonceKey,
string memory correlationId
) internal {
DomaState storage ds = domaState();
string memory proxyAddress = ds.proxyContracts[targetChainId];
if (bytes(proxyAddress).length == 0) {
revert ChainNotSupported(targetChainId);
}
bytes[] memory attributes = new bytes[](2);
attributes[0] = abi.encodeWithSelector(
GatewayUtils.CORRELATION_ID_ATTRIBUTE,
correlationId
);
attributes[1] = abi.encodeWithSelector(GatewayUtils.NONCE_KEY_ATTRIBUTE, nonceKey);
ds.crossChainGateway.sendMessage(
targetChainId,
CAIPUtils.format(targetChainId, proxyAddress),
data,
attributes
);
}
function detokenizeIfClaimed(
uint256 nameId,
Name memory name,
string memory correlationId
) internal {
DomaState storage ds = domaState();
// Detokenization is forbidden if permissioned synthetic exists
// We can check it if there's more than one token (ownership token always exists)
if (name.nameTokens.length > 1) {
revert LibDoma.DetokenizationForbidden(name.sld, name.tld);
}
LibDoma.NameToken memory ownershipToken = ds.nameTokens[nameId];
if (isClaimedByDomaProxy(name.claimedBy)) {
revert LibDoma.NameIsNotClaimed(name.sld, name.tld);
}
bytes memory detokenizeCalldata = abi.encodeCall(
IProxyDomaRecord.detokenize,
(nameId.toString(), ownershipToken.isSynthetic, ownershipToken.ownedBy, correlationId)
);
LibDoma.relayMessage(detokenizeCalldata, ownershipToken.chainId, nameId, correlationId);
}
function mintOwnershipToken(
uint256 nameId,
LibDoma.Name memory name,
LibDoma.NameToken memory token,
string memory correlationId
) internal {
IProxyDomaRecord.OwnershipTokenInfo[]
memory tokens = new IProxyDomaRecord.OwnershipTokenInfo[](1);
tokens[0] = IProxyDomaRecord.OwnershipTokenInfo(
name.sld,
name.tld,
token.tokenId.toString(),
name.expiresAt
);
// Send only domain-level capabilities (excludes registrar-level)
uint256[] memory domainCapabilities = new uint256[](1);
domainCapabilities[0] = LibDoma.calculateDomainLevelCapabilities(
name.supportedCapabilities,
name.nameservers
);
bytes memory mintOwnershipTokensCalldata = abi.encodeWithSignature(
// we have to use encodeWithSignature here to match overloaded function
"mintOwnershipTokens(uint256,(string,string,string,uint256)[],string,uint256[],string)",
name.registrarIanaId,
tokens,
token.ownedBy,
domainCapabilities,
correlationId
);
LibDoma.relayMessage(mintOwnershipTokensCalldata, token.chainId, nameId, correlationId);
}
function ensureTLDSupported(string memory tld) internal view {
DomaState storage ds = domaState();
if (!ds.tlds[tld]) {
revert TldNotSupported(tld);
}
}
function ensureChainSupported(string memory chainId) internal view {
DomaState storage ds = domaState();
if (bytes(ds.proxyContracts[chainId]).length == 0) {
revert ChainNotSupported(chainId);
}
}
function namehash(string memory sld, string memory tld) internal view returns (uint256) {
DomaState storage ds = domaState();
uint256 tldNamehash = ds.tldHashes[tld];
// Safety check to make sure TLD hash has been precomputed
// We can remove it once migration is done on devnet and testnet
if (tldNamehash == 0) {
tldNamehash = NameUtils.namehash(tld);
}
return NameUtils.namehash(tldNamehash, sld);
}
function isClaimedByDomaProxy(string memory claimedBy) internal view returns (bool) {
return Strings.equal(claimedBy, domaProxyAddress());
}
function domaProxyAddress() internal view returns (string memory) {
return
CAIPUtils.format(CAIPUtils.caip2Local(), "0x0000000000000000000000000000000000000000");
}
/**
* @dev Check if nameservers are a subset of Doma-controlled nameservers.
* @param nameservers Domain's nameserver list to check.
* @return True if all nameservers are Doma-controlled, false otherwise.
*/
function isUsingDomaNameservers(string[] memory nameservers) internal view returns (bool) {
if (nameservers.length == 0) return false;
DomaState storage ds = domaState();
for (uint256 i = 0; i < nameservers.length; i++) {
if (!ds.domaNameservers[nameservers[i]]) {
return false;
}
}
return true;
}
/**
* @notice Check if a specific capability is present in the capabilities bit field
* @param capabilities The combined capabilities bit field
* @param requiredCapability The required capability bit mask
* @return True if the capability is present
*/
function hasCapability(
uint256 capabilities,
uint256 requiredCapability
) internal pure returns (bool) {
return (capabilities & requiredCapability) == requiredCapability;
}
/**
* @notice Calculate effective capabilities for a name
* @dev Combines registrar-level and domain-level capabilities with implicit DNS capability
* @param registrarCapabilities The registrar's supported capabilities
* @param domainCapabilities The domain's supported capabilities
* @param nameservers The domain's nameserver list
* @return effectiveCapabilities The combined effective capabilities
*/
function calculateEffectiveCapabilities(
uint256 registrarCapabilities,
uint256 domainCapabilities,
string[] memory nameservers
) internal view returns (uint256 effectiveCapabilities) {
// Base capabilities = registrar + domain
effectiveCapabilities = registrarCapabilities | domainCapabilities;
// Add implicit DNS capability if using Doma nameservers
if (isUsingDomaNameservers(nameservers)) {
effectiveCapabilities |= CAPABILITY_DNS_RECORDS_MANAGEMENT;
}
}
/**
* @notice Calculate domain-level capabilities only (excludes registrar-level capabilities)
* @dev Used for propagating capabilities to tokenization chains where only domain-specific
* capabilities need to be stored. Registrar-level capabilities remain on the DOMA chain
* and are combined during authorization checks.
* @param domainCapabilities The domain's explicit capabilities
* @param nameservers The domain's nameserver list
* @return capabilities The combined domain-level capabilities (explicit + implicit)
*/
function calculateDomainLevelCapabilities(
uint256 domainCapabilities,
string[] memory nameservers
) internal view returns (uint256 capabilities) {
// Start with explicit domain capabilities
capabilities = domainCapabilities;
// Add implicit DNS capability if using Doma nameservers
if (isUsingDomaNameservers(nameservers)) {
capabilities |= CAPABILITY_DNS_RECORDS_MANAGEMENT;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface ICreatorToken {
/**
* @notice Emitted when the transfer validator is updated
* @param oldValidator The previous transfer validator address
* @param newValidator The new transfer validator address
*/
event TransferValidatorUpdated(address oldValidator, address newValidator);
/**
* @notice Returns the current transfer validator address
* @return validator The address of the transfer validator contract
*/
function getTransferValidator() external view returns (address validator);
/**
* @notice Sets a new transfer validator
* @param validator The address of the new transfer validator contract
*/
function setTransferValidator(address validator) external;
/**
* @notice Returns the function signature used for transfer validation
* @return functionSignature The function selector for validation
* @return isViewFunction Whether the validation function is a view function
*/
function getTransferValidationFunction()
external
view
returns (bytes4 functionSignature, bool isViewFunction);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
interface IDomaRecord {
struct NameInfo {
string sld;
string tld;
}
struct DSKey {
uint32 keyTag;
uint8 algorithm;
uint8 digestType;
bytes digest;
}
enum ProofOfContactsSource {
NONE,
REGISTRAR,
DOMA
}
function initiateTokenization(
uint256 registrarIanaId,
NameInfo[] calldata names,
string calldata ownershipTokenChainId,
string calldata ownershipTokenOwnerAddress,
string calldata correlationId
) external;
function claimOwnership(
string calldata tokenId,
string calldata chainId,
string calldata claimedBy,
ProofOfContactsSource proofSource,
uint256 registrantHandle,
string calldata correlationId
) external;
function bridge(
string calldata tokenId,
string calldata targetChainId,
string calldata targetOwnerAddress,
string calldata correlationId
) external;
function ownerDetokenize(
string calldata tokenId,
string calldata chainId,
string calldata ownerAddress,
string calldata correlationId
) external;
function completeDetokenization(
string calldata tokenId,
string calldata correlationId
) external;
function tokenTransfer(
string calldata chainId,
string calldata tokenId,
string calldata oldOwnerAddress,
string calldata newOwnerAddress,
string calldata correlationId
) external;
function setNameservers(
uint256 tokenId,
string[] calldata nameservers,
string calldata correlationId
) external;
function setDSKeys(
uint256 tokenId,
DSKey[] calldata dsKeys,
string calldata correlationId
) external;
function setDNSRRSet(
uint256 tokenId,
string calldata host,
string calldata recordType,
uint32 ttl,
string[] calldata records,
string calldata correlationId
) external;
function convertToSynthetic(
uint256 tokenId,
string calldata chainId,
string calldata owner,
string calldata correlationId
) external;
function completeSyntheticConversion(
string calldata tokenId,
string calldata correlationId
) external;
function convertToOwnership(
uint256 tokenId,
string calldata chainId,
string calldata owner,
string calldata correlationId
) external;
function completeOwnershipConversion(
string calldata tokenId,
string calldata correlationId
) external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
interface IERC7786GatewaySource {
event MessagePosted(
bytes32 indexed outboxId,
string sender,
string receiver,
bytes payload,
uint256 value,
bytes[] attributes
);
error UnsupportedAttribute(bytes4 selector);
function supportsAttribute(bytes4 selector) external view returns (bool);
function sendMessage(
string calldata destinationChain, // CAIP-2 chain identifier
string calldata receiver, // CAIP-10 account address
bytes calldata payload,
bytes[] calldata attributes
) external payable returns (bytes32 outboxId);
}
interface IERC7786Receiver {
function executeMessage(
string calldata sourceChain, // [CAIP-2] chain identifier
string calldata sender, // [CAIP-10] account address
bytes calldata payload,
bytes[] calldata attributes
) external payable returns (bytes4);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
interface IProxyDomaRecord {
struct OwnershipTokenInfo {
string sld;
string tld;
string tokenId;
uint256 expiresAt;
}
struct SyntheticTokenMintInfo {
uint256 tokenId;
uint256 registrarIanaId;
string owner;
bool revocable;
string sld;
string tld;
uint256 expiresAt;
uint256 capabilities;
string host;
uint256 parentTokenId;
uint256 groupId;
}
function mintOwnershipTokens(
uint256 registrarIanaId,
OwnershipTokenInfo[] calldata tokens,
string calldata ownerAddress,
uint256[] calldata domainCapabilities,
string calldata correlationId
) external;
// override to retain backward compatibility
function mintOwnershipTokens(
uint256 registrarIanaId,
OwnershipTokenInfo[] calldata tokens,
string calldata ownerAddress,
string calldata correlationId
) external;
function mintSyntheticFromOwnership(
SyntheticTokenMintInfo calldata info,
string calldata correlationId
) external;
function mintOwnershipFromSynthetic(
OwnershipTokenInfo calldata info,
string calldata ownerAddress,
uint256 registrarIanaId,
uint256 domainCapabilities,
string calldata correlationId
) external;
function renew(
string calldata tokenId,
bool isSynthetic,
uint256 expiresAt,
string calldata correlationId
) external;
function detokenize(
string calldata tokenId,
bool isSynthetic,
string calldata claimedBy,
string calldata correlationId
) external;
function detokenizeUnchecked(
string calldata tokenId,
bool isSynthetic,
string calldata correlationId
) external;
function changeLockStatus(
string calldata tokenId,
bool isSynthetic,
bool isTransferLocked,
string calldata correlationId
) external;
function updateDomainCapabilities(
string calldata tokenId,
uint256 capabilities,
string calldata correlationId
) external;
function tokenTransfer(uint256 tokenId, address from, address to) external;
function setDNSRRSet(
uint256 tokenId,
string calldata host,
string calldata recordType,
uint32 ttl,
string[] calldata records,
bool isSynthetic
) external payable;
function setDNSRRSet(
uint256 tokenId,
string calldata host,
string calldata recordType,
uint32 ttl,
string[] calldata records
) external payable;
function convertToSynthetic(uint256 tokenId) external payable;
function convertToOwnership(uint256 tokenId) external payable;
function mintSyntheticSubdomain(
uint256 parentTokenId,
string calldata host,
uint256 capabilities,
uint256 expiresAt,
bool revocable,
uint256 groupId,
address receiver
) external payable returns (uint256);
function revokeGroup(uint256 parentTokenId, uint256 groupId) external;
function renounceSynthetic(uint256 tokenId) external;
function revokeSynthetic(uint256 tokenId) external;
function isLabelForbidden(string calldata label) external view returns (bool);
function syntheticToken() external view returns (address);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
/**
* @title ISyntheticToken
* @notice Interface for SyntheticToken functions used by fractionalization contracts
*/
interface ISyntheticToken {
/**
* @notice Get the owner of a token, even if it has been burned.
* @dev If the token exists, returns the current owner via ownerOf().
* If the token has been burned, returns the last owner stored before burning.
* Used to allow users to claim staked tokens after their subdomain NFT is revoked and burned.
* @param tokenId The ID of the token.
* @return The address of the current owner if token exists, or last owner if burned.
*/
function lastOwnerOf(uint256 tokenId) external view returns (address);
/**
* @notice Check if a subdomain is operational (not revoked or expired)
* @param parentTokenId The parent domain token ID
* @param label The subdomain label
* @return True if operational, false otherwise
*/
function isSubdomainOperational(
uint256 parentTokenId,
string calldata label
) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface ITransferValidator {
/**
* @notice Validates a token transfer with token ID
* @param caller The address initiating the transfer
* @param from The address sending the token
* @param to The address receiving the token
* @param tokenId The ID of the token being transferred
*/
function validateTransfer(
address caller,
address from,
address to,
uint256 tokenId
) external view;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface ITransferValidatorSetTokenType {
/**
* @notice Sets the token type for a collection
* @param collection The address of the collection contract
* @param tokenType The type of token (e.g., ERC721, ERC1155, ERC20)
*/
function setTokenTypeOfCollection(address collection, uint16 tokenType) external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable-v4/proxy/utils/UUPSUpgradeable.sol";
import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable-v4/token/ERC721/ERC721Upgradeable.sol";
import { ERC721BurnableUpgradeable } from "@openzeppelin/contracts-upgradeable-v4/token/ERC721/extensions/ERC721BurnableUpgradeable.sol";
import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable-v4/access/AccessControlUpgradeable.sol";
import { IProxyDomaRecord } from "./interfaces/IProxyDomaRecord.sol";
import { ICreatorToken } from "./interfaces/ICreatorToken.sol";
import { IERC2981 } from "@openzeppelin/contracts/interfaces/IERC2981.sol";
import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol";
import { ERC721C } from "./utils/ERC721C.sol";
import { AccessControlOwnable } from "./utils/AccessControlOwnable.sol";
import { DateUtils } from "./utils/DateUtils.sol";
/**
* @title NameToken
* @notice Abstract base contract for name NFTs in the DOMA protocol.
* @dev This contract implements an ERC721 NFT that represents tokenized names.
* Features include expiration tracking, transfer locking, name capabilities management,
* royalty support (ERC2981), and integration with proxy DOMA record contracts.
*/
abstract contract NameToken is
UUPSUpgradeable,
ERC721Upgradeable,
ERC721BurnableUpgradeable,
AccessControlOwnable,
ERC721C,
IERC165,
IERC2981
{
/**
* @dev Storage structure for ERC2981 royalty information.
*/
struct ERC2981RoyaltyInfo {
address receiver;
uint96 royaltyFraction;
}
/**
* @dev Storage structure for ERC2981 royalty information.
* Contains the contract-wide royalty information that applies to all tokens.
*/
/// @custom:storage-location erc7201:doma.storage.ERC2981
struct ERC2981Storage {
ERC2981RoyaltyInfo royaltyInfo;
}
// Storage slot for ERC2981 storage
bytes32 private constant _ERC2981_STORAGE_SLOT =
keccak256(abi.encode(uint256(keccak256(bytes("doma.storage.ERC2981"))) - 1)) &
~bytes32(uint256(0xff));
/**
* @dev Role required to mint tokens.
* Equals to 0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6
*/
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
/**
* @dev Role required to update metadata.
*/
bytes32 public constant METADATA_OWNER_ROLE = keccak256("METADATA_OWNER_ROLE");
/**
* @notice Thrown when attempting to transfer a token that is locked for transfers.
* @param tokenId The ID of the locked token
*/
error TransferLocked(uint256 tokenId);
/**
* @notice Thrown when owner attempts to use burn functionality that is not supported.
*/
error OwnerBurnNotSupported();
/**
* @notice Thrown when proxy DOMA record address is not set but required for operation.
*/
error ProxyDomaRecordNotSet();
/**
* @notice Thrown when a zero address is provided where a valid address is required.
*/
error ZeroAddress();
/**
* @notice Thrown when an invalid royalty receiver address is provided.
*/
error InvalidRoyaltyReceiver();
/**
* @notice Thrown when royalty fraction exceeds the maximum allowed value.
* @param feeNumerator The provided fee numerator
* @param feeDenominator The fee denominator (10000 for basis points)
*/
error InvalidRoyaltyFraction(uint96 feeNumerator, uint96 feeDenominator);
/**
* @notice Thrown when array parameters have mismatched lengths.
*/
error ArrayLengthMismatch();
/**
* @notice Thrown when the token does not exist
*/
error NameTokenDoesNotExist(uint256 tokenId);
/**
* @notice Emitted when name token is renewed.
* @param tokenId The ID of the name token.
* @param expiresAt The expiration date of the name token (UNIX seconds).
* @param correlationId Correlation id associated with a renewal event. Used by registrars to track on-chain operations.
*/
event NameTokenRenewed(uint256 indexed tokenId, uint256 expiresAt, string correlationId);
/**
* @notice Emitted when name token is burned.
* Similar to ERC721 `Transfer` event with zero `to`, but with an additional correlation id included.
* @param tokenId The ID of the name token.
* @param owner Owner address at the time of burning.
* @param correlationId Correlation id associated with a burn event. Used by registrars to track on-chain operations.
*/
event NameTokenBurned(uint256 indexed tokenId, address owner, string correlationId);
/**
* @notice Emitted when name token is locked or unlocked.
* @param tokenId The ID of the name token.
* @param isTransferLocked Whether token transfer is locked or not.
* @param correlationId Correlation id associated with a lock status change event. Used by registrars to track on-chain operations.
*/
event LockStatusChanged(uint256 indexed tokenId, bool isTransferLocked, string correlationId);
/**
* @notice Emitted when metadata is updated for a token.
* Can happen when token is renewed.
* Follows IERC4906 Metadata Update Extension.
*/
event MetadataUpdate(uint256 tokenId);
/**
* @notice Emitted when domain-level capabilities are updated for a token.
* @param tokenId The ID of the name token.
* @param capabilities The new domain capabilities bitmask.
* @param correlationId Correlation id for tracking operations.
*/
event DomainCapabilitiesUpdated(
uint256 indexed tokenId,
uint256 capabilities,
string correlationId
);
/**
* @dev Base URI for NFT tokens.
*/
string internal _baseTokenURI;
/**
* @dev TLDs list is deprecated and no longer used.
* Variable is left in place to keep storage layout the same.
* In future, this storage slot could be reused for a different purpose.
*/
mapping(string => uint256) internal _tlds;
/**
* @dev Maps Token ID to its expiration date.
* Expiration date is a timestamp in seconds.
* Since contract guarantees there could be only one token with a given ID, we can use Token ID as a key.
*/
/// @custom:oz-renamed-from expirations
mapping(uint256 => uint256) internal _expirations;
/**
* @dev Maps Token ID to its lock status
* If set to true, token cannot be transferred
*/
/// @custom:oz-renamed-from transferLocks
mapping(uint256 => bool) internal _transferLocks;
/**
* @dev Storage gap, from previous version of the contract, to keep storage layout the same
* Storage slot from a removed `gracePeriod` variable is reused
* We can do this since mappings don't store any data in a storage slot
*/
/// @custom:oz-renamed-from gracePeriod
/// @custom:oz-retyped-from uint256
mapping(uint256 => uint256) internal _registrarIanaIds;
/**
* @dev Block all transfers and minting. Used to prevent transfers during migration.
*/
bool public blockAllTransfers;
/**
* @dev Address if ProxyDomaRecord Contract for proxy call
*/
IProxyDomaRecord public proxyDomaRecord;
/**
* @dev Storage gap for future upgrades.
*/
uint256[50] private _storageGap;
/**
* @dev Maps Token ID to its domain-level capabilities.
* These are the capabilities supported by this specific domain.
* Propagated automatically from Doma Chain when capabilities change.
*/
mapping(uint256 => uint256) internal _domainCapabilities;
modifier onlyMinter() {
_checkRole(MINTER_ROLE);
_;
}
modifier onlyOwner() {
_checkRole(DEFAULT_ADMIN_ROLE);
_;
}
modifier onlyMetadataOwner() {
_checkRole(METADATA_OWNER_ROLE);
_;
}
/**
* @notice Constructor that disables initializers on an implementation contract.
* @dev This is recommended for upgradable contracts.
*/
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/**
* @notice Initialize the contract with name, symbol, URI, and initial owner.
* @dev Acts as constructor for upgradeable contract. Sets up roles and inheritance chain.
* The initial owner receives DEFAULT_ADMIN_ROLE and can manage other roles.
* @param name Name for the NFT collection (different contract instances may have different names)
* @param symbol Symbol for the NFT collection (usually a short version of the name)
* @param uri Base URI for NFT token metadata (typically points to off-chain mutable storage like S3)
* @param initialOwner Address that will receive admin role and contract ownership
*/
function initialize(
string calldata name,
string calldata symbol,
string calldata uri,
address initialOwner
) public initializer {
__UUPSUpgradeable_init();
__AccessControl_init();
__ERC721_init(name, symbol);
__ERC721Burnable_init();
if (initialOwner == address(0)) {
revert ZeroAddress();
}
_setupRole(DEFAULT_ADMIN_ROLE, initialOwner);
_setRoleAdmin(MINTER_ROLE, DEFAULT_ADMIN_ROLE);
_setRoleAdmin(METADATA_OWNER_ROLE, DEFAULT_ADMIN_ROLE);
_baseTokenURI = uri;
}
/**
* @notice User-initiated burn is not supported.
* @dev This override prevents direct token burning by users. Only the bulkBurn function
* should be used by authorized minters to ensure proper cleanup and event emission.
* Base class (ERC721BurnableUpgradeable) is kept for compatibility with previous storage layout.
*/
function burn(uint256) public virtual override {
revert OwnerBurnNotSupported();
}
/**
* @dev Internal function to burn a name token and clean up all associated storage.
* @param tokenId The ID of the token to burn.
* @param correlationId Correlation ID for tracking the burn operation.
*/
function _burnNameToken(uint256 tokenId, string memory correlationId) internal {
address owner = ownerOf(tokenId);
delete _domainCapabilities[tokenId];
delete _expirations[tokenId];
delete _transferLocks[tokenId];
delete _registrarIanaIds[tokenId];
_burn(tokenId);
emit NameTokenBurned(tokenId, owner, correlationId);
}
/**
* @notice Trigger metadata update event for a specific token.
* @dev Only metadata owners can call this function. Follows IERC4906 Metadata Update Extension.
* This is useful when off-chain metadata changes and the update needs to be signaled on-chain.
* @param tokenId The ID of the token whose metadata was updated
*/
function metadataUpdated(uint256 tokenId) public onlyMetadataOwner {
emit MetadataUpdate(tokenId);
}
/**
* @notice Returns expiration date for a token. After this date, token transfer will be blocked.
* @param id Token ID.
* @return uint256 Unix timestamp in seconds.
*/
function expirationOf(uint256 id) external view virtual returns (uint256) {
return _expirations[id];
}
/**
* @notice Returns registrar IANA ID for a token.
* @param id Token ID.
* @return uint256 Registrar IANA ID.
*/
function registrarOf(uint256 id) external view returns (uint256) {
return _registrarIanaIds[id];
}
/**
* @notice Returns transfer lock status for a token. If 'true', token cannot be transferred.
* @param id Token ID.
*/
function lockStatusOf(uint256 id) external view returns (bool) {
return _transferLocks[id] || blockAllTransfers;
}
/**
* @notice Returns domain-level capabilities for a token.
* These are the capabilities supported by this specific domain.
* @param id Token ID.
* @return uint256 Domain capabilities bitmask.
*/
function domainCapabilitiesOf(uint256 id) external view returns (uint256) {
return _domainCapabilities[id];
}
/**
* @notice Check if a token has a specific capability.
* @param id Token ID.
* @param requiredCapability The required capability bit mask.
* @return bool True if the token has the capability.
*/
function hasCapability(uint256 id, uint256 requiredCapability) external view returns (bool) {
uint256 capabilities = _domainCapabilities[id];
return (capabilities & requiredCapability) == requiredCapability;
}
/**
* @notice Returns true if a token with the given ID exists.
* @param id Token ID.
*/
function exists(uint256 id) external view returns (bool) {
return _exists(id);
}
/**
* @notice Overrides behavior of isApprovedForAll such that if an operator is not explicitly approved
* for all, the contract owner can optionally auto-approve the transfer validator for transfers.
* @param owner The owner of the tokens
* @param operator The operator to check approval for
* @return isApproved True if the operator is approved for all tokens
*/
function isApprovedForAll(
address owner,
address operator
) public view virtual override(ERC721Upgradeable) returns (bool isApproved) {
isApproved = super.isApprovedForAll(owner, operator);
if (!isApproved) {
ERC721C.ERC721CStorage storage storage_ = _erc721Storage();
if (storage_.autoApproveTransfersFromValidator) {
isApproved = operator == getTransferValidator();
}
}
}
/**
* @notice Check if the contract supports a given interface.
* @dev Extends the standard ERC721 interface support to include additional interfaces:
* Metadata Update (EIP-4906), Creator Token, ERC2981 Royalty, and Ownable interfaces.
* @param interfaceId The interface identifier to check
* @return Whether the contract supports the interface
*/
function supportsInterface(
bytes4 interfaceId
)
public
view
virtual
override(ERC721Upgradeable, AccessControlUpgradeable, IERC165)
returns (bool)
{
// Additional check for Metadata Update interface, ICreatorToken interface, IERC2981 interface, and Ownable interface
return
interfaceId == bytes4(0x49064906) || // Metadata Update interface
interfaceId == type(ICreatorToken).interfaceId ||
interfaceId == bytes4(0x7f5828d0) || // Ownable interface
super.supportsInterface(interfaceId);
}
/**
* @notice Get royalty information for a token sale (ERC2981).
* @dev Returns the royalty receiver and amount for a given sale price.
* Royalty is calculated as (salePrice * royaltyFraction) / 10000.
* @param salePrice The sale price of the token
* @return receiver The address that should receive the royalty payment
* @return royaltyAmount The royalty amount to be paid
*/
function royaltyInfo(
uint256,
uint256 salePrice
) external view virtual override returns (address receiver, uint256 royaltyAmount) {
ERC2981Storage storage storage_ = _erc2981Storage();
ERC2981RoyaltyInfo storage royalty = storage_.royaltyInfo;
uint256 royaltyFraction = royalty.royaltyFraction;
receiver = royalty.receiver;
royaltyAmount = (salePrice * royaltyFraction) / _feeDenominator();
}
/**
* @notice Update the base URI for all tokens in the collection.
* @dev Admin-only function that affects metadata for all existing and future tokens.
* Should be used with care as it impacts the entire collection.
* @param newUri The new base URI to set
*/
function setURI(string calldata newUri) external onlyOwner {
_baseTokenURI = newUri;
}
/**
* @notice Enable or disable all token transfers globally.
* @dev Admin-only function used to block transfers during migration or emergency situations.
* When enabled, no tokens can be transferred regardless of individual lock status.
* @param newBlockAllTransfers True to block all transfers, false to allow normal operation
*/
function setBlockAllTransfers(bool newBlockAllTransfers) external onlyOwner {
blockAllTransfers = newBlockAllTransfers;
}
/**
* @notice Set transfer lock status for a specific token.
* @dev Minter-only function to prevent or allow transfers of individual tokens.
* Emits LockStatusChanged event for tracking purposes.
* @param id The token ID to modify
* @param isTransferLocked True to lock transfers, false to unlock
* @param correlationId Correlation ID for tracking the operation
*/
function setLockStatus(
uint256 id,
bool isTransferLocked,
string calldata correlationId
) external onlyMinter {
_transferLocks[id] = isTransferLocked;
emit LockStatusChanged(id, isTransferLocked, correlationId);
}
/**
* @notice Update domain capabilities for a specific token.
* @dev Minter-only function called when capabilities are updated on DOMA chain.
* Domain capabilities determine what operations the token holder can perform.
* @param id The token ID to update
* @param capabilities The new domain capabilities bitmask
* @param correlationId Correlation ID for tracking the operation
*/
function updateDomainCapabilities(
uint256 id,
uint256 capabilities,
string calldata correlationId
) external onlyMinter {
_domainCapabilities[id] = capabilities;
emit DomainCapabilitiesUpdated(id, capabilities, correlationId);
}
/**
* @notice Set the address of the ProxyDomaRecord contract.
* @dev Admin-only function to update the proxy contract reference.
* Used for cross-chain communication and operations.
* @param proxyDomaRecord_ The new ProxyDomaRecord contract address
*/
function setProxyDomaRecord(IProxyDomaRecord proxyDomaRecord_) external onlyOwner {
if (address(proxyDomaRecord_) == address(0)) revert ZeroAddress();
proxyDomaRecord = proxyDomaRecord_;
}
/**
* @notice Set royalty information for all tokens in the collection.
* @dev Admin-only function implementing ERC2981 royalty standard.
* Royalties are paid as (salePrice * feeNumerator) / 10000.
* @param receiver Address that should receive royalty payments
* @param feeNumerator The royalty fee numerator (basis points, max 10000)
*/
function setRoyaltyInfo(address receiver, uint96 feeNumerator) external onlyOwner {
_setRoyaltyInfo(receiver, feeNumerator);
}
/**
* @notice Remove royalty information from the contract.
* @dev Admin-only function to disable royalties for all tokens.
*/
function deleteRoyaltyInfo() external onlyOwner {
_deleteRoyaltyInfo();
}
function _mint(
uint256 registrarIanaId,
uint256 tokenId,
uint256 expiresAt,
address to
) internal {
DateUtils.validateExpirationDate(expiresAt);
_safeMint(to, tokenId);
_expirations[tokenId] = expiresAt;
_registrarIanaIds[tokenId] = registrarIanaId;
}
function _baseURI() internal view override returns (string memory) {
return _baseTokenURI;
}
/**
* @dev Overridden to support token expiration and ERC721C validation.
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 firstTokenId,
uint256
) internal virtual override {
// Handle only transfer between owners
// Burn is not public and handled internally
// Mint has a special logic to handle expirations
if (from != address(0) && to != address(0)) {
if (
_isTokenExpired(firstTokenId) || _transferLocks[firstTokenId] || blockAllTransfers
) {
revert TransferLocked(firstTokenId);
}
// Revert if proxyDomaRecord is not set
if (address(proxyDomaRecord) == address(0)) {
revert ProxyDomaRecordNotSet();
}
proxyDomaRecord.tokenTransfer(firstTokenId, from, to);
// Call ERC721C validation
_preValidateTransfer(_msgSender(), from, to, firstTokenId, msg.value);
}
}
function _isTokenExpired(uint256 id) internal view virtual returns (bool) {
return _expirations[id] < block.timestamp;
}
/* solhint-disable-next-line no-empty-blocks */
function _authorizeUpgrade(address newImplementation) internal virtual override onlyOwner {}
/**
* @dev Implementation required by ERC721C for contract ownership verification.
* Uses the DEFAULT_ADMIN_ROLE to determine contract ownership.
*/
function _requireCallerIsContractOwner() internal view virtual override {
_checkRole(DEFAULT_ADMIN_ROLE);
}
function _setRoyaltyInfo(address receiver, uint96 feeNumerator) internal virtual {
if (feeNumerator > _feeDenominator()) {
revert InvalidRoyaltyFraction(feeNumerator, _feeDenominator());
}
if (receiver == address(0)) {
revert InvalidRoyaltyReceiver();
}
ERC2981Storage storage storage_ = _erc2981Storage();
storage_.royaltyInfo = ERC2981RoyaltyInfo(receiver, feeNumerator);
}
/**
* @dev Internal function to remove all royalty information from storage.
*/
function _deleteRoyaltyInfo() internal virtual {
delete _erc2981Storage().royaltyInfo;
}
function version() external pure virtual returns (string memory);
function _erc2981Storage() internal pure returns (ERC2981Storage storage storageStruct) {
bytes32 slot = _ERC2981_STORAGE_SLOT;
// solhint-disable-next-line no-inline-assembly
assembly {
storageStruct.slot := slot
}
}
/**
* @dev Returns the denominator for royalty fee calculations.
* @return The fee denominator (10000, representing basis points)
*/
function _feeDenominator() internal pure virtual returns (uint96) {
return 10000;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
import { NameToken } from "./NameToken.sol";
import { DateUtils } from "./utils/DateUtils.sol";
contract OwnershipToken is NameToken {
/**
* @notice Emitted when an ownership token is minted.
* Emitted together with standard ERC-721 Transfer event, but contains additional information.
* @param tokenId The ID of the ownership token.
* @param registrarIanaId The IANA ID of a sponsoring registrar.
* @param to The address that received the ownership token.
* @param sld The second-level domain of the name. E.g. "example" in "example.com".
* @param tld The top-level domain of the name. E.g. "com" in "example.com".
* @param expiresAt The expiration date of the name (UNIX seconds).
* @param correlationId Correlation id associated with a mint event. Used by registrars to track on-chain operations.
*/
event OwnershipTokenMinted(
uint256 indexed tokenId,
uint256 registrarIanaId,
address to,
string sld,
string tld,
uint256 expiresAt,
string correlationId
);
struct OwnershipTokenMintInfo {
uint256 registrarIanaId;
string sld;
string tld;
uint256 tokenId;
uint256 expiresAt;
address owner;
}
/**
* @notice Bulk mint tokens without domain capabilities. Could be called only by a minter role.
* @param names Names to mint.
* @param correlationId Correlation id associated with a given mint request.
*/
function bulkMint(
OwnershipTokenMintInfo[] calldata names,
string calldata correlationId
) public {
// onlyMinter modifier on the overloaded function
uint256[] memory domainCapabilities = new uint256[](names.length);
// Array is initialized with zero capabilities by default
bulkMint(names, domainCapabilities, correlationId);
}
/**
* @notice Bulk mint tokens with domain capabilities. Could be called only by a minter role.
* @param names Names to mint.
* @param domainCapabilities Domain capabilities for each token (must match names length, use 0 for no capabilities).
* @param correlationId Correlation id associated with a given mint request.
*/
function bulkMint(
OwnershipTokenMintInfo[] calldata names,
uint256[] memory domainCapabilities,
string calldata correlationId
) public onlyMinter {
if (names.length != domainCapabilities.length) {
revert ArrayLengthMismatch();
}
for (uint256 i = 0; i < names.length; i++) {
OwnershipTokenMintInfo memory name = names[i];
_mint(name.registrarIanaId, name.tokenId, name.expiresAt, name.owner);
// Set domain capabilities atomically during mint
if (domainCapabilities[i] != 0) {
_domainCapabilities[name.tokenId] = domainCapabilities[i];
}
emit OwnershipTokenMinted(
name.tokenId,
name.registrarIanaId,
name.owner,
name.sld,
name.tld,
name.expiresAt,
correlationId
);
// always emit even if capabilities are zero
emit DomainCapabilitiesUpdated(name.tokenId, domainCapabilities[i], correlationId);
}
}
/**
* @notice Renew SLD token. Could be called only by a minter role.
* @param tokenId Name token ID to renew.
* @param expiresAt Expiration date for a token (Unix timestamp seconds).
* @param correlationId Order id associated with a given mint.
*/
function renew(
uint256 tokenId,
uint256 expiresAt,
string calldata correlationId
) public onlyMinter {
DateUtils.validateExpirationDate(expiresAt);
address currentOwner = _ownerOf(tokenId);
if (currentOwner == address(0)) {
revert NameTokenDoesNotExist(tokenId);
}
_expirations[tokenId] = expiresAt;
emit NameTokenRenewed(tokenId, expiresAt, correlationId);
}
/**
* @notice Burn multiple ownership tokens.
* @dev Only authorized minters can call this function.
* @param tokenIds Array of token IDs to burn.
* @param correlationId Correlation ID associated with the burn operation for tracking.
*/
function bulkBurn(
uint256[] calldata tokenIds,
string calldata correlationId
) public onlyMinter {
uint256 len = tokenIds.length;
for (uint256 i = 0; i < len; ) {
_burnNameToken(tokenIds[i], correlationId);
unchecked {
i++;
}
}
}
function version() external pure override returns (string memory) {
return "2.1.0";
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
import { IERC7786GatewaySource } from "../../interfaces/IERC7786.sol";
import { NameToken } from "../../NameToken.sol";
import { OwnershipToken } from "../../OwnershipToken.sol";
import { SyntheticToken } from "../../SyntheticToken.sol";
import { CAIPUtils } from "../../utils/CAIPUtils.sol";
import { GatewayUtils } from "../../utils/GatewayUtils.sol";
import { NameUtils } from "../../utils/NameUtils.sol";
import { CAPABILITY_DNS_RECORDS_MANAGEMENT } from "../../doma-record/libraries/LibDoma.sol";
library LibProxyDomaRecord {
using Strings for uint256;
struct ProxyDomaRecordStorage {
/**
* @dev List of allowed EIP-712 voucher signers for each registrar.
* Maps signer to registrar IANA ID, to be able to lookup registrar from a current signer.
*/
mapping(address => uint256) registrarSigners;
/**
* @dev List of allowed EIP-712 voucher signers for doma protocol.
* Used to verify Doma-provided proof-of-contacts vouchers.
*/
mapping(address => bool) domaSigners;
/**
* @dev Used voucher nonces.
* Necessary to prevent replay attacks.
*/
mapping(uint256 => bool) nonces;
/**
* @dev Address of a cross-chain gateway to send cross-chain messages.
*/
IERC7786GatewaySource crossChainGateway;
/**
* @dev Address of an ownership token contract.
*/
OwnershipToken ownershipToken;
/**
* @dev CAIP-2 Chain ID of the Doma Chain.
*/
string domaChainId;
/**
* @dev CAIP-10 Address of a Doma Record Contract on Doma Chain.
*/
string domaRecordAddress;
/**
* @dev Internal lists to keep track of registrar signers for enumeration.
*/
mapping(uint256 => address[]) registrarSignerList;
address[] domaSignerList;
/**
* @dev ChainLink Price Fee contract.
*/
AggregatorV3Interface priceFeed;
/**
* @notice Fees per operation in USD cents (2 decimal places)
*/
mapping(bytes32 => uint256) feesUSDCents;
/**
* @dev Treasury account to collect fees.
*/
address treasury;
/**
* @dev Authorized cross-chain senders.
* Maps sender address to boolean indicating if authorized.
*/
mapping(address => bool) crossChainSenders;
/**
* @dev Used to generate unique correlation Id even on the same block.
*/
uint256 correlationNonce;
/**
* @dev List of supported target chain IDs for bridging.
* Maps chain ID to a boolean indicating if it's supported.
*/
mapping(string => bool) supportedTargetChains;
/**
* @dev Protocol admin management.
* Maps admin address to boolean indicating if authorized as protocol admin.
*/
mapping(address => bool) protocolAdmins;
/**
* @dev Forbidden subdomain labels.
* Maps label hash (lowercase) to boolean indicating if forbidden.
*/
mapping(bytes32 => bool) forbiddenLabels;
/**
* @dev Maps Registrar IANA ID to its capabilities.
* These are the capabilities supported by a given registrar.
*/
mapping(uint256 => uint256) registrarCapabilities;
/**
* @dev Address of a synthetic token contract.
*/
SyntheticToken syntheticToken;
}
// Storage location for the ProxyDomaRecord storage
bytes32 private constant _PROXY_DOMA_RECORD_STORAGE_LOCATION =
keccak256(abi.encode(uint256(keccak256("doma.storage.ProxyDomaRecord")) - 1)) &
~bytes32(uint256(0xff));
function proxyDomaRecordStorage() internal pure returns (ProxyDomaRecordStorage storage ds) {
bytes32 position = _PROXY_DOMA_RECORD_STORAGE_LOCATION;
// solhint-disable-next-line no-inline-assembly
assembly {
ds.slot := position
}
}
/**
* @notice `requestTokenization` operation key (for fee retrieval).
*/
bytes32 public constant REQUEST_TOKENIZATION_OPERATION = keccak256("REQUEST_TOKENIZATION");
/**
* @notice `claimOwnership` operation key (for fee retrieval).
*/
bytes32 public constant CLAIM_OWNERSHIP_OPERATION = keccak256("CLAIM_OWNERSHIP");
/**
* @notice `bridge` operation key (for fee retrieval).
*/
bytes32 public constant BRIDGE_OPERATION = keccak256("BRIDGE");
/**
* @notice `setNameservers` operation key (for fee retrieval).
*/
bytes32 public constant SET_NAMESERVERS_OPERATION = keccak256("SET_NAMESERVERS");
/**
* @notice `setDSKeys` operation key (for fee retrieval).
*/
bytes32 public constant SET_DS_KEYS_OPERATION = keccak256("SET_DS_KEYS");
/**
* @notice `setDNSRRSet` operation key (for fee retrieval).
*/
bytes32 public constant SET_DNS_RRSET_OPERATION = keccak256("SET_DNS_RRSET");
/**
* @notice `convertToSynthetic` operation key (for fee retrieval).
*/
bytes32 public constant CONVERT_TO_SYNTHETIC_OPERATION = keccak256("CONVERT_TO_SYNTHETIC");
/**
* @notice `convertToOwnership` operation key (for fee retrieval).
*/
bytes32 public constant CONVERT_TO_OWNERSHIP_OPERATION = keccak256("CONVERT_TO_OWNERSHIP");
error ZeroAddress();
error InvalidOwnerAddress(address owner, address tokenOwner);
error InvalidOperation(bytes32 operation);
error AccessControlNotAProtocolAdmin(address user);
error ArrayLengthMismatch();
error TransferLocked(uint256 tokenId);
error NameTokenHasExpired(uint256 tokenId, uint256 expiresAt);
error TokenAlreadySynthetic(uint256 tokenId);
error TokenNotSynthetic(uint256 tokenId);
error HasActiveSubdomains(uint256 tokenId, uint256 subdomainCount);
error NotSubdomain(uint256 tokenId);
error TokenNotRevocable(uint256 tokenId);
error GroupRevoked(uint256 parentTokenId, uint256 groupId);
error InsufficientCapabilities(uint256 tokenId, uint256 requiredCapability);
function _useCorrelationId() internal returns (string memory) {
ProxyDomaRecordStorage storage _storage = proxyDomaRecordStorage();
uint256 correlationId = uint256(
keccak256(abi.encodePacked(block.number, block.chainid, _storage.correlationNonce++))
);
return correlationId.toHexString();
}
function _relayMessage(
bytes memory data,
string memory correlationId,
uint256 nonceKey
) internal {
bytes[] memory attributes;
if (bytes(correlationId).length == 0) {
attributes = new bytes[](1);
} else {
attributes = new bytes[](2);
attributes[1] = abi.encodeWithSelector(
GatewayUtils.CORRELATION_ID_ATTRIBUTE,
correlationId
);
}
attributes[0] = abi.encodeWithSelector(GatewayUtils.NONCE_KEY_ATTRIBUTE, nonceKey);
ProxyDomaRecordStorage storage ds = proxyDomaRecordStorage();
ds.crossChainGateway.sendMessage(
ds.domaChainId,
CAIPUtils.format(ds.domaChainId, ds.domaRecordAddress),
data,
attributes
);
}
function _verifyTokenOwnership(uint256 tokenId, address owner) internal view {
ProxyDomaRecordStorage storage ds = proxyDomaRecordStorage();
address tokenOwner;
if (isSyntheticToken(tokenId)) {
tokenOwner = ds.syntheticToken.ownerOf(tokenId);
} else {
tokenOwner = ds.ownershipToken.ownerOf(tokenId);
}
if (owner != tokenOwner) {
revert InvalidOwnerAddress(owner, tokenOwner);
}
}
function _isValidOperation(bytes32 operation) internal pure returns (bool) {
return
operation == REQUEST_TOKENIZATION_OPERATION ||
operation == CLAIM_OWNERSHIP_OPERATION ||
operation == BRIDGE_OPERATION ||
operation == SET_NAMESERVERS_OPERATION ||
operation == SET_DS_KEYS_OPERATION ||
operation == SET_DNS_RRSET_OPERATION ||
operation == CONVERT_TO_SYNTHETIC_OPERATION ||
operation == CONVERT_TO_OWNERSHIP_OPERATION;
}
function _requireTokenExists(NameToken token, uint256 tokenId) internal view {
if (!token.exists(tokenId)) {
revert NameToken.NameTokenDoesNotExist(tokenId);
}
}
function _burnOwnershipToken(uint256 tokenId, string memory correlationId) internal {
ProxyDomaRecordStorage storage ds = proxyDomaRecordStorage();
uint256[] memory tokenIds = new uint256[](1);
tokenIds[0] = tokenId;
ds.ownershipToken.bulkBurn(tokenIds, correlationId);
}
/**
* @notice Check if a token is a synthetic token.
* @dev Checks ownership token contract first, then synthetic token contract.
* Reverts with NameTokenDoesNotExist error if token doesn't exist in either contract.
* @param tokenId The ID of the token to check.
* @return True if the token is a synthetic token, false if it's an ownership token.
*/
function isSyntheticToken(uint256 tokenId) internal view returns (bool) {
ProxyDomaRecordStorage storage ds = proxyDomaRecordStorage();
// Check ownership token first
if (ds.ownershipToken.exists(tokenId)) {
return false;
}
// Check synthetic or ownership token exists
if (!ds.syntheticToken.exists(tokenId)) {
revert NameToken.NameTokenDoesNotExist(tokenId);
}
return true;
}
/**
* @notice Get domain capabilities for a token (works for both ownership and synthetic tokens).
* @dev Checks existence in contracts to determine which one to query.
* @param tokenId The ID of the token.
* @return The domain capabilities bitmask for the token.
*/
function _getDomainCapabilities(uint256 tokenId) internal view returns (uint256) {
ProxyDomaRecordStorage storage ds = proxyDomaRecordStorage();
if (isSyntheticToken(tokenId)) {
return ds.syntheticToken.domainCapabilitiesOf(tokenId);
} else {
return ds.ownershipToken.domainCapabilitiesOf(tokenId);
}
}
/**
* @notice Validate a synthetic token for conversion to ownership token.
* @dev Checks that token is synthetic, caller owns it, it's not locked, not expired, has no subdomains.
* @param tokenId The ID of the token to validate.
* @param owner The expected owner of the token.
*/
function _validateSyntheticTokenForConversion(uint256 tokenId, address owner) internal view {
ProxyDomaRecordStorage storage ds = proxyDomaRecordStorage();
if (!isSyntheticToken(tokenId)) {
revert TokenNotSynthetic(tokenId);
}
_verifyTokenOwnership(tokenId, owner);
bool isLocked = ds.syntheticToken.lockStatusOf(tokenId);
if (isLocked) {
revert TransferLocked(tokenId);
}
uint256 expiresAt = ds.syntheticToken.expirationOf(tokenId);
if (expiresAt < block.timestamp) {
revert NameTokenHasExpired(tokenId, expiresAt);
}
uint256 subdomainCount = ds.syntheticToken.subdomainCountOf(tokenId);
if (subdomainCount > 0) {
revert HasActiveSubdomains(tokenId, subdomainCount);
}
}
/**
* @notice Validate an ownership token for conversion to synthetic token.
* @dev Checks that token is not synthetic, caller owns it, has DNS_RRSET capability, it's not locked, not expired.
* @param tokenId The ID of the token to validate.
* @param owner The expected owner of the token.
*/
function _validateOwnershipTokenForConversion(uint256 tokenId, address owner) internal view {
ProxyDomaRecordStorage storage ds = proxyDomaRecordStorage();
if (isSyntheticToken(tokenId)) {
revert TokenAlreadySynthetic(tokenId);
}
_verifyTokenOwnership(tokenId, owner);
uint256 capabilities = _getDomainCapabilities(tokenId);
if (
(capabilities & CAPABILITY_DNS_RECORDS_MANAGEMENT) != CAPABILITY_DNS_RECORDS_MANAGEMENT
) {
revert InsufficientCapabilities(tokenId, CAPABILITY_DNS_RECORDS_MANAGEMENT);
}
bool isLocked = ds.ownershipToken.lockStatusOf(tokenId);
if (isLocked) {
revert TransferLocked(tokenId);
}
uint256 expiresAt = ds.ownershipToken.expirationOf(tokenId);
if (expiresAt < block.timestamp) {
revert NameTokenHasExpired(tokenId, expiresAt);
}
}
/**
* @notice Validates subdomain extraction parameters.
* @dev Checks parent token state, host label format, and capability requirements.
* @param parentTokenId The ID of the parent synthetic token.
* @param host The subdomain host label.
* @param capabilities The capability flags being requested.
*/
function _validateSubdomainExtraction(
uint256 parentTokenId,
string calldata host,
uint256 capabilities
) internal view {
ProxyDomaRecordStorage storage ds = proxyDomaRecordStorage();
// Validate parent token exists and is synthetic
if (!isSyntheticToken(parentTokenId)) {
revert TokenNotSynthetic(parentTokenId);
}
// Validate parent token is not locked
bool isLocked = ds.syntheticToken.lockStatusOf(parentTokenId);
if (isLocked) {
revert TransferLocked(parentTokenId);
}
// Validate parent token is not expired
uint256 parentExpiresAt = ds.syntheticToken.expirationOf(parentTokenId);
if (parentExpiresAt < block.timestamp) {
revert NameTokenHasExpired(parentTokenId, parentExpiresAt);
}
// Validate host label format
NameUtils.ensureValidLabel(host);
// Validate capability includes CAPABILITY_DNS_RECORDS_MANAGEMENT (4)
if (
(capabilities & CAPABILITY_DNS_RECORDS_MANAGEMENT) != CAPABILITY_DNS_RECORDS_MANAGEMENT
) {
revert InsufficientCapabilities(parentTokenId, CAPABILITY_DNS_RECORDS_MANAGEMENT);
}
// Validate parent has the capabilities being delegated
uint256 parentCapabilities = _getDomainCapabilities(parentTokenId);
if ((parentCapabilities & capabilities) != capabilities) {
revert InsufficientCapabilities(parentTokenId, capabilities);
}
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
import { NameToken } from "./NameToken.sol";
import { DateUtils } from "./utils/DateUtils.sol";
import { NameUtils } from "./utils/NameUtils.sol";
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import { ISyntheticToken } from "./interfaces/ISyntheticToken.sol";
/**
* @title SyntheticToken
* @notice Synthetic ownership tokens for subdomain/fractional ownership in DOMA protocol.
* @dev Extends NameToken. Uses ERC-7201 namespaced storage to avoid collisions with inherited storage.
*/
contract SyntheticToken is NameToken, ISyntheticToken {
// ============================================
// Libraries
// ============================================
using EnumerableSet for EnumerableSet.UintSet;
// ============================================
// Types
// ============================================
/**
* @dev Data structure for synthetic token-specific information.
* Storage: 4 slots (parentTokenId: slot 0, host: slot 1, subdomainCount + revocable: slot 2, groupId: slot 3).
* @param parentTokenId Token ID of the parent domain (0 for root domains).
* @param host Subdomain host string (empty for root domains).
* @param subdomainCount Number of active subdomains (uint64 allows 18 quintillion subdomains).
* @param revocable Whether the synthetic token can be revoked by the parent owner.
* @param groupId Group identifier for the token.
*/
struct SyntheticData {
uint256 parentTokenId;
string host;
uint64 subdomainCount;
bool revocable;
uint256 groupId;
}
/**
* @dev Mint information structure for creating synthetic ownership tokens.
* @param tokenId Unique identifier for the token.
* @param registrarIanaId IANA ID of the sponsoring registrar.
* @param owner Address of the token owner.
* @param revocable Whether the token can be revoked.
* @param sld Second-level domain label.
* @param tld Top-level domain.
* @param expiresAt Expiration timestamp.
* @param capabilities Domain-level capability flags.
* @param host Subdomain host (empty for root).
* @param parentTokenId Parent token ID (0 for root).
* @param groupId Group identifier for the token.
*/
struct SyntheticTokenMintInfo {
uint256 tokenId;
uint256 registrarIanaId;
address owner;
bool revocable;
string sld;
string tld;
uint256 expiresAt;
uint256 capabilities;
string host;
uint256 parentTokenId;
uint256 groupId;
}
/**
* @dev Mint information structure for creating synthetic subdomain tokens without sld/tld.
* @param receiver Address that will receive and own the token.
* @param revocable Whether the token can be revoked.
* @param expiresAt Expiration timestamp (0 for inheriting from parent).
* @param capabilities Domain-level capability flags.
* @param host Subdomain host label.
* @param parentTokenId Parent token ID.
* @param groupId Group identifier for the token.
*/
struct SyntheticSubdomainMintInfo {
address receiver;
bool revocable;
uint256 expiresAt;
uint256 capabilities;
string host;
uint256 parentTokenId;
uint256 groupId;
}
// ============================================
// Storage (ERC-7201 Namespaced Storage Pattern)
// ============================================
/**
* @dev Storage structure following ERC-7201 namespaced storage pattern.
* This prevents storage collisions in upgradeable contracts.
* @param syntheticData Maps token ID to synthetic-specific data.
* @param currentTokenId Global counter for generating token IDs (added to baseTokenId).
* @param subdomainTokenIds Maps namehash (baseTokenId) to set of active token IDs.
*/
/// @custom:storage-location erc7201:doma.storage.SyntheticToken
struct SyntheticTokenStorage {
mapping(uint256 => SyntheticData) syntheticData;
// group management mappings
// ----------------------------
// ParentId => GroupId => Count
mapping(uint256 => mapping(uint256 => uint256)) groupCounts;
// ParentId => GroupId => isRevoked
mapping(uint256 => mapping(uint256 => bool)) isGroupRevoked;
// subdomain minting
// ----------------------------
// Global counter added to baseTokenId for generating unique token IDs
uint256 currentTokenId;
// Namehash (baseTokenId) => Set of active TokenIds
mapping(uint256 => EnumerableSet.UintSet) subdomainTokenIds;
// Track last owner before token is burned (for claim after revocation)
// TokenId => Last owner address
mapping(uint256 => address) lastOwner;
}
/**
* @dev Storage slot for SyntheticToken storage (ERC-7201).
* keccak256(abi.encode(uint256(keccak256("doma.storage.SyntheticToken")) - 1)) & ~bytes32(uint256(0xff))
*/
bytes32 private constant _SYNTHETIC_TOKEN_STORAGE_SLOT =
keccak256(abi.encode(uint256(keccak256(bytes("doma.storage.SyntheticToken"))) - 1)) &
~bytes32(uint256(0xff));
/**
* @dev Group ID reserved for non-revocable tokens (group 0).
* Non-revocable tokens must be assigned to this group and it cannot be revoked.
*/
uint256 public constant NON_REVOCABLE_GROUP = 0;
// ============================================
// Errors
// ============================================
/**
* @notice Thrown when attempting to transfer a token that belongs to a revoked group.
* @param parentTokenId The parent token ID.
* @param groupId The ID of the revoked group.
*/
error GroupRevoked(uint256 parentTokenId, uint256 groupId);
/**
* @notice Thrown when caller is not the owner of the token.
* @param caller The address of the caller.
* @param tokenId The ID of the token.
*/
error NotTokenOwner(address caller, uint256 tokenId);
/**
* @notice Thrown when attempting to renounce a root ownership token.
* @param tokenId The ID of the token.
*/
error CannotRenounceOwnershipToken(uint256 tokenId);
/**
* @notice Thrown when attempting to revoke a non-revocable token.
* @param tokenId The ID of the token.
*/
error TokenNotRevocable(uint256 tokenId);
/**
* @notice Thrown when subdomain expiration exceeds parent token expiration.
* @param parentTokenId The parent token ID.
* @param expiration The subdomain's expiration timestamp.
* @param parentExpiration The parent token's expiration timestamp.
*/
error ExpirationExceedsParent(
uint256 parentTokenId,
uint256 expiration,
uint256 parentExpiration
);
/**
* @notice Thrown when attempting to perform an operation on an expired token.
* @param tokenId The ID of the expired token.
* @param expiresAt The expiration timestamp of the token.
*/
error NameTokenHasExpired(uint256 tokenId, uint256 expiresAt);
/**
* @notice Thrown when attempting to mint a new subdomain version while the previous version is still active.
* @param previousTokenId The ID of the previous subdomain version that is still operational.
*/
error SubdomainActive(uint256 previousTokenId);
/**
* @notice Thrown when attempting to renew a subdomain to an expiry not greater than current expiry.
* @param tokenId The ID of the subdomain.
* @param currentExpiry The current expiration timestamp.
* @param newExpiry The attempted new expiration timestamp.
*/
error RenewalMustExtendExpiry(uint256 tokenId, uint256 currentExpiry, uint256 newExpiry);
/**
* @notice Thrown when attempting to revoke group 0 (NON_REVOCABLE_GROUP).
* @param groupId The group ID that cannot be revoked (always 0).
*/
error CannotLockNonRevocableGroup(uint256 groupId);
/**
* @notice Thrown when a revocable token is assigned to NON_REVOCABLE_GROUP or vice versa.
* @param parentTokenId The parent token ID.
* @param groupId The invalid group ID.
* @param revocable Whether the token is revocable.
*/
error InvalidGroupAssignment(uint256 parentTokenId, uint256 groupId, bool revocable);
/**
* @notice Thrown when attempting to mint a synthetic subdomain token with a parent that is not an ownership token.
* only 1 level deep to start with. We will update this in the future to support deeper hierarchies.
* @param parentTokenId The ID of the parent token.
*/
error ParentMustBeOwnershipToken(uint256 parentTokenId);
/**
* @notice Thrown when attempting to burn a token that doesn't meet burn criteria.
* @param tokenId The ID of the token.
*/
error CannotBurnToken(uint256 tokenId);
// ============================================
// Events
// ============================================
/**
* @notice Emitted when a synthetic token is minted.
* @param tokenId The ID of the minted token.
* @param registrarIanaId IANA ID of the sponsoring registrar.
* @param to Address receiving the token.
* @param sld Second-level domain label.
* @param tld Top-level domain.
* @param expiresAt Expiration timestamp.
* @param ownership Whether this is a root ownership token (parentTokenId == 0).
* @param host Subdomain host string.
* @param capabilities Domain-level capability flags.
* @param revocable Whether the token can be revoked.
* @param groupId Group identifier for the token.
* @param correlationId Cross-chain operation correlation ID.
*/
event SyntheticTokenMinted(
uint256 indexed tokenId,
uint256 registrarIanaId,
address to,
string sld,
string tld,
uint256 expiresAt,
bool ownership,
string host,
uint256 capabilities,
bool revocable,
uint256 groupId,
string correlationId
);
/**
* @notice Emitted when a synthetic subdomain token is minted directly (without cross-chain call).
* @param tokenId The ID of the minted subdomain token.
* @param parentTokenId The ID of the parent token.
* @param to Address receiving the token.
* @param host Subdomain host label.
* @param expiresAt Expiration timestamp.
* @param capabilities Domain-level capability flags.
* @param revocable Whether the token can be revoked.
* @param groupId Group identifier for the token.
* @param correlationId Correlation ID for tracking the operation.
*/
event SyntheticSubdomainMinted(
uint256 indexed tokenId,
uint256 indexed parentTokenId,
address to,
string host,
uint256 expiresAt,
uint256 capabilities,
bool revocable,
uint256 groupId,
string correlationId
);
/**
* @notice Emitted when a group is revoked.
* @param parentTokenId The ID of the parent token.
* @param groupId The ID of the group that was revoked.
* @param groupCount The number of tokens in the revoked group.
*/
event SyntheticGroupRevoked(
uint256 indexed parentTokenId,
uint256 indexed groupId,
uint256 groupCount
);
/**
* @notice Emitted when a synthetic subdomain token is renounced.
* @param tokenId The ID of the renounced token.
* @param owner The owner who renounced the token.
* @param parentTokenId The ID of the parent token.
* @param groupId The ID of the group the token belonged to.
*/
event SyntheticTokenRenounced(
uint256 indexed tokenId,
address indexed owner,
uint256 indexed parentTokenId,
uint256 groupId
);
/**
* @notice Emitted when a synthetic subdomain token is revoked by parent owner.
* @param tokenId The ID of the revoked token.
* @param revokedFrom The address whose token was revoked.
* @param revokedBy The parent token owner who revoked it.
* @param parentTokenId The ID of the parent token.
* @param groupId The ID of the group the token belonged to.
*/
event SyntheticTokenRevoked(
uint256 indexed tokenId,
address indexed revokedFrom,
address indexed revokedBy,
uint256 parentTokenId,
uint256 groupId
);
function _getSyntheticTokenStorage()
private
pure
returns (SyntheticTokenStorage storage syntheticStorage)
{
bytes32 slot = _SYNTHETIC_TOKEN_STORAGE_SLOT;
// solhint-disable-next-line no-inline-assembly
assembly {
syntheticStorage.slot := slot
}
}
/**
* @notice Mints a synthetic ownership token.
* @dev Can only be called by authorized minter (ProxyDomaRecord). Emits SyntheticTokenMinted event.
* @param info Synthetic token mint information including tokenId, registrar, owner and capabilities.
* @param correlationId Correlation ID for tracking cross-chain operations.
*/
function mintSyntheticFromOwnership(
SyntheticTokenMintInfo calldata info,
string calldata correlationId
) external onlyMinter {
_mint(info.registrarIanaId, info.tokenId, info.expiresAt, info.owner);
if (info.capabilities != 0) {
_domainCapabilities[info.tokenId] = info.capabilities;
}
emit SyntheticTokenMinted(
info.tokenId,
info.registrarIanaId,
info.owner,
info.sld,
info.tld,
info.expiresAt,
info.parentTokenId == 0,
info.host,
info.capabilities,
info.revocable,
info.groupId,
correlationId
);
}
/**
* @notice Mints a synthetic subdomain token.
* @dev Can only be called by authorized minter (ProxyDomaRecord). Stores synthetic data and emits event.
* If expiresAt is 0, stores 0 to indicate subdomain should inherit parent expiry dynamically.
* Token ID is generated from global counter.
* Uses SyntheticSubdomainMintInfo struct without sld/tld fields (inherited from parent).
* Emits SyntheticSubdomainMinted event.
* @param info Synthetic subdomain mint information.
* @param correlationId Correlation ID for tracking operations.
* @return tokenId The generated token ID from global counter.
*/
function mintSyntheticSubdomain(
SyntheticSubdomainMintInfo calldata info,
string calldata correlationId
) external onlyMinter returns (uint256) {
// Validate host label
NameUtils.ensureValidLabel(info.host);
// Generate baseTokenId from parent + normalized host
uint256 baseTokenId = NameUtils.namehash(info.parentTokenId, info.host);
// Validate subdomain expiry doesn't exceed parent expiry
uint256 parentExpiry = _getEffectiveExpiration(info.parentTokenId);
if (info.expiresAt != 0) {
_validateSubdomainExpiration(info.parentTokenId, info.expiresAt, parentExpiry);
}
// Get storage
SyntheticTokenStorage storage syntheticStorage = _getSyntheticTokenStorage();
// Validate group assignment rules
// Revocable tokens cannot use group 0
if (info.revocable && info.groupId == NON_REVOCABLE_GROUP) {
revert InvalidGroupAssignment(info.parentTokenId, info.groupId, info.revocable);
}
// Non-revocable tokens must use group 0
if (!info.revocable && info.groupId != NON_REVOCABLE_GROUP) {
revert InvalidGroupAssignment(info.parentTokenId, info.groupId, info.revocable);
}
// Determine group assignment based on revocability
uint256 assignedGroupId = info.revocable ? info.groupId : NON_REVOCABLE_GROUP;
// Validate group is not revoked
if (syntheticStorage.isGroupRevoked[info.parentTokenId][assignedGroupId]) {
revert GroupRevoked(info.parentTokenId, assignedGroupId);
}
if (syntheticStorage.syntheticData[info.parentTokenId].parentTokenId != 0) {
revert ParentMustBeOwnershipToken(info.parentTokenId);
}
// check for duplicates
EnumerableSet.UintSet storage existingTokens = syntheticStorage.subdomainTokenIds[
baseTokenId
];
uint256[] memory tokenList = existingTokens.values();
for (uint256 i = 0; i < tokenList.length; i++) {
uint256 existingTokenId = tokenList[i];
if (_isOperational(existingTokenId, syntheticStorage)) {
// Found an operational subdomain - prevent duplicate
// this can be changed in the future when we have other use cases
revert SubdomainActive(existingTokenId);
}
}
// Generate unique token ID: baseTokenId + global counter
uint256 tokenId = baseTokenId + (++syntheticStorage.currentTokenId);
// Add new token to the set
existingTokens.add(tokenId);
// Get registrarIanaId from parent token
uint256 registrarIanaId = _registrarIanaIds[info.parentTokenId];
// Mint without validation (already validated above)
_safeMint(info.receiver, tokenId);
_expirations[tokenId] = info.expiresAt;
_registrarIanaIds[tokenId] = registrarIanaId;
if (info.capabilities != 0) {
_domainCapabilities[tokenId] = info.capabilities;
}
// Store synthetic data
SyntheticData storage data = syntheticStorage.syntheticData[tokenId];
data.parentTokenId = info.parentTokenId;
data.host = info.host;
data.revocable = info.revocable;
data.subdomainCount = 0;
data.groupId = assignedGroupId;
// Increment group count and parent subdomain count
unchecked {
syntheticStorage.groupCounts[info.parentTokenId][assignedGroupId]++;
syntheticStorage.syntheticData[info.parentTokenId].subdomainCount++;
}
emit SyntheticSubdomainMinted(
tokenId,
info.parentTokenId,
info.receiver,
info.host,
info.expiresAt,
info.capabilities,
info.revocable,
assignedGroupId,
correlationId
);
return tokenId;
}
/**
* @notice Renews a synthetic token by extending its expiration date.
* @dev Can only be called by authorized minter (ProxyDomaRecord). Token must exist.
* @param tokenId The ID of the token to renew.
* @param expiresAt New expiration timestamp.
* @param correlationId Correlation ID for tracking operations.
*/
function renew(
uint256 tokenId,
uint256 expiresAt,
string calldata correlationId
) external onlyMinter {
DateUtils.validateExpirationDate(expiresAt);
address currentOwner = _ownerOf(tokenId);
if (currentOwner == address(0)) {
revert NameTokenDoesNotExist(tokenId);
}
// Validate subdomain expiration doesn't exceed parent expiration
SyntheticTokenStorage storage syntheticStorage = _getSyntheticTokenStorage();
SyntheticData storage data = syntheticStorage.syntheticData[tokenId];
if (data.parentTokenId != 0) {
// Get effective current expiry (inherit from parent if not explicitly set)
uint256 currentExpiry = _getEffectiveExpiration(tokenId);
// Validate new expiry is greater than current expiry
if (expiresAt <= currentExpiry) {
revert RenewalMustExtendExpiry(tokenId, currentExpiry, expiresAt);
}
// Validate new expiry doesn't exceed parent expiry
uint256 parentExpiry = _getEffectiveExpiration(data.parentTokenId);
_validateSubdomainExpiration(data.parentTokenId, expiresAt, parentExpiry);
}
_expirations[tokenId] = expiresAt;
emit NameTokenRenewed(tokenId, expiresAt, correlationId);
}
/// @notice Returns the current version of the contract
/// @return The version string
function version() external pure override returns (string memory) {
return "1.0.0";
}
/**
* @notice Get the expiration timestamp for a token, respecting parent expiry for subdomains.
* @dev For subdomains, returns the minimum of own expiry and parent expiry.
* @param tokenId The ID of the token.
* @return The effective expiration timestamp.
*/
function expirationOf(uint256 tokenId) external view override returns (uint256) {
if (!_exists(tokenId)) {
revert NameTokenDoesNotExist(tokenId);
}
return _getEffectiveExpiration(tokenId);
}
/**
* @notice Get the subdomain count for a synthetic token.
* @dev Returns the number of active subdomains for the given token.
* @param tokenId The ID of the synthetic token.
* @return The number of active subdomains.
*/
function subdomainCountOf(uint256 tokenId) external view returns (uint256) {
SyntheticTokenStorage storage _storage = _getSyntheticTokenStorage();
return _storage.syntheticData[tokenId].subdomainCount;
}
/**
* @notice Check if a group is revoked.
* @dev Returns whether a specific group for a parent token is revoked.
* @param parentTokenId The ID of the parent token.
* @param groupId The group identifier.
* @return True if the group is revoked, false otherwise.
*/
function isGroupRevoked(uint256 parentTokenId, uint256 groupId) external view returns (bool) {
SyntheticTokenStorage storage syntheticStorage = _getSyntheticTokenStorage();
return syntheticStorage.isGroupRevoked[parentTokenId][groupId];
}
/**
* @notice Get the parent token ID for a subdomain.
* @param tokenId The ID of the token.
* @return The parent token ID (0 for root tokens).
*/
function getParentTokenId(uint256 tokenId) external view returns (uint256) {
SyntheticTokenStorage storage syntheticStorage = _getSyntheticTokenStorage();
return syntheticStorage.syntheticData[tokenId].parentTokenId;
}
/**
* @notice Get the full synthetic data for a token.
* @param tokenId The ID of the token.
* @return The complete synthetic data struct.
*/
function getSyntheticData(uint256 tokenId) external view returns (SyntheticData memory) {
SyntheticTokenStorage storage syntheticStorage = _getSyntheticTokenStorage();
return syntheticStorage.syntheticData[tokenId];
}
/**
* @notice Get the owner of a token, even if it has been burned.
* @dev If the token exists, returns the current owner via ownerOf().
* If the token has been burned, returns the last owner stored before burning.
* Used to allow users to claim staked tokens after their subdomain NFT is revoked and burned.
* @param tokenId The ID of the token.
* @return The address of the current owner if token exists, or last owner if burned.
*/
function lastOwnerOf(uint256 tokenId) external view returns (address) {
if (_exists(tokenId)) {
return ownerOf(tokenId);
}
SyntheticTokenStorage storage syntheticStorage = _getSyntheticTokenStorage();
return syntheticStorage.lastOwner[tokenId];
}
/**
* @notice Get all operational subdomain token IDs for a given parent and host.
* @dev Computes the base token ID using namehash and returns only operational tokens.
* Returns empty array if no operational subdomains exist.
* Host is normalized to lowercase for case-insensitive matching (DNS standard).
* Filters out expired, burned, or group-revoked tokens.
* @param parentTokenId The ID of the parent token.
* @param host The subdomain host label.
* @return Array of operational token IDs for this subdomain.
*/
function getActiveSubdomainTokenIds(
uint256 parentTokenId,
string calldata host
) external view returns (uint256[] memory) {
NameUtils.ensureValidLabel(host);
// Compute base token ID from parent + normalized host
uint256 baseTokenId = NameUtils.namehash(parentTokenId, host);
// Get all tokens for this subdomain
SyntheticTokenStorage storage syntheticStorage = _getSyntheticTokenStorage();
uint256[] memory allTokens = syntheticStorage.subdomainTokenIds[baseTokenId].values();
// First pass: count operational tokens
uint256 operationalCount = 0;
for (uint256 i = 0; i < allTokens.length; i++) {
if (_isOperational(allTokens[i], syntheticStorage)) {
operationalCount++;
}
}
// Second pass: build array of operational tokens
uint256[] memory operationalTokens = new uint256[](operationalCount);
uint256 index = 0;
for (uint256 i = 0; i < allTokens.length; i++) {
if (_isOperational(allTokens[i], syntheticStorage)) {
operationalTokens[index++] = allTokens[i];
}
}
return operationalTokens;
}
/**
* @notice Check if any subdomain is operational for a given parent and host.
* @dev Returns true if at least one operational token exists in the set.
* probably we pass in capabilities to check for specific operations in the future.
* @param parentTokenId The ID of the parent token.
* @param host The subdomain host label.
* @return True if at least one subdomain token is operational, false otherwise.
*/
function isSubdomainOperational(
uint256 parentTokenId,
string calldata host
) external view returns (bool) {
NameUtils.ensureValidLabel(host);
uint256 baseTokenId = NameUtils.namehash(parentTokenId, host);
SyntheticTokenStorage storage syntheticStorage = _getSyntheticTokenStorage();
uint256[] memory tokens = syntheticStorage.subdomainTokenIds[baseTokenId].values();
// Check if any token is operational
for (uint256 i = 0; i < tokens.length; i++) {
if (_isOperational(tokens[i], syntheticStorage)) {
return true;
}
}
return false;
}
/**
* @notice Revoke a group, preventing transfers of all tokens in that group.
* @dev Can only be called by the minter. Reduces parent subdomain count.
* @param parentTokenId The ID of the parent token.
* @param groupId The group identifier to revoke.
*/
function revokeGroup(uint256 parentTokenId, uint256 groupId) external onlyMinter {
// Cannot revoke NON_REVOCABLE_GROUP
if (groupId == NON_REVOCABLE_GROUP) {
revert CannotLockNonRevocableGroup(groupId);
}
SyntheticTokenStorage storage syntheticStorage = _getSyntheticTokenStorage();
if (syntheticStorage.isGroupRevoked[parentTokenId][groupId]) {
revert GroupRevoked(parentTokenId, groupId);
}
uint256 groupCount = syntheticStorage.groupCounts[parentTokenId][groupId];
syntheticStorage.isGroupRevoked[parentTokenId][groupId] = true;
syntheticStorage.syntheticData[parentTokenId].subdomainCount -= uint64(groupCount);
emit SyntheticGroupRevoked(parentTokenId, groupId, groupCount);
}
/**
* @notice Burn a synthetic token.
* @dev For subdomain tokens, calls _burnSyntheticSubdomain to properly clean up parent relationships.
* For root tokens, performs standard burn cleanup.
* @param tokenId The ID of the token to burn.
* @param correlationId Correlation ID for tracking the burn operation.
*/
function burnSynthetic(uint256 tokenId, string calldata correlationId) external onlyMinter {
SyntheticTokenStorage storage syntheticStorage = _getSyntheticTokenStorage();
SyntheticData storage data = syntheticStorage.syntheticData[tokenId];
uint256 parentTokenId = data.parentTokenId;
// For subdomain tokens, use specialized burn logic
if (parentTokenId != 0) {
uint256 groupId = data.groupId;
_burnSyntheticSubdomain(tokenId, parentTokenId, groupId, correlationId);
} else {
// For root tokens (synthetic ownership tokens), use standard cleanup
delete syntheticStorage.syntheticData[tokenId];
_burnNameToken(tokenId, correlationId);
}
}
/**
* @notice Bulk burn expired or group-locked synthetic subdomain tokens.
* @dev Can be called by anyone to burn tokens that are either expired or in a locked group.
* This helps reclaim storage and maintain accurate subdomain counts.
* Only works on subdomain tokens (parentTokenId != 0).
* Note: NON_REVOCABLE_GROUP cannot be locked, so group-locked tokens are always revocable.
* @param tokenIds Array of token IDs to burn.
* @param correlationId Correlation ID for tracking the bulk burn operation.
*/
function bulkBurnInactiveSynthetic(
uint256[] calldata tokenIds,
string calldata correlationId
) external {
SyntheticTokenStorage storage syntheticStorage = _getSyntheticTokenStorage();
uint256 len = tokenIds.length;
for (uint256 i = 0; i < len; ) {
uint256 tokenId = tokenIds[i];
SyntheticData storage data = syntheticStorage.syntheticData[tokenId];
if (data.parentTokenId == 0) {
revert CannotBurnToken(tokenId);
}
if (_isOperational(tokenId, syntheticStorage)) {
revert CannotBurnToken(tokenId);
}
uint256 parentTokenId = data.parentTokenId;
uint256 groupId = data.groupId;
_burnSyntheticSubdomain(tokenId, parentTokenId, groupId, correlationId);
unchecked {
++i;
}
}
}
/**
* @notice Check if a synthetic token is operational.
* @dev Returns true if token exists, is not expired, and (for revocable subdomains) not in a locked group.
* Accepts pre-loaded storage to avoid repeated storage pointer lookups in loops.
* @param tokenId The ID of the synthetic token.
* @param syntheticStorage Pre-loaded storage pointer.
* @return True if the token is operational, false otherwise.
*/
function _isOperational(
uint256 tokenId,
SyntheticTokenStorage storage syntheticStorage
) internal view returns (bool) {
if (!_exists(tokenId)) {
return false;
}
uint256 effectiveExpiry = _getEffectiveExpiration(tokenId);
if (effectiveExpiry < block.timestamp) {
return false;
}
SyntheticData storage data = syntheticStorage.syntheticData[tokenId];
if (data.parentTokenId != 0 && data.revocable) {
if (syntheticStorage.isGroupRevoked[data.parentTokenId][data.groupId]) {
return false;
}
}
return true;
}
/**
* @dev Get the effective expiration timestamp for a token, respecting parent expiry for subdomains.
* @param tokenId The ID of the token.
* @return The effective expiration timestamp.
*/
function _getEffectiveExpiration(uint256 tokenId) internal view returns (uint256) {
uint256 ownExpiry = _expirations[tokenId];
SyntheticTokenStorage storage syntheticStorage = _getSyntheticTokenStorage();
uint256 parentTokenId = syntheticStorage.syntheticData[tokenId].parentTokenId;
if (parentTokenId == 0) {
return ownExpiry;
}
uint256 parentExpiry = _expirations[parentTokenId];
return ownExpiry == 0 ? parentExpiry : ownExpiry;
}
/**
* @dev Override to check effective expiration for tokens with dynamic inheritance.
* For subdomains with 0 expiry, this checks parent expiration instead of stored 0 value.
*/
function _isTokenExpired(uint256 id) internal view override returns (bool) {
return _getEffectiveExpiration(id) < block.timestamp;
}
/**
* @dev Validates that a subdomain's expiration doesn't exceed its parent's expiration.
* @param parentTokenId The parent token ID (used for error reporting)
* @param expiresAt The proposed expiration for the subdomain
* @param parentExpiry The parent token's expiration
*/
function _validateSubdomainExpiration(
uint256 parentTokenId,
uint256 expiresAt,
uint256 parentExpiry
) private pure {
if (expiresAt > parentExpiry) {
revert ExpirationExceedsParent(parentTokenId, expiresAt, parentExpiry);
}
}
/**
* @dev Internal function to burn a synthetic subdomain token and clean up all associated storage.
* @param tokenId The ID of the token to burn.
* @param parentTokenId The parent token ID.
* @param groupId The group ID of the token.
* @param correlationId Correlation ID for tracking the burn operation.
*/
function _burnSyntheticSubdomain(
uint256 tokenId,
uint256 parentTokenId,
uint256 groupId,
string memory correlationId
) private {
SyntheticTokenStorage storage syntheticStorage = _getSyntheticTokenStorage();
// Remove token from subdomain set
string memory host = syntheticStorage.syntheticData[tokenId].host;
uint256 baseTokenId = NameUtils.namehash(parentTokenId, host);
syntheticStorage.subdomainTokenIds[baseTokenId].remove(tokenId);
syntheticStorage.groupCounts[parentTokenId][groupId]--;
// Check group revocation status internally
bool groupRevoked = syntheticStorage.isGroupRevoked[parentTokenId][groupId];
// Only decrement subdomain count if group is not revoked
if (!groupRevoked) {
syntheticStorage.syntheticData[parentTokenId].subdomainCount--;
}
if (parentTokenId != 0) {
address lastOwner = _ownerOf(tokenId);
syntheticStorage.lastOwner[tokenId] = lastOwner;
}
delete syntheticStorage.syntheticData[tokenId];
_burnNameToken(tokenId, correlationId);
}
/**
* @dev Override to check if token belongs to a locked group before transfer.
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 firstTokenId,
uint256 batchSize
) internal override {
super._beforeTokenTransfer(from, to, firstTokenId, batchSize);
if (from != address(0) && to != address(0)) {
SyntheticTokenStorage storage syntheticStorage = _getSyntheticTokenStorage();
SyntheticData storage data = syntheticStorage.syntheticData[firstTokenId];
if (
data.parentTokenId != 0 &&
syntheticStorage.isGroupRevoked[data.parentTokenId][data.groupId]
) {
revert GroupRevoked(data.parentTokenId, data.groupId);
}
}
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable-v4/access/AccessControlUpgradeable.sol";
/**
* @dev This interface exists for Magic Eden collection ownership support.
* It provides standard ownership functionality for collections on the Magic Eden marketplace.
*/
abstract contract AccessControlOwnable is AccessControlUpgradeable {
error OwnerCannotBeZeroAddress();
error NotSupported();
error AtLeastOneAdminRequired();
error OwnerMustHaveAdminRole(address owner);
/**
* @dev Emitted when ownership is transferred.
*/
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Storage structure for Ownable data.
* Contains the owner address.
*/
/// @custom:storage-location erc7201:doma.storage.Ownable
struct OwnableStorage {
address owner;
address[] admins;
}
// Storage slot for Ownable storage
bytes32 private constant _OWNABLE_STORAGE_SLOT =
keccak256(abi.encode(uint256(keccak256(bytes("doma.storage.Ownable"))) - 1)) &
~bytes32(uint256(0xff));
/// @notice Returns the address of the current owner
/// @return The owner address
function owner() public view virtual returns (address) {
return _ownableStorage().owner;
}
/// @notice Renounces ownership (not supported - always reverts)
/// @dev Kept for compatibility with OpenZeppelin's Ownable
function renounceOwnership() public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
revert NotSupported();
}
/// @notice Transfers ownership to a new owner (admin role only)
/// @dev New owner must have the DEFAULT_ADMIN_ROLE
/// @param newOwner Address of the new owner
function transferOwnership(address newOwner) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
if (!hasRole(DEFAULT_ADMIN_ROLE, newOwner)) {
revert OwnerMustHaveAdminRole(newOwner);
}
OwnableStorage storage s = _ownableStorage();
emit OwnershipTransferred(s.owner, newOwner);
s.owner = newOwner;
}
/**
* @dev Returns the storage struct for Ownable
*/
function _ownableStorage() internal pure returns (OwnableStorage storage storageStruct) {
bytes32 slot = _OWNABLE_STORAGE_SLOT;
// solhint-disable-next-line no-inline-assembly
assembly {
storageStruct.slot := slot
}
}
function _grantRole(bytes32 role, address account) internal virtual override {
super._grantRole(role, account);
if (role == DEFAULT_ADMIN_ROLE) {
OwnableStorage storage s = _ownableStorage();
if (s.owner == address(0)) {
s.owner = account;
emit OwnershipTransferred(address(0), account);
}
bool isAdminAlreadyExists = false;
for (uint256 i = 0; i < s.admins.length; i++) {
if (s.admins[i] == account) {
isAdminAlreadyExists = true;
break;
}
}
if (!isAdminAlreadyExists) {
s.admins.push(account);
}
}
}
function _revokeRole(bytes32 role, address account) internal virtual override {
super._revokeRole(role, account);
if (role == DEFAULT_ADMIN_ROLE) {
OwnableStorage storage s = _ownableStorage();
if (s.admins.length <= 1) {
revert AtLeastOneAdminRequired();
}
for (uint256 i = 0; i < s.admins.length; i++) {
if (s.admins[i] == account) {
// Move the last element into the place to delete and pop
s.admins[i] = s.admins[s.admins.length - 1];
s.admins.pop();
break;
}
}
if (s.owner == account) {
// If the owner is being revoked, set the first admin as the new owner
address newAdmin = s.admins[0];
s.owner = newAdmin;
emit OwnershipTransferred(account, newAdmin);
}
}
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
/**
* Modified Bytes.sol from OpenZeppelin library
* Original version cannot be used due to its reliance on `mcopy` instruction.
* https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.2.0/contracts/utils/Bytes.sol
*/
library ByteUtils {
/**
* @dev Backward search for `s` in `buffer` starting at position `pos`
* * If `s` is present in the buffer (at or before `pos`), returns the index of the previous instance
* * If `s` is not present in the buffer (at or before `pos`), returns type(uint256).max
*
* NOTE: replicates the behavior of
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf[Javascript's `Array.lastIndexOf`]
*/
function lastIndexOf(
bytes memory buffer,
bytes1 s,
uint256 pos
) internal pure returns (uint256) {
unchecked {
uint256 length = buffer.length;
// NOTE here we cannot do `i = Math.min(pos + 1, length)` because `pos + 1` could overflow
for (uint256 i = Math.min(pos, length - 1) + 1; i > 0; --i) {
if (bytes1(_unsafeReadBytesOffset(buffer, i - 1)) == s) {
return i - 1;
}
}
return type(uint256).max;
}
}
/**
* @dev Reads a bytes32 from a bytes array without bounds checking.
*
* NOTE: making this function internal would mean it could be used with memory unsafe offset, and marking the
* assembly block as such would prevent some optimizations.
*/
function _unsafeReadBytesOffset(
bytes memory buffer,
uint256 offset
) private pure returns (bytes32 value) {
// This is not memory safe in the general case, but all calls to this private function are within bounds.
/* solhint-disable-next-line no-inline-assembly */
assembly ("memory-safe") {
value := mload(add(buffer, add(0x20, offset)))
}
}
/**
* @dev Taken from https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol#L228
*/
function slice(
bytes memory buffer,
uint256 start,
uint256 length
) internal pure returns (bytes memory) {
/* solhint-disable-next-line custom-errors,gas-custom-errors */
require(length + 31 >= length, "slice_overflow");
/* solhint-disable-next-line custom-errors,gas-custom-errors */
require(buffer.length >= start + length, "slice_outOfBounds");
bytes memory tempBytes;
/* solhint-disable-next-line no-inline-assembly */
assembly {
switch iszero(length)
case 0 {
// Get a location of some free memory and store it in tempBytes as
// Solidity does for memory variables.
tempBytes := mload(0x40)
// The first word of the slice result is potentially a partial
// word read from the original array. To read it, we calculate
// the length of that partial word and start copying that many
// bytes into the array. The first word we copy will start with
// data we don't care about, but the last `lengthmod` bytes will
// land at the beginning of the contents of the new array. When
// we're done copying, we overwrite the full first word with
// the actual length of the slice.
let lengthmod := and(length, 31)
// The multiplication in the next line is necessary
// because when slicing multiples of 32 bytes (lengthmod == 0)
// the following copy loop was copying the origin's length
// and then ending prematurely not copying everything it should.
let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
let end := add(mc, length)
for {
// The multiplication in the next line has the same exact purpose
// as the one above.
let cc := add(add(add(buffer, lengthmod), mul(0x20, iszero(lengthmod))), start)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
mstore(tempBytes, length)
//update free-memory pointer
//allocating the array padded to 32 bytes like the compiler does now
mstore(0x40, and(add(mc, 31), not(31)))
}
//if we want a zero-length slice let's just return a zero-length array
default {
tempBytes := mload(0x40)
//zero out the 32 bytes slice we are about to return
//we need to do it because Solidity does not garbage collect
mstore(tempBytes, 0)
mstore(0x40, add(tempBytes, 0x20))
}
}
return tempBytes;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { ByteUtils } from "./ByteUtils.sol";
/**
* Library for CAIP-2 and CAIP-10 support.
* OpenZeppelin implementation cannot be used, since it requires 'cancun' evmVersion support
* CAIP-2 source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.2.0/contracts/utils/CAIP2.sol
* CAIP-10 source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.2.0/contracts/utils/CAIP10.sol
*/
library CAIPUtils {
using Strings for address;
using Strings for uint256;
error LocalChainRequired(string caip2);
/// @dev Return the CAIP-2 identifier for the current (local) chain.
function caip2Local() internal view returns (string memory) {
return format("eip155", block.chainid.toString());
}
/**
* @dev Formats a CAIP identifier.
*/
function format(
string memory prefix,
string memory value
) internal pure returns (string memory) {
return string.concat(prefix, ":", value);
}
/**
* @dev Parse CAIP 10 identifier into CAIP 2 and account address.
*/
function parseCAIP10(
string memory caip10
) internal pure returns (string memory caip2, string memory account) {
bytes memory buffer = bytes(caip10);
uint256 bufferLength = buffer.length;
uint256 pos = ByteUtils.lastIndexOf(buffer, ":", type(uint256).max);
return (
string(ByteUtils.slice(buffer, 0, pos)),
string(ByteUtils.slice(buffer, pos + 1, bufferLength - pos - 1))
);
}
function parseLocalCAIP10(string memory caip10) internal view returns (address) {
(string memory caip2, string memory accountString) = parseCAIP10(caip10);
if (!Strings.equal(caip2, caip2Local())) {
revert LocalChainRequired(caip2);
}
return Strings.parseAddress(accountString);
}
/**
* @dev Parse CAIP-2 identifier to extract namespace.
* For example: "eip155:1" returns "eip155", "solana:mainnet" returns "solana"
*/
function parseCAIP2Namespace(string memory caip2) internal pure returns (string memory) {
bytes memory buffer = bytes(caip2);
// Find the first colon separator
for (uint256 i = 0; i < buffer.length; i++) {
if (buffer[i] == ":") {
return string(ByteUtils.slice(buffer, 0, i));
}
}
// If no colon found, return the entire string (shouldn't happen with valid CAIP-2)
return caip2;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
library DateUtils {
error PastExpirationDate(uint256 expirationDate);
error TooBigExpirationDate(uint256 expirationDate, uint256 maxAllowedDate);
/**
* Used to perform a defensive check during mint & renewals.
* Uses 366 days to account for potential leap years.
*/
uint256 internal constant _TEN_YEARS = 10 * 366 days;
function validateExpirationDate(uint256 expiresAt) internal view {
if (expiresAt < block.timestamp) {
revert PastExpirationDate(expiresAt);
}
uint256 maxAllowedDate = block.timestamp + _TEN_YEARS;
if (expiresAt > maxAllowedDate) {
revert TooBigExpirationDate(expiresAt, maxAllowedDate);
}
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
import { ICreatorToken } from "../interfaces/ICreatorToken.sol";
import { ITransferValidator } from "../interfaces/ITransferValidator.sol";
import { ITransferValidatorSetTokenType } from "../interfaces/ITransferValidatorSetTokenType.sol";
abstract contract ERC721C is ICreatorToken {
/**
* @dev Storage structure for ERC721C
*/
struct ERC721CStorage {
// Address of the transfer validator contract
address transferValidator;
// Flag to automatically approve transfers from the validator
bool autoApproveTransfersFromValidator;
}
// Storage slot for ERC721C storage
bytes32 private constant _STORAGE_SLOT = keccak256("doma.storage.ERC721C");
/**
* @dev Returns the storage struct for ERC721C
*/
function _erc721Storage() internal pure returns (ERC721CStorage storage storageStruct) {
bytes32 slot = _STORAGE_SLOT;
// solhint-disable-next-line no-inline-assembly
assembly {
storageStruct.slot := slot
}
}
// Token type constant for ERC721C
uint16 private constant _TOKEN_TYPE_ERC721 = 1;
/**
* @dev Emitted when automatic approval of transfers from validator is updated
*/
event AutomaticApprovalOfTransferValidatorSet(bool autoApproved);
/**
* @dev Thrown when setting a transfer validator address that has no deployed code
*/
error InvalidTransferValidatorContract();
/**
* @notice Sets the transfer validator for the token contract (owner only)
* @param validator The address of the transfer validator contract
*/
function setTransferValidator(address validator) public virtual {
_requireCallerIsContractOwner();
bool isValidTransferValidator = validator.code.length > 0;
if (validator != address(0) && !isValidTransferValidator) {
revert InvalidTransferValidatorContract();
}
emit TransferValidatorUpdated(getTransferValidator(), validator);
ERC721CStorage storage storage_ = _erc721Storage();
storage_.transferValidator = validator;
_registerTokenType(validator);
}
/**
* @notice Returns the transfer validator contract address for this token contract
* @return validator The address of the transfer validator
*/
function getTransferValidator() public view virtual returns (address validator) {
ERC721CStorage storage storage_ = _erc721Storage();
validator = storage_.transferValidator;
}
/**
* @notice Returns the function selector for the transfer validator's validation function
* @return functionSignature The function selector
* @return isViewFunction Whether the function is a view function
*/
function getTransferValidationFunction()
external
pure
virtual
returns (bytes4 functionSignature, bool isViewFunction)
{
functionSignature = bytes4(keccak256("validateTransfer(address,address,address,uint256)"));
isViewFunction = true;
}
/**
* @notice Sets if the transfer validator is automatically approved as an operator for all token owners (owner only)
* @param autoApprove If true, the collection's transfer validator will be automatically approved
*/
function setAutomaticApprovalOfTransfersFromValidator(bool autoApprove) external virtual {
_requireCallerIsContractOwner();
ERC721CStorage storage storage_ = _erc721Storage();
storage_.autoApproveTransfersFromValidator = autoApprove;
emit AutomaticApprovalOfTransferValidatorSet(autoApprove);
}
/**
* @notice Checks if automatic approval of transfers from validator is enabled
* @return True if automatic approval is enabled
*/
function getAutomaticApprovalOfTransfersFromValidator() external view virtual returns (bool) {
return _erc721Storage().autoApproveTransfersFromValidator;
}
/**
* @dev Pre-validates a token transfer, reverting if the transfer is not allowed by this token's security policy
*/
function _preValidateTransfer(
address caller,
address from,
address to,
uint256 tokenId,
uint256
) internal virtual {
address validator = getTransferValidator();
if (validator != address(0)) {
if (msg.sender == validator) {
return;
}
ITransferValidator(validator).validateTransfer(caller, from, to, tokenId);
}
}
/**
* @dev Returns the token type for this contract (ERC721)
*/
function _tokenType() internal pure virtual returns (uint16) {
return _TOKEN_TYPE_ERC721;
}
/**
* @dev Registers the token type with the transfer validator
*/
/* solhint-disable no-empty-blocks */
function _registerTokenType(address validator) internal virtual {
if (validator != address(0)) {
uint256 validatorCodeSize;
// solhint-disable-next-line no-inline-assembly
assembly {
validatorCodeSize := extcodesize(validator)
}
if (validatorCodeSize > 0) {
try
ITransferValidatorSetTokenType(validator).setTokenTypeOfCollection(
address(this),
_tokenType()
)
{} catch {}
}
}
}
/**
* @dev Checks if the caller is the contract owner
*/
function _requireCallerIsContractOwner() internal virtual;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
library GatewayUtils {
bytes4 internal constant NONCE_ATTRIBUTE = bytes4(keccak256("nonce(uint64)"));
bytes4 internal constant CORRELATION_ID_ATTRIBUTE = bytes4(keccak256("correlationId(bytes32)"));
bytes4 internal constant NONCE_KEY_ATTRIBUTE = bytes4(keccak256("nonceKey(uint256)"));
error PayableCallsUnsupported();
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
import { ByteUtils } from "./ByteUtils.sol";
library NameUtils {
error InvalidLabel(string label);
error InvalidName(string name);
error InvalidHost(string host);
function ensureValidLabel(string memory label) internal pure {
if (!isValidLabel(label)) {
revert InvalidLabel(label);
}
}
function ensureValidName(string memory name) internal pure {
if (!isValidName(name)) {
revert InvalidName(name);
}
}
function ensureValidHost(string memory host) internal pure {
if (!isValidHost(host)) {
revert InvalidHost(host);
}
}
function isValidName(string memory name) internal pure returns (bool) {
return _validateDotSeparated(name, false);
}
function isValidLabel(string memory label) internal pure returns (bool) {
return _isValidLabelInternal(label, false);
}
/**
* @dev Validates a DNS host per RFC 2181 and RFC 8552.
* Allows underscores for special DNS names like _acme-challenge, _dmarc, etc.
* Supports multi-level hosts like "_api.hostname.example".
*/
function isValidHost(string memory host) internal pure returns (bool) {
return _validateDotSeparated(host, true);
}
/**
* @dev Validates a single DNS host label allowing underscores.
* Used by isValidHost to validate each dot-separated part.
*/
function isValidHostLabel(string memory label) internal pure returns (bool) {
return _isValidLabelInternal(label, true);
}
function _validateDotSeparated(
string memory input,
bool allowUnderscore
) private pure returns (bool) {
bytes memory b = bytes(input);
uint256 len = b.length;
// Check length (1-253 characters, max domain name length in DNS)
if (len == 0 || len > 253) {
return false;
}
// Ensure no leading or trailing dots
if (b[0] == 0x2E || b[len - 1] == 0x2E) {
// ASCII '.' = 0x2E
return false;
}
// Split into labels and validate each one
string memory currentLabel;
uint256 start = 0;
for (uint256 i = 0; i < len; i++) {
if (b[i] == 0x2E) {
// If character is '.'
if (i == start) {
// Empty label (double dot case like "example..com")
return false;
}
// Extract the label and validate it
currentLabel = substring(input, start, i);
if (!_isValidLabelInternal(currentLabel, allowUnderscore)) {
return false;
}
start = i + 1;
}
}
// Validate the last label (after the last dot)
currentLabel = substring(input, start, len);
if (!_isValidLabelInternal(currentLabel, allowUnderscore)) {
return false;
}
return true;
}
function _isValidLabelInternal(
string memory label,
bool allowUnderscore
) private pure returns (bool) {
bytes memory b = bytes(label);
uint256 len = b.length;
// Check length (1-63 characters)
if (len == 0 || len > 63) {
return false;
}
// Check if it's a Punycode label (starts with "xn--")
bool isPunycode = (len > 4 &&
b[0] == 0x78 && // 'x'
b[1] == 0x6E && // 'n'
b[2] == 0x2D && // '-'
b[3] == 0x2D); // '-'
// Validate characters and hyphen placement rules
for (uint256 i = 0; i < len; i++) {
bytes1 char = b[i];
bool isValidChar = (char >= 0x61 && char <= 0x7A) || // a-z
(char >= 0x30 && char <= 0x39) || // 0-9
(char == 0x2D) || // '-' hyphen
(allowUnderscore && char == 0x5F); // '_' underscore (if allowed)
if (!isValidChar) {
return false;
}
// Ensure no hyphen at start or end (underscore is allowed at start)
if ((i == 0 || i == len - 1) && char == 0x2D) {
return false;
}
// Check for double hyphen (`--`) at positions 3 and 4 (unless "xn--")
if (i == 2 && char == 0x2D && i + 1 < len && b[i + 1] == 0x2D) {
if (!isPunycode) {
return false; // Double hyphen not allowed in 3rd and 4th position unless "xn--"
}
}
}
return true;
}
/**
* @dev Computes a full name namehash, e.g. `example.com`
*/
function namehash(string memory name) internal pure returns (uint256) {
bytes memory nameBytes = bytes(name);
uint256 nameHash = 0;
uint256 lastDotPosition = nameBytes.length;
while (lastDotPosition > 0) {
uint256 labelStart = lastDotPosition - 1;
// Find the start of the label
while (labelStart > 0 && nameBytes[labelStart - 1] != ".") {
labelStart--;
}
uint256 labelLength = lastDotPosition - labelStart;
string memory label = string(ByteUtils.slice(nameBytes, labelStart, labelLength));
nameHash = namehash(nameHash, label);
if (labelStart == 0) {
break;
} else {
lastDotPosition = labelStart - 1;
}
}
return nameHash;
}
function namehash(uint256 parent, string memory label) internal pure returns (uint256) {
return uint256(keccak256(abi.encodePacked(parent, keccak256(abi.encodePacked(label)))));
}
function substring(
string memory str,
uint256 startIndex,
uint256 endIndex
) private pure returns (string memory) {
bytes memory strBytes = bytes(str);
bytes memory result = new bytes(endIndex - startIndex);
for (uint256 i = startIndex; i < endIndex; i++) {
result[i - startIndex] = strBytes[i];
}
return string(result);
}
}{
"evmVersion": "istanbul",
"optimizer": {
"enabled": true,
"runs": 200
},
"viaIR": true,
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"name":"ECDSAInvalidSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"ECDSAInvalidSignatureLength","type":"error"},{"inputs":[{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"ECDSAInvalidSignatureS","type":"error"},{"inputs":[{"internalType":"bytes32","name":"operation","type":"bytes32"},{"internalType":"uint256","name":"nameCount","type":"uint256"}],"name":"FeeNotRequired","type":"error"},{"inputs":[{"internalType":"uint256","name":"parentTokenId","type":"uint256"},{"internalType":"uint256","name":"groupId","type":"uint256"}],"name":"GroupRevoked","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"subdomainCount","type":"uint256"}],"name":"HasActiveSubdomains","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"requiredCapability","type":"uint256"}],"name":"InsufficientCapabilities","type":"error"},{"inputs":[{"internalType":"uint256","name":"expectedFee","type":"uint256"},{"internalType":"uint256","name":"providedFee","type":"uint256"}],"name":"InvalidFee","type":"error"},{"inputs":[{"internalType":"string","name":"host","type":"string"}],"name":"InvalidHost","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[{"internalType":"string","name":"label","type":"string"}],"name":"InvalidLabel","type":"error"},{"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"InvalidName","type":"error"},{"inputs":[{"internalType":"bytes32","name":"operation","type":"bytes32"}],"name":"InvalidOperation","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"tokenOwner","type":"address"}],"name":"InvalidOwnerAddress","type":"error"},{"inputs":[{"internalType":"int256","name":"price","type":"int256"}],"name":"InvalidPrice","type":"error"},{"inputs":[{"internalType":"uint256","name":"ianaId","type":"uint256"},{"internalType":"uint256","name":"expectedIanaId","type":"uint256"}],"name":"InvalidRegistrar","type":"error"},{"inputs":[{"internalType":"address","name":"signer","type":"address"}],"name":"InvalidSigner","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"InvalidTokenId","type":"error"},{"inputs":[{"internalType":"string","name":"sld","type":"string"},{"internalType":"string","name":"tld","type":"string"}],"name":"NameAlreadyTokenized","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"NameTokenDoesNotExist","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"expiresAt","type":"uint256"}],"name":"NameTokenHasExpired","type":"error"},{"inputs":[{"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"NonceAlreadyUsed","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"NotSubdomain","type":"error"},{"inputs":[{"internalType":"uint80","name":"roundId","type":"uint80"},{"internalType":"uint256","name":"updatedAt","type":"uint256"}],"name":"PriceFeedStalePrice","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"length","type":"uint256"}],"name":"StringsInsufficientHexLength","type":"error"},{"inputs":[{"internalType":"string","name":"host","type":"string"}],"name":"SubdomainHostInUse","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"SyntheticTokenNotSupported","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"TokenAlreadySynthetic","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"TokenNotRevocable","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"TokenNotSynthetic","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"TransferLocked","type":"error"},{"inputs":[{"internalType":"string","name":"chainId","type":"string"}],"name":"UnsupportedTargetChain","type":"error"},{"inputs":[{"internalType":"uint256","name":"expiresAt","type":"uint256"},{"internalType":"uint256","name":"currentTime","type":"uint256"}],"name":"VoucherExpired","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[],"name":"EIP712DomainChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"feeWei","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeUsdCents","type":"uint256"},{"indexed":false,"internalType":"string","name":"correlationId","type":"string"}],"name":"FeeCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"parentTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"groupId","type":"uint256"}],"name":"SyntheticTokenRenounced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"revokedFrom","type":"address"},{"indexed":true,"internalType":"address","name":"revokedBy","type":"address"},{"indexed":false,"internalType":"uint256","name":"parentTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"groupId","type":"uint256"}],"name":"SyntheticTokenRevoked","type":"event"},{"inputs":[],"name":"BRIDGE_OPERATION","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"CLAIM_OWNERSHIP_OPERATION","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"CONVERT_TO_OWNERSHIP_OPERATION","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"CONVERT_TO_SYNTHETIC_OPERATION","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"REQUEST_TOKENIZATION_OPERATION","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"SET_DNS_RRSET_OPERATION","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"SET_DS_KEYS_OPERATION","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"SET_NAMESERVERS_OPERATION","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"string","name":"targetChainId","type":"string"},{"internalType":"string","name":"targetOwnerAddress","type":"string"}],"name":"bridge","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"targetChainId","type":"string"},{"internalType":"string","name":"targetOwnerAddress","type":"string"}],"name":"bridge","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"components":[{"internalType":"uint256","name":"registrantHandle","type":"uint256"},{"internalType":"enum IDomaRecord.ProofOfContactsSource","name":"proofSource","type":"uint8"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiresAt","type":"uint256"}],"internalType":"struct ProxyDomaRecordUserFacet.ProofOfContactsVoucher","name":"proofOfContactsVoucher","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"claimOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"components":[{"internalType":"uint256","name":"registrantHandle","type":"uint256"},{"internalType":"enum IDomaRecord.ProofOfContactsSource","name":"proofSource","type":"uint8"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiresAt","type":"uint256"}],"internalType":"struct ProxyDomaRecordUserFacet.ProofOfContactsVoucher","name":"proofOfContactsVoucher","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"claimOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"convertToOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"convertToSynthetic","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"domainVersion","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"operation","type":"bytes32"}],"name":"feesUSDCents","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"feeUSDCents","type":"uint256"}],"name":"getNativePrice","outputs":[{"internalType":"uint256","name":"nativeFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"operation","type":"bytes32"}],"name":"getOperationFeeInNative","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"targetChainId","type":"string"}],"name":"isTargetChainSupported","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"parentTokenId","type":"uint256"},{"internalType":"string","name":"host","type":"string"},{"internalType":"uint256","name":"capabilities","type":"uint256"},{"internalType":"uint256","name":"expiresAt","type":"uint256"},{"internalType":"bool","name":"revocable","type":"bool"},{"internalType":"uint256","name":"groupId","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"mintSyntheticSubdomain","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"expiresAt","type":"uint256"}],"name":"renewSubdomain","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"renounceSynthetic","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"requestDetokenization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"requestDetokenization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"string","name":"sld","type":"string"},{"internalType":"string","name":"tld","type":"string"}],"internalType":"struct IDomaRecord.NameInfo[]","name":"names","type":"tuple[]"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiresAt","type":"uint256"},{"internalType":"address","name":"ownerAddress","type":"address"}],"internalType":"struct ProxyDomaRecordUserFacet.TokenizationVoucher","name":"voucher","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"requestTokenization","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"parentTokenId","type":"uint256"},{"internalType":"uint256","name":"groupId","type":"uint256"}],"name":"revokeGroup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"revokeSynthetic","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"string","name":"host","type":"string"},{"internalType":"string","name":"recordType","type":"string"},{"internalType":"uint32","name":"ttl","type":"uint32"},{"internalType":"string[]","name":"records","type":"string[]"},{"internalType":"bool","name":"","type":"bool"}],"name":"setDNSRRSet","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"string","name":"host","type":"string"},{"internalType":"string","name":"recordType","type":"string"},{"internalType":"uint32","name":"ttl","type":"uint32"},{"internalType":"string[]","name":"records","type":"string[]"}],"name":"setDNSRRSet","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"components":[{"internalType":"uint32","name":"keyTag","type":"uint32"},{"internalType":"uint8","name":"algorithm","type":"uint8"},{"internalType":"uint8","name":"digestType","type":"uint8"},{"internalType":"bytes","name":"digest","type":"bytes"}],"internalType":"struct IDomaRecord.DSKey[]","name":"dsKeys","type":"tuple[]"}],"name":"setDSKeys","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"components":[{"internalType":"uint32","name":"keyTag","type":"uint32"},{"internalType":"uint8","name":"algorithm","type":"uint8"},{"internalType":"uint8","name":"digestType","type":"uint8"},{"internalType":"bytes","name":"digest","type":"bytes"}],"internalType":"struct IDomaRecord.DSKey[]","name":"dsKeys","type":"tuple[]"},{"internalType":"bool","name":"","type":"bool"}],"name":"setDSKeys","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"string[]","name":"nameservers","type":"string[]"},{"internalType":"bool","name":"","type":"bool"}],"name":"setNameservers","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"string[]","name":"nameservers","type":"string[]"}],"name":"setNameservers","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"}]Contract Creation Code
60808060405234601557615624908161001b8239f35b600080fdfe6080604052600436101561001257600080fd5b60003560e01c80630cbc4e93146102375780630e160be6146102325780632f945faa1461022d57806335abf04a146102285780633761014014610223578063379006ba1461021e5780633827aaaf1461021957806340fb6b8e1461021457806354fd4d501461020f578063575c472d1461020a578063628c19061461020557806373d9d085146102005780637f13e412146101fb5780638141f9ed146101f657806384b0196e146101f157806386d45941146101ec578063870a3d84146101e7578063989082f9146101e25780639c7d2ab7146101dd578063a28df472146101d8578063aa00c548146101d3578063ade81788146101ce578063b36210c1146101c9578063ba26a79d146101c4578063bce69e8c146101bf578063c4851c0c146101ba578063de174837146101b5578063e89174bd146101b0578063e952a25a146101ab578063ea0b1829146101a6578063eabd785c146101a1578063eb7ce2491461019c578063efd0c5a6146101975763efea996a1461019257600080fd5b611c0d565b611be4565b611b16565b611a42565b61193d565b611914565b6118dc565b611797565b61175d565b61158a565b61131d565b611304565b6112f0565b6112c7565b6112af565b61126a565b6111e8565b61116b565b611142565b6110c2565b610e0f565b610cc3565b610ae6565b610a52565b610a05565b6109ba565b610800565b6105c4565b6104f9565b610497565b610327565b61029b565b610275565b61024c565b600091031261024757565b600080fd5b3461024757600036600319011261024757602060405160008051602061550f8339815191528152f35b34610247576020366003190112610247576020610293600435611d85565b604051908152f35b3461024757600036600319011261024757602060405160008051602061556f8339815191528152f35b9181601f84011215610247578235916001600160401b038311610247576020808501948460051b01011161024757565b9060406003198301126102475760043591602435906001600160401b03821161024757610323916004016102c4565b9091565b610330366102f4565b61033b839293612b8c565b610470579161034a3383612c8b565b61035382612da3565b61035b612f0d565b9061036582612fdd565b60405190637992685160e11b60208301528460848301856024850152606060448501525260a4820160a48660051b8401019582600090607e1981360301935b8383106103e157508589036023190160648701526103df8888886103da816103cc8f85610984565b03601f198101835282611d0f565b613860565b005b90919293949860a31987820301825289358681121561024757602061046060019360806104508885960163ffffffff61041982610a41565b16845260ff8682013561042b81611e17565b168685015260ff604082013561044081611e17565b1660408501526060810190611e22565b9190928160608201520191611e53565b9b019201930191909493926103a4565b5063d08fd27160e01b60005260045260246000fd5b60209060031901126102475760043590565b346102475760206102936104aa36610485565b611e74565b8015150361024757565b9060606003198301126102475760043591602435906001600160401b038211610247576104e8916004016102c4565b90916044356104f6816104af565b90565b610502366104b9565b5061050e839293612b8c565b6104705761051c3383612c8b565b61052582612b8c565b610470576103df926105373384612c8b565b61054083612da3565b6103da61054b612f0d565b9261055584613229565b6103cc610585604051948593639f63ae2360e01b6020860152886024860152606060448601526084850191612301565b82810360231901606484015285610984565b9181601f84011215610247578235916001600160401b038311610247576020838186019501011161024757565b6060366003190112610247576004356024356001600160401b038111610247576105f2903690600401610597565b91906044356001600160401b03811161024757610613903690600401610597565b61061f84939293612b8c565b6107eb5761062d3385612c8b565b61063e61063a8684612653565b1590565b6107cc5761064a612f0d565b92610654846132ac565b61067e610672600461066461295b565b01546001600160a01b031690565b6001600160a01b031690565b6040516364f1992360e01b81526004810187905290602090829060249082905afa9081156107825760009161079d575b50610787576106c3610672600461066461295b565b604051633329dc9d60e11b81526004810187905290602090829060249082905afa90811561078257600091610753575b504281106107395750946103cc846103da936103df98610713838a6139a8565b61071c89613a7e565b60405163069322e360e51b60208201529788969160248801611ed1565b636581bb3f60e11b600052600486905260245260445b6000fd5b610775915060203d60201161077b575b61076d8183611d0f565b810190611ec2565b386106f3565b503d610763565b611d79565b63a9de8c6f60e01b600052600485905260246000fd5b6107bf915060203d6020116107c5575b6107b78183611d0f565b810190611ead565b386106ae565b503d6107ad565b604051620d153960e01b8152806107e7878560048401611e8f565b0390fd5b8363d08fd27160e01b60005260045260246000fd5b346102475760403660031901126102475760043561081f6024356104af565b61082881612b8c565b610901576108363382612c8b565b61083f81612b8c565b610901576103df906108513382612c8b565b610859612f0d565b6108ae6103da61086884613a7e565b6103cc8461087546613a7e565b6108dd600160409260266020855161088d8782611d0f565b6006815201916565697031353560d01b835285519a8b936020850190610915565b601d60f91b828401526108cb815180926020602787019101610961565b8201010301601f198101885287611d0f565b6108e633614173565b9051631acdf90760e21b602082015295869460248601612795565b63d08fd27160e01b60005260045260246000fd5b60005b6006811061092b57505060066000910152565b8181015183820152602001610918565b60005b6062811061095157505060626000910152565b818101518382015260200161093e565b60005b8381106109745750506000910152565b8181015183820152602001610964565b9060209161099d81518092818552858086019101610961565b601f01601f1916010190565b9060206104f6928181520190610984565b3461024757600036600319011261024757610a0160408051906109dd8183611d0f565b60058252640312e312e360dc1b602083015251918291602083526020830190610984565b0390f35b3461024757600036600319011261024757602060405160008051602061552f8339815191528152f35b6064359063ffffffff8216820361024757565b359063ffffffff8216820361024757565b60c0366003190112610247576004356024356001600160401b03811161024757610a80903690600401610597565b6044929192356001600160401b03811161024757610aa2903690600401610597565b610aad949194610a2e565b90608435956001600160401b03871161024757610ad16103df9736906004016102c4565b95909460a43597610ae1896104af565b611f46565b3461024757602036600319011261024757600435610b043382612c8b565b6012610b0e61295b565b018054909190610b26906001600160a01b0316610672565b6040516316740b0b60e31b8152600481018390529092600082602481875afa91821561078257600092610ca0575b50815115610c8a578151608083018051604051633d854fa160e11b81526004810193909352602483015294602090829060449082905afa90811561078257600091610c6b575b50610c4d57610bbb610672610bad612f0d565b92546001600160a01b031690565b803b156102475760405163576bd72160e01b8152916000918391829084908290610be9908a60048401612033565b03925af1801561078257610c32575b505191516040519081523391907f5489bf68a3ad4b454ddf44e2cc0863dd86a8a874fcf2191c8818b6908bd1a4eb9080602081015b0390a4005b80610c416000610c4793611d0f565b8061023c565b38610bf8565b815184516314f241a760e01b60005260049190915260245260446000fd5b610c84915060203d6020116107c5576107b78183611d0f565b38610b9a565b632db04fed60e21b600052600483905260246000fd5b610cbc91923d8091833e610cb48183611d0f565b810190611f6e565b9038610b54565b3461024757604036600319011261024757602435600435610ce43382612c8b565b610cec61295b565b610cf861063a83612b8c565b610dc95760120154610d12906001600160a01b0316610672565b60405163cfa651f360e01b815260048101839052909290602081602481875afa90811561078257600091610daa575b50610d9457823b1561024757604051633f89f20960e11b815260048101929092526024820152906000908290818381604481015b03925af1801561078257610d8557005b80610c4160006103df93611d0f565b63ed15e6cf60e01b600052600482905260246000fd5b610dc3915060203d60201161077b5761076d8183611d0f565b38610d41565b635a55e97160e11b600052600482905260246000fd5b608090604319011261024757604490565b608090602319011261024757602490565b908160809103126102475790565b6040366003190112610247576004356001600160401b03811161024757610e3a903690600401610e01565b6024356001600160401b03811161024757610e59903690600401610597565b9190610e686040830135613bd6565b610e756020830135613bf8565b6060820191610e866106728461204a565b330361106257610ec5610e998280612054565b959050610ea4612f0d565b93610eaf858861332f565b610ec0610ebb85613de7565b613ef4565b613f76565b9360005b818110610f5e5750506103df93610f3083610ee784610f3e95612054565b9096610f13610f0e610f08610efb46613a7e565b610f0361414f565b614a73565b9261204a565b614173565b9160405198899663b5999e7960e01b6020890152602488016121c2565b03601f198101845283611d0f565b6040516020810190610f55816103cc4387866122a2565b51902091613860565b610f7a610f7582610f6f8680612054565b9061209f565b612118565b610f848151613fe7565b6020810190610f93825161401d565b6110026020610fd2610fba610fcd85516103cc8851610fc760405195869489860190612186565b601760f91b815260010190565b90612186565b6140a1565b610fe2610672600461066461295b565b6040518080958194634f558e7960e01b8352600483019190602083019252565b03915afa90811561078257600091611044575b50611024575050600101610ec9565b519051604051630e3ba82b60e31b81529182916107e7916004840161219d565b61105c915060203d81116107c5576107b78183611d0f565b38611015565b61074f61106e8461204a565b63adabb9d960e01b600052336004526001600160a01b0316602452604490565b906020808351928381520192019060005b8181106110ac5750505090565b825184526020938401939092019160010161109f565b34610247576000366003190112610247576111136110de6142b2565b610a016110e96142d4565b6111216110f46122d7565b91604051958695600f60f81b875260e0602088015260e0870190610984565b908582036040870152610984565b90466060850152306080850152600060a085015283820360c085015261108e565b3461024757600036600319011261024757602060405160008051602061554f8339815191528152f35b6080366003190112610247576004356111856024356104af565b6044356001600160401b038111610247576111a4903690600401610597565b91906064356001600160401b038111610247576111c5903690600401610597565b6111d184939293612b8c565b6107eb576111df3385612c8b565b61061f84612b8c565b60a0366003190112610247576004356024356001600160401b03811161024757611216903690600401610597565b916044356001600160401b03811161024757611236903690600401610597565b61123e610a2e565b91608435956001600160401b038711610247576112626103df9736906004016102c4565b9690956123be565b34610247576020366003190112610247576004356001600160401b038111610247576112a561129f6020923690600401610597565b90612653565b6040519015158152f35b346102475760206102936112c236610485565b61267b565b346102475760003660031901126102475760206040516000805160206155af8339815191528152f35b6112f9366102f4565b610525839293612b8c565b3461024757602036600319011261024757600435610836565b3461024757602036600319011261024757600435601261133b61295b565b018054611350906001600160a01b0316610672565b6040516316740b0b60e31b81526004810184905290929091600083602481875afa9283156107825760009361156f575b508251156115595761139483513390612c8b565b6113a461063a6060850151151590565b611543578251608084018051604051633d854fa160e11b8152600481019390935260248301529390602081604481895afa90811561078257600091611524575b50611507576040516331a9108f60e11b81526004810184905294602090869060249082905afa948515610782576000956114d6575b50611436610672611428612f0d565b93546001600160a01b031690565b803b156102475760405163576bd72160e01b8152926000918491829084908290611464908a60048401612033565b03925af1908115610782577f676dac521fa16607d417251e9ab50656928e72849236f2c82525fdbfa23d2cd492610c2d926114c1575b5051935160408051958652602086019190915233956001600160a01b031694918291820190565b80610c4160006114d093611d0f565b3861149a565b6114f991955060203d602011611500575b6114f18183611d0f565b8101906127cf565b9338611419565b503d6114e7565b5183516314f241a760e01b60005260049190915260245260446000fd5b61153d915060203d6020116107c5576107b78183611d0f565b386113e4565b63117c7f6560e01b600052600482905260246000fd5b632db04fed60e21b600052600482905260246000fd5b61158391933d8091833e610cb48183611d0f565b9138611380565b60e0366003190112610247576004356115a46024356104af565b6115ad36610ddf565b9060c4356001600160401b038111610247576115cd903690600401610597565b6115d683612b8c565b611748576115e43384612c8b565b6115ed83612b8c565b611748576115fb3384612c8b565b6116086060850135613bd6565b6116156040850135613bf8565b611621610ebb8561457e565b91602085019260016116328561287c565b61163b8161286d565b03611734579061164b9291613f76565b9261165c610672600461066461295b565b6040516369b0777f60e11b81526004810185905290602090829060249082905afa90811561078257600091611715575b508481036116fc57506103df93505b6103da6116a6612f0d565b926116b0846133b2565b6103cc846116bd87613a7e565b926116ca610efb46613a7e565b956116dd6116d733614173565b9261287c565b903591604051978896630bb9f0b760e01b602089015260248801612889565b63023d647f60e41b600052600485905260245260446000fd5b61172e915060203d60201161077b5761076d8183611d0f565b3861168c565b91611743916103df9693614684565b61169b565b8263d08fd27160e01b60005260045260246000fd5b3461024757600036600319011261024757602060405160008051602061558f8339815191528152f35b6001600160a01b0381160361024757565b60e0366003190112610247576004356024356001600160401b03811161024757611896916117cb6020923690600401610597565b6044359060643593611869608435946117e3866104af565b60a4359360c435956117f487611786565b6117fe3386612c8b565b61180a818385886143ff565b61181261295b565b986118586118356106726012611826612f0d565b9d01546001600160a01b031690565b99611850611841611f1c565b6001600160a01b03909b168b52565b1515898d0152565b6040880152606087015236916120c6565b608084015260a083015260c08201526000604051809681958294639414f66b60e01b8452600484016127e4565b03925af1801561078257610a01916000916118bd575b506040519081529081906020820190565b6118d6915060203d60201161077b5761076d8183611d0f565b386118ac565b60c0366003190112610247576004356118f436610df0565b9060a4356001600160401b038111610247576115e4903690600401610597565b346102475760003660031901126102475760206040516000805160206154ef8339815191528152f35b611946366104b9565b50611952839293612b8c565b610470576119603383612c8b565b61196982612b8c565b61047057916119783383612c8b565b61198182612da3565b611989612f0d565b9061199382612fdd565b60405190637992685160e11b60208301528460848301856024850152606060448501525260a4820160a48660051b8401019582600090607e1981360301935b8383106119fa57508589036023190160648701526103df8888886103da816103cc8f85610984565b90919293949860a319878203018252893586811215610247576020611a3260019360806104508885960163ffffffff61041982610a41565b9b019201930191909493926119d2565b602036600319011261024757600435611a5b3382612c8b565b611a6361295b565b90611a886106726012611a74612f0d565b94611a7e86613435565b61066433866146c8565b6040516331a9108f60e11b8152600481018390529290602090849060249082905afa8015610782576103df93600091611af7575b506103da611ad5611acf610efb46613a7e565b92614173565b916103cc84604051948593630225715360e61b602086015288602486016128e8565b611b10915060203d602011611500576114f18183611d0f565b38611abc565b602036600319011261024757600435611b2f3382612c8b565b611b3761295b565b90611b5c6106726004611b48612f0d565b94611b52866134b8565b6106643386614852565b6040516331a9108f60e11b8152600481018390529290602090849060249082905afa8015610782576103df93600091611bc5575b506103da611ba3611acf610efb46613a7e565b916103cc84604051948593633316357f60e21b602086015288602486016128e8565b611bde915060203d602011611500576114f18183611d0f565b38611b90565b346102475760003660031901126102475760206040516000805160206155cf8339815191528152f35b6040366003190112610247576024356004356012611c2961295b565b018054611c3e906001600160a01b0316610672565b60405163cfa651f360e01b81526004810184905290602090829060249082905afa90811561078257600091611cba575b508015610c8a57611c80903390612c8b565b611c8e610672610bad612f0d565b803b1561024757610d7593600080946040519687958694859363fabea1c360e01b855260048501612908565b611cd3915060203d60201161077b5761076d8183611d0f565b38611c6e565b634e487b7160e01b600052604160045260246000fd5b60a081019081106001600160401b03821117611d0a57604052565b611cd9565b90601f801991011681019081106001600160401b03821117611d0a57604052565b519069ffffffffffffffffffff8216820361024757565b908160a091031261024757611d5b81611d30565b916020820151916040810151916104f6608060608401519301611d30565b6040513d6000823e3d90fd5b600460a06001600160a01b036009611d9b61295b565b01541660405192838092633fabe5a360e21b82525afa918215610782576104f69260008093600092611dd9575b509083611dd49261299f565b612ab3565b9050611dd49350611e02915060a03d60a011611e10575b611dfa8183611d0f565b810190611d47565b509194509091905083611dc8565b503d611df0565b60ff81160361024757565b9035601e19823603018112156102475701602081359101916001600160401b03821161024757813603831361024757565b908060209392818452848401376000828201840152601f01601f1916010190565b600a611e7e61295b565b019060005260205260406000205490565b9160206104f6938181520191611e53565b5190611eab826104af565b565b9081602091031261024757516104f6816104af565b90816020910312610247575190565b94906104f6969492611f0e94611ef2611f009360808a5260808a0190610984565b9188830360208a0152611e53565b918583036040870152611e53565b916060818403910152610984565b60405190611eab60e083611d0f565b6001600160401b038111611d0a57601f01601f191660200190565b611eab9850611f553382612c8b565b6123be565b51906001600160401b038216820361024757565b602081830312610247578051906001600160401b03821161024757019060a0828203126102475760405191611fa283611cef565b8051835260208101516001600160401b0381116102475781019180601f8401121561024757825192611fd384611f2b565b91611fe16040519384611d0f565b84835260208583010111610247576080936120029160208085019101610961565b602084015261201360408201611f5a565b604084015261202460608201611ea0565b60608401520151608082015290565b6040906104f6939281528160208201520190610984565b356104f681611786565b903590601e198136030182121561024757018035906001600160401b03821161024757602001918160051b3603831361024757565b634e487b7160e01b600052603260045260246000fd5b91908110156120c15760051b81013590603e1981360301821215610247570190565b612089565b9291926120d282611f2b565b916120e06040519384611d0f565b829481845281830111610247578281602093846000960137010152565b9080601f83011215610247578160206104f6933591016120c6565b6040813603126102475760405190604082018281106001600160401b03821117611d0a5760405280356001600160401b0381116102475761215c90369083016120fd565b82526020810135906001600160401b0382116102475761217e913691016120fd565b602082015290565b9061219960209282815194859201610961565b0190565b90916121b46104f693604084526040840190610984565b916020818403910152610984565b949280979694929160a0870190875260a060208801525260c0850160c08860051b8701019782600090603e1981360301935b838310612237575050505050509061221b8661222993866104f69899036040880152610984565b908482036060860152610984565b916080818403910152610984565b90919293949a60bf198a82030182528b3586811215610247576020612292600193868394019061228561227b61226d8480611e22565b604085526040850191611e53565b9285810190611e22565b9185818503910152611e53565b9d019201930191909493926121f4565b60209291906122b8849282815194859201610961565b019081520190565b6001600160401b038111611d0a5760051b60200190565b604051906122e6602083611d0f565b6000808352366020840137565b908092918237016000815290565b90602083828152019260208260051b82010193836000925b8484106123295750505050505090565b909192939495602080612351600193601f1986820301885261234b8b88611e22565b90611e53565b9801940194019294939190612319565b969261239c906123b0969561238e6104f69b999563ffffffff958c5260c060208d015260c08c0190610984565b918a830360408c0152611e53565b931660608701528583036080870152612301565b9160a0818403910152610984565b94959096939192936123ce61295b565b91841597881561263b575b6123e288612b8c565b976000891561263557506012850154612403906001600160a01b0316610672565b60405163cfa651f360e01b81526004810183905290602090829060249082905afa90811561078257600091612616575b50945b898061260e575b612569575b61244c3383612c8b565b61245582612ed6565b61245d612f0d565b996124678b61353b565b80612560575b1561254057601201546124b39594939291600091612493906001600160a01b0316610672565b60405180809981946316740b0b60e31b8352600483019190602083019252565b03915afa93841561078257611eab9b6103da9860208c976103cc99600091612525575b500151929c15612501575050955b604051631bee0bf560e11b60208201529889978c60248a01612361565b6103cc61251f9293610fc7610fba60405196879560208701916122f3565b956124e4565b61253a91503d806000833e610cb48183611d0f565b386124d6565b506103da9699506103cc94509061251f611eab9b8a9594939b36916120c6565b5085151561246d565b61257c612577368a8f6120c6565b614360565b6012820154612593906001600160a01b0316610672565b60206040518092635da1452f60e01b825281806125b4878a60048401612033565b03915afa908115610782576000916125ef575b506125d25750612442565b60405163c0c9447760e01b81529081906107e790600483016109a9565b612608915060203d6020116107c5576107b78183611d0f565b386125c7565b50851561243d565b61262f915060203d60201161077b5761076d8183611d0f565b38612433565b94612436565b61264e61264936888d6120c6565b6142f3565b6123d9565b90602060ff92600e61266361295b565b01836040519485938437820190815203019020541690565b6000805160206154ef8339815191528114801561277e575b8015612767575b8015612750575b8015612739575b8015612722575b801561270b575b80156126f4575b156126e0576126cb90611e74565b80156126da576104f690611d85565b50600090565b63332ed6cd60e21b60005260045260246000fd5b506000805160206155af83398151915281146126bd565b5060008051602061550f83398151915281146126b6565b5060008051602061558f83398151915281146126af565b5060008051602061552f83398151915281146126a8565b5060008051602061556f83398151915281146126a1565b5060008051602061554f833981519152811461269a565b506000805160206155cf8339815191528114612693565b926127c16104f695936127b3611f0e94608088526080880190610984565b908682036020880152610984565b908482036040860152610984565b9081602091031261024757516104f681611786565b916104f6926040815260018060a01b03835116604082015260208301511515606082015260408301516080820152606083015160a082015260c0612837608085015160e083850152610120840190610984565b9360a081015160e084015201516101008201526020818403910152610984565b634e487b7160e01b600052602160045260246000fd5b6003111561287757565b612857565b3560038110156102475790565b92906128c3926128a76128b59298969860c0875260c0870190610984565b908582036020870152610984565b908382036040850152610984565b926003851015612877576104f6946060830152608082015260a0818403910152610984565b926127c16104f69593611f0e938652608060208701526080860190610984565b6104f69392606092825260208201528160408201520190610984565b634e487b7160e01b600052601160045260246000fd5b60001981019190821161294957565b612924565b9190820391821161294957565b604051602081017f4d4b313ebcb0a912513922edb5e106a88014bff7fd8b7c782480f94eaff75a96815260208252612994604083611d0f565b9051902060ff191690565b9060008113156129e657506201517f1942014281116129495782106129c2575050565b69ffffffffffffffffffff9063142f6edf60e11b6000521660045260245260446000fd5b6338ee04a760e01b60005260045260246000fd5b9081602091031261024757516104f681611e17565b60ff166002039060ff821161294957565b60ff6001199116019060ff821161294957565b60ff16604d811161294957600a0a90565b90670de0b6b3a7640000820291808304670de0b6b3a7640000149015171561294957565b600181901b91906001600160ff1b0381160361294957565b8181029291811591840414171561294957565b8115612a9d570490565b634e487b7160e01b600052601260045260246000fd5b60046020612ac7610672600961066461295b565b60405163313ce56760e01b815292839182905afa90811561078257600091612b5d575b506000831315612b4757600260ff821610612b2b57612b216104f69392612b1b612b16612b2694612a20565b612a33565b90612a80565b612a44565b612a93565b612b416104f693612b1b612b16612b2694612a0f565b91612a44565b6338ee04a760e01b600052600483905260246000fd5b612b7f915060203d602011612b85575b612b778183611d0f565b8101906129fa565b38612aea565b503d612b6d565b612b9461295b565b6004810154612bab906001600160a01b0316610672565b604051634f558e7960e01b81526004810184905290602090829060249082905afa90811561078257600091612c6c575b50612c655760120154612bf6906001600160a01b0316610672565b604051634f558e7960e01b81526004810183905290602090829060249082905afa90811561078257600091612c46575b5015612c325750600190565b63701a312d60e11b60005260045260246000fd5b612c5f915060203d6020116107c5576107b78183611d0f565b38612c26565b5050600090565b612c85915060203d6020116107c5576107b78183611d0f565b38612bdb565b612c9361295b565b612c9c82612b8c565b15612d4d5760120154612cde91602091612cbe906001600160a01b0316610672565b60405180809581946331a9108f60e11b8352600483019190602083019252565b03915afa90811561078257600091612d2e575b50905b6001600160a01b0382811690821603612d0b575050565b63adabb9d960e01b6000526001600160a01b039081166004521660245260446000fd5b612d47915060203d602011611500576114f18183611d0f565b38612cf1565b60040154612d6a91602091612cbe906001600160a01b0316610672565b03915afa90811561078257600091612d84575b5090612cf4565b612d9d915060203d602011611500576114f18183611d0f565b38612d7d565b612dab61295b565b612db482612b8c565b15612e5f576012810154612dd0906001600160a01b0316610672565b6040516369b0777f60e11b81526004810184905290602090829060249082905afa8015610782576001926011612e1d928594600091612e40575b50915b0190600052602052604060002090565b541603612e275750565b631d11134b60e01b600052600452600160245260446000fd5b612e59915060203d60201161077b5761076d8183611d0f565b38612e0a565b6004810154612e76906001600160a01b0316610672565b6040516369b0777f60e11b81526004810184905290602090829060249082905afa8015610782576001926011612e1d928594600091612eb7575b5091612e0d565b612ed0915060203d60201161077b5761076d8183611d0f565b38612eb0565b612ede61295b565b50600280612eeb83614993565b1603612ef45750565b631d11134b60e01b600052600452600260245260446000fd5b600d612f1761295b565b0180546000198114612949576104f6916001820190556040516020810191438352466040830152606082015260608152612f52608082611d0f565b51902060016fffffffffffffffffffffffffffffffff821160071b6001600160401b0383821c1160061b1763ffffffff83821c1160051b1761ffff83821c1160041b1760ff83612fa28360031c90565b921c11170190614c0e565b3d15612fd8573d90612fbe82611f2b565b91612fcc6040519384611d0f565b82523d6000602084013e565b606090565b612fe561295b565b90613016600161301060008051602061552f833981519152600a860190600052602052604060002090565b54612a80565b9182156131f7576009810154613034906001600160a01b0316610672565b604051633fabe5a360e21b815260a081600481855afa8015610782576000906000906000906131c6575b82935061306f90826130759461299f565b86612ab3565b3403613117575b5050600b01546001600160a01b031680156131065760009081908190819034906130ae906001600160a01b0316610672565b5af16130b8612fad565b50156130f5577fe639fa7b09c9280a0841241aaca60ee854005312117168919f20f2cc4b96673c916130f06040519283923484612908565b0390a1565b6312171d8360e31b60005260046000fd5b63d92e233d60e01b60005260046000fd5b61312090614a32565b604051639a6fc8f560e01b815269ffffffffffffffffffff821660048201529160a090839060249082905afa9182156107825761317492600091600091613199575b508161316e929361299f565b84612ab3565b803403613181578061307c565b631e0bd6c160e31b6000526004523460245260446000fd5b61316e92506131b7915060a03d60a011611e1057611dfa8183611d0f565b50919350909150829050613162565b5050506130756131e761306f9260a03d60a011611e1057611dfa8183611d0f565b509294508493509091905061305e565b5050503461320157565b636a71130f60e01b60005260008051602061552f833981519152600452600160245260446000fd5b61323161295b565b9061325c600161301060008051602061556f833981519152600a860190600052602052604060002090565b91821561327a576009810154613034906001600160a01b0316610672565b5050503461328457565b636a71130f60e01b60005260008051602061556f833981519152600452600160245260446000fd5b6132b461295b565b906132df600161301060008051602061554f833981519152600a860190600052602052604060002090565b9182156132fd576009810154613034906001600160a01b0316610672565b5050503461330757565b636a71130f60e01b60005260008051602061554f833981519152600452600160245260446000fd5b9061333861295b565b613361836130106000805160206154ef833981519152600a850190600052602052604060002090565b92831561338057506009810154613034906001600160a01b0316610672565b925050503461338c5750565b636a71130f60e01b6000526000805160206154ef83398151915260045260245260446000fd5b6133ba61295b565b906133e560016130106000805160206155cf833981519152600a860190600052602052604060002090565b918215613403576009810154613034906001600160a01b0316610672565b5050503461340d57565b636a71130f60e01b6000526000805160206155cf833981519152600452600160245260446000fd5b61343d61295b565b9061346860016130106000805160206155af833981519152600a860190600052602052604060002090565b918215613486576009810154613034906001600160a01b0316610672565b5050503461349057565b636a71130f60e01b6000526000805160206155af833981519152600452600160245260446000fd5b6134c061295b565b906134eb600161301060008051602061550f833981519152600a860190600052602052604060002090565b918215613509576009810154613034906001600160a01b0316610672565b5050503461351357565b636a71130f60e01b60005260008051602061550f833981519152600452600160245260446000fd5b61354361295b565b9061356e600161301060008051602061558f833981519152600a860190600052602052604060002090565b91821561358c576009810154613034906001600160a01b0316610672565b5050503461359657565b636a71130f60e01b60005260008051602061558f833981519152600452600160245260446000fd5b604051606091906135cf8382611d0f565b6002815291601f19018260005b8281106135e857505050565b8060606020809385010152016135dc565b6040805190919061360a8382611d0f565b6001815291601f19018260005b82811061362357505050565b806060602080938501015201613617565b8051600110156120c15760400190565b8051156120c15760200190565b80518210156120c15760209160051b010190565b90600182811c92168015613695575b602083101461367f57565b634e487b7160e01b600052602260045260246000fd5b91607f1691613674565b90604051918260008254926136b384613665565b808452936001811690811561371f57506001146136d8575b50611eab92500383611d0f565b90506000929192526020600020906000915b818310613703575050906020611eab92820101386136cb565b60209193508060019154838589010152019101909184926136ea565b905060209250611eab94915060ff191682840152151560051b820101386136cb565b9080602083519182815201916020808360051b8301019401926000915b83831061376d57505050505090565b909192939460208061378b600193601f198682030187528951610984565b9701930193019193929061375e565b92949391608084526000918054906137b182613665565b9182608088015260018116908160001461383757506001146137f8575b5050946127c1826104f69697866137ea95036020880152610984565b916060818403910152613741565b600090815260208120929350915b818310613820575050830160a001906127c16104f66137ce565b805460a08488010152602090920191600101613806565b60ff191660a08881019190915292151560051b870190920193506127c191506104f690506137ce565b81519192916139455760209192506103cc61389f61387c6135f9565b945b604051633b325fc560e01b8682015260248101919091529182906044820190565b6138a884613644565b526138b283613644565b506138bb61295b565b60038101546138d2906001600160a01b0316610672565b9060006138f860058301926138f260066138eb8661369f565b920161369f565b90614a73565b9161391960405197889687958694631f3f66f760e21b86526004860161379a565b03925af180156107825761392a5750565b6139429060203d60201161077b5761076d8183611d0f565b50565b6103cc61389f6020936103cc61397861395c6135be565b9760405192839163f23f601b60e01b8a840152602483016109a9565b61398187613634565b5261398b86613634565b5061387e565b90916121b46104f69360408452604084019061108e565b6139b061295b565b6139ba60016122c0565b916139c86040519384611d0f565b600183526139d660016122c0565b6020840190601f19013682378351156120c1575260040154613a00906001600160a01b0316610672565b91823b1561024757613a2c926000928360405180968195829463a4362b3960e01b845260048401613991565b03925af1801561078257613a3d5750565b80610c416000611eab93611d0f565b90613a5682611f2b565b613a636040519182611d0f565b8281528092613a74601f1991611f2b565b0190602036910137565b8060009172184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b821015613bb3575b806d04ee2d6d415b85acef8100000000600a921015613b97575b662386f26fc10000811015613b82575b6305f5e100811015613b70575b612710811015613b60575b6064811015613b51575b1015613b46575b613b316021613b0560018501613a4c565b938401015b60001901916f181899199a1a9b1b9c1cb0b131b232b360811b600a82061a8353600a900490565b8015613b4157613b319091613b0a565b505090565b600190910190613af4565b60029060649004930192613aed565b6004906127109004930192613ae3565b6008906305f5e1009004930192613ad8565b601090662386f26fc100009004930192613acb565b6020906d04ee2d6d415b85acef81000000009004930192613abb565b506040915072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b8104613aa1565b428110613be05750565b633bd4ab4160e01b6000526004524260245260446000fd5b6002613c0261295b565b01908060005281602052600160ff60406000205416151514613c36576000526020526040600020600160ff19825416179055565b632472ad4160e21b60005260045260246000fd5b90613c54826122c0565b613c616040519182611d0f565b8281528092613a74601f19916122c0565b60405190613c81604083611d0f565b601f82527f4e616d65496e666f28737472696e6720736c642c737472696e6720746c6429006020830152565b613cb5613c72565b604051613ce360208281613cd28183019687815193849201610961565b81010301601f198101835282611d0f565b51902090565b604051613cf7608082611d0f565b605a815260208101907f546f6b656e697a6174696f6e566f7563686572284e616d65496e666f5b5d206e82527f616d65732c75696e74323536206e6f6e63652c75696e7432353620657870697260408201527f657341742c61646472657373206f776e657241646472657373290000000000006060820152613ce3613d7a613c72565b916020604051938492613d9583850197889251928391610961565b8301613da982518093858085019101610961565b010103601f198101835282611d0f565b805160209091019060005b818110613dd15750505090565b8251845260209384019390920191600101613dc4565b613dfb613df48280612054565b9050613c4a565b9060005b613e098280612054565b9050811015613e7c5780613e26610f75600193610f6f8680612054565b613e2e613cad565b815180516020918201209281015180519082012060408051928301938452820193909352606081019290925290613e6881608081016103cc565b519020613e758286613651565b5201613dff565b50613ce3613e88613ce9565b92604051613e9e816103cc602082018095613db9565b5190206103cc602084013593613ebb60606040830135920161204a565b604080516020810198895290810194909452606084019590955260808301526001600160a01b0390931660a082015291829060c0820190565b604290613eff61535a565b613f076153c4565b6040519060208201927f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8452604083015260608201524660808201523060a082015260a08152613f5860c082611d0f565b519020906040519161190160f01b8352600283015260228201522090565b613f88613f8e92613f979436916120c6565b90614caf565b90929192614cf5565b613fba81613fa361295b565b9060018060a01b0316600052602052604060002090565b54908115613fc6575090565b63bf18af4360e01b60009081526001600160a01b0391909116600452602490fd5b613ff081614d77565b15613ff85750565b604051630a9644cf60e01b8152602060048201529081906107e7906024830190610984565b61402681615197565b1561402e5750565b604051637f19f48d60e01b8152602060048201529081906107e7906024830190610984565b8051600110156120c15760210190565b8051600210156120c15760220190565b8051600310156120c15760230190565b9081518110156120c1570160200190565b8015612949576000190190565b6000815191825b6140b3575b50905090565b6000198301838111612949575b80151580614115575b156140dc576140d790614094565b6140c0565b916140f56140ee846140fb949661294e565b8486614b42565b90614bce565b9080156000036140ad5761410e9061293a565b91826140a8565b50601760f91b6001600160f81b03196141476141396141338561293a565b86614083565b516001600160f81b03191690565b1614156140c9565b6040519061415e604083611d0f565b600682526565697031353560d01b6020830152565b6001600160a01b03168061419761419261418d6014612a68565b614337565b613a4c565b9160306141a384613644565b5360786141af84614053565b536141c26141bd6014612a68565b614345565b60018111614277575061425e57506028602282012060601c6029905b600182116141eb57505090565b614209816007600f61420f9416118061423c575b6142155760041c90565b91614094565b906141de565b600160fd1b6142276141398688614083565b1860001a6142358587614083565b5360041c90565b50606060ff614257614251614139888a614083565b60f81c90565b16116141ff565b63e22e27eb60e01b600052600452601460245260446000fd5b90600f811660108110156120c1576142ad91614209916f181899199a1a9b1b9c1cb0b131b232b360811b901a6142358588614083565b6141c2565b604051906142c1604083611d0f565b6004825263444f4d4160e01b6020830152565b604051906142e3604083611d0f565b60018252603160f81b6020830152565b6142fc81615285565b156143045750565b60405163d4c19e3b60e01b8152602060048201529081906107e7906024830190610984565b90601f820180921161294957565b906002820180921161294957565b906001820180921161294957565b9190820180921161294957565b805180156143fb57805b614372575090565b601760f91b6143966143896141396141338561293a565b6001600160f81b03191690565b146143aa576143a490614094565b8061436a565b6143ba614192828495945161294e565b9060005b82518110156143f457806143e06141396143da60019486614353565b88614083565b60001a6143ed8286614083565b53016143be565b5090925050565b5090565b9161440861295b565b61441461063a85612b8c565b614568576012015461442e906001600160a01b0316610672565b6040516364f1992360e01b815260048101859052602081602481855afa90811561078257600091614549575b5061453357604051633329dc9d60e11b81526004810185905290602090829060249082905afa90811561078257600091614514575b504281106144fb57506144ac916144a79136916120c6565b613fe7565b6002808316036144e25781806144c183614993565b16036144cb575050565b631d11134b60e01b60005260045260245260446000fd5b631d11134b60e01b600052600452600260245260446000fd5b636581bb3f60e11b600052600484905260245260446000fd5b61452d915060203d60201161077b5761076d8183611d0f565b3861448f565b63a9de8c6f60e01b600052600484905260246000fd5b614562915060203d6020116107c5576107b78183611d0f565b3861445a565b635a55e97160e11b600052600484905260246000fd5b61458a6020820161287c565b600381101561287757613ce36040516145a460a082611d0f565b6062815261742960f01b608060208301927f50726f6f664f66436f6e7461637473566f75636865722875696e74323536207284527f656769737472616e7448616e646c652c75696e74382070726f6f66536f75726360408201527f652c75696e74323536206e6f6e63652c75696e743235362065787069726573416060820152015260405161463760208201809361093b565b60628152614646608282611d0f565b519020604080516020810192835285358183015260ff9094166060858101919091529085013560808501529093013560a08301528160c081016103cc565b613f88613f8e926146969436916120c6565b60ff6146be8260016146a661295b565b019060018060a01b0316600052602052604060002090565b541615613fc65750565b906146d161295b565b906146de61063a84612b8c565b61483c5760126146f5926106646106729386612c8b565b6040516364f1992360e01b815260048101839052909190602081602481865afa9081156107825760009161481d575b5061480957604051633329dc9d60e11b815260048101829052602081602481865afa908115610782576000916147ea575b504281106147d0575060405163a34e303d60e01b81526004810182905291602090839060249082905afa918215610782576000926147af575b5081614798575050565b63f9f7458560e01b60005260045260245260446000fd5b6147c991925060203d60201161077b5761076d8183611d0f565b903861478e565b636581bb3f60e11b60005260049190915260245260446000fd5b614803915060203d60201161077b5761076d8183611d0f565b38614755565b63a9de8c6f60e01b60005260045260246000fd5b614836915060203d6020116107c5576107b78183611d0f565b38614724565b635a55e97160e11b600052600483905260246000fd5b9061485b61295b565b9061486583612b8c565b61497d576148739083612c8b565b60028061487f84614993565b1603614962576004015461489b906001600160a01b0316610672565b6040516364f1992360e01b815260048101839052909190602081602481865afa90811561078257600091614943575b5061480957604051633329dc9d60e11b81526004810182905291602090839060249082905afa91821561078257600092614922575b5042821061490b575050565b636581bb3f60e11b60005260045260245260446000fd5b61493c91925060203d60201161077b5761076d8183611d0f565b90386148ff565b61495c915060203d6020116107c5576107b78183611d0f565b386148ca565b631d11134b60e01b6000526004829052600260245260446000fd5b630bfffc8160e31b600052600483905260246000fd5b61499b61295b565b6149a482612b8c565b15614a1557601201546149e6916020916149c6906001600160a01b0316610672565b60405180809581946332b9c2b360e21b8352600483019190602083019252565b03915afa908115610782576000916149fc575090565b6104f6915060203d60201161077b5761076d8183611d0f565b600401546149e6916020916149c6906001600160a01b0316610672565b6001600160401b03811660018111156143fb57600019016001600160401b038111612949576001600160401b031669ffff0000000000000000919091161790565b6001611eab9193929360206040519582614a968894518092858088019101610961565b8301601d60f91b83820152614ab48251809385602185019101610961565b01010301601f198101845283611d0f565b15614acc57565b60405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b6044820152606490fd5b15614b0957565b60405162461bcd60e51b8152602060048201526011602482015270736c6963655f6f75744f66426f756e647360781b6044820152606490fd5b91614b5781614b5081614329565b1015614ac5565b614b6d8351614b668385614353565b1115614b02565b80614b8657505050604051600081526020810160405290565b60405192601f821692831560051b80858701019484860193010101905b808410614bbb5750508252601f01601f191660405290565b9092602080918551815201930190614ba3565b90604051614bec60208281613cd28183019687815193849201610961565b5190206040519060208201928352604082015260408152613ce3606082611d0f565b9081614c1f61419261418d84612a68565b926030614c2b85613644565b536078614c3785614053565b53614c446141bd84612a68565b60018111614c6d5750614c5657505090565b63e22e27eb60e01b60005260045260245260446000fd5b90600f81169060108210156120c157614caa916f181899199a1a9b1b9c1cb0b131b232b360811b901a614ca08488614083565b5360041c91614094565b614c44565b8151919060418303614ce057614cd992506020820151906060604084015193015160001a90615409565b9192909190565b505060009160029190565b6004111561287757565b614cfe81614ceb565b80614d07575050565b614d1081614ceb565b60018103614d295763f645eedf60e01b60005260046000fd5b614d3281614ceb565b60028103614d4f575063fce698f760e01b60005260045260246000fd5b80614d5b600392614ceb565b14614d635750565b6335e2f38360e21b60005260045260246000fd5b805180158015614f71575b612c6557600481119081614f4d575b81614f29575b81614f05575b81614ee1575b60005b818110614db65750505050600190565b614dc66143896141398387614083565b606160f81b81101580614ed3575b8015614eb6575b8015614ea9575b8015614ea1575b15614e795781158015614e90575b80614e83575b614e7957600282149081614e6b575b5080614e5a575b80614e35575b614e26575b600101614da6565b82614e1e575b50505050600090565b50602d60f81b6001600160f81b0319614e536141396143da85614345565b1614614e19565b5081614e6582614345565b10614e13565b602d60f81b14905038614e0c565b5050505050600090565b50602d60f81b8114614dfd565b50614e9a8361293a565b8214614df7565b506000614de9565b50602d60f81b8114614de2565b50600360fc1b8110801590614ddb5750603960f81b811115614ddb565b50603d60f91b811115614dd4565b9050602d60f81b6001600160f81b0319614efd61413985614073565b161490614da3565b9050602d60f81b6001600160f81b0319614f2161413985614063565b161490614d9d565b9050603760f91b6001600160f81b0319614f4561413985614053565b161490614d97565b9050600f60fb1b6001600160f81b0319614f6961413985613644565b161490614d91565b50603f8111614d82565b8051918215801561518d575b61518557600483119182615161575b8261513d575b82615119575b826150f5575b60005b848110614fbc575050505050600190565b614fcc6143896141398385614083565b606160f81b811015806150e7575b80156150ca575b80156150bd575b80156150a8575b1561507f5781158015615097575b8061508a575b61507f57600282149081615071575b5080615060575b8061503b575b61502c575b600101614fab565b83615024575050505050600090565b50602d60f81b6001600160f81b031961505961413961413385614345565b161461501f565b508461506b82614345565b10615019565b602d60f81b14905038615012565b505050505050600090565b50602d60f81b8114615003565b506150a18661293a565b8214614ffd565b50838015614fef5750605f60f81b8114614fef565b50602d60f81b8114614fe8565b50600360fc1b8110801590614fe15750603960f81b811115614fe1565b50603d60f91b811115614fda565b9150602d60f81b6001600160f81b031961511161413985614073565b161491614fa8565b9150602d60f81b6001600160f81b031961513561413985614063565b161491614fa2565b9150603760f91b6001600160f81b031961515961413985614053565b161491614f9c565b9150600f60fb1b6001600160f81b031961517d61413985613644565b161491614f96565b505050600090565b50603f8311614f87565b8051908115801561527b575b612c6557601760f91b6001600160f81b03196151c161413984613644565b16148015615257575b612c655760009060005b83811061520257506151ef6151f49361063a93600093615492565b614f7b565b6151fd57600190565b600090565b601760f91b6152176143896141398486614083565b14615225575b6001016151d4565b91808314614e2c5761063a60006151ef856152409486615492565b61518557600161524f83614345565b92905061521d565b50601760f91b61527561438961413961526f8661293a565b85614083565b146151ca565b5060fd82116151a3565b80519081158015615350575b612c6557601760f91b6001600160f81b03196152af61413984613644565b16148015615332575b612c655760009060005b8381106152dd57506151ef6151f49361063a93600193615492565b601760f91b6152f26143896141398486614083565b14615300575b6001016152c2565b91808314614e2c5761063a60016151ef8561531b9486615492565b61518557600161532a83614345565b9290506152f8565b50601760f91b61534a61438961413961526f8661293a565b146152b8565b5060fd8211615291565b6153626142b2565b8051908115615372576020012090565b50507fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10054801561539f5790565b507fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47090565b6153cc6142d4565b80519081156153dc576020012090565b50507fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10154801561539f5790565b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08411615486579160209360809260ff60009560405194855216868401526040830152606082015282805260015afa15610782576000516001600160a01b0381161561547a5790600090600090565b50600090600190600090565b50505060009160039190565b91818103818111612949576154a690613a4c565b92825b8281106154b7575050505090565b6001600160f81b03196154ca8284614083565b511690848103818111612949576154e760019360001a9188614083565b53016154a956fe41274d8380e68ced671e39b24773a9a28190d6ae058a9a0a57272be30902055c4a2248149817dde7dfda12ec5979900c9a0aba1d5ecdd54d38762a0cc40039961ee9a1c13af222b9e4d98567d0238a554fbf843b89f4720f3aa34731720cdffa08fb31c3e81624356c3314088aa971b73bcc82d22bc3e3b184b4593077ae3278add9df460364fdc361c17268aded1a218d04bba51bfce5119914d05b8bd10ababbecd033b99e35d8972b316640f0b13a68063e02065b4e964541d3498c941657272cf3b1b4a64c268809c75d1290f1c3ac8c1e2df8ebefee53369069b86f0649a19646f5f88bada8fee59cab6efcdf841c1797d1344642f9af3a4e0415431506a26469706673582212205d03e947712de15fd7c777d5d9ce651c353462302ef3f97fcafe502bcfa078fc64736f6c634300081c0033
Deployed Bytecode
0x6080604052600436101561001257600080fd5b60003560e01c80630cbc4e93146102375780630e160be6146102325780632f945faa1461022d57806335abf04a146102285780633761014014610223578063379006ba1461021e5780633827aaaf1461021957806340fb6b8e1461021457806354fd4d501461020f578063575c472d1461020a578063628c19061461020557806373d9d085146102005780637f13e412146101fb5780638141f9ed146101f657806384b0196e146101f157806386d45941146101ec578063870a3d84146101e7578063989082f9146101e25780639c7d2ab7146101dd578063a28df472146101d8578063aa00c548146101d3578063ade81788146101ce578063b36210c1146101c9578063ba26a79d146101c4578063bce69e8c146101bf578063c4851c0c146101ba578063de174837146101b5578063e89174bd146101b0578063e952a25a146101ab578063ea0b1829146101a6578063eabd785c146101a1578063eb7ce2491461019c578063efd0c5a6146101975763efea996a1461019257600080fd5b611c0d565b611be4565b611b16565b611a42565b61193d565b611914565b6118dc565b611797565b61175d565b61158a565b61131d565b611304565b6112f0565b6112c7565b6112af565b61126a565b6111e8565b61116b565b611142565b6110c2565b610e0f565b610cc3565b610ae6565b610a52565b610a05565b6109ba565b610800565b6105c4565b6104f9565b610497565b610327565b61029b565b610275565b61024c565b600091031261024757565b600080fd5b3461024757600036600319011261024757602060405160008051602061550f8339815191528152f35b34610247576020366003190112610247576020610293600435611d85565b604051908152f35b3461024757600036600319011261024757602060405160008051602061556f8339815191528152f35b9181601f84011215610247578235916001600160401b038311610247576020808501948460051b01011161024757565b9060406003198301126102475760043591602435906001600160401b03821161024757610323916004016102c4565b9091565b610330366102f4565b61033b839293612b8c565b610470579161034a3383612c8b565b61035382612da3565b61035b612f0d565b9061036582612fdd565b60405190637992685160e11b60208301528460848301856024850152606060448501525260a4820160a48660051b8401019582600090607e1981360301935b8383106103e157508589036023190160648701526103df8888886103da816103cc8f85610984565b03601f198101835282611d0f565b613860565b005b90919293949860a31987820301825289358681121561024757602061046060019360806104508885960163ffffffff61041982610a41565b16845260ff8682013561042b81611e17565b168685015260ff604082013561044081611e17565b1660408501526060810190611e22565b9190928160608201520191611e53565b9b019201930191909493926103a4565b5063d08fd27160e01b60005260045260246000fd5b60209060031901126102475760043590565b346102475760206102936104aa36610485565b611e74565b8015150361024757565b9060606003198301126102475760043591602435906001600160401b038211610247576104e8916004016102c4565b90916044356104f6816104af565b90565b610502366104b9565b5061050e839293612b8c565b6104705761051c3383612c8b565b61052582612b8c565b610470576103df926105373384612c8b565b61054083612da3565b6103da61054b612f0d565b9261055584613229565b6103cc610585604051948593639f63ae2360e01b6020860152886024860152606060448601526084850191612301565b82810360231901606484015285610984565b9181601f84011215610247578235916001600160401b038311610247576020838186019501011161024757565b6060366003190112610247576004356024356001600160401b038111610247576105f2903690600401610597565b91906044356001600160401b03811161024757610613903690600401610597565b61061f84939293612b8c565b6107eb5761062d3385612c8b565b61063e61063a8684612653565b1590565b6107cc5761064a612f0d565b92610654846132ac565b61067e610672600461066461295b565b01546001600160a01b031690565b6001600160a01b031690565b6040516364f1992360e01b81526004810187905290602090829060249082905afa9081156107825760009161079d575b50610787576106c3610672600461066461295b565b604051633329dc9d60e11b81526004810187905290602090829060249082905afa90811561078257600091610753575b504281106107395750946103cc846103da936103df98610713838a6139a8565b61071c89613a7e565b60405163069322e360e51b60208201529788969160248801611ed1565b636581bb3f60e11b600052600486905260245260445b6000fd5b610775915060203d60201161077b575b61076d8183611d0f565b810190611ec2565b386106f3565b503d610763565b611d79565b63a9de8c6f60e01b600052600485905260246000fd5b6107bf915060203d6020116107c5575b6107b78183611d0f565b810190611ead565b386106ae565b503d6107ad565b604051620d153960e01b8152806107e7878560048401611e8f565b0390fd5b8363d08fd27160e01b60005260045260246000fd5b346102475760403660031901126102475760043561081f6024356104af565b61082881612b8c565b610901576108363382612c8b565b61083f81612b8c565b610901576103df906108513382612c8b565b610859612f0d565b6108ae6103da61086884613a7e565b6103cc8461087546613a7e565b6108dd600160409260266020855161088d8782611d0f565b6006815201916565697031353560d01b835285519a8b936020850190610915565b601d60f91b828401526108cb815180926020602787019101610961565b8201010301601f198101885287611d0f565b6108e633614173565b9051631acdf90760e21b602082015295869460248601612795565b63d08fd27160e01b60005260045260246000fd5b60005b6006811061092b57505060066000910152565b8181015183820152602001610918565b60005b6062811061095157505060626000910152565b818101518382015260200161093e565b60005b8381106109745750506000910152565b8181015183820152602001610964565b9060209161099d81518092818552858086019101610961565b601f01601f1916010190565b9060206104f6928181520190610984565b3461024757600036600319011261024757610a0160408051906109dd8183611d0f565b60058252640312e312e360dc1b602083015251918291602083526020830190610984565b0390f35b3461024757600036600319011261024757602060405160008051602061552f8339815191528152f35b6064359063ffffffff8216820361024757565b359063ffffffff8216820361024757565b60c0366003190112610247576004356024356001600160401b03811161024757610a80903690600401610597565b6044929192356001600160401b03811161024757610aa2903690600401610597565b610aad949194610a2e565b90608435956001600160401b03871161024757610ad16103df9736906004016102c4565b95909460a43597610ae1896104af565b611f46565b3461024757602036600319011261024757600435610b043382612c8b565b6012610b0e61295b565b018054909190610b26906001600160a01b0316610672565b6040516316740b0b60e31b8152600481018390529092600082602481875afa91821561078257600092610ca0575b50815115610c8a578151608083018051604051633d854fa160e11b81526004810193909352602483015294602090829060449082905afa90811561078257600091610c6b575b50610c4d57610bbb610672610bad612f0d565b92546001600160a01b031690565b803b156102475760405163576bd72160e01b8152916000918391829084908290610be9908a60048401612033565b03925af1801561078257610c32575b505191516040519081523391907f5489bf68a3ad4b454ddf44e2cc0863dd86a8a874fcf2191c8818b6908bd1a4eb9080602081015b0390a4005b80610c416000610c4793611d0f565b8061023c565b38610bf8565b815184516314f241a760e01b60005260049190915260245260446000fd5b610c84915060203d6020116107c5576107b78183611d0f565b38610b9a565b632db04fed60e21b600052600483905260246000fd5b610cbc91923d8091833e610cb48183611d0f565b810190611f6e565b9038610b54565b3461024757604036600319011261024757602435600435610ce43382612c8b565b610cec61295b565b610cf861063a83612b8c565b610dc95760120154610d12906001600160a01b0316610672565b60405163cfa651f360e01b815260048101839052909290602081602481875afa90811561078257600091610daa575b50610d9457823b1561024757604051633f89f20960e11b815260048101929092526024820152906000908290818381604481015b03925af1801561078257610d8557005b80610c4160006103df93611d0f565b63ed15e6cf60e01b600052600482905260246000fd5b610dc3915060203d60201161077b5761076d8183611d0f565b38610d41565b635a55e97160e11b600052600482905260246000fd5b608090604319011261024757604490565b608090602319011261024757602490565b908160809103126102475790565b6040366003190112610247576004356001600160401b03811161024757610e3a903690600401610e01565b6024356001600160401b03811161024757610e59903690600401610597565b9190610e686040830135613bd6565b610e756020830135613bf8565b6060820191610e866106728461204a565b330361106257610ec5610e998280612054565b959050610ea4612f0d565b93610eaf858861332f565b610ec0610ebb85613de7565b613ef4565b613f76565b9360005b818110610f5e5750506103df93610f3083610ee784610f3e95612054565b9096610f13610f0e610f08610efb46613a7e565b610f0361414f565b614a73565b9261204a565b614173565b9160405198899663b5999e7960e01b6020890152602488016121c2565b03601f198101845283611d0f565b6040516020810190610f55816103cc4387866122a2565b51902091613860565b610f7a610f7582610f6f8680612054565b9061209f565b612118565b610f848151613fe7565b6020810190610f93825161401d565b6110026020610fd2610fba610fcd85516103cc8851610fc760405195869489860190612186565b601760f91b815260010190565b90612186565b6140a1565b610fe2610672600461066461295b565b6040518080958194634f558e7960e01b8352600483019190602083019252565b03915afa90811561078257600091611044575b50611024575050600101610ec9565b519051604051630e3ba82b60e31b81529182916107e7916004840161219d565b61105c915060203d81116107c5576107b78183611d0f565b38611015565b61074f61106e8461204a565b63adabb9d960e01b600052336004526001600160a01b0316602452604490565b906020808351928381520192019060005b8181106110ac5750505090565b825184526020938401939092019160010161109f565b34610247576000366003190112610247576111136110de6142b2565b610a016110e96142d4565b6111216110f46122d7565b91604051958695600f60f81b875260e0602088015260e0870190610984565b908582036040870152610984565b90466060850152306080850152600060a085015283820360c085015261108e565b3461024757600036600319011261024757602060405160008051602061554f8339815191528152f35b6080366003190112610247576004356111856024356104af565b6044356001600160401b038111610247576111a4903690600401610597565b91906064356001600160401b038111610247576111c5903690600401610597565b6111d184939293612b8c565b6107eb576111df3385612c8b565b61061f84612b8c565b60a0366003190112610247576004356024356001600160401b03811161024757611216903690600401610597565b916044356001600160401b03811161024757611236903690600401610597565b61123e610a2e565b91608435956001600160401b038711610247576112626103df9736906004016102c4565b9690956123be565b34610247576020366003190112610247576004356001600160401b038111610247576112a561129f6020923690600401610597565b90612653565b6040519015158152f35b346102475760206102936112c236610485565b61267b565b346102475760003660031901126102475760206040516000805160206155af8339815191528152f35b6112f9366102f4565b610525839293612b8c565b3461024757602036600319011261024757600435610836565b3461024757602036600319011261024757600435601261133b61295b565b018054611350906001600160a01b0316610672565b6040516316740b0b60e31b81526004810184905290929091600083602481875afa9283156107825760009361156f575b508251156115595761139483513390612c8b565b6113a461063a6060850151151590565b611543578251608084018051604051633d854fa160e11b8152600481019390935260248301529390602081604481895afa90811561078257600091611524575b50611507576040516331a9108f60e11b81526004810184905294602090869060249082905afa948515610782576000956114d6575b50611436610672611428612f0d565b93546001600160a01b031690565b803b156102475760405163576bd72160e01b8152926000918491829084908290611464908a60048401612033565b03925af1908115610782577f676dac521fa16607d417251e9ab50656928e72849236f2c82525fdbfa23d2cd492610c2d926114c1575b5051935160408051958652602086019190915233956001600160a01b031694918291820190565b80610c4160006114d093611d0f565b3861149a565b6114f991955060203d602011611500575b6114f18183611d0f565b8101906127cf565b9338611419565b503d6114e7565b5183516314f241a760e01b60005260049190915260245260446000fd5b61153d915060203d6020116107c5576107b78183611d0f565b386113e4565b63117c7f6560e01b600052600482905260246000fd5b632db04fed60e21b600052600482905260246000fd5b61158391933d8091833e610cb48183611d0f565b9138611380565b60e0366003190112610247576004356115a46024356104af565b6115ad36610ddf565b9060c4356001600160401b038111610247576115cd903690600401610597565b6115d683612b8c565b611748576115e43384612c8b565b6115ed83612b8c565b611748576115fb3384612c8b565b6116086060850135613bd6565b6116156040850135613bf8565b611621610ebb8561457e565b91602085019260016116328561287c565b61163b8161286d565b03611734579061164b9291613f76565b9261165c610672600461066461295b565b6040516369b0777f60e11b81526004810185905290602090829060249082905afa90811561078257600091611715575b508481036116fc57506103df93505b6103da6116a6612f0d565b926116b0846133b2565b6103cc846116bd87613a7e565b926116ca610efb46613a7e565b956116dd6116d733614173565b9261287c565b903591604051978896630bb9f0b760e01b602089015260248801612889565b63023d647f60e41b600052600485905260245260446000fd5b61172e915060203d60201161077b5761076d8183611d0f565b3861168c565b91611743916103df9693614684565b61169b565b8263d08fd27160e01b60005260045260246000fd5b3461024757600036600319011261024757602060405160008051602061558f8339815191528152f35b6001600160a01b0381160361024757565b60e0366003190112610247576004356024356001600160401b03811161024757611896916117cb6020923690600401610597565b6044359060643593611869608435946117e3866104af565b60a4359360c435956117f487611786565b6117fe3386612c8b565b61180a818385886143ff565b61181261295b565b986118586118356106726012611826612f0d565b9d01546001600160a01b031690565b99611850611841611f1c565b6001600160a01b03909b168b52565b1515898d0152565b6040880152606087015236916120c6565b608084015260a083015260c08201526000604051809681958294639414f66b60e01b8452600484016127e4565b03925af1801561078257610a01916000916118bd575b506040519081529081906020820190565b6118d6915060203d60201161077b5761076d8183611d0f565b386118ac565b60c0366003190112610247576004356118f436610df0565b9060a4356001600160401b038111610247576115e4903690600401610597565b346102475760003660031901126102475760206040516000805160206154ef8339815191528152f35b611946366104b9565b50611952839293612b8c565b610470576119603383612c8b565b61196982612b8c565b61047057916119783383612c8b565b61198182612da3565b611989612f0d565b9061199382612fdd565b60405190637992685160e11b60208301528460848301856024850152606060448501525260a4820160a48660051b8401019582600090607e1981360301935b8383106119fa57508589036023190160648701526103df8888886103da816103cc8f85610984565b90919293949860a319878203018252893586811215610247576020611a3260019360806104508885960163ffffffff61041982610a41565b9b019201930191909493926119d2565b602036600319011261024757600435611a5b3382612c8b565b611a6361295b565b90611a886106726012611a74612f0d565b94611a7e86613435565b61066433866146c8565b6040516331a9108f60e11b8152600481018390529290602090849060249082905afa8015610782576103df93600091611af7575b506103da611ad5611acf610efb46613a7e565b92614173565b916103cc84604051948593630225715360e61b602086015288602486016128e8565b611b10915060203d602011611500576114f18183611d0f565b38611abc565b602036600319011261024757600435611b2f3382612c8b565b611b3761295b565b90611b5c6106726004611b48612f0d565b94611b52866134b8565b6106643386614852565b6040516331a9108f60e11b8152600481018390529290602090849060249082905afa8015610782576103df93600091611bc5575b506103da611ba3611acf610efb46613a7e565b916103cc84604051948593633316357f60e21b602086015288602486016128e8565b611bde915060203d602011611500576114f18183611d0f565b38611b90565b346102475760003660031901126102475760206040516000805160206155cf8339815191528152f35b6040366003190112610247576024356004356012611c2961295b565b018054611c3e906001600160a01b0316610672565b60405163cfa651f360e01b81526004810184905290602090829060249082905afa90811561078257600091611cba575b508015610c8a57611c80903390612c8b565b611c8e610672610bad612f0d565b803b1561024757610d7593600080946040519687958694859363fabea1c360e01b855260048501612908565b611cd3915060203d60201161077b5761076d8183611d0f565b38611c6e565b634e487b7160e01b600052604160045260246000fd5b60a081019081106001600160401b03821117611d0a57604052565b611cd9565b90601f801991011681019081106001600160401b03821117611d0a57604052565b519069ffffffffffffffffffff8216820361024757565b908160a091031261024757611d5b81611d30565b916020820151916040810151916104f6608060608401519301611d30565b6040513d6000823e3d90fd5b600460a06001600160a01b036009611d9b61295b565b01541660405192838092633fabe5a360e21b82525afa918215610782576104f69260008093600092611dd9575b509083611dd49261299f565b612ab3565b9050611dd49350611e02915060a03d60a011611e10575b611dfa8183611d0f565b810190611d47565b509194509091905083611dc8565b503d611df0565b60ff81160361024757565b9035601e19823603018112156102475701602081359101916001600160401b03821161024757813603831361024757565b908060209392818452848401376000828201840152601f01601f1916010190565b600a611e7e61295b565b019060005260205260406000205490565b9160206104f6938181520191611e53565b5190611eab826104af565b565b9081602091031261024757516104f6816104af565b90816020910312610247575190565b94906104f6969492611f0e94611ef2611f009360808a5260808a0190610984565b9188830360208a0152611e53565b918583036040870152611e53565b916060818403910152610984565b60405190611eab60e083611d0f565b6001600160401b038111611d0a57601f01601f191660200190565b611eab9850611f553382612c8b565b6123be565b51906001600160401b038216820361024757565b602081830312610247578051906001600160401b03821161024757019060a0828203126102475760405191611fa283611cef565b8051835260208101516001600160401b0381116102475781019180601f8401121561024757825192611fd384611f2b565b91611fe16040519384611d0f565b84835260208583010111610247576080936120029160208085019101610961565b602084015261201360408201611f5a565b604084015261202460608201611ea0565b60608401520151608082015290565b6040906104f6939281528160208201520190610984565b356104f681611786565b903590601e198136030182121561024757018035906001600160401b03821161024757602001918160051b3603831361024757565b634e487b7160e01b600052603260045260246000fd5b91908110156120c15760051b81013590603e1981360301821215610247570190565b612089565b9291926120d282611f2b565b916120e06040519384611d0f565b829481845281830111610247578281602093846000960137010152565b9080601f83011215610247578160206104f6933591016120c6565b6040813603126102475760405190604082018281106001600160401b03821117611d0a5760405280356001600160401b0381116102475761215c90369083016120fd565b82526020810135906001600160401b0382116102475761217e913691016120fd565b602082015290565b9061219960209282815194859201610961565b0190565b90916121b46104f693604084526040840190610984565b916020818403910152610984565b949280979694929160a0870190875260a060208801525260c0850160c08860051b8701019782600090603e1981360301935b838310612237575050505050509061221b8661222993866104f69899036040880152610984565b908482036060860152610984565b916080818403910152610984565b90919293949a60bf198a82030182528b3586811215610247576020612292600193868394019061228561227b61226d8480611e22565b604085526040850191611e53565b9285810190611e22565b9185818503910152611e53565b9d019201930191909493926121f4565b60209291906122b8849282815194859201610961565b019081520190565b6001600160401b038111611d0a5760051b60200190565b604051906122e6602083611d0f565b6000808352366020840137565b908092918237016000815290565b90602083828152019260208260051b82010193836000925b8484106123295750505050505090565b909192939495602080612351600193601f1986820301885261234b8b88611e22565b90611e53565b9801940194019294939190612319565b969261239c906123b0969561238e6104f69b999563ffffffff958c5260c060208d015260c08c0190610984565b918a830360408c0152611e53565b931660608701528583036080870152612301565b9160a0818403910152610984565b94959096939192936123ce61295b565b91841597881561263b575b6123e288612b8c565b976000891561263557506012850154612403906001600160a01b0316610672565b60405163cfa651f360e01b81526004810183905290602090829060249082905afa90811561078257600091612616575b50945b898061260e575b612569575b61244c3383612c8b565b61245582612ed6565b61245d612f0d565b996124678b61353b565b80612560575b1561254057601201546124b39594939291600091612493906001600160a01b0316610672565b60405180809981946316740b0b60e31b8352600483019190602083019252565b03915afa93841561078257611eab9b6103da9860208c976103cc99600091612525575b500151929c15612501575050955b604051631bee0bf560e11b60208201529889978c60248a01612361565b6103cc61251f9293610fc7610fba60405196879560208701916122f3565b956124e4565b61253a91503d806000833e610cb48183611d0f565b386124d6565b506103da9699506103cc94509061251f611eab9b8a9594939b36916120c6565b5085151561246d565b61257c612577368a8f6120c6565b614360565b6012820154612593906001600160a01b0316610672565b60206040518092635da1452f60e01b825281806125b4878a60048401612033565b03915afa908115610782576000916125ef575b506125d25750612442565b60405163c0c9447760e01b81529081906107e790600483016109a9565b612608915060203d6020116107c5576107b78183611d0f565b386125c7565b50851561243d565b61262f915060203d60201161077b5761076d8183611d0f565b38612433565b94612436565b61264e61264936888d6120c6565b6142f3565b6123d9565b90602060ff92600e61266361295b565b01836040519485938437820190815203019020541690565b6000805160206154ef8339815191528114801561277e575b8015612767575b8015612750575b8015612739575b8015612722575b801561270b575b80156126f4575b156126e0576126cb90611e74565b80156126da576104f690611d85565b50600090565b63332ed6cd60e21b60005260045260246000fd5b506000805160206155af83398151915281146126bd565b5060008051602061550f83398151915281146126b6565b5060008051602061558f83398151915281146126af565b5060008051602061552f83398151915281146126a8565b5060008051602061556f83398151915281146126a1565b5060008051602061554f833981519152811461269a565b506000805160206155cf8339815191528114612693565b926127c16104f695936127b3611f0e94608088526080880190610984565b908682036020880152610984565b908482036040860152610984565b9081602091031261024757516104f681611786565b916104f6926040815260018060a01b03835116604082015260208301511515606082015260408301516080820152606083015160a082015260c0612837608085015160e083850152610120840190610984565b9360a081015160e084015201516101008201526020818403910152610984565b634e487b7160e01b600052602160045260246000fd5b6003111561287757565b612857565b3560038110156102475790565b92906128c3926128a76128b59298969860c0875260c0870190610984565b908582036020870152610984565b908382036040850152610984565b926003851015612877576104f6946060830152608082015260a0818403910152610984565b926127c16104f69593611f0e938652608060208701526080860190610984565b6104f69392606092825260208201528160408201520190610984565b634e487b7160e01b600052601160045260246000fd5b60001981019190821161294957565b612924565b9190820391821161294957565b604051602081017f4d4b313ebcb0a912513922edb5e106a88014bff7fd8b7c782480f94eaff75a96815260208252612994604083611d0f565b9051902060ff191690565b9060008113156129e657506201517f1942014281116129495782106129c2575050565b69ffffffffffffffffffff9063142f6edf60e11b6000521660045260245260446000fd5b6338ee04a760e01b60005260045260246000fd5b9081602091031261024757516104f681611e17565b60ff166002039060ff821161294957565b60ff6001199116019060ff821161294957565b60ff16604d811161294957600a0a90565b90670de0b6b3a7640000820291808304670de0b6b3a7640000149015171561294957565b600181901b91906001600160ff1b0381160361294957565b8181029291811591840414171561294957565b8115612a9d570490565b634e487b7160e01b600052601260045260246000fd5b60046020612ac7610672600961066461295b565b60405163313ce56760e01b815292839182905afa90811561078257600091612b5d575b506000831315612b4757600260ff821610612b2b57612b216104f69392612b1b612b16612b2694612a20565b612a33565b90612a80565b612a44565b612a93565b612b416104f693612b1b612b16612b2694612a0f565b91612a44565b6338ee04a760e01b600052600483905260246000fd5b612b7f915060203d602011612b85575b612b778183611d0f565b8101906129fa565b38612aea565b503d612b6d565b612b9461295b565b6004810154612bab906001600160a01b0316610672565b604051634f558e7960e01b81526004810184905290602090829060249082905afa90811561078257600091612c6c575b50612c655760120154612bf6906001600160a01b0316610672565b604051634f558e7960e01b81526004810183905290602090829060249082905afa90811561078257600091612c46575b5015612c325750600190565b63701a312d60e11b60005260045260246000fd5b612c5f915060203d6020116107c5576107b78183611d0f565b38612c26565b5050600090565b612c85915060203d6020116107c5576107b78183611d0f565b38612bdb565b612c9361295b565b612c9c82612b8c565b15612d4d5760120154612cde91602091612cbe906001600160a01b0316610672565b60405180809581946331a9108f60e11b8352600483019190602083019252565b03915afa90811561078257600091612d2e575b50905b6001600160a01b0382811690821603612d0b575050565b63adabb9d960e01b6000526001600160a01b039081166004521660245260446000fd5b612d47915060203d602011611500576114f18183611d0f565b38612cf1565b60040154612d6a91602091612cbe906001600160a01b0316610672565b03915afa90811561078257600091612d84575b5090612cf4565b612d9d915060203d602011611500576114f18183611d0f565b38612d7d565b612dab61295b565b612db482612b8c565b15612e5f576012810154612dd0906001600160a01b0316610672565b6040516369b0777f60e11b81526004810184905290602090829060249082905afa8015610782576001926011612e1d928594600091612e40575b50915b0190600052602052604060002090565b541603612e275750565b631d11134b60e01b600052600452600160245260446000fd5b612e59915060203d60201161077b5761076d8183611d0f565b38612e0a565b6004810154612e76906001600160a01b0316610672565b6040516369b0777f60e11b81526004810184905290602090829060249082905afa8015610782576001926011612e1d928594600091612eb7575b5091612e0d565b612ed0915060203d60201161077b5761076d8183611d0f565b38612eb0565b612ede61295b565b50600280612eeb83614993565b1603612ef45750565b631d11134b60e01b600052600452600260245260446000fd5b600d612f1761295b565b0180546000198114612949576104f6916001820190556040516020810191438352466040830152606082015260608152612f52608082611d0f565b51902060016fffffffffffffffffffffffffffffffff821160071b6001600160401b0383821c1160061b1763ffffffff83821c1160051b1761ffff83821c1160041b1760ff83612fa28360031c90565b921c11170190614c0e565b3d15612fd8573d90612fbe82611f2b565b91612fcc6040519384611d0f565b82523d6000602084013e565b606090565b612fe561295b565b90613016600161301060008051602061552f833981519152600a860190600052602052604060002090565b54612a80565b9182156131f7576009810154613034906001600160a01b0316610672565b604051633fabe5a360e21b815260a081600481855afa8015610782576000906000906000906131c6575b82935061306f90826130759461299f565b86612ab3565b3403613117575b5050600b01546001600160a01b031680156131065760009081908190819034906130ae906001600160a01b0316610672565b5af16130b8612fad565b50156130f5577fe639fa7b09c9280a0841241aaca60ee854005312117168919f20f2cc4b96673c916130f06040519283923484612908565b0390a1565b6312171d8360e31b60005260046000fd5b63d92e233d60e01b60005260046000fd5b61312090614a32565b604051639a6fc8f560e01b815269ffffffffffffffffffff821660048201529160a090839060249082905afa9182156107825761317492600091600091613199575b508161316e929361299f565b84612ab3565b803403613181578061307c565b631e0bd6c160e31b6000526004523460245260446000fd5b61316e92506131b7915060a03d60a011611e1057611dfa8183611d0f565b50919350909150829050613162565b5050506130756131e761306f9260a03d60a011611e1057611dfa8183611d0f565b509294508493509091905061305e565b5050503461320157565b636a71130f60e01b60005260008051602061552f833981519152600452600160245260446000fd5b61323161295b565b9061325c600161301060008051602061556f833981519152600a860190600052602052604060002090565b91821561327a576009810154613034906001600160a01b0316610672565b5050503461328457565b636a71130f60e01b60005260008051602061556f833981519152600452600160245260446000fd5b6132b461295b565b906132df600161301060008051602061554f833981519152600a860190600052602052604060002090565b9182156132fd576009810154613034906001600160a01b0316610672565b5050503461330757565b636a71130f60e01b60005260008051602061554f833981519152600452600160245260446000fd5b9061333861295b565b613361836130106000805160206154ef833981519152600a850190600052602052604060002090565b92831561338057506009810154613034906001600160a01b0316610672565b925050503461338c5750565b636a71130f60e01b6000526000805160206154ef83398151915260045260245260446000fd5b6133ba61295b565b906133e560016130106000805160206155cf833981519152600a860190600052602052604060002090565b918215613403576009810154613034906001600160a01b0316610672565b5050503461340d57565b636a71130f60e01b6000526000805160206155cf833981519152600452600160245260446000fd5b61343d61295b565b9061346860016130106000805160206155af833981519152600a860190600052602052604060002090565b918215613486576009810154613034906001600160a01b0316610672565b5050503461349057565b636a71130f60e01b6000526000805160206155af833981519152600452600160245260446000fd5b6134c061295b565b906134eb600161301060008051602061550f833981519152600a860190600052602052604060002090565b918215613509576009810154613034906001600160a01b0316610672565b5050503461351357565b636a71130f60e01b60005260008051602061550f833981519152600452600160245260446000fd5b61354361295b565b9061356e600161301060008051602061558f833981519152600a860190600052602052604060002090565b91821561358c576009810154613034906001600160a01b0316610672565b5050503461359657565b636a71130f60e01b60005260008051602061558f833981519152600452600160245260446000fd5b604051606091906135cf8382611d0f565b6002815291601f19018260005b8281106135e857505050565b8060606020809385010152016135dc565b6040805190919061360a8382611d0f565b6001815291601f19018260005b82811061362357505050565b806060602080938501015201613617565b8051600110156120c15760400190565b8051156120c15760200190565b80518210156120c15760209160051b010190565b90600182811c92168015613695575b602083101461367f57565b634e487b7160e01b600052602260045260246000fd5b91607f1691613674565b90604051918260008254926136b384613665565b808452936001811690811561371f57506001146136d8575b50611eab92500383611d0f565b90506000929192526020600020906000915b818310613703575050906020611eab92820101386136cb565b60209193508060019154838589010152019101909184926136ea565b905060209250611eab94915060ff191682840152151560051b820101386136cb565b9080602083519182815201916020808360051b8301019401926000915b83831061376d57505050505090565b909192939460208061378b600193601f198682030187528951610984565b9701930193019193929061375e565b92949391608084526000918054906137b182613665565b9182608088015260018116908160001461383757506001146137f8575b5050946127c1826104f69697866137ea95036020880152610984565b916060818403910152613741565b600090815260208120929350915b818310613820575050830160a001906127c16104f66137ce565b805460a08488010152602090920191600101613806565b60ff191660a08881019190915292151560051b870190920193506127c191506104f690506137ce565b81519192916139455760209192506103cc61389f61387c6135f9565b945b604051633b325fc560e01b8682015260248101919091529182906044820190565b6138a884613644565b526138b283613644565b506138bb61295b565b60038101546138d2906001600160a01b0316610672565b9060006138f860058301926138f260066138eb8661369f565b920161369f565b90614a73565b9161391960405197889687958694631f3f66f760e21b86526004860161379a565b03925af180156107825761392a5750565b6139429060203d60201161077b5761076d8183611d0f565b50565b6103cc61389f6020936103cc61397861395c6135be565b9760405192839163f23f601b60e01b8a840152602483016109a9565b61398187613634565b5261398b86613634565b5061387e565b90916121b46104f69360408452604084019061108e565b6139b061295b565b6139ba60016122c0565b916139c86040519384611d0f565b600183526139d660016122c0565b6020840190601f19013682378351156120c1575260040154613a00906001600160a01b0316610672565b91823b1561024757613a2c926000928360405180968195829463a4362b3960e01b845260048401613991565b03925af1801561078257613a3d5750565b80610c416000611eab93611d0f565b90613a5682611f2b565b613a636040519182611d0f565b8281528092613a74601f1991611f2b565b0190602036910137565b8060009172184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b821015613bb3575b806d04ee2d6d415b85acef8100000000600a921015613b97575b662386f26fc10000811015613b82575b6305f5e100811015613b70575b612710811015613b60575b6064811015613b51575b1015613b46575b613b316021613b0560018501613a4c565b938401015b60001901916f181899199a1a9b1b9c1cb0b131b232b360811b600a82061a8353600a900490565b8015613b4157613b319091613b0a565b505090565b600190910190613af4565b60029060649004930192613aed565b6004906127109004930192613ae3565b6008906305f5e1009004930192613ad8565b601090662386f26fc100009004930192613acb565b6020906d04ee2d6d415b85acef81000000009004930192613abb565b506040915072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b8104613aa1565b428110613be05750565b633bd4ab4160e01b6000526004524260245260446000fd5b6002613c0261295b565b01908060005281602052600160ff60406000205416151514613c36576000526020526040600020600160ff19825416179055565b632472ad4160e21b60005260045260246000fd5b90613c54826122c0565b613c616040519182611d0f565b8281528092613a74601f19916122c0565b60405190613c81604083611d0f565b601f82527f4e616d65496e666f28737472696e6720736c642c737472696e6720746c6429006020830152565b613cb5613c72565b604051613ce360208281613cd28183019687815193849201610961565b81010301601f198101835282611d0f565b51902090565b604051613cf7608082611d0f565b605a815260208101907f546f6b656e697a6174696f6e566f7563686572284e616d65496e666f5b5d206e82527f616d65732c75696e74323536206e6f6e63652c75696e7432353620657870697260408201527f657341742c61646472657373206f776e657241646472657373290000000000006060820152613ce3613d7a613c72565b916020604051938492613d9583850197889251928391610961565b8301613da982518093858085019101610961565b010103601f198101835282611d0f565b805160209091019060005b818110613dd15750505090565b8251845260209384019390920191600101613dc4565b613dfb613df48280612054565b9050613c4a565b9060005b613e098280612054565b9050811015613e7c5780613e26610f75600193610f6f8680612054565b613e2e613cad565b815180516020918201209281015180519082012060408051928301938452820193909352606081019290925290613e6881608081016103cc565b519020613e758286613651565b5201613dff565b50613ce3613e88613ce9565b92604051613e9e816103cc602082018095613db9565b5190206103cc602084013593613ebb60606040830135920161204a565b604080516020810198895290810194909452606084019590955260808301526001600160a01b0390931660a082015291829060c0820190565b604290613eff61535a565b613f076153c4565b6040519060208201927f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8452604083015260608201524660808201523060a082015260a08152613f5860c082611d0f565b519020906040519161190160f01b8352600283015260228201522090565b613f88613f8e92613f979436916120c6565b90614caf565b90929192614cf5565b613fba81613fa361295b565b9060018060a01b0316600052602052604060002090565b54908115613fc6575090565b63bf18af4360e01b60009081526001600160a01b0391909116600452602490fd5b613ff081614d77565b15613ff85750565b604051630a9644cf60e01b8152602060048201529081906107e7906024830190610984565b61402681615197565b1561402e5750565b604051637f19f48d60e01b8152602060048201529081906107e7906024830190610984565b8051600110156120c15760210190565b8051600210156120c15760220190565b8051600310156120c15760230190565b9081518110156120c1570160200190565b8015612949576000190190565b6000815191825b6140b3575b50905090565b6000198301838111612949575b80151580614115575b156140dc576140d790614094565b6140c0565b916140f56140ee846140fb949661294e565b8486614b42565b90614bce565b9080156000036140ad5761410e9061293a565b91826140a8565b50601760f91b6001600160f81b03196141476141396141338561293a565b86614083565b516001600160f81b03191690565b1614156140c9565b6040519061415e604083611d0f565b600682526565697031353560d01b6020830152565b6001600160a01b03168061419761419261418d6014612a68565b614337565b613a4c565b9160306141a384613644565b5360786141af84614053565b536141c26141bd6014612a68565b614345565b60018111614277575061425e57506028602282012060601c6029905b600182116141eb57505090565b614209816007600f61420f9416118061423c575b6142155760041c90565b91614094565b906141de565b600160fd1b6142276141398688614083565b1860001a6142358587614083565b5360041c90565b50606060ff614257614251614139888a614083565b60f81c90565b16116141ff565b63e22e27eb60e01b600052600452601460245260446000fd5b90600f811660108110156120c1576142ad91614209916f181899199a1a9b1b9c1cb0b131b232b360811b901a6142358588614083565b6141c2565b604051906142c1604083611d0f565b6004825263444f4d4160e01b6020830152565b604051906142e3604083611d0f565b60018252603160f81b6020830152565b6142fc81615285565b156143045750565b60405163d4c19e3b60e01b8152602060048201529081906107e7906024830190610984565b90601f820180921161294957565b906002820180921161294957565b906001820180921161294957565b9190820180921161294957565b805180156143fb57805b614372575090565b601760f91b6143966143896141396141338561293a565b6001600160f81b03191690565b146143aa576143a490614094565b8061436a565b6143ba614192828495945161294e565b9060005b82518110156143f457806143e06141396143da60019486614353565b88614083565b60001a6143ed8286614083565b53016143be565b5090925050565b5090565b9161440861295b565b61441461063a85612b8c565b614568576012015461442e906001600160a01b0316610672565b6040516364f1992360e01b815260048101859052602081602481855afa90811561078257600091614549575b5061453357604051633329dc9d60e11b81526004810185905290602090829060249082905afa90811561078257600091614514575b504281106144fb57506144ac916144a79136916120c6565b613fe7565b6002808316036144e25781806144c183614993565b16036144cb575050565b631d11134b60e01b60005260045260245260446000fd5b631d11134b60e01b600052600452600260245260446000fd5b636581bb3f60e11b600052600484905260245260446000fd5b61452d915060203d60201161077b5761076d8183611d0f565b3861448f565b63a9de8c6f60e01b600052600484905260246000fd5b614562915060203d6020116107c5576107b78183611d0f565b3861445a565b635a55e97160e11b600052600484905260246000fd5b61458a6020820161287c565b600381101561287757613ce36040516145a460a082611d0f565b6062815261742960f01b608060208301927f50726f6f664f66436f6e7461637473566f75636865722875696e74323536207284527f656769737472616e7448616e646c652c75696e74382070726f6f66536f75726360408201527f652c75696e74323536206e6f6e63652c75696e743235362065787069726573416060820152015260405161463760208201809361093b565b60628152614646608282611d0f565b519020604080516020810192835285358183015260ff9094166060858101919091529085013560808501529093013560a08301528160c081016103cc565b613f88613f8e926146969436916120c6565b60ff6146be8260016146a661295b565b019060018060a01b0316600052602052604060002090565b541615613fc65750565b906146d161295b565b906146de61063a84612b8c565b61483c5760126146f5926106646106729386612c8b565b6040516364f1992360e01b815260048101839052909190602081602481865afa9081156107825760009161481d575b5061480957604051633329dc9d60e11b815260048101829052602081602481865afa908115610782576000916147ea575b504281106147d0575060405163a34e303d60e01b81526004810182905291602090839060249082905afa918215610782576000926147af575b5081614798575050565b63f9f7458560e01b60005260045260245260446000fd5b6147c991925060203d60201161077b5761076d8183611d0f565b903861478e565b636581bb3f60e11b60005260049190915260245260446000fd5b614803915060203d60201161077b5761076d8183611d0f565b38614755565b63a9de8c6f60e01b60005260045260246000fd5b614836915060203d6020116107c5576107b78183611d0f565b38614724565b635a55e97160e11b600052600483905260246000fd5b9061485b61295b565b9061486583612b8c565b61497d576148739083612c8b565b60028061487f84614993565b1603614962576004015461489b906001600160a01b0316610672565b6040516364f1992360e01b815260048101839052909190602081602481865afa90811561078257600091614943575b5061480957604051633329dc9d60e11b81526004810182905291602090839060249082905afa91821561078257600092614922575b5042821061490b575050565b636581bb3f60e11b60005260045260245260446000fd5b61493c91925060203d60201161077b5761076d8183611d0f565b90386148ff565b61495c915060203d6020116107c5576107b78183611d0f565b386148ca565b631d11134b60e01b6000526004829052600260245260446000fd5b630bfffc8160e31b600052600483905260246000fd5b61499b61295b565b6149a482612b8c565b15614a1557601201546149e6916020916149c6906001600160a01b0316610672565b60405180809581946332b9c2b360e21b8352600483019190602083019252565b03915afa908115610782576000916149fc575090565b6104f6915060203d60201161077b5761076d8183611d0f565b600401546149e6916020916149c6906001600160a01b0316610672565b6001600160401b03811660018111156143fb57600019016001600160401b038111612949576001600160401b031669ffff0000000000000000919091161790565b6001611eab9193929360206040519582614a968894518092858088019101610961565b8301601d60f91b83820152614ab48251809385602185019101610961565b01010301601f198101845283611d0f565b15614acc57565b60405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b6044820152606490fd5b15614b0957565b60405162461bcd60e51b8152602060048201526011602482015270736c6963655f6f75744f66426f756e647360781b6044820152606490fd5b91614b5781614b5081614329565b1015614ac5565b614b6d8351614b668385614353565b1115614b02565b80614b8657505050604051600081526020810160405290565b60405192601f821692831560051b80858701019484860193010101905b808410614bbb5750508252601f01601f191660405290565b9092602080918551815201930190614ba3565b90604051614bec60208281613cd28183019687815193849201610961565b5190206040519060208201928352604082015260408152613ce3606082611d0f565b9081614c1f61419261418d84612a68565b926030614c2b85613644565b536078614c3785614053565b53614c446141bd84612a68565b60018111614c6d5750614c5657505090565b63e22e27eb60e01b60005260045260245260446000fd5b90600f81169060108210156120c157614caa916f181899199a1a9b1b9c1cb0b131b232b360811b901a614ca08488614083565b5360041c91614094565b614c44565b8151919060418303614ce057614cd992506020820151906060604084015193015160001a90615409565b9192909190565b505060009160029190565b6004111561287757565b614cfe81614ceb565b80614d07575050565b614d1081614ceb565b60018103614d295763f645eedf60e01b60005260046000fd5b614d3281614ceb565b60028103614d4f575063fce698f760e01b60005260045260246000fd5b80614d5b600392614ceb565b14614d635750565b6335e2f38360e21b60005260045260246000fd5b805180158015614f71575b612c6557600481119081614f4d575b81614f29575b81614f05575b81614ee1575b60005b818110614db65750505050600190565b614dc66143896141398387614083565b606160f81b81101580614ed3575b8015614eb6575b8015614ea9575b8015614ea1575b15614e795781158015614e90575b80614e83575b614e7957600282149081614e6b575b5080614e5a575b80614e35575b614e26575b600101614da6565b82614e1e575b50505050600090565b50602d60f81b6001600160f81b0319614e536141396143da85614345565b1614614e19565b5081614e6582614345565b10614e13565b602d60f81b14905038614e0c565b5050505050600090565b50602d60f81b8114614dfd565b50614e9a8361293a565b8214614df7565b506000614de9565b50602d60f81b8114614de2565b50600360fc1b8110801590614ddb5750603960f81b811115614ddb565b50603d60f91b811115614dd4565b9050602d60f81b6001600160f81b0319614efd61413985614073565b161490614da3565b9050602d60f81b6001600160f81b0319614f2161413985614063565b161490614d9d565b9050603760f91b6001600160f81b0319614f4561413985614053565b161490614d97565b9050600f60fb1b6001600160f81b0319614f6961413985613644565b161490614d91565b50603f8111614d82565b8051918215801561518d575b61518557600483119182615161575b8261513d575b82615119575b826150f5575b60005b848110614fbc575050505050600190565b614fcc6143896141398385614083565b606160f81b811015806150e7575b80156150ca575b80156150bd575b80156150a8575b1561507f5781158015615097575b8061508a575b61507f57600282149081615071575b5080615060575b8061503b575b61502c575b600101614fab565b83615024575050505050600090565b50602d60f81b6001600160f81b031961505961413961413385614345565b161461501f565b508461506b82614345565b10615019565b602d60f81b14905038615012565b505050505050600090565b50602d60f81b8114615003565b506150a18661293a565b8214614ffd565b50838015614fef5750605f60f81b8114614fef565b50602d60f81b8114614fe8565b50600360fc1b8110801590614fe15750603960f81b811115614fe1565b50603d60f91b811115614fda565b9150602d60f81b6001600160f81b031961511161413985614073565b161491614fa8565b9150602d60f81b6001600160f81b031961513561413985614063565b161491614fa2565b9150603760f91b6001600160f81b031961515961413985614053565b161491614f9c565b9150600f60fb1b6001600160f81b031961517d61413985613644565b161491614f96565b505050600090565b50603f8311614f87565b8051908115801561527b575b612c6557601760f91b6001600160f81b03196151c161413984613644565b16148015615257575b612c655760009060005b83811061520257506151ef6151f49361063a93600093615492565b614f7b565b6151fd57600190565b600090565b601760f91b6152176143896141398486614083565b14615225575b6001016151d4565b91808314614e2c5761063a60006151ef856152409486615492565b61518557600161524f83614345565b92905061521d565b50601760f91b61527561438961413961526f8661293a565b85614083565b146151ca565b5060fd82116151a3565b80519081158015615350575b612c6557601760f91b6001600160f81b03196152af61413984613644565b16148015615332575b612c655760009060005b8381106152dd57506151ef6151f49361063a93600193615492565b601760f91b6152f26143896141398486614083565b14615300575b6001016152c2565b91808314614e2c5761063a60016151ef8561531b9486615492565b61518557600161532a83614345565b9290506152f8565b50601760f91b61534a61438961413961526f8661293a565b146152b8565b5060fd8211615291565b6153626142b2565b8051908115615372576020012090565b50507fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10054801561539f5790565b507fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47090565b6153cc6142d4565b80519081156153dc576020012090565b50507fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10154801561539f5790565b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08411615486579160209360809260ff60009560405194855216868401526040830152606082015282805260015afa15610782576000516001600160a01b0381161561547a5790600090600090565b50600090600190600090565b50505060009160039190565b91818103818111612949576154a690613a4c565b92825b8281106154b7575050505090565b6001600160f81b03196154ca8284614083565b511690848103818111612949576154e760019360001a9188614083565b53016154a956fe41274d8380e68ced671e39b24773a9a28190d6ae058a9a0a57272be30902055c4a2248149817dde7dfda12ec5979900c9a0aba1d5ecdd54d38762a0cc40039961ee9a1c13af222b9e4d98567d0238a554fbf843b89f4720f3aa34731720cdffa08fb31c3e81624356c3314088aa971b73bcc82d22bc3e3b184b4593077ae3278add9df460364fdc361c17268aded1a218d04bba51bfce5119914d05b8bd10ababbecd033b99e35d8972b316640f0b13a68063e02065b4e964541d3498c941657272cf3b1b4a64c268809c75d1290f1c3ac8c1e2df8ebefee53369069b86f0649a19646f5f88bada8fee59cab6efcdf841c1797d1344642f9af3a4e0415431506a26469706673582212205d03e947712de15fd7c777d5d9ce651c353462302ef3f97fcafe502bcfa078fc64736f6c634300081c0033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.