Overview
ETH Balance
0 ETH
ETH Value
$0.00More Info
Private Name Tags
ContractCreator
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Source Code Verified (Exact Match)
Contract Name:
CommentsImpl
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
Yes with 1000000 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol"; import {IHasContractName} from "@zoralabs/shared-contracts/interfaces/IContractMetadata.sol"; import {ContractVersionBase} from "./version/ContractVersionBase.sol"; import {IZoraCreator1155} from "./interfaces/IZoraCreator1155.sol"; import {IComments} from "./interfaces/IComments.sol"; import {ICoinComments} from "./interfaces/ICoinComments.sol"; import {IProtocolRewards} from "@zoralabs/shared-contracts/interfaces/IProtocolRewards.sol"; import {UnorderedNoncesUpgradeable} from "@zoralabs/shared-contracts/utils/UnorderedNoncesUpgradeable.sol"; import {EIP712UpgradeableWithChainId} from "./utils/EIP712UpgradeableWithChainId.sol"; import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; import {IMultiOwnable} from "./interfaces/IMultiOwnable.sol"; import {CommentsImplConstants} from "./CommentsImplConstants.sol"; /// @title CommentsImpl /// @notice Contract for comments and sparking (liking with value) Zora 1155 posts. /// @dev Implements comment creation, sparking, and backfilling functionality. Implementation contract /// meant to be used with a UUPS upgradeable proxy contract. /// @author oveddan / IsabellaSmallcombe contract CommentsImpl is IComments, AccessControlUpgradeable, UUPSUpgradeable, ContractVersionBase, EIP712UpgradeableWithChainId, UnorderedNoncesUpgradeable, CommentsImplConstants, IHasContractName { /// @notice keccak256(abi.encode(uint256(keccak256("comments.storage.CommentsStorage")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant COMMENTS_STORAGE_LOCATION = 0x9e5d0d3a4c7e8d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d00; /// @notice the spark value to comment uint256 public immutable sparkValue; /// @notice the address of the protocol rewards contract IProtocolRewards public immutable protocolRewards; /// @notice account that receives rewards Zora Rewards for from a portion of the sparks value address immutable zoraRecipient; /// @custom:storage-location erc7201:comments.storage.CommentsStorage struct CommentsStorage { mapping(bytes32 => Comment) comments; // gap that held old zora recipient. address __gap; // Global autoincrementing nonce uint256 nonce; } function _getCommentsStorage() private pure returns (CommentsStorage storage $) { assembly { $.slot := COMMENTS_STORAGE_LOCATION } } function comments(bytes32 commentId) internal view returns (Comment storage) { return _getCommentsStorage().comments[commentId]; } /// @notice Returns the total number of sparks a given comment has received /// @param commentIdentifier The identifier of the comment /// @return The total number of sparks a comment has received function commentSparksQuantity(CommentIdentifier memory commentIdentifier) external view returns (uint256) { return comments(hashCommentIdentifier(commentIdentifier)).totalSparks; } /// @notice Returns the next nonce for comment creation /// @return The next nonce function nextNonce() external view returns (bytes32) { return bytes32(_getCommentsStorage().nonce); } /// @notice Returns the implementation address of the contract /// @return The implementation address function implementation() external view returns (address) { return ERC1967Utils.getImplementation(); } /// @notice Contract constructor /// @param _sparkValue The value of a spark /// @param _protocolRewards The address of the protocol rewards contract /// @param _zoraRecipient The address of the zora recipient constructor(uint256 _sparkValue, address _protocolRewards, address _zoraRecipient) { if (_protocolRewards == address(0) || _zoraRecipient == address(0)) { revert AddressZero(); } _disableInitializers(); sparkValue = _sparkValue; protocolRewards = IProtocolRewards(_protocolRewards); zoraRecipient = _zoraRecipient; } /// @notice Initializes the contract with default admin, backfiller, and delegate commenters /// @param defaultAdmin The address of the default admin /// @param backfiller The address of the backfiller /// @param delegateCommenters The addresses of the delegate commenters function initialize(address defaultAdmin, address backfiller, address[] calldata delegateCommenters) public initializer { if (defaultAdmin == address(0) || backfiller == address(0)) { revert AddressZero(); } __AccessControl_init(); __UUPSUpgradeable_init(); __EIP712_init(DOMAIN_NAME, DOMAIN_VERSION); _grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin); _grantRole(BACKFILLER_ROLE, backfiller); for (uint256 i = 0; i < delegateCommenters.length; i++) { _grantRole(DELEGATE_COMMENTER, delegateCommenters[i]); } } /// @notice Hashes a comment identifier to generate a unique ID /// @param commentIdentifier The comment identifier to hash /// @return The hashed comment identifier function hashCommentIdentifier(CommentIdentifier memory commentIdentifier) public pure returns (bytes32) { return keccak256(abi.encode(commentIdentifier)); } /// @notice Hashes a comment identifier and checks if a comment exists with that id /// @param commentIdentifier The comment identifier to check /// @return commentId The hashed comment identifier /// @return exists Whether the comment exists function hashAndCheckCommentExists(CommentIdentifier memory commentIdentifier) public view returns (bytes32 commentId, bool exists) { commentId = hashCommentIdentifier(commentIdentifier); exists = comments(commentId).exists; } /// @notice Validates that a comment exists and returns its ID /// @param commentIdentifier The comment identifier to validate /// @return commentId The hashed comment identifier function hashAndValidateCommentExists(CommentIdentifier memory commentIdentifier) public view returns (bytes32 commentId) { bool exists; (commentId, exists) = hashAndCheckCommentExists(commentIdentifier); if (!exists) { revert CommentDoesntExist(); } } /// @notice Creates a new comment. Equivalant sparks value in eth must be sent with the transaction. Must be a holder or creator of the referenced 1155 token. /// If not the owner, must send 1 spark. /// @param contractAddress The address of the contract /// @param tokenId The token ID /// @param commenter The address of the commenter /// @param text The text content of the comment /// @param replyTo The identifier of the comment being replied to (if any) /// @param commenterSmartWallet If the commenter has a smart wallet, the smart wallet, which can checked to be the owner or creator of the 1155 token /// @param referrer The address of the referrer (if any) /// @return commentIdentifier The identifier of the created comment, including the nonce function comment( address commenter, address contractAddress, uint256 tokenId, string calldata text, CommentIdentifier calldata replyTo, address commenterSmartWallet, address referrer ) external payable returns (CommentIdentifier memory commentIdentifier) { uint256 sparksQuantity = _getAndValidateSingleSparkQuantityFromValue(msg.value); commentIdentifier = _createCommentIdentifier(contractAddress, tokenId, commenter); _comment({ commenter: msg.sender, commentIdentifier: commentIdentifier, text: text, sparksQuantity: sparksQuantity, replyTo: replyTo, commenterSmartWallet: commenterSmartWallet, referrer: referrer, mustSendAtLeastOneSpark: true }); } // gets the sparks quantity from the value sent with the transaction, // ensuring that at most 1 spark is sent. function _getAndValidateSingleSparkQuantityFromValue(uint256 value) internal view returns (uint256) { if (value == 0) { return 0; } if (value != sparkValue) { revert IncorrectETHAmountForSparks(value, sparkValue); } return 1; } // Allows another contract to call this function to signify a caller commented, and is trusted // to provide who the original commenter was. Allows no sparks to be sent. /// @notice Allows another contract to delegate comment creation on behalf of a user /// @param commenter The address of the commenter /// @param contractAddress The address of the contract /// @param tokenId The token ID /// @param text The text content of the comment /// @param replyTo The identifier of the comment being replied to (if any) /// @param referrer The address of the referrer (if any) /// @param commenterSmartWalletOwner If the commenter has a smart wallet, the smart wallet owner address /// @return commentIdentifier The identifier of the created comment, including the nonce function delegateComment( address commenter, address contractAddress, uint256 tokenId, string calldata text, CommentIdentifier calldata replyTo, address commenterSmartWalletOwner, address referrer ) external payable onlyRole(DELEGATE_COMMENTER) returns (CommentIdentifier memory commentIdentifier, bytes32 commentId) { uint256 sparksQuantity = _getAndValidateSingleSparkQuantityFromValue(msg.value); commentIdentifier = _createCommentIdentifier(contractAddress, tokenId, commenter); commentId = _comment({ commenter: commentIdentifier.commenter, commentIdentifier: commentIdentifier, text: text, sparksQuantity: sparksQuantity, replyTo: replyTo, commenterSmartWallet: commenterSmartWalletOwner, referrer: referrer, mustSendAtLeastOneSpark: false }); } function _createCommentIdentifier(address contractAddress, uint256 tokenId, address commenter) private returns (CommentIdentifier memory) { CommentsStorage storage $ = _getCommentsStorage(); return CommentIdentifier({commenter: commenter, contractAddress: contractAddress, tokenId: tokenId, nonce: bytes32($.nonce++)}); } function _comment( address commenter, CommentIdentifier memory commentIdentifier, string memory text, uint256 sparksQuantity, CommentIdentifier memory replyTo, address commenterSmartWallet, address referrer, bool mustSendAtLeastOneSpark ) internal returns (bytes32) { if (commentIdentifier.commenter != commenter) { revert CommenterMismatch(commentIdentifier.commenter, commenter); } (bytes32 commentId, bytes32 replyToId) = _validateComment( commentIdentifier, replyTo, text, sparksQuantity, commenterSmartWallet, mustSendAtLeastOneSpark ); _saveCommentAndTransferSparks(commentId, commentIdentifier, text, sparksQuantity, replyToId, replyTo, block.timestamp, referrer); return commentId; } function _validateIdentifiersMatch(CommentIdentifier memory commentIdentifier, CommentIdentifier memory replyTo) internal pure { if (commentIdentifier.contractAddress != replyTo.contractAddress || commentIdentifier.tokenId != replyTo.tokenId) { revert CommentAddressOrTokenIdsDoNotMatch(commentIdentifier.contractAddress, commentIdentifier.tokenId, replyTo.contractAddress, replyTo.tokenId); } } function _validateComment( CommentIdentifier memory commentIdentifier, CommentIdentifier memory replyTo, string memory text, uint256 sparksQuantity, address commenterSmartWallet, bool mustSendAtLeastOneSpark ) internal view returns (bytes32 commentId, bytes32 replyToId) { // verify that the commenter specified in the identifier is the one expected commentId = hashCommentIdentifier(commentIdentifier); if (replyTo.commenter != address(0)) { replyToId = hashAndValidateCommentExists(replyTo); _validateIdentifiersMatch(commentIdentifier, replyTo); } if (bytes(text).length == 0) { revert EmptyComment(); } _validateCommenterAndSparksQuantity(commentIdentifier, sparksQuantity, mustSendAtLeastOneSpark, commenterSmartWallet); } function _validateCommenterAndSparksQuantity( CommentIdentifier memory commentIdentifier, uint256 sparksQuantity, bool mustSendAtLeastOneSpark, address commenterSmartWallet ) internal view { if (commenterSmartWallet != address(0)) { if (commenterSmartWallet.code.length == 0) { revert NotSmartWallet(); } // check if the commenter is a smart wallet owner if (!IMultiOwnable(commenterSmartWallet).isOwnerAddress(commentIdentifier.commenter)) { revert NotSmartWalletOwner(); } } // check that the commenter or smart wallet is a token admin - if they are, then they can comment for free if ( _accountOrSmartWalletIsTokenAdmin(commentIdentifier.contractAddress, commentIdentifier.tokenId, commentIdentifier.commenter, commenterSmartWallet) ) { return; } // if they aren't, commenter or smart wallet must be a token holder, and have included at least 1 spark if ( !_accountOrSmartWalletIsTokenHolder(commentIdentifier.contractAddress, commentIdentifier.tokenId, commentIdentifier.commenter, commenterSmartWallet) ) { revert NotTokenHolderOrAdmin(); } if (mustSendAtLeastOneSpark && sparksQuantity == 0) { revert MustSendAtLeastOneSpark(); } } function _getRewardDeposits( address sparksRecipient, address referrer, uint256 sparksValue ) internal view returns (address[] memory, uint256[] memory, bytes4[] memory) { uint256 recipientCount = referrer != address(0) ? 3 : 2; address[] memory recipients = new address[](recipientCount); uint256[] memory amounts = new uint256[](recipientCount); bytes4[] memory reasons = new bytes4[](recipientCount); if (referrer != address(0)) { uint256 zoraReward = (ZORA_REWARD_PCT * sparksValue) / BPS_TO_PERCENT_2_DECIMAL_PERCISION; recipients[0] = zoraRecipient; amounts[0] = zoraReward; reasons[0] = ZORA_REWARD_REASON; uint256 referrerReward = (REFERRER_REWARD_PCT * sparksValue) / BPS_TO_PERCENT_2_DECIMAL_PERCISION; recipients[1] = referrer; amounts[1] = referrerReward; reasons[1] = REFERRER_REWARD_REASON; uint256 sparksRecipientReward = sparksValue - zoraReward - referrerReward; recipients[2] = sparksRecipient; amounts[2] = sparksRecipientReward; reasons[2] = SPARKS_RECIPIENT_REWARD_REASON; } else { uint256 zoraRewardNoReferrer = (ZORA_REWARD_NO_REFERRER_PCT * sparksValue) / BPS_TO_PERCENT_2_DECIMAL_PERCISION; recipients[0] = zoraRecipient; amounts[0] = zoraRewardNoReferrer; reasons[0] = ZORA_REWARD_REASON; uint256 sparkRecipientReward = sparksValue - zoraRewardNoReferrer; recipients[1] = sparksRecipient; amounts[1] = sparkRecipientReward; reasons[1] = SPARKS_RECIPIENT_REWARD_REASON; } return (recipients, amounts, reasons); } function _transferSparksValueToRecipient(address sparksRecipient, address referrer, uint256 sparksValue, string memory depositBatchComment) internal { (address[] memory recipients, uint256[] memory amounts, bytes4[] memory reasons) = _getRewardDeposits(sparksRecipient, referrer, sparksValue); protocolRewards.depositBatch{value: sparksValue}(recipients, amounts, reasons, depositBatchComment); } function _accountOrSmartWalletIsTokenAdmin(address contractAddress, uint256 tokenId, address user, address smartWallet) internal view returns (bool) { bool isCoin = _isCoinComment(contractAddress, tokenId); if (isCoin) { return ICoinComments(contractAddress).isOwner(user) || (smartWallet != address(0) && ICoinComments(contractAddress).isOwner(smartWallet)); } else { return _isTokenAdmin(contractAddress, tokenId, user) || (smartWallet != address(0) && _isTokenAdmin(contractAddress, tokenId, smartWallet)); } } function _accountOrSmartWalletIsTokenHolder(address contractAddress, uint256 tokenId, address user, address smartWallet) internal view returns (bool) { bool isCoin = _isCoinComment(contractAddress, tokenId); if (isCoin) { return ICoinComments(contractAddress).balanceOf(user) >= 1e18 || (smartWallet != address(0) && ICoinComments(contractAddress).balanceOf(smartWallet) >= 1e18); } else { return _isTokenHolder(contractAddress, tokenId, user) || (smartWallet != address(0) && _isTokenHolder(contractAddress, tokenId, smartWallet)); } } function _isTokenAdmin(address contractAddress, uint256 tokenId, address user) internal view returns (bool) { try IZoraCreator1155(contractAddress).isAdminOrRole(user, tokenId, PERMISSION_BIT_ADMIN) returns (bool isAdmin) { return isAdmin; } catch { return false; } } function _isTokenHolder(address contractAddress, uint256 tokenId, address user) internal view returns (bool) { try IERC1155(contractAddress).balanceOf(user, tokenId) returns (uint256 balance) { return balance > 0; } catch { return false; } } function _isCoinComment(address contractAddress, uint256 tokenId) internal view returns (bool) { return tokenId == 0 && IERC165(contractAddress).supportsInterface(type(ICoinComments).interfaceId); } function _getCommentSparksRecipient(CommentIdentifier memory commentIdentifier, CommentIdentifier memory replyTo) internal view returns (address) { // if there is no reply to, then creator reward recipient of the 1155 token gets the sparks // otherwise, the replay to commenter gets the sparks if (replyTo.commenter == address(0)) { return _getCreatorRewardRecipient(commentIdentifier); } return replyTo.commenter; } // executes the comment. assumes sparks have already been transferred to recipient, and data has been validated // assume that the commentId and replyToId are valid function _saveCommentAndTransferSparks( bytes32 commentId, CommentIdentifier memory commentIdentifier, string memory text, uint256 sparksQuantity, bytes32 replyToId, CommentIdentifier memory replyToIdentifier, uint256 timestamp, address referrer ) internal { _saveComment(commentId, commentIdentifier, text, sparksQuantity, replyToId, replyToIdentifier, timestamp, referrer); string memory depositBatchComment = "Comment"; // update reason if replying to a comment if (replyToId != 0) { depositBatchComment = "Comment Reply"; } if (sparksQuantity > 0) { address sparksRecipient = _getCommentSparksRecipient(commentIdentifier, replyToIdentifier); _transferSparksValueToRecipient(sparksRecipient, referrer, sparksQuantity * sparkValue, depositBatchComment); } } function _saveComment( bytes32 commentId, CommentIdentifier memory commentIdentifier, string memory text, uint256 sparksQuantity, bytes32 replyToId, CommentIdentifier memory replyToIdentifier, uint256 timestamp, address referrer ) internal { if (comments(commentId).exists) { revert DuplicateComment(commentId); } comments(commentId).exists = true; emit Commented(commentId, commentIdentifier, replyToId, replyToIdentifier, sparksQuantity, text, timestamp, referrer); } /// @notice Sparks a comment. Equivalant sparks value in eth to sparksQuantity must be sent with the transaction. Sparking a comment is /// similar to liking it, except it is liked with the value of sparks attached. The spark value gets sent to the commenter, with a fee taken out. /// @param commentIdentifier The identifier of the comment to spark /// @param sparksQuantity The quantity of sparks to send /// @param referrer The referrer of the comment function sparkComment(CommentIdentifier calldata commentIdentifier, uint256 sparksQuantity, address referrer) public payable { if (sparksQuantity == 0) { revert MustSendAtLeastOneSpark(); } _validateSparksQuantityMatchesValue(sparksQuantity, msg.value); _sparkComment(commentIdentifier, msg.sender, sparksQuantity, referrer); } function _validateSparksQuantityMatchesValue(uint256 sparksQuantity, uint256 value) internal view { if (value != sparksQuantity * sparkValue) { revert IncorrectETHAmountForSparks(value, sparksQuantity * sparkValue); } } function _sparkComment(CommentIdentifier memory commentIdentifier, address sparker, uint256 sparksQuantity, address referrer) internal { if (sparker == commentIdentifier.commenter) { revert CannotSparkOwnComment(); } bytes32 commentId = hashCommentIdentifier(commentIdentifier); if (!comments(commentId).exists) { revert CommentDoesntExist(); } comments(commentId).totalSparks += uint256(sparksQuantity); _transferSparksValueToRecipient(commentIdentifier.commenter, referrer, sparksQuantity * sparkValue, "Sparked Comment"); emit SparkedComment(commentId, commentIdentifier, sparksQuantity, sparker, block.timestamp, referrer); } function _hashCommentIdentifier(CommentIdentifier memory commentIdentifier) internal pure returns (bytes32) { return keccak256( abi.encode( COMMENT_IDENTIFIER_DOMAIN, commentIdentifier.contractAddress, commentIdentifier.tokenId, commentIdentifier.commenter, commentIdentifier.nonce ) ); } /// @notice Hashes a permit comment struct for signing /// @param permit The permit comment struct /// @return The hash to sign function hashPermitComment(PermitComment calldata permit) public view returns (bytes32) { bytes32 structHash = keccak256( abi.encode( PERMIT_COMMENT_DOMAIN, permit.contractAddress, permit.tokenId, permit.commenter, _hashCommentIdentifier(permit.replyTo), keccak256(bytes(permit.text)), permit.deadline, permit.nonce, permit.commenterSmartWallet, permit.referrer, permit.sourceChainId, permit.destinationChainId ) ); return _hashTypedDataV4(structHash, permit.sourceChainId); } function _validatePermit(bytes32 digest, bytes32 nonce, bytes calldata signature, address signer, uint256 deadline) internal { if (block.timestamp > deadline) { revert ERC2612ExpiredSignature(deadline); } _useCheckedNonce(signer, nonce); _validateSignerIsCommenter(digest, signature, signer); } /// @notice Creates a comment on behalf of another account using a signed message. Supports cross-chain permits /// by specifying the source and destination chain ids. The signature must be signed by the commenter on the source chain. /// @param permit The permit that was signed off-chain on the source chain /// @param signature The signature of the permit comment function permitComment(PermitComment calldata permit, bytes calldata signature) public payable { if (permit.destinationChainId != uint32(block.chainid)) { revert IncorrectDestinationChain(permit.destinationChainId); } bytes32 digest = hashPermitComment(permit); _validatePermit(digest, permit.nonce, signature, permit.commenter, permit.deadline); CommentIdentifier memory commentIdentifier = _createCommentIdentifier(permit.contractAddress, permit.tokenId, permit.commenter); uint256 sparksQuantity = _getAndValidateSingleSparkQuantityFromValue(msg.value); (bytes32 commentId, bytes32 replyToId) = _validateComment( commentIdentifier, permit.replyTo, permit.text, sparksQuantity, permit.commenterSmartWallet, true ); _saveCommentAndTransferSparks(commentId, commentIdentifier, permit.text, sparksQuantity, replyToId, permit.replyTo, block.timestamp, permit.referrer); } /// @notice Hashes a permit spark comment struct for signing /// @param permit The permit spark comment struct function hashPermitSparkComment(PermitSparkComment calldata permit) public view returns (bytes32) { bytes32 structHash = keccak256( abi.encode( PERMIT_SPARK_COMMENT_DOMAIN, _hashCommentIdentifier(permit.comment), permit.sparker, permit.sparksQuantity, permit.deadline, permit.nonce, permit.referrer, permit.sourceChainId, permit.destinationChainId ) ); return _hashTypedDataV4(structHash, permit.sourceChainId); } /// @notice Sparks a comment on behalf of another account using a signed message. Supports cross-chain permits /// by specifying the source and destination chain ids. The signature must be signed by the sparker on the source chain. /// @param permit The permit spark comment struct /// @param signature The signature of the permit. Must be signed by the sparker. function permitSparkComment(PermitSparkComment calldata permit, bytes calldata signature) public payable { if (permit.destinationChainId != uint32(block.chainid)) { revert IncorrectDestinationChain(permit.destinationChainId); } bytes32 digest = hashPermitSparkComment(permit); _validatePermit(digest, permit.nonce, signature, permit.sparker, permit.deadline); if (permit.sparksQuantity == 0) { revert MustSendAtLeastOneSpark(); } _validateSparksQuantityMatchesValue(permit.sparksQuantity, msg.value); _sparkComment(permit.comment, permit.sparker, permit.sparksQuantity, permit.referrer); } function _validateSignerIsCommenter(bytes32 digest, bytes calldata signature, address signer) internal view { if (!SignatureChecker.isValidSignatureNow(signer, digest, signature)) { revert InvalidSignature(); } } /// @notice Backfills comments created by other contracts. Only callable by an account with the backfiller role. /// @param commentIdentifiers Array of comment identifiers /// @param texts Array of comment texts /// @param timestamps Array of comment timestamps /// @param originalTransactionHashes Array of original transaction hashes function backfillBatchAddComment( CommentIdentifier[] calldata commentIdentifiers, string[] calldata texts, uint256[] calldata timestamps, bytes32[] calldata originalTransactionHashes ) public onlyRole(BACKFILLER_ROLE) { if (commentIdentifiers.length != texts.length || texts.length != timestamps.length || timestamps.length != originalTransactionHashes.length) { revert ArrayLengthMismatch(); } for (uint256 i = 0; i < commentIdentifiers.length; i++) { bytes32 commentId = hashCommentIdentifier(commentIdentifiers[i]); if (comments(commentId).exists) { revert DuplicateComment(commentId); } comments(commentId).exists = true; // create blank replyTo - assume that these were created without replyTo emit BackfilledComment(commentId, commentIdentifiers[i], texts[i], timestamps[i], originalTransactionHashes[i]); } } function _getCoinPayoutRecipient(address contractAddress, uint256 tokenId) internal view returns (address) { if (_isCoinComment(contractAddress, tokenId)) { try ICoinComments(contractAddress).payoutRecipient() returns (address payoutRecipient) { return payoutRecipient; } catch { return address(0); } } return address(0); } function _getFundsRecipient(address contractAddress) internal view returns (address) { try IZoraCreator1155(contractAddress).config() returns (address, uint96, address payable fundsRecipient, uint96, address, uint96) { if (fundsRecipient != address(0)) { return fundsRecipient; } } catch {} try IZoraCreator1155(contractAddress).owner() returns (address owner) { if (owner != address(0)) { return owner; } } catch {} return address(0); } function _tryGetCreatorRewardRecipient(CommentIdentifier memory commentIdentifier) internal view returns (address) { try IZoraCreator1155(commentIdentifier.contractAddress).getCreatorRewardRecipient(commentIdentifier.tokenId) returns (address creatorRecipient) { return creatorRecipient; } catch { return address(0); } } function _getCreatorRewardRecipient(CommentIdentifier memory commentIdentifier) internal view returns (address) { address payoutRecipient = _getCoinPayoutRecipient(commentIdentifier.contractAddress, commentIdentifier.tokenId); if (payoutRecipient != address(0)) { return payoutRecipient; } address creatorRecipient = _tryGetCreatorRewardRecipient(commentIdentifier); if (creatorRecipient != address(0)) { return creatorRecipient; } address fundsRecipient = _getFundsRecipient(commentIdentifier.contractAddress); if (fundsRecipient != address(0)) { return fundsRecipient; } revert NoFundsRecipient(); } /// @notice Returns the name of the contract /// @return The name of the contract function contractName() public pure returns (string memory) { return "Zora Comments"; } function _authorizeUpgrade(address newImplementation) internal override onlyRole(DEFAULT_ADMIN_ROLE) { // check that new implementation's contract name matches the current contract name if (!_equals(IHasContractName(newImplementation).contractName(), this.contractName())) { revert UpgradeToMismatchedContractName(this.contractName(), IHasContractName(newImplementation).contractName()); } } function _equals(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.0.0) (access/AccessControl.sol) pragma solidity ^0.8.20; import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol"; import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol"; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ```solidity * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ```solidity * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules} * to enforce additional security measures for this role. */ abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable { struct RoleData { mapping(address account => bool) hasRole; bytes32 adminRole; } bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /// @custom:storage-location erc7201:openzeppelin.storage.AccessControl struct AccessControlStorage { mapping(bytes32 role => RoleData) _roles; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800; function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) { assembly { $.slot := AccessControlStorageLocation } } /** * @dev Modifier that checks that an account has a specific role. Reverts * with an {AccessControlUnauthorizedAccount} error including the required role. */ modifier onlyRole(bytes32 role) { _checkRole(role); _; } function __AccessControl_init() internal onlyInitializing { } function __AccessControl_init_unchained() internal onlyInitializing { } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) public view virtual returns (bool) { AccessControlStorage storage $ = _getAccessControlStorage(); return $._roles[role].hasRole[account]; } /** * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()` * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier. */ function _checkRole(bytes32 role) internal view virtual { _checkRole(role, _msgSender()); } /** * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account` * is missing `role`. */ function _checkRole(bytes32 role, address account) internal view virtual { if (!hasRole(role, account)) { revert AccessControlUnauthorizedAccount(account, role); } } /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) { AccessControlStorage storage $ = _getAccessControlStorage(); return $._roles[role].adminRole; } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleGranted} event. */ function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) { _grantRole(role, account); } /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleRevoked} event. */ function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) { _revokeRole(role, account); } /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been revoked `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `callerConfirmation`. * * May emit a {RoleRevoked} event. */ function renounceRole(bytes32 role, address callerConfirmation) public virtual { if (callerConfirmation != _msgSender()) { revert AccessControlBadConfirmation(); } _revokeRole(role, callerConfirmation); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { AccessControlStorage storage $ = _getAccessControlStorage(); bytes32 previousAdminRole = getRoleAdmin(role); $._roles[role].adminRole = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /** * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted. * * Internal function without access restriction. * * May emit a {RoleGranted} event. */ function _grantRole(bytes32 role, address account) internal virtual returns (bool) { AccessControlStorage storage $ = _getAccessControlStorage(); if (!hasRole(role, account)) { $._roles[role].hasRole[account] = true; emit RoleGranted(role, account, _msgSender()); return true; } else { return false; } } /** * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked. * * Internal function without access restriction. * * May emit a {RoleRevoked} event. */ function _revokeRole(bytes32 role, address account) internal virtual returns (bool) { AccessControlStorage storage $ = _getAccessControlStorage(); if (hasRole(role, account)) { $._roles[role].hasRole[account] = false; emit RoleRevoked(role, account, _msgSender()); return true; } else { return false; } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/UUPSUpgradeable.sol) pragma solidity ^0.8.20; import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol"; import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; import {Initializable} from "./Initializable.sol"; /** * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy. * * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing * `UUPSUpgradeable` with a custom implementation of upgrades. * * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism. */ abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable { /// @custom:oz-upgrades-unsafe-allow state-variable-immutable address private immutable __self = address(this); /** * @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)` * and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called, * while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string. * If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function * during an upgrade. */ string public constant UPGRADE_INTERFACE_VERSION = "5.0.0"; /** * @dev The call is from an unauthorized context. */ error UUPSUnauthorizedCallContext(); /** * @dev The storage `slot` is unsupported as a UUID. */ error UUPSUnsupportedProxiableUUID(bytes32 slot); /** * @dev Check that the execution is being performed through a delegatecall call and that the execution context is * a proxy contract with an implementation (as defined in 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() { _checkProxy(); _; } /** * @dev Check that the execution is not being performed through a delegate call. This allows a function to be * callable on the implementing contract but not through proxies. */ modifier notDelegated() { _checkNotDelegated(); _; } function __UUPSUpgradeable_init() internal onlyInitializing { } function __UUPSUpgradeable_init_unchained() internal onlyInitializing { } /** * @dev Implementation of the 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 notDelegated returns (bytes32) { return ERC1967Utils.IMPLEMENTATION_SLOT; } /** * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call * encoded in `data`. * * Calls {_authorizeUpgrade}. * * Emits an {Upgraded} event. * * @custom:oz-upgrades-unsafe-allow-reachable delegatecall */ function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy { _authorizeUpgrade(newImplementation); _upgradeToAndCallUUPS(newImplementation, data); } /** * @dev Reverts if the execution is not performed via delegatecall or the execution * context is not of a proxy with an ERC1967-compliant implementation pointing to self. * See {_onlyProxy}. */ function _checkProxy() internal view virtual { if ( address(this) == __self || // Must be called through delegatecall ERC1967Utils.getImplementation() != __self // Must be called through an active proxy ) { revert UUPSUnauthorizedCallContext(); } } /** * @dev Reverts if the execution is performed via delegatecall. * See {notDelegated}. */ function _checkNotDelegated() internal view virtual { if (address(this) != __self) { // Must not be called through delegatecall revert UUPSUnauthorizedCallContext(); } } /** * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by * {upgradeToAndCall}. * * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}. * * ```solidity * function _authorizeUpgrade(address) internal onlyOwner {} * ``` */ function _authorizeUpgrade(address newImplementation) internal virtual; /** * @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call. * * As a security check, {proxiableUUID} is invoked in the new implementation, and the return value * is expected to be the implementation slot in ERC1967. * * Emits an {IERC1967-Upgraded} event. */ function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private { try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) { if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) { revert UUPSUnsupportedProxiableUUID(slot); } ERC1967Utils.upgradeToAndCall(newImplementation, data); } catch { // The implementation is not UUPS revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (token/ERC1155/IERC1155.sol) pragma solidity ^0.8.20; import {IERC165} from "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC1155 compliant contract, as defined in the * https://eips.ethereum.org/EIPS/eip-1155[EIP]. */ interface IERC1155 is IERC165 { /** * @dev Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`. */ event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value); /** * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all * transfers. */ event TransferBatch( address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values ); /** * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to * `approved`. */ event ApprovalForAll(address indexed account, address indexed operator, bool approved); /** * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI. * * If an {URI} event was emitted for `id`, the standard * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value * returned by {IERC1155MetadataURI-uri}. */ event URI(string value, uint256 indexed id); /** * @dev Returns the value of tokens of token type `id` owned by `account`. * * Requirements: * * - `account` cannot be the zero address. */ function balanceOf(address account, uint256 id) external view returns (uint256); /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}. * * Requirements: * * - `accounts` and `ids` must have the same length. */ function balanceOfBatch( address[] calldata accounts, uint256[] calldata ids ) external view returns (uint256[] memory); /** * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`, * * Emits an {ApprovalForAll} event. * * Requirements: * * - `operator` cannot be the caller. */ function setApprovalForAll(address operator, bool approved) external; /** * @dev Returns true if `operator` is approved to transfer ``account``'s tokens. * * See {setApprovalForAll}. */ function isApprovedForAll(address account, address operator) external view returns (bool); /** * @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`. * * WARNING: This function can potentially allow a reentrancy attack when transferring tokens * to an untrusted contract, when invoking {onERC1155Received} on the receiver. * Ensure to follow the checks-effects-interactions pattern and consider employing * reentrancy guards when interacting with untrusted contracts. * * Emits a {TransferSingle} event. * * Requirements: * * - `to` cannot be the zero address. * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}. * - `from` must have a balance of tokens of type `id` of at least `value` amount. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the * acceptance magic value. */ function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes calldata data) external; /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}. * * WARNING: This function can potentially allow a reentrancy attack when transferring tokens * to an untrusted contract, when invoking {onERC1155BatchReceived} on the receiver. * Ensure to follow the checks-effects-interactions pattern and consider employing * reentrancy guards when interacting with untrusted contracts. * * Emits either a {TransferSingle} or a {TransferBatch} event, depending on the length of the array arguments. * * Requirements: * * - `ids` and `values` must have the same length. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the * acceptance magic value. */ function safeBatchTransferFrom( address from, address to, uint256[] calldata ids, uint256[] calldata values, bytes calldata data ) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol) pragma solidity ^0.8.20; import {IERC165} from "../utils/introspection/IERC165.sol";
// SPDX-License-Identifier: MIT pragma solidity >=0.8.17; interface IHasContractName { /// @notice Contract name returns the pretty contract name function contractName() external returns (string memory); } interface IContractMetadata is IHasContractName { /// @notice Contract URI returns the uri for more information about the given contract function contractURI() external returns (string memory); }
// This file is automatically generated by code; do not manually update // SPDX-License-Identifier: MIT pragma solidity ^0.8.23; import {IVersionedContract} from "@zoralabs/shared-contracts/interfaces/IVersionedContract.sol"; /// @title ContractVersionBase /// @notice Base contract for versioning contracts contract ContractVersionBase is IVersionedContract { /// @notice The version of the contract function contractVersion() external pure override returns (string memory) { return "0.0.3"; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; import {IERC1155} from "@openzeppelin/contracts/interfaces/IERC1155.sol"; import {IZoraCreator1155TypesV1} from "./IZoraCreator1155TypesV1.sol"; interface IZoraCreator1155 is IERC1155, IZoraCreator1155TypesV1 { function isAdminOrRole(address user, uint256 tokenId, uint256 role) external view returns (bool); function getCreatorRewardRecipient(uint256 tokenId) external view returns (address); function supportsInterface(bytes4 interfaceId) external view returns (bool); function config() external view returns (address owner, uint96 __gap1, address payable fundsRecipient, uint96 __gap2, address transferHook, uint96 __gap3); function owner() external view returns (address); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; /// @title IComments /// @notice Interface for the Comments contract, which allows for comments and sparking (liking with value) on Zora 1155 posts /// @author oveddan / IsabellaSmallcombe interface IComments { /// @notice Struct representing a unique identifier for a comment struct CommentIdentifier { address commenter; address contractAddress; uint256 tokenId; bytes32 nonce; } /// @notice Struct representing a comment struct Comment { // has this comment been created bool exists; // total sparks for this comment uint256 totalSparks; } /// @notice Struct representing a permit for creating a comment struct PermitComment { // The account that is creating the comment. // Must match the account that is signing the permit address commenter; // If the commenter, has a smart wallet, the smart wallet address. // If not, zero address. If set, this address can be checked // to see if the token is owned by the smart wallet. address commenterSmartWallet; // The contract address that is being commented on address contractAddress; // The token ID that is being commented on uint256 tokenId; // The comment identifier of the comment being replied to CommentIdentifier replyTo; // The text of the comment string text; // Referrer address - will get referral reward from the spark address referrer; // Permit deadline - execution of permit must be before this timestamp uint256 deadline; // Nonce to prevent replay attacks bytes32 nonce; // Chain the permit was signed on uint32 sourceChainId; // Chain to execute the permit on uint32 destinationChainId; } /// @notice Struct representing a permit for sparking a comment struct PermitSparkComment { // Comment that is being sparked CommentIdentifier comment; // Address of the user that is sparking the comment. // Must match the address that is signing the permit address sparker; // Number of sparks to spark uint256 sparksQuantity; // Permit deadline - execution of permit must be before this timestamp uint256 deadline; // Nonce to prevent replay attacks bytes32 nonce; // Referrer address - will get referral reward from the spark address referrer; // Chain the permit was signed on uint32 sourceChainId; // Chain to execute the permit on uint32 destinationChainId; } /// @notice Event emitted when a comment is created /// @param commentId Unique ID for the comment, generated from a hash of the commentIdentifier /// @param commentIdentifier Identifier for the comment, containing details about the comment /// @param replyToId Unique ID of the comment being replied to (if any) /// @param replyTo Identifier of the comment being replied to (if any) /// @param sparksQuantity Number of sparks associated with this comment /// @param text The actual text content of the comment /// @param timestamp Timestamp when the comment was created /// @param referrer Address of the referrer who referred the commenter, if any event Commented( bytes32 indexed commentId, // Unique ID for the comment, generated from a hash of the commentIdentifier CommentIdentifier commentIdentifier, // Identifier for the comment, containing details about the comment bytes32 replyToId, // Unique ID of the comment being replied to (if any) CommentIdentifier replyTo, // Identifier of the comment being replied to (if any) uint256 sparksQuantity, // Number of sparks associated with this comment string text, // The actual text content of the comment uint256 timestamp, // Timestamp when the comment was created address referrer // Address of the referrer who referred the commenter, if any ); /// @notice Event emitted when a comment is backfilled /// @param commentId Unique identifier for the backfilled comment /// @param commentIdentifier Identifier for the comment /// @param text The actual text content of the backfilled comment /// @param timestamp Timestamp when the original comment was created /// @param originalTransactionId Transaction ID of the original comment (before backfilling) event BackfilledComment( bytes32 indexed commentId, // Unique identifier for the backfilled comment CommentIdentifier commentIdentifier, // Identifier for the comment string text, // The actual text content of the backfilled comment uint256 timestamp, // Timestamp when the original comment was created bytes32 originalTransactionId // Transaction ID of the original comment (before backfilling) ); /// @notice Event emitted when a comment is Sparked /// @param commentId Unique identifier of the comment being sparked /// @param commentIdentifier Struct containing details about the comment and commenter /// @param sparksQuantity Number of sparks added to the comment /// @param sparker Address of the user who sparked the comment /// @param timestamp Timestamp when the spark action occurred /// @param referrer Address of the referrer who referred the sparker, if any event SparkedComment( bytes32 indexed commentId, // Unique identifier of the comment being sparked CommentIdentifier commentIdentifier, // Struct containing details about the comment and commenter uint256 sparksQuantity, // Number of sparks added to the comment address sparker, // Address of the user who sparked the comment uint256 timestamp, // Timestamp when the spark action occurred address referrer // Address of the referrer who referred the sparker, if any ); /// @notice Occurs when attempting to add a comment that already exists /// @param commentId The unique identifier of the duplicate comment error DuplicateComment(bytes32 commentId); /// @notice Occurs when the amount of ETH sent with the transaction doesn't match the corresponding sparks quantity error IncorrectETHAmountForSparks(uint256 actual, uint256 expected); /// @notice Occurs when the commenter address doesn't match the expected address /// @param expected The address that was expected to be the commenter /// @param actual The actual address that attempted to comment error CommenterMismatch(address expected, address actual); /// @notice Occurs when a non-token holder or non-admin attempts an action restricted to token holders or admins error NotTokenHolderOrAdmin(); /// @notice Occurs when attempting to spark a comment without sending at least one spark error MustSendAtLeastOneSpark(); /// @notice Occurs when attempting to submit an empty comment error EmptyComment(); /// @notice Occurs when trying to interact with a comment that doesn't exist error CommentDoesntExist(); /// @notice Occurs when a transfer of funds fails error TransferFailed(); /// @notice Occurs when a user attempts to spark their own comment error CannotSparkOwnComment(); /// @notice Occurs when a function restricted to the Sparks contract is called by another address error OnlySparksContract(); /// @notice Occurs when attempting to upgrade to a contract with a name that doesn't match the current contract's name /// @param currentName The name of the current contract /// @param newName The name of the contract being upgraded to error UpgradeToMismatchedContractName(string currentName, string newName); /// @notice Occurs when the lengths of arrays passed to a function do not match error ArrayLengthMismatch(); /// @notice Occurs when the address or token IDs in a comment identifier do not match the expected values /// @param commentAddress The address in the comment identifier /// @param commentTokenId The token ID in the comment identifier /// @param replyAddress The address in the reply identifier /// @param replyTokenId The token ID in the reply identifier error CommentAddressOrTokenIdsDoNotMatch(address commentAddress, uint256 commentTokenId, address replyAddress, uint256 replyTokenId); /// @notice Occurs when the signature is invalid error InvalidSignature(); /// @notice Occurs when the destination chain ID doesn't match the current chain ID in a permit error IncorrectDestinationChain(uint256 wrongDestinationChainId); /// @notice Occurs when the commenter is not a smart wallet owner error NotSmartWalletOwner(); /// @notice Occurs when the address is not a smart wallet error NotSmartWallet(); /// @notice Occurs when the deadline has expired error ERC2612ExpiredSignature(uint256 deadline); /// @notice Occurs when the funds recipient does not exist error NoFundsRecipient(); /// @notice Address cannot be zero error AddressZero(); /// @notice Creates a new comment /// @param commenter The address of the commenter /// @param contractAddress The address of the contract /// @param tokenId The token ID /// @param text The text content of the comment /// @param replyTo The identifier of the comment being replied to (if any) /// @param commenterSmartWalletOwner If the commenter has a smart wallet, the smart wallet owner address /// @param referrer The address of the referrer (if any) /// @return commentIdentifier The identifier of the created comment, including the nonce function comment( address commenter, address contractAddress, uint256 tokenId, string calldata text, CommentIdentifier calldata replyTo, address commenterSmartWalletOwner, address referrer ) external payable returns (CommentIdentifier memory); /// @notice Allows another contract to delegate comment creation on behalf of a user /// @param commenter The address of the commenter /// @param contractAddress The address of the contract /// @param tokenId The token ID /// @param text The text content of the comment /// @param replyTo The identifier of the comment being replied to (if any) /// @param commenterSmartWalletOwner If the commenter has a smart wallet, the smart wallet owner address /// @param referrer The address of the referrer (if any) /// @return commentIdentifier The identifier of the created comment, including the nonce function delegateComment( address commenter, address contractAddress, uint256 tokenId, string calldata text, CommentIdentifier calldata replyTo, address commenterSmartWalletOwner, address referrer ) external payable returns (CommentIdentifier memory, bytes32 commentId); function initialize(address commentsAdmin, address backfiller, address[] calldata delegateCommenters) external; /// @notice Sparks a comment /// @param commentIdentifier The identifier of the comment to spark /// @param sparksQuantity The quantity of sparks to send /// @param referrer The referrer of the comment function sparkComment(CommentIdentifier calldata commentIdentifier, uint256 sparksQuantity, address referrer) external payable; /// @notice Returns the value of a single spark /// @return The value of a single spark function sparkValue() external view returns (uint256); /// @notice Hashes a comment identifier to generate a unique ID /// @param commentIdentifier The comment identifier to hash /// @return The hashed comment identifier function hashCommentIdentifier(CommentIdentifier calldata commentIdentifier) external view returns (bytes32); /// @notice Returns the next nonce for comment creation /// @return The next nonce function nextNonce() external view returns (bytes32); /// @notice Returns the implementation address of the contract /// @return The implementation address function implementation() external view returns (address); /// @notice Returns the total number of sparks a given comment has received /// @param commentIdentifier The identifier of the comment /// @return The total number of sparks a comment has received function commentSparksQuantity(CommentIdentifier memory commentIdentifier) external view returns (uint256); /// @notice Hashes a comment identifier and checks if a comment exists with that id /// @param commentIdentifier The comment identifier to check /// @return commentId The hashed comment identifier /// @return exists Whether the comment exists function hashAndCheckCommentExists(CommentIdentifier memory commentIdentifier) external view returns (bytes32 commentId, bool exists); /// @notice Validates that a comment exists and returns its ID /// @param commentIdentifier The comment identifier to validate /// @return commentId The hashed comment identifier function hashAndValidateCommentExists(CommentIdentifier memory commentIdentifier) external view returns (bytes32 commentId); /// @notice Hashes a permit comment struct for signing /// @param permit The permit comment struct /// @return The hash to sign function hashPermitComment(PermitComment calldata permit) external view returns (bytes32); /// @notice Creates a comment on behalf of another account using a signed message /// @param permit The permit that was signed off-chain on the source chain /// @param signature The signature of the permit comment function permitComment(PermitComment calldata permit, bytes calldata signature) external payable; /// @notice Hashes a permit spark comment struct for signing /// @param permit The permit spark comment struct /// @return The hash to sign function hashPermitSparkComment(PermitSparkComment calldata permit) external view returns (bytes32); /// @notice Sparks a comment on behalf of another account using a signed message /// @param permit The permit spark comment struct /// @param signature The signature of the permit function permitSparkComment(PermitSparkComment calldata permit, bytes calldata signature) external payable; /// @notice Backfills comments created by other contracts /// @param commentIdentifiers Array of comment identifiers /// @param texts Array of comment texts /// @param timestamps Array of comment timestamps /// @param originalTransactionHashes Array of original transaction hashes function backfillBatchAddComment( CommentIdentifier[] calldata commentIdentifiers, string[] calldata texts, uint256[] calldata timestamps, bytes32[] calldata originalTransactionHashes ) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface ICoinComments { function isOwner(address) external view returns (bool); function payoutRecipient() external view returns (address); function balanceOf(address) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.17; /// @title IProtocolRewards /// @notice The interface for deposits & withdrawals for Protocol Rewards interface IProtocolRewards { /// @notice Rewards Deposit Event /// @param creator Creator for NFT rewards /// @param createReferral Creator referral /// @param mintReferral Mint referral user /// @param firstMinter First minter reward recipient /// @param zora ZORA recipient /// @param from The caller of the deposit /// @param creatorReward Creator reward amount /// @param createReferralReward Creator referral reward /// @param mintReferralReward Mint referral amount /// @param firstMinterReward First minter reward amount /// @param zoraReward ZORA amount event RewardsDeposit( address indexed creator, address indexed createReferral, address indexed mintReferral, address firstMinter, address zora, address from, uint256 creatorReward, uint256 createReferralReward, uint256 mintReferralReward, uint256 firstMinterReward, uint256 zoraReward ); /// @notice Deposit Event /// @param from From user /// @param to To user (within contract) /// @param reason Optional bytes4 reason for indexing /// @param amount Amount of deposit /// @param comment Optional user comment event Deposit(address indexed from, address indexed to, bytes4 indexed reason, uint256 amount, string comment); /// @notice Withdraw Event /// @param from From user /// @param to To user (within contract) /// @param amount Amount of deposit event Withdraw(address indexed from, address indexed to, uint256 amount); /// @notice Cannot send to address zero error ADDRESS_ZERO(); /// @notice Function argument array length mismatch error ARRAY_LENGTH_MISMATCH(); /// @notice Invalid deposit error INVALID_DEPOSIT(); /// @notice Invalid signature for deposit error INVALID_SIGNATURE(); /// @notice Invalid withdraw error INVALID_WITHDRAW(); /// @notice Signature for withdraw is too old and has expired error SIGNATURE_DEADLINE_EXPIRED(); /// @notice Low-level ETH transfer has failed error TRANSFER_FAILED(); /// @notice Generic function to deposit ETH for a recipient, with an optional comment /// @param to Address to deposit to /// @param to Reason system reason for deposit (used for indexing) /// @param comment Optional comment as reason for deposit function deposit(address to, bytes4 why, string calldata comment) external payable; /// @notice Generic function to deposit ETH for multiple recipients, with an optional comment /// @param recipients recipients to send the amount to, array aligns with amounts /// @param amounts amounts to send to each recipient, array aligns with recipients /// @param reasons optional bytes4 hash for indexing /// @param comment Optional comment to include with mint function depositBatch(address[] calldata recipients, uint256[] calldata amounts, bytes4[] calldata reasons, string calldata comment) external payable; /// @notice Used by Zora ERC-721 & ERC-1155 contracts to deposit protocol rewards /// @param creator Creator for NFT rewards /// @param creatorReward Creator reward amount /// @param createReferral Creator referral /// @param createReferralReward Creator referral reward /// @param mintReferral Mint referral user /// @param mintReferralReward Mint referral amount /// @param firstMinter First minter reward /// @param firstMinterReward First minter reward amount /// @param zora ZORA recipient /// @param zoraReward ZORA amount function depositRewards( address creator, uint256 creatorReward, address createReferral, uint256 createReferralReward, address mintReferral, uint256 mintReferralReward, address firstMinter, uint256 firstMinterReward, address zora, uint256 zoraReward ) external payable; /// @notice Withdraw protocol rewards /// @param to Withdraws from msg.sender to this address /// @param amount amount to withdraw function withdraw(address to, uint256 amount) external; /// @notice Withdraw rewards on behalf of an address /// @param to The address to withdraw for /// @param amount The amount to withdraw (0 for total balance) function withdrawFor(address to, uint256 amount) external; /// @notice Execute a withdraw of protocol rewards via signature /// @param from Withdraw from this address /// @param to Withdraw to this address /// @param amount Amount to withdraw /// @param deadline Deadline for the signature to be valid /// @param v V component of signature /// @param r R component of signature /// @param s S component of signature function withdrawWithSig(address from, address to, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external; /// @notice Get the balance of an account /// @param account The account to get the balance of function balanceOf(address account) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; /** * @dev Provides tracking nonces for addresses. Nonces can be in any order and just need to be unique. */ abstract contract UnorderedNoncesUpgradeable { /** * @dev The nonce used for an `account` is not the expected current nonce. */ error InvalidAccountNonce(address account, bytes32 currentNonce); /// @custom:storage-location erc7201:unorderedNonces.storage.UnorderedNoncesStorage struct UnorderedNoncesStorage { mapping(address account => mapping(bytes32 => bool)) nonces; } // keccak256(abi.encode(uint256(keccak256("unorderedNonces.storage.UnorderedNoncesStorage")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant UNORDERED_NONCES_STORAGE_LOCATION = 0xc84b62be2e432010aa71cc1bbdba4c7b02245544521aa5beae20093c70622400; function _getUnorderedNoncesStorage() private pure returns (UnorderedNoncesStorage storage $) { assembly { $.slot := UNORDERED_NONCES_STORAGE_LOCATION } } /** * @dev Returns whether a nonce has been used for an address. */ function nonceUsed(address owner, bytes32 nonce) public view virtual returns (bool) { return _getUnorderedNoncesStorage().nonces[owner][nonce]; } /** * @dev Same as {_useNonce} but checking that `nonce` passed in is valid. */ function _useCheckedNonce(address owner, bytes32 nonce) internal virtual { UnorderedNoncesStorage storage $ = _getUnorderedNoncesStorage(); if ($.nonces[owner][nonce]) { revert InvalidAccountNonce(owner, nonce); } $.nonces[owner][nonce] = true; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol"; import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; /// Extension of EIP712Upgradeable that allows for messages to be signed on other chains. abstract contract EIP712UpgradeableWithChainId is EIP712Upgradeable { bytes32 private constant TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); /** * @dev Returns the domain separator for the current chain. */ function _domainSeparatorV4(uint256 chainId) internal view returns (bytes32) { return _buildDomainSeparator(chainId); } function _buildDomainSeparator(uint256 chainId) private view returns (bytes32) { return keccak256(abi.encode(TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash(), chainId, address(this))); } function _hashTypedDataV4(bytes32 structHash, uint256 chainId) internal view returns (bytes32) { return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(chainId), structHash); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/SignatureChecker.sol) pragma solidity ^0.8.20; import {ECDSA} from "./ECDSA.sol"; import {IERC1271} from "../../interfaces/IERC1271.sol"; /** * @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like * Argent and Safe Wallet (previously Gnosis Safe). */ library SignatureChecker { /** * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`. * * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus * change through time. It could return true at block N and false at block N+1 (or the opposite). */ function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) { (address recovered, ECDSA.RecoverError error, ) = ECDSA.tryRecover(hash, signature); return (error == ECDSA.RecoverError.NoError && recovered == signer) || isValidERC1271SignatureNow(signer, hash, signature); } /** * @dev Checks if a signature is valid for a given signer and data hash. The signature is validated * against the signer smart contract using ERC1271. * * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus * change through time. It could return true at block N and false at block N+1 (or the opposite). */ function isValidERC1271SignatureNow( address signer, bytes32 hash, bytes memory signature ) internal view returns (bool) { (bool success, bytes memory result) = signer.staticcall( abi.encodeCall(IERC1271.isValidSignature, (hash, signature)) ); return (success && result.length >= 32 && abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol) pragma solidity ^0.8.20; import {IBeacon} from "../beacon/IBeacon.sol"; import {Address} from "../../utils/Address.sol"; import {StorageSlot} from "../../utils/StorageSlot.sol"; /** * @dev This abstract contract provides getters and event emitting update functions for * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots. */ library ERC1967Utils { // We re-declare ERC-1967 events here because they can't be used directly from IERC1967. // This will be fixed in Solidity 0.8.21. At that point we should remove these events. /** * @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); /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1. */ // solhint-disable-next-line private-vars-leading-underscore bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /** * @dev The `implementation` of the proxy is invalid. */ error ERC1967InvalidImplementation(address implementation); /** * @dev The `admin` of the proxy is invalid. */ error ERC1967InvalidAdmin(address admin); /** * @dev The `beacon` of the proxy is invalid. */ error ERC1967InvalidBeacon(address beacon); /** * @dev An upgrade function sees `msg.value > 0` that may be lost. */ error ERC1967NonPayable(); /** * @dev Returns the current implementation address. */ function getImplementation() internal view returns (address) { return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value; } /** * @dev Stores a new address in the EIP1967 implementation slot. */ function _setImplementation(address newImplementation) private { if (newImplementation.code.length == 0) { revert ERC1967InvalidImplementation(newImplementation); } StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation; } /** * @dev Performs implementation upgrade with additional setup call if data is nonempty. * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected * to avoid stuck value in the contract. * * Emits an {IERC1967-Upgraded} event. */ function upgradeToAndCall(address newImplementation, bytes memory data) internal { _setImplementation(newImplementation); emit Upgraded(newImplementation); if (data.length > 0) { Address.functionDelegateCall(newImplementation, data); } else { _checkNonPayable(); } } /** * @dev Storage slot with the admin of the contract. * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1. */ // solhint-disable-next-line private-vars-leading-underscore bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; /** * @dev Returns the current admin. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` */ function getAdmin() internal view returns (address) { return StorageSlot.getAddressSlot(ADMIN_SLOT).value; } /** * @dev Stores a new address in the EIP1967 admin slot. */ function _setAdmin(address newAdmin) private { if (newAdmin == address(0)) { revert ERC1967InvalidAdmin(address(0)); } StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin; } /** * @dev Changes the admin of the proxy. * * Emits an {IERC1967-AdminChanged} event. */ function changeAdmin(address newAdmin) internal { emit AdminChanged(getAdmin(), newAdmin); _setAdmin(newAdmin); } /** * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. * This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1. */ // solhint-disable-next-line private-vars-leading-underscore bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; /** * @dev Returns the current beacon. */ function getBeacon() internal view returns (address) { return StorageSlot.getAddressSlot(BEACON_SLOT).value; } /** * @dev Stores a new beacon in the EIP1967 beacon slot. */ function _setBeacon(address newBeacon) private { if (newBeacon.code.length == 0) { revert ERC1967InvalidBeacon(newBeacon); } StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon; address beaconImplementation = IBeacon(newBeacon).implementation(); if (beaconImplementation.code.length == 0) { revert ERC1967InvalidImplementation(beaconImplementation); } } /** * @dev Change the beacon and trigger a setup call if data is nonempty. * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected * to avoid stuck value in the contract. * * Emits an {IERC1967-BeaconUpgraded} event. * * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for * efficiency. */ function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal { _setBeacon(newBeacon); emit BeaconUpgraded(newBeacon); if (data.length > 0) { Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data); } else { _checkNonPayable(); } } /** * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract * if an upgrade doesn't perform an initialization call. */ function _checkNonPayable() private { if (msg.value > 0) { revert ERC1967NonPayable(); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; /// Extracted interface from Coinbase Smart Wallet's MultiOwnable contract, /// which we use to check if an address is an owner of the smart wallet /// in tests without having to include the entire contract in the package /// Original function can be seen here: https://github.com/coinbase/talaria/blob/main/contracts/src/MultiOwnable.sol interface IMultiOwnable { function isOwnerAddress(address account) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; /// @title CommentsImplConstants /// @notice Constants for the CommentsImpl contract /// @author oveddan / IsabellaSmallcombe contract CommentsImplConstants { /// @notice this is the zora creator multisig that can upgrade the contract bytes32 public constant BACKFILLER_ROLE = keccak256("BACKFILLER_ROLE"); /// @notice allows to delegate comment bytes32 public constant DELEGATE_COMMENTER = keccak256("DELEGATE_COMMENTER"); /// @notice permission bit for admin uint256 public constant PERMISSION_BIT_ADMIN = 2 ** 1; /// @notice Zora reward percentage uint256 public constant ZORA_REWARD_PCT = 10; /// @notice referrer reward percentage uint256 public constant REFERRER_REWARD_PCT = 20; /// @notice Zora reward percentage when there is no referrer uint256 public constant ZORA_REWARD_NO_REFERRER_PCT = 30; /// @notice BPS to percent conversion uint256 internal constant BPS_TO_PERCENT_2_DECIMAL_PERCISION = 100; /// @notice domain name for comments string public constant DOMAIN_NAME = "Comments"; /// @notice domain version for comments string public constant DOMAIN_VERSION = "1"; /// @notice Zora reward reason bytes4 constant ZORA_REWARD_REASON = bytes4(keccak256("zoraRewardForCommentDeposited()")); /// @notice referrer reward reason bytes4 constant REFERRER_REWARD_REASON = bytes4(keccak256("referrerRewardForCommentDeposited()")); /// @notice sparks recipient reward reason bytes4 constant SPARKS_RECIPIENT_REWARD_REASON = bytes4(keccak256("sparksRecipientRewardForCommentDeposited()")); /// @notice permint comment domain bytes32 constant PERMIT_COMMENT_DOMAIN = keccak256( "PermitComment(address contractAddress,uint256 tokenId,address commenter,CommentIdentifier replyTo,string text,uint256 deadline,bytes32 nonce,address commenterSmartWallet,address referrer,uint32 sourceChainId,uint32 destinationChainId)CommentIdentifier(address contractAddress,uint256 tokenId,address commenter,bytes32 nonce)" ); /// @notice comment identifier domain bytes32 constant COMMENT_IDENTIFIER_DOMAIN = keccak256("CommentIdentifier(address contractAddress,uint256 tokenId,address commenter,bytes32 nonce)"); /// @notice permit spark comment domain bytes32 constant PERMIT_SPARK_COMMENT_DOMAIN = keccak256( "PermitSparkComment(CommentIdentifier comment,address sparker,uint256 sparksQuantity,uint256 deadline,bytes32 nonce,address referrer,uint32 sourceChainId,uint32 destinationChainId)CommentIdentifier(address contractAddress,uint256 tokenId,address commenter,bytes32 nonce)" ); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol) pragma solidity ^0.8.20; /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IAccessControl { /** * @dev The `account` is missing a role. */ error AccessControlUnauthorizedAccount(address account, bytes32 neededRole); /** * @dev The caller of a function is not the expected one. * * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}. */ error AccessControlBadConfirmation(); /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call, an admin role * bearer except when using {AccessControl-_setupRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) external view returns (bool); /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {AccessControl-_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) external view returns (bytes32); /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) external; /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) external; /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `callerConfirmation`. */ function renounceRole(bytes32 role, address callerConfirmation) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) pragma solidity ^0.8.20; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { } function __Context_init_unchained() internal onlyInitializing { } function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol) pragma solidity ^0.8.20; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {Initializable} from "../../proxy/utils/Initializable.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement 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); * } * ``` */ abstract contract ERC165Upgradeable is Initializable, IERC165 { function __ERC165_init() internal onlyInitializing { } function __ERC165_init_unchained() internal onlyInitializing { } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.20; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ```solidity * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Storage of the initializable contract. * * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions * when using with upgradeable contracts. * * @custom:storage-location erc7201:openzeppelin.storage.Initializable */ struct InitializableStorage { /** * @dev Indicates that the contract has been initialized. */ uint64 _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool _initializing; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00; /** * @dev The contract is already initialized. */ error InvalidInitialization(); /** * @dev The contract is not initializing. */ error NotInitializing(); /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint64 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in * production. * * Emits an {Initialized} event. */ modifier initializer() { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); // Cache values to avoid duplicated sloads bool isTopLevelCall = !$._initializing; uint64 initialized = $._initialized; // Allowed calls: // - initialSetup: the contract is not in the initializing state and no previous version was // initialized // - construction: the contract is initialized at version 1 (no reininitialization) and the // current contract is just being deployed bool initialSetup = initialized == 0 && isTopLevelCall; bool construction = initialized == 1 && address(this).code.length == 0; if (!initialSetup && !construction) { revert InvalidInitialization(); } $._initialized = 1; if (isTopLevelCall) { $._initializing = true; } _; if (isTopLevelCall) { $._initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint64 version) { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing || $._initialized >= version) { revert InvalidInitialization(); } $._initialized = version; $._initializing = true; _; $._initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { _checkInitializing(); _; } /** * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}. */ function _checkInitializing() internal view virtual { if (!_isInitializing()) { revert NotInitializing(); } } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing) { revert InvalidInitialization(); } if ($._initialized != type(uint64).max) { $._initialized = type(uint64).max; emit Initialized(type(uint64).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint64) { return _getInitializableStorage()._initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _getInitializableStorage()._initializing; } /** * @dev Returns a pointer to the storage namespace. */ // solhint-disable-next-line var-name-mixedcase function _getInitializableStorage() private pure returns (InitializableStorage storage $) { assembly { $.slot := INITIALIZABLE_STORAGE } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC1822.sol) pragma solidity ^0.8.20; /** * @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 IERC1822Proxiable { /** * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation * address. * * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this * function revert if invoked through a proxy. */ function proxiableUUID() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol) pragma solidity ^0.8.20; /** * @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 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[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 pragma solidity >=0.8.17; interface IVersionedContract { function contractVersion() external pure returns (string memory); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1155.sol) pragma solidity ^0.8.20; import {IERC1155} from "../token/ERC1155/IERC1155.sol";
// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; /* ░░░░░░░░░░░░░░ ░░▒▒░░░░░░░░░░░░░░░░░░░░ ░░▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░ ░░▒▒▒▒░░░░░░░░░░░░░░ ░░░░░░░░ ░▓▓▒▒▒▒░░░░░░░░░░░░ ░░░░░░░ ░▓▓▓▒▒▒▒░░░░░░░░░░░░ ░░░░░░░░ ░▓▓▓▒▒▒▒░░░░░░░░░░░░░░ ░░░░░░░░░░ ░▓▓▓▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░▓▓▓▓▓▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░ ░▓▓▓▓▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░ ░░▓▓▓▓▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░ ░░▓▓▓▓▓▓▒▒▒▒▒▒▒▒░░░░░░░░░▒▒▒▒▒░░ ░░▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░ ░░▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░░ OURS TRULY, */ /// Imagine. Mint. Enjoy. /// @notice Interface for types used across the ZoraCreator1155 contract /// @author @iainnash / @tbtstl interface IZoraCreator1155TypesV1 { /// @notice Used to store individual token data struct TokenData { string uri; uint256 maxSupply; uint256 totalMinted; } /// @notice Used to store contract-level configuration struct ContractConfig { address owner; uint96 __gap1; address payable fundsRecipient; uint96 __gap2; address transferHook; uint96 __gap3; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.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: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain * separator of the implementation contract. This will cause the {_domainSeparatorV4} function to always rebuild the * separator from the immutable values, which 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); } /** * @dev See {IERC-5267}. */ 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.0.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[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712] * specifications. */ library MessageHashUtils { /** * @dev Returns the keccak256 digest of an EIP-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://eth.wiki/json-rpc/API#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) { /// @solidity memory-safe-assembly assembly { 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 EIP-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://eth.wiki/json-rpc/API#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 EIP-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 Returns the keccak256 digest of an EIP-712 typed data (EIP-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) { /// @solidity memory-safe-assembly assembly { 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.0.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, RecoverError, bytes32) { 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. /// @solidity memory-safe-assembly assembly { 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[EIP-2098 short signatures] */ function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) { 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, RecoverError, bytes32) { // 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.0.0) (interfaces/IERC1271.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC1271 standard signature validation method for * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271]. */ interface IERC1271 { /** * @dev Should return whether the signature provided is valid for the provided data * @param hash Hash of the data to be signed * @param signature Signature byte array associated with _data */ function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol) pragma solidity ^0.8.20; /** * @dev This is the interface that {BeaconProxy} expects of its beacon. */ interface IBeacon { /** * @dev Must return an address that can be used as a delegate call target. * * {UpgradeableBeacon} will check that this address is a contract. */ function implementation() external view returns (address); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol) pragma solidity ^0.8.20; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error AddressInsufficientBalance(address account); /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedInnerCall(); /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert AddressInsufficientBalance(address(this)); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert FailedInnerCall(); } } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {FailedInnerCall} error. * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert AddressInsufficientBalance(address(this)); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an * unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {FailedInnerCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}. */ function _revert(bytes memory returndata) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert FailedInnerCall(); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.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 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(newImplementation.code.length > 0); * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; * } * } * ``` */ library StorageSlot { 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 v5.0.0) (interfaces/IERC5267.sol) pragma solidity ^0.8.20; 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.0.0) (utils/Strings.sol) pragma solidity ^0.8.20; import {Math} from "./math/Math.sol"; import {SignedMath} from "./math/SignedMath.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant HEX_DIGITS = "0123456789abcdef"; uint8 private constant ADDRESS_LENGTH = 20; /** * @dev The `value` string doesn't fit in the specified `length`. */ error StringsInsufficientHexLength(uint256 value, uint256 length); /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), 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 Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol) pragma solidity ^0.8.20; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Muldiv operation overflow. */ error MathOverflowedMulDiv(); enum Rounding { Floor, // Toward negative infinity Ceil, // Toward positive infinity Trunc, // Toward zero Expand // Away from zero } /** * @dev Returns the addition of two unsigned integers, with an overflow flag. */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the subtraction of two unsigned integers, with an overflow flag. */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds 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. return a / b; } // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or * denominator == 0. * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by * Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0 = x * y; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. if (denominator <= prod1) { revert MathOverflowedMulDiv(); } /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. // Always >= 1. See https://cs.stackexchange.com/q/138556/92363. uint256 twos = denominator & (0 - denominator); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^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 (unsignedRoundsUp(rounding) && 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 * towards zero. * * 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 + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; 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 + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0); } } /** * @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 + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256 of a positive value rounded towards zero. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; 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 + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0); } } /** * @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.0.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.20; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
{ "remappings": [ "ds-test/=node_modules/ds-test/src/", "forge-std/=node_modules/forge-std/src/", "@openzeppelin/=node_modules/@openzeppelin/", "@zoralabs/protocol-rewards/=node_modules/@zoralabs/protocol-rewards/", "@zoralabs/shared-contracts/=node_modules/@zoralabs/shared-contracts/src/", "@zoralabs/sparks-contracts/=node_modules/@zoralabs/sparks-contracts/src/", "@zoralabs/erc20z/=node_modules/@zoralabs/erc20z/src/", "solady/=node_modules/solady/src/" ], "optimizer": { "enabled": true, "runs": 1000000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "shanghai", "viaIR": true, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"uint256","name":"_sparkValue","type":"uint256"},{"internalType":"address","name":"_protocolRewards","type":"address"},{"internalType":"address","name":"_zoraRecipient","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[],"name":"AddressZero","type":"error"},{"inputs":[],"name":"ArrayLengthMismatch","type":"error"},{"inputs":[],"name":"CannotSparkOwnComment","type":"error"},{"inputs":[{"internalType":"address","name":"commentAddress","type":"address"},{"internalType":"uint256","name":"commentTokenId","type":"uint256"},{"internalType":"address","name":"replyAddress","type":"address"},{"internalType":"uint256","name":"replyTokenId","type":"uint256"}],"name":"CommentAddressOrTokenIdsDoNotMatch","type":"error"},{"inputs":[],"name":"CommentDoesntExist","type":"error"},{"inputs":[{"internalType":"address","name":"expected","type":"address"},{"internalType":"address","name":"actual","type":"address"}],"name":"CommenterMismatch","type":"error"},{"inputs":[{"internalType":"bytes32","name":"commentId","type":"bytes32"}],"name":"DuplicateComment","type":"error"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"name":"ERC1967InvalidImplementation","type":"error"},{"inputs":[],"name":"ERC1967NonPayable","type":"error"},{"inputs":[{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"ERC2612ExpiredSignature","type":"error"},{"inputs":[],"name":"EmptyComment","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[{"internalType":"uint256","name":"wrongDestinationChainId","type":"uint256"}],"name":"IncorrectDestinationChain","type":"error"},{"inputs":[{"internalType":"uint256","name":"actual","type":"uint256"},{"internalType":"uint256","name":"expected","type":"uint256"}],"name":"IncorrectETHAmountForSparks","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"currentNonce","type":"bytes32"}],"name":"InvalidAccountNonce","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"MustSendAtLeastOneSpark","type":"error"},{"inputs":[],"name":"NoFundsRecipient","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"NotSmartWallet","type":"error"},{"inputs":[],"name":"NotSmartWalletOwner","type":"error"},{"inputs":[],"name":"NotTokenHolderOrAdmin","type":"error"},{"inputs":[],"name":"OnlySparksContract","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"UUPSUnauthorizedCallContext","type":"error"},{"inputs":[{"internalType":"bytes32","name":"slot","type":"bytes32"}],"name":"UUPSUnsupportedProxiableUUID","type":"error"},{"inputs":[{"internalType":"string","name":"currentName","type":"string"},{"internalType":"string","name":"newName","type":"string"}],"name":"UpgradeToMismatchedContractName","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"commentId","type":"bytes32"},{"components":[{"internalType":"address","name":"commenter","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"indexed":false,"internalType":"struct IComments.CommentIdentifier","name":"commentIdentifier","type":"tuple"},{"indexed":false,"internalType":"string","name":"text","type":"string"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"originalTransactionId","type":"bytes32"}],"name":"BackfilledComment","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"commentId","type":"bytes32"},{"components":[{"internalType":"address","name":"commenter","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"indexed":false,"internalType":"struct IComments.CommentIdentifier","name":"commentIdentifier","type":"tuple"},{"indexed":false,"internalType":"bytes32","name":"replyToId","type":"bytes32"},{"components":[{"internalType":"address","name":"commenter","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"indexed":false,"internalType":"struct IComments.CommentIdentifier","name":"replyTo","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"sparksQuantity","type":"uint256"},{"indexed":false,"internalType":"string","name":"text","type":"string"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"},{"indexed":false,"internalType":"address","name":"referrer","type":"address"}],"name":"Commented","type":"event"},{"anonymous":false,"inputs":[],"name":"EIP712DomainChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"commentId","type":"bytes32"},{"components":[{"internalType":"address","name":"commenter","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"indexed":false,"internalType":"struct IComments.CommentIdentifier","name":"commentIdentifier","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"sparksQuantity","type":"uint256"},{"indexed":false,"internalType":"address","name":"sparker","type":"address"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"},{"indexed":false,"internalType":"address","name":"referrer","type":"address"}],"name":"SparkedComment","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"BACKFILLER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DELEGATE_COMMENTER","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_NAME","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMISSION_BIT_ADMIN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REFERRER_REWARD_PCT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UPGRADE_INTERFACE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ZORA_REWARD_NO_REFERRER_PCT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ZORA_REWARD_PCT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"commenter","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"internalType":"struct IComments.CommentIdentifier[]","name":"commentIdentifiers","type":"tuple[]"},{"internalType":"string[]","name":"texts","type":"string[]"},{"internalType":"uint256[]","name":"timestamps","type":"uint256[]"},{"internalType":"bytes32[]","name":"originalTransactionHashes","type":"bytes32[]"}],"name":"backfillBatchAddComment","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"commenter","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"string","name":"text","type":"string"},{"components":[{"internalType":"address","name":"commenter","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"internalType":"struct IComments.CommentIdentifier","name":"replyTo","type":"tuple"},{"internalType":"address","name":"commenterSmartWallet","type":"address"},{"internalType":"address","name":"referrer","type":"address"}],"name":"comment","outputs":[{"components":[{"internalType":"address","name":"commenter","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"internalType":"struct IComments.CommentIdentifier","name":"commentIdentifier","type":"tuple"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"commenter","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"internalType":"struct IComments.CommentIdentifier","name":"commentIdentifier","type":"tuple"}],"name":"commentSparksQuantity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contractName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"contractVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"commenter","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"string","name":"text","type":"string"},{"components":[{"internalType":"address","name":"commenter","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"internalType":"struct IComments.CommentIdentifier","name":"replyTo","type":"tuple"},{"internalType":"address","name":"commenterSmartWalletOwner","type":"address"},{"internalType":"address","name":"referrer","type":"address"}],"name":"delegateComment","outputs":[{"components":[{"internalType":"address","name":"commenter","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"internalType":"struct IComments.CommentIdentifier","name":"commentIdentifier","type":"tuple"},{"internalType":"bytes32","name":"commentId","type":"bytes32"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","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":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"commenter","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"internalType":"struct IComments.CommentIdentifier","name":"commentIdentifier","type":"tuple"}],"name":"hashAndCheckCommentExists","outputs":[{"internalType":"bytes32","name":"commentId","type":"bytes32"},{"internalType":"bool","name":"exists","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"commenter","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"internalType":"struct IComments.CommentIdentifier","name":"commentIdentifier","type":"tuple"}],"name":"hashAndValidateCommentExists","outputs":[{"internalType":"bytes32","name":"commentId","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"commenter","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"internalType":"struct IComments.CommentIdentifier","name":"commentIdentifier","type":"tuple"}],"name":"hashCommentIdentifier","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"commenter","type":"address"},{"internalType":"address","name":"commenterSmartWallet","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"components":[{"internalType":"address","name":"commenter","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"internalType":"struct IComments.CommentIdentifier","name":"replyTo","type":"tuple"},{"internalType":"string","name":"text","type":"string"},{"internalType":"address","name":"referrer","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"},{"internalType":"uint32","name":"sourceChainId","type":"uint32"},{"internalType":"uint32","name":"destinationChainId","type":"uint32"}],"internalType":"struct IComments.PermitComment","name":"permit","type":"tuple"}],"name":"hashPermitComment","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"commenter","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"internalType":"struct IComments.CommentIdentifier","name":"comment","type":"tuple"},{"internalType":"address","name":"sparker","type":"address"},{"internalType":"uint256","name":"sparksQuantity","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"},{"internalType":"address","name":"referrer","type":"address"},{"internalType":"uint32","name":"sourceChainId","type":"uint32"},{"internalType":"uint32","name":"destinationChainId","type":"uint32"}],"internalType":"struct IComments.PermitSparkComment","name":"permit","type":"tuple"}],"name":"hashPermitSparkComment","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"defaultAdmin","type":"address"},{"internalType":"address","name":"backfiller","type":"address"},{"internalType":"address[]","name":"delegateCommenters","type":"address[]"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nextNonce","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"nonceUsed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"commenter","type":"address"},{"internalType":"address","name":"commenterSmartWallet","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"components":[{"internalType":"address","name":"commenter","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"internalType":"struct IComments.CommentIdentifier","name":"replyTo","type":"tuple"},{"internalType":"string","name":"text","type":"string"},{"internalType":"address","name":"referrer","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"},{"internalType":"uint32","name":"sourceChainId","type":"uint32"},{"internalType":"uint32","name":"destinationChainId","type":"uint32"}],"internalType":"struct IComments.PermitComment","name":"permit","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"permitComment","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"commenter","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"internalType":"struct IComments.CommentIdentifier","name":"comment","type":"tuple"},{"internalType":"address","name":"sparker","type":"address"},{"internalType":"uint256","name":"sparksQuantity","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"},{"internalType":"address","name":"referrer","type":"address"},{"internalType":"uint32","name":"sourceChainId","type":"uint32"},{"internalType":"uint32","name":"destinationChainId","type":"uint32"}],"internalType":"struct IComments.PermitSparkComment","name":"permit","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"permitSparkComment","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"protocolRewards","outputs":[{"internalType":"contract IProtocolRewards","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"commenter","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"internalType":"struct IComments.CommentIdentifier","name":"commentIdentifier","type":"tuple"},{"internalType":"uint256","name":"sparksQuantity","type":"uint256"},{"internalType":"address","name":"referrer","type":"address"}],"name":"sparkComment","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"sparkValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"}]
Contract Creation Code
61010034620001a557601f6200542838819003918201601f19168301926001600160401b0392909183851183861017620001a9578160609284926040978852833981010312620001a557805162000066846200005e60208501620001bd565b9301620001bd565b30608052926001600160a01b0392831692831590811562000199575b5062000188577ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009081549060ff82881c166200017757808083160362000132575b50505060a05260c05260e052516152559081620001d38239608051818181611d500152611e49015260a0518181816126760152818161383801528181613a7701528181613e4e0152613f73015260c05181818161242a01526144e9015260e0518181816143e301526146790152f35b6001600160401b0319909116811790915584519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a15f8080620000c3565b865163f92ee8a960e01b8152600490fd5b8451639fabe1c160e01b8152600490fd5b90508416155f62000082565b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b0382168203620001a55756fe6080806040526004361015610012575f80fd5b5f3560e01c90816246365d146126435750806301ffc9a7146125845780630e230f97146124f1578063248a9ca3146124875780632937ccc61461244e57806329df6479146123e05780632f2ff15d1461236657806336568abe146122dc5780633a7dfb3a146122845780634f1ef28614611dca57806352d1902d14611d0b57806353c6dcd514611ae35780635c60da1b14611a73578063622110d314611a1b5780637130a0e7146119e257806375ae094f1461198457806375d0c0dc1461190957806377a24f36146111fd578063796f077b146111c457806384b0196e1461100e57806391d1485414610f7a57806394053b3414610dd557806397de148714610a3f578063a0a8e460146109c4578063a2152d1814610951578063a217fddf14610919578063acb8cc49146108cc578063ad3cb1cc1461084d578063b845291914610813578063c0464356146107da578063c46a4ae11461078a578063ce5263c314610521578063cf59d007146104ae578063d547741f14610434578063d69c3d30146103da578063e30e9ae314610398578063e5c97b131461024f578063eb231345146102165763ffb5d589146101c8575f80fd5b346102125760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021257602061020a61020536612a01565b612f77565b604051908152f35b5f80fd5b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021257602060405160148152f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360161018081126102125761016013610212576101643567ffffffffffffffff8111610212576102a4903690600401612805565b6102ac612b02565b9163ffffffff928380461691160361035e576102e292506102cb612d7f565b916102d4612b27565b9160c4359360e435906134dd565b60a4358015610334576102f53482613e4c565b6102fd612b27565b90610104359073ffffffffffffffffffffffffffffffffffffffff82168203610212576103329261032d36612a01565b613ec3565b005b60046040517f58574dc5000000000000000000000000000000000000000000000000000000008152fd5b602483610369612b02565b6040517f16d5b96e00000000000000000000000000000000000000000000000000000000815291166004820152fd5b346102125760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021257602061020a6103d536612a01565b612f0c565b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102125760207f9e5d0d3a4c7e8d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d0254604051908152f35b346102125760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576103326004356104716126bc565b90805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526104a9600160405f200154612fb4565b6133c0565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360160c08112610212576080136102125760843560a43573ffffffffffffffffffffffffffffffffffffffff8116810361021257811561033457610332916105173482613e4c565b3361032d36612a01565b61052a36612934565b929596909394610538612d4b565b50335f9081527ff42a8efff468fcef324535266976260ddee6dd8f36b86533dba0904c131e743060205260409020547fc590cf330eebec919872b979b32e0cfb49fcb1f4a0cef88b6b29d9a1713fb87d9060ff16156107535750906105a6916105a034613830565b9861376c565b946105d76105cf73ffffffffffffffffffffffffffffffffffffffff96878951169336916127cf565b923690612a7f565b90858751169080820361071c5750505f6105f087612f0c565b958083511661068f575b508251156106655760a09761061261061b96896141e4565b429388886138b4565b61065e60405180936060809173ffffffffffffffffffffffffffffffffffffffff8082511685526020820151166020850152604081015160408501520151910152565b6080820152f35b60046040517f2b3f4713000000000000000000000000000000000000000000000000000000008152fd5b905061069a82612f77565b90806020890151169060208401511680821480159061070a575b6106be57506105fa565b90836084926040808c015192015191604051937ffbd068270000000000000000000000000000000000000000000000000000000085526004850152602484015260448301526064820152fd5b506040890151604085015114156106b4565b60449250604051917f30a3507600000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b604490604051907fe2517d3f0000000000000000000000000000000000000000000000000000000082523360048301526024820152fd5b346102125760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102125760406107cc6107c736612a01565b612ed0565b825191825215156020820152f35b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021257602060405160028152f35b34610212576101607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021257602061020a612d7f565b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576108c860405161088a8161271c565b600581527f352e302e300000000000000000000000000000000000000000000000000000006020820152604051918291602083526020830190612854565b0390f35b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576108c8610905612ac9565b604051918291602083526020830190612854565b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102125760206040515f8152f35b346102125760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102125761098c6103d536612a01565b5f527f9e5d0d3a4c7e8d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d006020526020600160405f200154604051908152f35b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576108c8604051610a018161271c565b600581527f302e302e330000000000000000000000000000000000000000000000000000006020820152604051918291602083526020830190612854565b346102125760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102125760043567ffffffffffffffff8082116102125736602383011215610212578082600401351161021257366024836004013560071b840101116102125760243581811161021257610ac2903690600401612897565b60449391933583811161021257610add903690600401612897565b91909360643590811161021257610af8903690600401612897565b335f9081527f3dcf8136aea27c4e59adba4b5b5f26ca29dc6e5a703de453b2361643c43217c7602052604090205490959192907f2294cdd65f68a33a3e7097ee86a70cf0f4adf6b27328c6b154d47834bdfcdf4e9060ff1615610753575080856004013514801590610dcb575b8015610dc1575b610d97575f5b85600401358110610b7f57005b610b9d6103d536610b98848a6004013560248c01612d6f565b612a7f565b90815f527f9e5d0d3a4c7e8d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d0060205260ff60405f205416610d6657815f527f9e5d0d3a4c7e8d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d0060205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055610c3781886004013560248a01612d6f565b9183821015610d39577f0bc97795ba26a99c8fa64f7929d95dc237ecae3c6db64508d07ca5cf1ef8a2a286866001957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8f8f898f9291610cae82610ca6610cb5958260051b810190612b6b565b96909a612d23565b3598612d23565b3560405196879573ffffffffffffffffffffffffffffffffffffffff80610cdb836126df565b168852610cea602083016126df565b166020880152604081013560408801526060809101359087015260e060808701528260e08701528261010097888801375f87848801015260a086015260c085015201168101030190a201610b72565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b602482604051907f8bdcb6160000000000000000000000000000000000000000000000000000000082526004820152fd5b60046040517fa24a13a6000000000000000000000000000000000000000000000000000000008152fd5b5085841415610b6c565b5083811415610b65565b610e11610e09610e19610de736612934565b96909794610df99a949399929a612d4b565b50610e0334613830565b9661376c565b9636916127cf565b953690612a7f565b73ffffffffffffffffffffffffffffffffffffffff9182865116338103610f4357505f90610e4687612f0c565b9380845116610eb6575b5087511561066557608097610e69610e7197838a614073565b8742956138b4565b610eb460405180926060809173ffffffffffffffffffffffffffffffffffffffff8082511685526020820151166020850152604081015160408501520151910152565bf35b9150610ec183612f77565b918060208901511690602085015116808214801590610f31575b610ee55750610e50565b90846084926040808c015192015191604051937ffbd068270000000000000000000000000000000000000000000000000000000085526004850152602484015260448301526064820152fd5b50604089015160408601511415610edb565b604490604051907f30a350760000000000000000000000000000000000000000000000000000000082526004820152336024820152fd5b346102125760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021257610fb16126bc565b6004355f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205273ffffffffffffffffffffffffffffffffffffffff60405f2091165f52602052602060ff60405f2054166040519015158152f35b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212577fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10054158061119b575b1561113d57611072613c86565b61107a613d8d565b9060405191602083019280841067ffffffffffffffff851117611110576110f06108c8926110e2956040525f83526040519586957f0f00000000000000000000000000000000000000000000000000000000000000875260e0602088015260e0870190612854565b908582036040870152612854565b904660608501523060808501525f60a085015283820360c0850152612901565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4549503731323a20556e696e697469616c697a656400000000000000000000006044820152fd5b507fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1015415611065565b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576108c86109056128c8565b346102125760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021257611234612699565b61123c6126bc565b60443567ffffffffffffffff81116102125761125c903690600401612897565b91907ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009283549267ffffffffffffffff8416958615806118fb575b600180981490816118f1575b1590816118e8575b506118be57867fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000861617865560ff8560401c1615611889575b73ffffffffffffffffffffffffffffffffffffffff8082161590811561187e575b5061185457611312614729565b61131a614729565b6113226128c8565b61132a612ac9565b90611333614729565b61133b614729565b80519067ffffffffffffffff82116111105781906113797fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10254613c35565b601f81116117c5575b50602090601f83116001146116aa575f9261169f575b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff828b1b9260031b1c1916177fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d102555b805167ffffffffffffffff8111611110577fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d103916114268354613c35565b601f811161163a575b50602090601f83116001146115725791806114ce94926114d49796945f92611567575b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff828d1b9260031b1c19161790555b5f7fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d100555f7fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1015561302e565b50613109565b50845f5b82811061154357508460ff8560401c16156114ef57005b7fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d291817fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff60209354169055604051908152a1005b61155e611559611554838688612d23565b612b4a565b613207565b500185906114d8565b015190508c80611452565b82917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08c941691855f527f5f9ce34815f8e11431c7bb75a8e6886a91478f7ffc1dbb0a98dc240fddd76b75925f5b8181106116225750916114d49897959391856114ce989694106115eb575b505050811b019055611483565b01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88460031b161c191690558c80806115de565b8284015185558e9690940193602093840193016115c0565b835f527f5f9ce34815f8e11431c7bb75a8e6886a91478f7ffc1dbb0a98dc240fddd76b75601f840160051c81019160208510611695575b601f0160051c01908b905b82811061168a57505061142f565b5f8155018b9061167c565b9091508190611671565b015190508a80611398565b908b93507fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1025f527f42ad5d3e1f2e6e70edcf6d991b8a3023d3fca8047a131592f9edb9fd9b89d57d915f5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0851681106117ad5750837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0811610611776575b505050811b017fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d102556113e9565b01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88460031b161c191690558a8080611749565b8183015184558d9590930192602092830192016116f5565b9091507fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1025f527f42ad5d3e1f2e6e70edcf6d991b8a3023d3fca8047a131592f9edb9fd9b89d57d601f840160051c8101916020851061184a575b84939291601f8e920160051c01915b82811061183c575050611382565b5f81558594508d910161182e565b909150819061181f565b60046040517f9fabe1c1000000000000000000000000000000000000000000000000000000008152fd5b905082161588611305565b680100000000000000017fffffffffffffffffffffffffffffffffffffffffffffff00000000000000000086161786556112e4565b60046040517ff92ee8a9000000000000000000000000000000000000000000000000000000008152fd5b905015886112ab565b303b1591506112a3565b5060ff8560401c1615611297565b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576108c86040516119468161271c565b600d81527f5a6f726120436f6d6d656e7473000000000000000000000000000000000000006020820152604051918291602083526020830190612854565b34610212577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc602081360112610212576004359067ffffffffffffffff8211610212576101c09082360301126102125761020a602091600401612bbc565b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576020604051600a8152f35b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102125760206040517fc590cf330eebec919872b979b32e0cfb49fcb1f4a0cef88b6b29d9a1713fb87d8152f35b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021257602073ffffffffffffffffffffffffffffffffffffffff7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416604051908152f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc604081360112610212576004359067ffffffffffffffff808311610212576101c0836004019284360301126102125760243590811161021257611b4b903690600401612805565b6101a484019391611b5b85612b16565b63ffffffff9081804616911603611cff575090611b9991611b7b85612bbc565b91611b8586612b4a565b9161014486013593610164870135906134dd565b611bbd611ba860448301612b4a565b611bb184612b4a565b9060648401359061376c565b90611bc734613830565b916084820191610104810193611bdd8587612b6b565b939095611bfa611bef60248601612b4a565b97610e093689612a7f565b935f94611c0684612f0c565b9673ffffffffffffffffffffffffffffffffffffffff80825116611c72575b5050511561066557611c57611c4e611c5f936103329b611c496101249c8989614073565b612b6b565b99909201612b4a565b9736916127cf565b90611c6c42963690612a7f565b946138b4565b909650611c7e87612f77565b968160208701511691602082015116808314801590611ced575b611ca25750611c25565b6084935060408088015192015191604051937ffbd068270000000000000000000000000000000000000000000000000000000085526004850152602484015260448301526064820152fd5b50604087015160408301511415611c98565b60249061036987612b16565b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102125773ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163003611da05760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b60046040517fe07c8dba000000000000000000000000000000000000000000000000000000008152fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60408136011261021257611dfd612699565b90602491823567ffffffffffffffff8111610212573660238201121561021257611e3090369085816004013591016127cf565b9073ffffffffffffffffffffffffffffffffffffffff807f000000000000000000000000000000000000000000000000000000000000000016803014908115612256575b50611da057335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602090815260409091205490919060ff161561222057821693604051907f75d0c0dc00000000000000000000000000000000000000000000000000000000918281525f81600481838b5af19081156121ba575f91612206575b50604051908382525f82600481305afa9182156121ba575f926121ea575b5084815191012090848151910120036120fe5750506040517f52d1902d0000000000000000000000000000000000000000000000000000000081528181600481885afa5f91816120cf575b50611f96578585604051907f4c9c8ce30000000000000000000000000000000000000000000000000000000082526004820152fd5b8490867f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc918281036120a15750843b156120725750817fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055604051907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a283511561203f57505f83819261033295519101845af4612039614782565b91614b81565b925050503461204a57005b807fb398979f0000000000000000000000000000000000000000000000000000000060049252fd5b82604051907f4c9c8ce30000000000000000000000000000000000000000000000000000000082526004820152fd5b604051907faa1d49a40000000000000000000000000000000000000000000000000000000082526004820152fd5b9091508281813d83116120f7575b6120e78183612754565b8101031261021257519087611f61565b503d6120dd565b9250925050604051918083525f83600481305afa9283156121ba575f936121c5575b505f929360048492604051958693849283525af180156121ba5761218494612192935f92612196575b506040519586957fa23cbf7b000000000000000000000000000000000000000000000000000000008752604060048801526044870190612854565b928584030190850152612854565b0390fd5b6121b39192503d805f833e6121ab8183612754565b81019061347e565b9086612149565b6040513d5f823e3d90fd5b5f93509360046121e08593963d8086833e6121ab8183612754565b9450509390612120565b6121ff9192503d805f833e6121ab8183612754565b9089611f16565b61221a91503d805f833e6121ab8183612754565b88611ef8565b6044865f604051917fe2517d3f000000000000000000000000000000000000000000000000000000008352336004840152820152fd5b9050817f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416141586611e74565b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102125760206040517f2294cdd65f68a33a3e7097ee86a70cf0f4adf6b27328c6b154d47834bdfcdf4e8152f35b346102125760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576123136126bc565b3373ffffffffffffffffffffffffffffffffffffffff82160361233c57610332906004356133c0565b60046040517f6697b232000000000000000000000000000000000000000000000000000000008152fd5b346102125760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576103326004356123a36126bc565b90805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526123db600160405f200154612fb4565b6132fe565b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021257602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576020604051601e8152f35b346102125760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576004355f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526020600160405f200154604051908152f35b346102125760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102125773ffffffffffffffffffffffffffffffffffffffff61253d612699565b165f527fc84b62be2e432010aa71cc1bbdba4c7b02245544521aa5beae20093c7062240060205260405f206024355f52602052602060ff60405f2054166040519015158152f35b346102125760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576004357fffffffff00000000000000000000000000000000000000000000000000000000811680910361021257807f7965db0b0000000000000000000000000000000000000000000000000000000060209214908115612619575b506040519015158152f35b7f01ffc9a7000000000000000000000000000000000000000000000000000000009150148261260e565b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576020907f00000000000000000000000000000000000000000000000000000000000000008152f35b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361021257565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361021257565b359073ffffffffffffffffffffffffffffffffffffffff8216820361021257565b6080810190811067ffffffffffffffff82111761111057604052565b6040810190811067ffffffffffffffff82111761111057604052565b60c0810190811067ffffffffffffffff82111761111057604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761111057604052565b67ffffffffffffffff811161111057601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b9291926127db82612795565b916127e96040519384612754565b829481845281830111610212578281602093845f960137010152565b9181601f840112156102125782359167ffffffffffffffff8311610212576020838186019501011161021257565b5f5b8381106128445750505f910152565b8181015183820152602001612835565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209361289081518092818752878088019101612833565b0116010190565b9181601f840112156102125782359167ffffffffffffffff8311610212576020808501948460051b01011161021257565b604051906128d58261271c565b600882527f436f6d6d656e74730000000000000000000000000000000000000000000000006020830152565b9081518082526020808093019301915f5b828110612920575050505090565b835185529381019392810192600101612912565b6101407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126102125773ffffffffffffffffffffffffffffffffffffffff916004358381168103610212579260243581811681036102125792604435926064359267ffffffffffffffff84116102125760806129d6847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7c96600401612805565b9590959401126102125760849161010435828116810361021257916101243590811681036102125790565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60809101126102125760405190612a3882612700565b8173ffffffffffffffffffffffffffffffffffffffff6004358181168103610212578252602435908116810361021257602082015260443560408201526060606435910152565b919082608091031261021257604051612a9781612700565b6060808294612aa5816126df565b8452612ab3602082016126df565b6020850152604081013560408501520135910152565b60405190612ad68261271c565b600182527f31000000000000000000000000000000000000000000000000000000000000006020830152565b6101443563ffffffff811681036102125790565b3563ffffffff811681036102125790565b60843573ffffffffffffffffffffffffffffffffffffffff811681036102125790565b3573ffffffffffffffffffffffffffffffffffffffff811681036102125790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610212570180359067ffffffffffffffff82116102125760200191813603831361021257565b612bc860408201612b4a565b90612bd281612b4a565b612be7612be23660808501612a7f565b613b1a565b92612c00612bf9610100850185612b6b565b36916127cf565b6020815191012091612c1460208501612b4a565b91612c226101208601612b4a565b90612c306101808701612b16565b93612c3e6101a08801612b16565b95604051987f24ba1c68e2ba339f50981ed71cb6c98d1a28a067665f83ea649433ab0b0b74e760208b015273ffffffffffffffffffffffffffffffffffffffff80968180961660408d015260608b013560608d01521660808b015260a08a015260c089015261014087013560e0890152610160870135610100890152166101208701521661014085015263ffffffff91828092166101608601521661018084015261018083526101a083019280841067ffffffffffffffff85111761111057610180612d1991612d2095604052602081519101209301612b16565b1690613b99565b90565b9190811015610d395760051b0190565b67ffffffffffffffff81116111105760051b60200190565b60405190612d5882612700565b5f6060838281528260208201528260408201520152565b9190811015610d395760071b0190565b60807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021257604051612db581612700565b73ffffffffffffffffffffffffffffffffffffffff90600435828116810361021257815260243590828216820361021257612e0491602082015260443560408201526064356060820152613b1a565b9060843591818316809303610212576101043591821680920361021257610124359163ffffffff908184168094036102125761014435918216809203610212576040519460208601937f01b6c3e5d963f61c036cc10c97aeba0ac10158c6cfa12416c35eeecd0a2508b985526040870152606086015260a435608086015260c43560a086015260e43560c086015260e0850152826101008501526101209081850152835261014083019280841067ffffffffffffffff85111761111057612d2093604052519020613b99565b612ed990612f0c565b90815f527f9e5d0d3a4c7e8d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d0060205260ff60405f20541690565b604051612f536020820180936060809173ffffffffffffffffffffffffffffffffffffffff8082511685526020820151166020850152604081015160408501520151910152565b6080815260a0810181811067ffffffffffffffff8211176111105760405251902090565b612f8090612ed0565b91909115612f8a57565b60046040517fc8e4a46f000000000000000000000000000000000000000000000000000000008152fd5b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260405f20335f5260205260ff60405f20541615612ff55750565b6040517fe2517d3f0000000000000000000000000000000000000000000000000000000081523360048201526024810191909152604490fd5b73ffffffffffffffffffffffffffffffffffffffff165f8181527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d60205260409020547f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268009060ff16613103575f805260205260405f20815f5260205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905533905f7f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4600190565b50505f90565b73ffffffffffffffffffffffffffffffffffffffff165f8181527f3dcf8136aea27c4e59adba4b5b5f26ca29dc6e5a703de453b2361643c43217c760205260409020547f2294cdd65f68a33a3e7097ee86a70cf0f4adf6b27328c6b154d47834bdfcdf4e91907f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268009060ff1661320057825f5260205260405f20815f5260205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b5050505f90565b73ffffffffffffffffffffffffffffffffffffffff165f8181527ff42a8efff468fcef324535266976260ddee6dd8f36b86533dba0904c131e743060205260409020547fc590cf330eebec919872b979b32e0cfb49fcb1f4a0cef88b6b29d9a1713fb87d91907f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268009060ff1661320057825f5260205260405f20815f5260205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008060205273ffffffffffffffffffffffffffffffffffffffff60405f20921691825f5260205260ff60405f205416155f1461320057825f5260205260405f20815f5260205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008060205273ffffffffffffffffffffffffffffffffffffffff60405f20921691825f5260205260ff60405f2054165f1461320057825f5260205260405f20815f5260205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905533917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b6020818303126102125780519067ffffffffffffffff8211610212570181601f820112156102125780516134b181612795565b926134bf6040519485612754565b8184526020828401011161021257612d209160208085019101612833565b919390929480421161373b575073ffffffffffffffffffffffffffffffffffffffff9384821690815f527fc84b62be2e432010aa71cc1bbdba4c7b02245544521aa5beae20093c7062240096602095888752604098895f20825f52885260ff8a5f2054166136ec579061358c939291855f528852895f20905f528752885f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905536916127cf565b906135978285614c21565b5060048198929810156136bf571596876136b3575b50505084156135eb575b50505050156135c25750565b600490517f8baa579f000000000000000000000000000000000000000000000000000000008152fd5b5f9293945090829186516136668161363a898201947f1626ba7e00000000000000000000000000000000000000000000000000000000998a875260248401528b60448401526064830190612854565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282612754565b51915afa91613673614782565b836136a6575b8361368b575b5050505f8080806135b6565b908092935081805181010312610212570151145f808061367f565b9250808351101592613679565b161494505f80806135ac565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b5088517f17a63d2100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff861660048201526024810191909152604490fd5b602490604051907f627913020000000000000000000000000000000000000000000000000000000082526004820152fd5b9190613776612d4b565b507f9e5d0d3a4c7e8d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d02918254927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841461380357600184019055604051936137d685612700565b73ffffffffffffffffffffffffffffffffffffffff80921685521660208401526040830152606082015290565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b801561389c577f0000000000000000000000000000000000000000000000000000000000000000808203613865575050600190565b60449250604051917f72a3bac100000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b505f90565b8181029291811591840414171561380357565b9096939591949294815f527f9e5d0d3a4c7e8d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d00928360205260409360ff855f205416613aea57916139f4917feee8cd8185162ef6658993ba27320099864440fe513fb9ae1c9ab7421113b57293855f52602052855f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905585519283928c613995856101a0926060809173ffffffffffffffffffffffffffffffffffffffff8082511685526020820151166020850152604081015160408501520151910152565b8c60808601526139de60a086018b6060809173ffffffffffffffffffffffffffffffffffffffff8082511685526020820151166020850152604081015160408501520151910152565b8b61012086015280610140860152840190612854565b9061016083015273ffffffffffffffffffffffffffffffffffffffff88166101808301520390a28051613a268161271c565b600781527f436f6d6d656e7400000000000000000000000000000000000000000000000000602082015294613aac575b5082613a64575b5050505050565b613a74613a9c91613aa2966142b9565b927f0000000000000000000000000000000000000000000000000000000000000000906138a1565b916142eb565b5f80808080613a5d565b90935051613ab98161271c565b600d81527f436f6d6d656e74205265706c79000000000000000000000000000000000000006020820152925f613a56565b6024848651907f8bdcb6160000000000000000000000000000000000000000000000000000000082526004820152fd5b73ffffffffffffffffffffffffffffffffffffffff80602083015116916060604082015192825116910151906040519260208401947ff50b5c2dfba3cda25b5ff058b098c69c9663b57c8f7fa26975400600bdc66cb6865260408501526060840152608083015260a082015260a08152613b9381612738565b51902090565b604291613ba46150e1565b90613bad61514b565b906040519160208301937f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f85526040840152606083015260808201523060a082015260a08152613bfc81612738565b51902090604051917f19010000000000000000000000000000000000000000000000000000000000008352600283015260228201522090565b90600182811c92168015613c7c575b6020831014613c4f57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f1691613c44565b604051905f827fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10291825492613cba84613c35565b80845293602091600191828116908115613d485750600114613ce8575b505050613ce692500383612754565b565b5f9081527f42ad5d3e1f2e6e70edcf6d991b8a3023d3fca8047a131592f9edb9fd9b89d57d9590935091905b828410613d305750613ce69450505081016020015f8080613cd7565b85548885018301529485019487945092810192613d14565b91505060209350613ce69592507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0091501682840152151560051b8201015f8080613cd7565b604051905f827fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10391825492613dc184613c35565b80845293602091600191828116908115613d485750600114613dec57505050613ce692500383612754565b5f9081527f5f9ce34815f8e11431c7bb75a8e6886a91478f7ffc1dbb0a98dc240fddd76b759590935091905b828410613e345750613ce69450505081016020015f8080613cd7565b85548885018301529485019487945092810192613e18565b7f000000000000000000000000000000000000000000000000000000000000000090613e7882826138a1565b8303613e8357505050565b60449291613e90916138a1565b604051917f72a3bac100000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b73ffffffffffffffffffffffffffffffffffffffff8080835116931692831461403157613eef82612f0c565b94855f527f9e5d0d3a4c7e8d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d008060205260ff60405f20541615612f8a57865f52602052600160405f200193845495808701809711613803577fb2c2c602f3c1f47735b32d6a660d741f13d555e25493cf652586575aac1962d4966101009655613fd584865116613f987f0000000000000000000000000000000000000000000000000000000000000000846138a1565b8560405192613fa68461271c565b600f84527f537061726b656420436f6d6d656e74000000000000000000000000000000000060208501526142eb565b61401860405180966060809173ffffffffffffffffffffffffffffffffffffffff8082511685526020820151166020850152604081015160408501520151910152565b608085015260a08401524260c08401521660e0820152a2565b60046040517f2951d9c9000000000000000000000000000000000000000000000000000000008152fd5b90816020910312610212575180151581036102125790565b909173ffffffffffffffffffffffffffffffffffffffff80821680614107575b50602083019281845116916140b3846040840194855184865116916147b1565b6140ff57806140c995511692519151169161491e565b156140d5571561033457565b60046040517fd8a26f99000000000000000000000000000000000000000000000000000000008152fd5b505050505050565b823b156141ba576020828551166024604051809481937fa2e1a8d800000000000000000000000000000000000000000000000000000000835260048301525afa9081156121ba575f9161418b575b5015614161575f614093565b60046040517fee4e75cf000000000000000000000000000000000000000000000000000000008152fd5b6141ad915060203d6020116141b3575b6141a58183612754565b81019061405b565b5f614155565b503d61419b565b60046040517f4eb2199c000000000000000000000000000000000000000000000000000000008152fd5b9073ffffffffffffffffffffffffffffffffffffffff80821680614240575b5060208301928184511691614223846040840194855184865116916147b1565b613a5d578061423995511692519151169161491e565b156140d557565b823b156141ba576020828551166024604051809481937fa2e1a8d800000000000000000000000000000000000000000000000000000000835260048301525afa9081156121ba575f9161429a575b5015614161575f614203565b6142b3915060203d6020116141b3576141a58183612754565b5f61428e565b9073ffffffffffffffffffffffffffffffffffffffff9182825116156142df5750511690565b9050612d209150614ab8565b73ffffffffffffffffffffffffffffffffffffffff93918416928315801593919291906147205760ff60035b1661432181612d33565b94604096879161433383519889612754565b83885261433f84612d33565b95602092838a01927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08099013685376143b761437a88612d33565b976143878851998a612754565b8089528a61439482612d33565b0136888b01376143af6143a682612d33565b98519889612754565b808852612d33565b8686019901368a37156146575788600a02600a81048a036138035760649004906143e08b614b47565b8d7f00000000000000000000000000000000000000000000000000000000000000001690528161440f88614b47565b527f538b62d60000000000000000000000000000000000000000000000000000000061443a87614b47565b5289601402601481048b03613803576144a1928c9261445f606461449c940494614b54565b528261446a8a614b54565b527feb4a589f0000000000000000000000000000000000000000000000000000000061449589614b54565b528b614b74565b614b74565b908b6144ac8b614b64565b911690526144b985614b64565b527ffbaf211e000000000000000000000000000000000000000000000000000000006144e484614b64565b525b897f00000000000000000000000000000000000000000000000000000000000000001697883b1561021257969493929089989698519a8b987f300def95000000000000000000000000000000000000000000000000000000008a5260848a0190608060048c01525180915260a48a0192915f905b82821061463a575050505061459b83917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc95868b83030160248c0152612901565b91848984030160448a015251918281520197915f5b82811061460157505050506145d3868694938593845f9a03016064850152612854565b03925af180156145f7576145e5575050565b67ffffffffffffffff82116111105752565b50513d5f823e3d90fd5b83517fffffffff00000000000000000000000000000000000000000000000000000000168a52988101988b9850928101926001016145b0565b8351811685528e9b5093860193928601926001919091019061455a565b5087601e02601e810489036138035760646146d791046146768b614b47565b8d7f0000000000000000000000000000000000000000000000000000000000000000169052806146a588614b47565b527f538b62d6000000000000000000000000000000000000000000000000000000006146d087614b47565b5289614b74565b908b6146e28b614b54565b911690526146ef85614b54565b527ffbaf211e0000000000000000000000000000000000000000000000000000000061471a84614b54565b526144e6565b60ff6002614317565b60ff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460401c161561475857565b60046040517fd7e6bcf8000000000000000000000000000000000000000000000000000000008152fd5b3d156147ac573d9061479382612795565b916147a16040519384612754565b82523d5f602084013e565b606090565b9291906147be8185614c5b565b156148c2575073ffffffffffffffffffffffffffffffffffffffff80931660405193807f2f54bf6e00000000000000000000000000000000000000000000000000000000938487521660048601526020938486602481865afa9586156121ba575f966148a3575b508515614835575b505050505090565b909192939450168015159283614855575b50505090505f8080808061482d565b849293506024906040519485938492835260048301525afa9182156121ba575f92614886575b5050805f8080614846565b61489c9250803d106141b3576141a58183612754565b5f8061487b565b6148bb919650853d87116141b3576141a58183612754565b945f614825565b9291906148d0908484614d00565b9283156148de575b50505090565b73ffffffffffffffffffffffffffffffffffffffff82161515935090918361490c575b5050505f80806148d8565b6149169350614d00565b5f8080614901565b906149298183614c5b565b15614a66575073ffffffffffffffffffffffffffffffffffffffff8091169060405191817f70a0823100000000000000000000000000000000000000000000000000000000948585521660048401526020918284602481855afa9384156121ba575f94614a37575b50670de0b6b3a7640000809410958615966149b0575b50505050505090565b909192939495501680151594856149d2575b50505050505f80808080806149a7565b839495506024906040949394519485938492835260048301525afa9182156121ba575f92614a09575b505010155f808080806149c2565b90809250813d8311614a30575b614a208183612754565b8101031261021257515f806149fb565b503d614a16565b9093508281813d8311614a5f575b614a4f8183612754565b810103126102125751925f614991565b503d614a45565b929091614a74908484614d90565b928315614a815750505090565b73ffffffffffffffffffffffffffffffffffffffff821615159350909183614aae575050505f80806148d8565b6149169350614d90565b6020810173ffffffffffffffffffffffffffffffffffffffff91614ae483835116604083015190614e51565b838116614b405750614af590614ef2565b828116614b3a575081614b09915116614f70565b908116612d205760046040517f06e65373000000000000000000000000000000000000000000000000000000008152fd5b91505090565b9250505090565b805115610d395760200190565b805160011015610d395760400190565b805160021015610d395760600190565b9190820391821161380357565b90614bc05750805115614b9657805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580614c18575b614bd1575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15614bc9565b8151919060418303614c5157614c4a9250602082015190606060408401519301515f1a90615190565b9192909190565b50505f9160029190565b90159081614c67575090565b73ffffffffffffffffffffffffffffffffffffffff91506020906024604051809481937f01ffc9a70000000000000000000000000000000000000000000000000000000083527f604c364a000000000000000000000000000000000000000000000000000000006004840152165afa9081156121ba575f91614ce7575090565b612d20915060203d6020116141b3576141a58183612754565b60646020929373ffffffffffffffffffffffffffffffffffffffff809360405196879586947f23bd0386000000000000000000000000000000000000000000000000000000008652166004850152602484015260026044840152165afa5f9181614d6f575b50612d2057505f90565b614d8991925060203d6020116141b3576141a58183612754565b905f614d65565b60446020929373ffffffffffffffffffffffffffffffffffffffff809360405196879586947efdd58e0000000000000000000000000000000000000000000000000000000086521660048501526024840152165afa5f9181614dfc575b50614df757505f90565b151590565b9091506020813d602011614e28575b81614e1860209383612754565b810103126102125751905f614ded565b3d9150614e0b565b519073ffffffffffffffffffffffffffffffffffffffff8216820361021257565b90614e5c9082614c5b565b614e6557505f90565b602073ffffffffffffffffffffffffffffffffffffffff916004604051809481937f3fb80b15000000000000000000000000000000000000000000000000000000008352165afa5f9181614ebd5750612d2057505f90565b9091506020813d602011614eea575b81614ed960209383612754565b8101031261021257614d8990614e30565b3d9150614ecc565b6020604073ffffffffffffffffffffffffffffffffffffffff82840151169201516024604051809481937f5e4e040400000000000000000000000000000000000000000000000000000000835260048301525afa5f9181614ebd5750612d2057505f90565b51906bffffffffffffffffffffffff8216820361021257565b73ffffffffffffffffffffffffffffffffffffffff809116906040517f79502c5500000000000000000000000000000000000000000000000000000000815260c081600481865afa5f9181615061575b50615053575b506020600492604051938480927f8da5cb5b0000000000000000000000000000000000000000000000000000000082525afa5f9281615017575b5061500b5750505f90565b8116612d205780613103565b9092506020813d60201161504b575b8161503360209383612754565b810103126102125761504490614e30565b915f615000565b3d9150615026565b81168015614fc65791505090565b90915060c0813d60c0116150d9575b8161507d60c09383612754565b810103126102125761508e81614e30565b5061509b60208201614f57565b5060408101519083821682036102125760a0816150bd60606150d19401614f57565b506150ca60808201614e30565b5001614f57565b50905f614fc0565b3d9150615070565b6150e9613c86565b80519081156150f9576020012090565b50507fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1005480156151265790565b507fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47090565b615153613d8d565b8051908115615163576020012090565b50507fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1015480156151265790565b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08411615214579160209360809260ff5f9560405194855216868401526040830152606082015282805260015afa156121ba575f5173ffffffffffffffffffffffffffffffffffffffff81161561520a57905f905f90565b505f906001905f90565b5050505f916003919056fea264697066735822122062c67af76306dae11e2ff1ae7b212190cd5be2b4ededdea01c6c81c5f0b577ae64736f6c63430008170033000000000000000000000000000000000000000000000000000000e8d4a510000000000000000000000000007777777f279eba3d3ad8f4e708545291a6fdba8b0000000000000000000000007a810dcd0f8d83b20212326813db6ef7e9fd030c
Deployed Bytecode
0x6080806040526004361015610012575f80fd5b5f3560e01c90816246365d146126435750806301ffc9a7146125845780630e230f97146124f1578063248a9ca3146124875780632937ccc61461244e57806329df6479146123e05780632f2ff15d1461236657806336568abe146122dc5780633a7dfb3a146122845780634f1ef28614611dca57806352d1902d14611d0b57806353c6dcd514611ae35780635c60da1b14611a73578063622110d314611a1b5780637130a0e7146119e257806375ae094f1461198457806375d0c0dc1461190957806377a24f36146111fd578063796f077b146111c457806384b0196e1461100e57806391d1485414610f7a57806394053b3414610dd557806397de148714610a3f578063a0a8e460146109c4578063a2152d1814610951578063a217fddf14610919578063acb8cc49146108cc578063ad3cb1cc1461084d578063b845291914610813578063c0464356146107da578063c46a4ae11461078a578063ce5263c314610521578063cf59d007146104ae578063d547741f14610434578063d69c3d30146103da578063e30e9ae314610398578063e5c97b131461024f578063eb231345146102165763ffb5d589146101c8575f80fd5b346102125760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021257602061020a61020536612a01565b612f77565b604051908152f35b5f80fd5b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021257602060405160148152f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360161018081126102125761016013610212576101643567ffffffffffffffff8111610212576102a4903690600401612805565b6102ac612b02565b9163ffffffff928380461691160361035e576102e292506102cb612d7f565b916102d4612b27565b9160c4359360e435906134dd565b60a4358015610334576102f53482613e4c565b6102fd612b27565b90610104359073ffffffffffffffffffffffffffffffffffffffff82168203610212576103329261032d36612a01565b613ec3565b005b60046040517f58574dc5000000000000000000000000000000000000000000000000000000008152fd5b602483610369612b02565b6040517f16d5b96e00000000000000000000000000000000000000000000000000000000815291166004820152fd5b346102125760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021257602061020a6103d536612a01565b612f0c565b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102125760207f9e5d0d3a4c7e8d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d0254604051908152f35b346102125760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576103326004356104716126bc565b90805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526104a9600160405f200154612fb4565b6133c0565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360160c08112610212576080136102125760843560a43573ffffffffffffffffffffffffffffffffffffffff8116810361021257811561033457610332916105173482613e4c565b3361032d36612a01565b61052a36612934565b929596909394610538612d4b565b50335f9081527ff42a8efff468fcef324535266976260ddee6dd8f36b86533dba0904c131e743060205260409020547fc590cf330eebec919872b979b32e0cfb49fcb1f4a0cef88b6b29d9a1713fb87d9060ff16156107535750906105a6916105a034613830565b9861376c565b946105d76105cf73ffffffffffffffffffffffffffffffffffffffff96878951169336916127cf565b923690612a7f565b90858751169080820361071c5750505f6105f087612f0c565b958083511661068f575b508251156106655760a09761061261061b96896141e4565b429388886138b4565b61065e60405180936060809173ffffffffffffffffffffffffffffffffffffffff8082511685526020820151166020850152604081015160408501520151910152565b6080820152f35b60046040517f2b3f4713000000000000000000000000000000000000000000000000000000008152fd5b905061069a82612f77565b90806020890151169060208401511680821480159061070a575b6106be57506105fa565b90836084926040808c015192015191604051937ffbd068270000000000000000000000000000000000000000000000000000000085526004850152602484015260448301526064820152fd5b506040890151604085015114156106b4565b60449250604051917f30a3507600000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b604490604051907fe2517d3f0000000000000000000000000000000000000000000000000000000082523360048301526024820152fd5b346102125760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102125760406107cc6107c736612a01565b612ed0565b825191825215156020820152f35b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021257602060405160028152f35b34610212576101607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021257602061020a612d7f565b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576108c860405161088a8161271c565b600581527f352e302e300000000000000000000000000000000000000000000000000000006020820152604051918291602083526020830190612854565b0390f35b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576108c8610905612ac9565b604051918291602083526020830190612854565b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102125760206040515f8152f35b346102125760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102125761098c6103d536612a01565b5f527f9e5d0d3a4c7e8d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d006020526020600160405f200154604051908152f35b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576108c8604051610a018161271c565b600581527f302e302e330000000000000000000000000000000000000000000000000000006020820152604051918291602083526020830190612854565b346102125760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102125760043567ffffffffffffffff8082116102125736602383011215610212578082600401351161021257366024836004013560071b840101116102125760243581811161021257610ac2903690600401612897565b60449391933583811161021257610add903690600401612897565b91909360643590811161021257610af8903690600401612897565b335f9081527f3dcf8136aea27c4e59adba4b5b5f26ca29dc6e5a703de453b2361643c43217c7602052604090205490959192907f2294cdd65f68a33a3e7097ee86a70cf0f4adf6b27328c6b154d47834bdfcdf4e9060ff1615610753575080856004013514801590610dcb575b8015610dc1575b610d97575f5b85600401358110610b7f57005b610b9d6103d536610b98848a6004013560248c01612d6f565b612a7f565b90815f527f9e5d0d3a4c7e8d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d0060205260ff60405f205416610d6657815f527f9e5d0d3a4c7e8d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d0060205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055610c3781886004013560248a01612d6f565b9183821015610d39577f0bc97795ba26a99c8fa64f7929d95dc237ecae3c6db64508d07ca5cf1ef8a2a286866001957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8f8f898f9291610cae82610ca6610cb5958260051b810190612b6b565b96909a612d23565b3598612d23565b3560405196879573ffffffffffffffffffffffffffffffffffffffff80610cdb836126df565b168852610cea602083016126df565b166020880152604081013560408801526060809101359087015260e060808701528260e08701528261010097888801375f87848801015260a086015260c085015201168101030190a201610b72565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b602482604051907f8bdcb6160000000000000000000000000000000000000000000000000000000082526004820152fd5b60046040517fa24a13a6000000000000000000000000000000000000000000000000000000008152fd5b5085841415610b6c565b5083811415610b65565b610e11610e09610e19610de736612934565b96909794610df99a949399929a612d4b565b50610e0334613830565b9661376c565b9636916127cf565b953690612a7f565b73ffffffffffffffffffffffffffffffffffffffff9182865116338103610f4357505f90610e4687612f0c565b9380845116610eb6575b5087511561066557608097610e69610e7197838a614073565b8742956138b4565b610eb460405180926060809173ffffffffffffffffffffffffffffffffffffffff8082511685526020820151166020850152604081015160408501520151910152565bf35b9150610ec183612f77565b918060208901511690602085015116808214801590610f31575b610ee55750610e50565b90846084926040808c015192015191604051937ffbd068270000000000000000000000000000000000000000000000000000000085526004850152602484015260448301526064820152fd5b50604089015160408601511415610edb565b604490604051907f30a350760000000000000000000000000000000000000000000000000000000082526004820152336024820152fd5b346102125760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021257610fb16126bc565b6004355f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205273ffffffffffffffffffffffffffffffffffffffff60405f2091165f52602052602060ff60405f2054166040519015158152f35b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212577fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10054158061119b575b1561113d57611072613c86565b61107a613d8d565b9060405191602083019280841067ffffffffffffffff851117611110576110f06108c8926110e2956040525f83526040519586957f0f00000000000000000000000000000000000000000000000000000000000000875260e0602088015260e0870190612854565b908582036040870152612854565b904660608501523060808501525f60a085015283820360c0850152612901565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4549503731323a20556e696e697469616c697a656400000000000000000000006044820152fd5b507fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1015415611065565b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576108c86109056128c8565b346102125760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021257611234612699565b61123c6126bc565b60443567ffffffffffffffff81116102125761125c903690600401612897565b91907ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009283549267ffffffffffffffff8416958615806118fb575b600180981490816118f1575b1590816118e8575b506118be57867fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000861617865560ff8560401c1615611889575b73ffffffffffffffffffffffffffffffffffffffff8082161590811561187e575b5061185457611312614729565b61131a614729565b6113226128c8565b61132a612ac9565b90611333614729565b61133b614729565b80519067ffffffffffffffff82116111105781906113797fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10254613c35565b601f81116117c5575b50602090601f83116001146116aa575f9261169f575b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff828b1b9260031b1c1916177fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d102555b805167ffffffffffffffff8111611110577fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d103916114268354613c35565b601f811161163a575b50602090601f83116001146115725791806114ce94926114d49796945f92611567575b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff828d1b9260031b1c19161790555b5f7fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d100555f7fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1015561302e565b50613109565b50845f5b82811061154357508460ff8560401c16156114ef57005b7fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d291817fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff60209354169055604051908152a1005b61155e611559611554838688612d23565b612b4a565b613207565b500185906114d8565b015190508c80611452565b82917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08c941691855f527f5f9ce34815f8e11431c7bb75a8e6886a91478f7ffc1dbb0a98dc240fddd76b75925f5b8181106116225750916114d49897959391856114ce989694106115eb575b505050811b019055611483565b01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88460031b161c191690558c80806115de565b8284015185558e9690940193602093840193016115c0565b835f527f5f9ce34815f8e11431c7bb75a8e6886a91478f7ffc1dbb0a98dc240fddd76b75601f840160051c81019160208510611695575b601f0160051c01908b905b82811061168a57505061142f565b5f8155018b9061167c565b9091508190611671565b015190508a80611398565b908b93507fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1025f527f42ad5d3e1f2e6e70edcf6d991b8a3023d3fca8047a131592f9edb9fd9b89d57d915f5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0851681106117ad5750837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0811610611776575b505050811b017fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d102556113e9565b01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88460031b161c191690558a8080611749565b8183015184558d9590930192602092830192016116f5565b9091507fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1025f527f42ad5d3e1f2e6e70edcf6d991b8a3023d3fca8047a131592f9edb9fd9b89d57d601f840160051c8101916020851061184a575b84939291601f8e920160051c01915b82811061183c575050611382565b5f81558594508d910161182e565b909150819061181f565b60046040517f9fabe1c1000000000000000000000000000000000000000000000000000000008152fd5b905082161588611305565b680100000000000000017fffffffffffffffffffffffffffffffffffffffffffffff00000000000000000086161786556112e4565b60046040517ff92ee8a9000000000000000000000000000000000000000000000000000000008152fd5b905015886112ab565b303b1591506112a3565b5060ff8560401c1615611297565b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576108c86040516119468161271c565b600d81527f5a6f726120436f6d6d656e7473000000000000000000000000000000000000006020820152604051918291602083526020830190612854565b34610212577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc602081360112610212576004359067ffffffffffffffff8211610212576101c09082360301126102125761020a602091600401612bbc565b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576020604051600a8152f35b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102125760206040517fc590cf330eebec919872b979b32e0cfb49fcb1f4a0cef88b6b29d9a1713fb87d8152f35b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021257602073ffffffffffffffffffffffffffffffffffffffff7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416604051908152f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc604081360112610212576004359067ffffffffffffffff808311610212576101c0836004019284360301126102125760243590811161021257611b4b903690600401612805565b6101a484019391611b5b85612b16565b63ffffffff9081804616911603611cff575090611b9991611b7b85612bbc565b91611b8586612b4a565b9161014486013593610164870135906134dd565b611bbd611ba860448301612b4a565b611bb184612b4a565b9060648401359061376c565b90611bc734613830565b916084820191610104810193611bdd8587612b6b565b939095611bfa611bef60248601612b4a565b97610e093689612a7f565b935f94611c0684612f0c565b9673ffffffffffffffffffffffffffffffffffffffff80825116611c72575b5050511561066557611c57611c4e611c5f936103329b611c496101249c8989614073565b612b6b565b99909201612b4a565b9736916127cf565b90611c6c42963690612a7f565b946138b4565b909650611c7e87612f77565b968160208701511691602082015116808314801590611ced575b611ca25750611c25565b6084935060408088015192015191604051937ffbd068270000000000000000000000000000000000000000000000000000000085526004850152602484015260448301526064820152fd5b50604087015160408301511415611c98565b60249061036987612b16565b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102125773ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002d54733cbebb23b8a10a935f6b42efaaa9f5d27e163003611da05760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b60046040517fe07c8dba000000000000000000000000000000000000000000000000000000008152fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60408136011261021257611dfd612699565b90602491823567ffffffffffffffff8111610212573660238201121561021257611e3090369085816004013591016127cf565b9073ffffffffffffffffffffffffffffffffffffffff807f0000000000000000000000002d54733cbebb23b8a10a935f6b42efaaa9f5d27e16803014908115612256575b50611da057335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602090815260409091205490919060ff161561222057821693604051907f75d0c0dc00000000000000000000000000000000000000000000000000000000918281525f81600481838b5af19081156121ba575f91612206575b50604051908382525f82600481305afa9182156121ba575f926121ea575b5084815191012090848151910120036120fe5750506040517f52d1902d0000000000000000000000000000000000000000000000000000000081528181600481885afa5f91816120cf575b50611f96578585604051907f4c9c8ce30000000000000000000000000000000000000000000000000000000082526004820152fd5b8490867f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc918281036120a15750843b156120725750817fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055604051907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a283511561203f57505f83819261033295519101845af4612039614782565b91614b81565b925050503461204a57005b807fb398979f0000000000000000000000000000000000000000000000000000000060049252fd5b82604051907f4c9c8ce30000000000000000000000000000000000000000000000000000000082526004820152fd5b604051907faa1d49a40000000000000000000000000000000000000000000000000000000082526004820152fd5b9091508281813d83116120f7575b6120e78183612754565b8101031261021257519087611f61565b503d6120dd565b9250925050604051918083525f83600481305afa9283156121ba575f936121c5575b505f929360048492604051958693849283525af180156121ba5761218494612192935f92612196575b506040519586957fa23cbf7b000000000000000000000000000000000000000000000000000000008752604060048801526044870190612854565b928584030190850152612854565b0390fd5b6121b39192503d805f833e6121ab8183612754565b81019061347e565b9086612149565b6040513d5f823e3d90fd5b5f93509360046121e08593963d8086833e6121ab8183612754565b9450509390612120565b6121ff9192503d805f833e6121ab8183612754565b9089611f16565b61221a91503d805f833e6121ab8183612754565b88611ef8565b6044865f604051917fe2517d3f000000000000000000000000000000000000000000000000000000008352336004840152820152fd5b9050817f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416141586611e74565b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102125760206040517f2294cdd65f68a33a3e7097ee86a70cf0f4adf6b27328c6b154d47834bdfcdf4e8152f35b346102125760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576123136126bc565b3373ffffffffffffffffffffffffffffffffffffffff82160361233c57610332906004356133c0565b60046040517f6697b232000000000000000000000000000000000000000000000000000000008152fd5b346102125760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576103326004356123a36126bc565b90805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526123db600160405f200154612fb4565b6132fe565b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021257602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000007777777f279eba3d3ad8f4e708545291a6fdba8b168152f35b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576020604051601e8152f35b346102125760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576004355f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526020600160405f200154604051908152f35b346102125760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102125773ffffffffffffffffffffffffffffffffffffffff61253d612699565b165f527fc84b62be2e432010aa71cc1bbdba4c7b02245544521aa5beae20093c7062240060205260405f206024355f52602052602060ff60405f2054166040519015158152f35b346102125760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576004357fffffffff00000000000000000000000000000000000000000000000000000000811680910361021257807f7965db0b0000000000000000000000000000000000000000000000000000000060209214908115612619575b506040519015158152f35b7f01ffc9a7000000000000000000000000000000000000000000000000000000009150148261260e565b34610212575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610212576020907f000000000000000000000000000000000000000000000000000000e8d4a510008152f35b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361021257565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361021257565b359073ffffffffffffffffffffffffffffffffffffffff8216820361021257565b6080810190811067ffffffffffffffff82111761111057604052565b6040810190811067ffffffffffffffff82111761111057604052565b60c0810190811067ffffffffffffffff82111761111057604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761111057604052565b67ffffffffffffffff811161111057601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b9291926127db82612795565b916127e96040519384612754565b829481845281830111610212578281602093845f960137010152565b9181601f840112156102125782359167ffffffffffffffff8311610212576020838186019501011161021257565b5f5b8381106128445750505f910152565b8181015183820152602001612835565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209361289081518092818752878088019101612833565b0116010190565b9181601f840112156102125782359167ffffffffffffffff8311610212576020808501948460051b01011161021257565b604051906128d58261271c565b600882527f436f6d6d656e74730000000000000000000000000000000000000000000000006020830152565b9081518082526020808093019301915f5b828110612920575050505090565b835185529381019392810192600101612912565b6101407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126102125773ffffffffffffffffffffffffffffffffffffffff916004358381168103610212579260243581811681036102125792604435926064359267ffffffffffffffff84116102125760806129d6847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7c96600401612805565b9590959401126102125760849161010435828116810361021257916101243590811681036102125790565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60809101126102125760405190612a3882612700565b8173ffffffffffffffffffffffffffffffffffffffff6004358181168103610212578252602435908116810361021257602082015260443560408201526060606435910152565b919082608091031261021257604051612a9781612700565b6060808294612aa5816126df565b8452612ab3602082016126df565b6020850152604081013560408501520135910152565b60405190612ad68261271c565b600182527f31000000000000000000000000000000000000000000000000000000000000006020830152565b6101443563ffffffff811681036102125790565b3563ffffffff811681036102125790565b60843573ffffffffffffffffffffffffffffffffffffffff811681036102125790565b3573ffffffffffffffffffffffffffffffffffffffff811681036102125790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610212570180359067ffffffffffffffff82116102125760200191813603831361021257565b612bc860408201612b4a565b90612bd281612b4a565b612be7612be23660808501612a7f565b613b1a565b92612c00612bf9610100850185612b6b565b36916127cf565b6020815191012091612c1460208501612b4a565b91612c226101208601612b4a565b90612c306101808701612b16565b93612c3e6101a08801612b16565b95604051987f24ba1c68e2ba339f50981ed71cb6c98d1a28a067665f83ea649433ab0b0b74e760208b015273ffffffffffffffffffffffffffffffffffffffff80968180961660408d015260608b013560608d01521660808b015260a08a015260c089015261014087013560e0890152610160870135610100890152166101208701521661014085015263ffffffff91828092166101608601521661018084015261018083526101a083019280841067ffffffffffffffff85111761111057610180612d1991612d2095604052602081519101209301612b16565b1690613b99565b90565b9190811015610d395760051b0190565b67ffffffffffffffff81116111105760051b60200190565b60405190612d5882612700565b5f6060838281528260208201528260408201520152565b9190811015610d395760071b0190565b60807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021257604051612db581612700565b73ffffffffffffffffffffffffffffffffffffffff90600435828116810361021257815260243590828216820361021257612e0491602082015260443560408201526064356060820152613b1a565b9060843591818316809303610212576101043591821680920361021257610124359163ffffffff908184168094036102125761014435918216809203610212576040519460208601937f01b6c3e5d963f61c036cc10c97aeba0ac10158c6cfa12416c35eeecd0a2508b985526040870152606086015260a435608086015260c43560a086015260e43560c086015260e0850152826101008501526101209081850152835261014083019280841067ffffffffffffffff85111761111057612d2093604052519020613b99565b612ed990612f0c565b90815f527f9e5d0d3a4c7e8d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d0060205260ff60405f20541690565b604051612f536020820180936060809173ffffffffffffffffffffffffffffffffffffffff8082511685526020820151166020850152604081015160408501520151910152565b6080815260a0810181811067ffffffffffffffff8211176111105760405251902090565b612f8090612ed0565b91909115612f8a57565b60046040517fc8e4a46f000000000000000000000000000000000000000000000000000000008152fd5b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260405f20335f5260205260ff60405f20541615612ff55750565b6040517fe2517d3f0000000000000000000000000000000000000000000000000000000081523360048201526024810191909152604490fd5b73ffffffffffffffffffffffffffffffffffffffff165f8181527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d60205260409020547f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268009060ff16613103575f805260205260405f20815f5260205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905533905f7f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4600190565b50505f90565b73ffffffffffffffffffffffffffffffffffffffff165f8181527f3dcf8136aea27c4e59adba4b5b5f26ca29dc6e5a703de453b2361643c43217c760205260409020547f2294cdd65f68a33a3e7097ee86a70cf0f4adf6b27328c6b154d47834bdfcdf4e91907f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268009060ff1661320057825f5260205260405f20815f5260205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b5050505f90565b73ffffffffffffffffffffffffffffffffffffffff165f8181527ff42a8efff468fcef324535266976260ddee6dd8f36b86533dba0904c131e743060205260409020547fc590cf330eebec919872b979b32e0cfb49fcb1f4a0cef88b6b29d9a1713fb87d91907f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268009060ff1661320057825f5260205260405f20815f5260205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008060205273ffffffffffffffffffffffffffffffffffffffff60405f20921691825f5260205260ff60405f205416155f1461320057825f5260205260405f20815f5260205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008060205273ffffffffffffffffffffffffffffffffffffffff60405f20921691825f5260205260ff60405f2054165f1461320057825f5260205260405f20815f5260205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905533917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b6020818303126102125780519067ffffffffffffffff8211610212570181601f820112156102125780516134b181612795565b926134bf6040519485612754565b8184526020828401011161021257612d209160208085019101612833565b919390929480421161373b575073ffffffffffffffffffffffffffffffffffffffff9384821690815f527fc84b62be2e432010aa71cc1bbdba4c7b02245544521aa5beae20093c7062240096602095888752604098895f20825f52885260ff8a5f2054166136ec579061358c939291855f528852895f20905f528752885f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905536916127cf565b906135978285614c21565b5060048198929810156136bf571596876136b3575b50505084156135eb575b50505050156135c25750565b600490517f8baa579f000000000000000000000000000000000000000000000000000000008152fd5b5f9293945090829186516136668161363a898201947f1626ba7e00000000000000000000000000000000000000000000000000000000998a875260248401528b60448401526064830190612854565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282612754565b51915afa91613673614782565b836136a6575b8361368b575b5050505f8080806135b6565b908092935081805181010312610212570151145f808061367f565b9250808351101592613679565b161494505f80806135ac565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b5088517f17a63d2100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff861660048201526024810191909152604490fd5b602490604051907f627913020000000000000000000000000000000000000000000000000000000082526004820152fd5b9190613776612d4b565b507f9e5d0d3a4c7e8d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d02918254927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841461380357600184019055604051936137d685612700565b73ffffffffffffffffffffffffffffffffffffffff80921685521660208401526040830152606082015290565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b801561389c577f000000000000000000000000000000000000000000000000000000e8d4a51000808203613865575050600190565b60449250604051917f72a3bac100000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b505f90565b8181029291811591840414171561380357565b9096939591949294815f527f9e5d0d3a4c7e8d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d00928360205260409360ff855f205416613aea57916139f4917feee8cd8185162ef6658993ba27320099864440fe513fb9ae1c9ab7421113b57293855f52602052855f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905585519283928c613995856101a0926060809173ffffffffffffffffffffffffffffffffffffffff8082511685526020820151166020850152604081015160408501520151910152565b8c60808601526139de60a086018b6060809173ffffffffffffffffffffffffffffffffffffffff8082511685526020820151166020850152604081015160408501520151910152565b8b61012086015280610140860152840190612854565b9061016083015273ffffffffffffffffffffffffffffffffffffffff88166101808301520390a28051613a268161271c565b600781527f436f6d6d656e7400000000000000000000000000000000000000000000000000602082015294613aac575b5082613a64575b5050505050565b613a74613a9c91613aa2966142b9565b927f000000000000000000000000000000000000000000000000000000e8d4a51000906138a1565b916142eb565b5f80808080613a5d565b90935051613ab98161271c565b600d81527f436f6d6d656e74205265706c79000000000000000000000000000000000000006020820152925f613a56565b6024848651907f8bdcb6160000000000000000000000000000000000000000000000000000000082526004820152fd5b73ffffffffffffffffffffffffffffffffffffffff80602083015116916060604082015192825116910151906040519260208401947ff50b5c2dfba3cda25b5ff058b098c69c9663b57c8f7fa26975400600bdc66cb6865260408501526060840152608083015260a082015260a08152613b9381612738565b51902090565b604291613ba46150e1565b90613bad61514b565b906040519160208301937f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f85526040840152606083015260808201523060a082015260a08152613bfc81612738565b51902090604051917f19010000000000000000000000000000000000000000000000000000000000008352600283015260228201522090565b90600182811c92168015613c7c575b6020831014613c4f57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f1691613c44565b604051905f827fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10291825492613cba84613c35565b80845293602091600191828116908115613d485750600114613ce8575b505050613ce692500383612754565b565b5f9081527f42ad5d3e1f2e6e70edcf6d991b8a3023d3fca8047a131592f9edb9fd9b89d57d9590935091905b828410613d305750613ce69450505081016020015f8080613cd7565b85548885018301529485019487945092810192613d14565b91505060209350613ce69592507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0091501682840152151560051b8201015f8080613cd7565b604051905f827fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10391825492613dc184613c35565b80845293602091600191828116908115613d485750600114613dec57505050613ce692500383612754565b5f9081527f5f9ce34815f8e11431c7bb75a8e6886a91478f7ffc1dbb0a98dc240fddd76b759590935091905b828410613e345750613ce69450505081016020015f8080613cd7565b85548885018301529485019487945092810192613e18565b7f000000000000000000000000000000000000000000000000000000e8d4a5100090613e7882826138a1565b8303613e8357505050565b60449291613e90916138a1565b604051917f72a3bac100000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b73ffffffffffffffffffffffffffffffffffffffff8080835116931692831461403157613eef82612f0c565b94855f527f9e5d0d3a4c7e8d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d9d5b9e8f9d008060205260ff60405f20541615612f8a57865f52602052600160405f200193845495808701809711613803577fb2c2c602f3c1f47735b32d6a660d741f13d555e25493cf652586575aac1962d4966101009655613fd584865116613f987f000000000000000000000000000000000000000000000000000000e8d4a51000846138a1565b8560405192613fa68461271c565b600f84527f537061726b656420436f6d6d656e74000000000000000000000000000000000060208501526142eb565b61401860405180966060809173ffffffffffffffffffffffffffffffffffffffff8082511685526020820151166020850152604081015160408501520151910152565b608085015260a08401524260c08401521660e0820152a2565b60046040517f2951d9c9000000000000000000000000000000000000000000000000000000008152fd5b90816020910312610212575180151581036102125790565b909173ffffffffffffffffffffffffffffffffffffffff80821680614107575b50602083019281845116916140b3846040840194855184865116916147b1565b6140ff57806140c995511692519151169161491e565b156140d5571561033457565b60046040517fd8a26f99000000000000000000000000000000000000000000000000000000008152fd5b505050505050565b823b156141ba576020828551166024604051809481937fa2e1a8d800000000000000000000000000000000000000000000000000000000835260048301525afa9081156121ba575f9161418b575b5015614161575f614093565b60046040517fee4e75cf000000000000000000000000000000000000000000000000000000008152fd5b6141ad915060203d6020116141b3575b6141a58183612754565b81019061405b565b5f614155565b503d61419b565b60046040517f4eb2199c000000000000000000000000000000000000000000000000000000008152fd5b9073ffffffffffffffffffffffffffffffffffffffff80821680614240575b5060208301928184511691614223846040840194855184865116916147b1565b613a5d578061423995511692519151169161491e565b156140d557565b823b156141ba576020828551166024604051809481937fa2e1a8d800000000000000000000000000000000000000000000000000000000835260048301525afa9081156121ba575f9161429a575b5015614161575f614203565b6142b3915060203d6020116141b3576141a58183612754565b5f61428e565b9073ffffffffffffffffffffffffffffffffffffffff9182825116156142df5750511690565b9050612d209150614ab8565b73ffffffffffffffffffffffffffffffffffffffff93918416928315801593919291906147205760ff60035b1661432181612d33565b94604096879161433383519889612754565b83885261433f84612d33565b95602092838a01927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08099013685376143b761437a88612d33565b976143878851998a612754565b8089528a61439482612d33565b0136888b01376143af6143a682612d33565b98519889612754565b808852612d33565b8686019901368a37156146575788600a02600a81048a036138035760649004906143e08b614b47565b8d7f0000000000000000000000007a810dcd0f8d83b20212326813db6ef7e9fd030c1690528161440f88614b47565b527f538b62d60000000000000000000000000000000000000000000000000000000061443a87614b47565b5289601402601481048b03613803576144a1928c9261445f606461449c940494614b54565b528261446a8a614b54565b527feb4a589f0000000000000000000000000000000000000000000000000000000061449589614b54565b528b614b74565b614b74565b908b6144ac8b614b64565b911690526144b985614b64565b527ffbaf211e000000000000000000000000000000000000000000000000000000006144e484614b64565b525b897f0000000000000000000000007777777f279eba3d3ad8f4e708545291a6fdba8b1697883b1561021257969493929089989698519a8b987f300def95000000000000000000000000000000000000000000000000000000008a5260848a0190608060048c01525180915260a48a0192915f905b82821061463a575050505061459b83917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc95868b83030160248c0152612901565b91848984030160448a015251918281520197915f5b82811061460157505050506145d3868694938593845f9a03016064850152612854565b03925af180156145f7576145e5575050565b67ffffffffffffffff82116111105752565b50513d5f823e3d90fd5b83517fffffffff00000000000000000000000000000000000000000000000000000000168a52988101988b9850928101926001016145b0565b8351811685528e9b5093860193928601926001919091019061455a565b5087601e02601e810489036138035760646146d791046146768b614b47565b8d7f0000000000000000000000007a810dcd0f8d83b20212326813db6ef7e9fd030c169052806146a588614b47565b527f538b62d6000000000000000000000000000000000000000000000000000000006146d087614b47565b5289614b74565b908b6146e28b614b54565b911690526146ef85614b54565b527ffbaf211e0000000000000000000000000000000000000000000000000000000061471a84614b54565b526144e6565b60ff6002614317565b60ff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460401c161561475857565b60046040517fd7e6bcf8000000000000000000000000000000000000000000000000000000008152fd5b3d156147ac573d9061479382612795565b916147a16040519384612754565b82523d5f602084013e565b606090565b9291906147be8185614c5b565b156148c2575073ffffffffffffffffffffffffffffffffffffffff80931660405193807f2f54bf6e00000000000000000000000000000000000000000000000000000000938487521660048601526020938486602481865afa9586156121ba575f966148a3575b508515614835575b505050505090565b909192939450168015159283614855575b50505090505f8080808061482d565b849293506024906040519485938492835260048301525afa9182156121ba575f92614886575b5050805f8080614846565b61489c9250803d106141b3576141a58183612754565b5f8061487b565b6148bb919650853d87116141b3576141a58183612754565b945f614825565b9291906148d0908484614d00565b9283156148de575b50505090565b73ffffffffffffffffffffffffffffffffffffffff82161515935090918361490c575b5050505f80806148d8565b6149169350614d00565b5f8080614901565b906149298183614c5b565b15614a66575073ffffffffffffffffffffffffffffffffffffffff8091169060405191817f70a0823100000000000000000000000000000000000000000000000000000000948585521660048401526020918284602481855afa9384156121ba575f94614a37575b50670de0b6b3a7640000809410958615966149b0575b50505050505090565b909192939495501680151594856149d2575b50505050505f80808080806149a7565b839495506024906040949394519485938492835260048301525afa9182156121ba575f92614a09575b505010155f808080806149c2565b90809250813d8311614a30575b614a208183612754565b8101031261021257515f806149fb565b503d614a16565b9093508281813d8311614a5f575b614a4f8183612754565b810103126102125751925f614991565b503d614a45565b929091614a74908484614d90565b928315614a815750505090565b73ffffffffffffffffffffffffffffffffffffffff821615159350909183614aae575050505f80806148d8565b6149169350614d90565b6020810173ffffffffffffffffffffffffffffffffffffffff91614ae483835116604083015190614e51565b838116614b405750614af590614ef2565b828116614b3a575081614b09915116614f70565b908116612d205760046040517f06e65373000000000000000000000000000000000000000000000000000000008152fd5b91505090565b9250505090565b805115610d395760200190565b805160011015610d395760400190565b805160021015610d395760600190565b9190820391821161380357565b90614bc05750805115614b9657805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580614c18575b614bd1575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15614bc9565b8151919060418303614c5157614c4a9250602082015190606060408401519301515f1a90615190565b9192909190565b50505f9160029190565b90159081614c67575090565b73ffffffffffffffffffffffffffffffffffffffff91506020906024604051809481937f01ffc9a70000000000000000000000000000000000000000000000000000000083527f604c364a000000000000000000000000000000000000000000000000000000006004840152165afa9081156121ba575f91614ce7575090565b612d20915060203d6020116141b3576141a58183612754565b60646020929373ffffffffffffffffffffffffffffffffffffffff809360405196879586947f23bd0386000000000000000000000000000000000000000000000000000000008652166004850152602484015260026044840152165afa5f9181614d6f575b50612d2057505f90565b614d8991925060203d6020116141b3576141a58183612754565b905f614d65565b60446020929373ffffffffffffffffffffffffffffffffffffffff809360405196879586947efdd58e0000000000000000000000000000000000000000000000000000000086521660048501526024840152165afa5f9181614dfc575b50614df757505f90565b151590565b9091506020813d602011614e28575b81614e1860209383612754565b810103126102125751905f614ded565b3d9150614e0b565b519073ffffffffffffffffffffffffffffffffffffffff8216820361021257565b90614e5c9082614c5b565b614e6557505f90565b602073ffffffffffffffffffffffffffffffffffffffff916004604051809481937f3fb80b15000000000000000000000000000000000000000000000000000000008352165afa5f9181614ebd5750612d2057505f90565b9091506020813d602011614eea575b81614ed960209383612754565b8101031261021257614d8990614e30565b3d9150614ecc565b6020604073ffffffffffffffffffffffffffffffffffffffff82840151169201516024604051809481937f5e4e040400000000000000000000000000000000000000000000000000000000835260048301525afa5f9181614ebd5750612d2057505f90565b51906bffffffffffffffffffffffff8216820361021257565b73ffffffffffffffffffffffffffffffffffffffff809116906040517f79502c5500000000000000000000000000000000000000000000000000000000815260c081600481865afa5f9181615061575b50615053575b506020600492604051938480927f8da5cb5b0000000000000000000000000000000000000000000000000000000082525afa5f9281615017575b5061500b5750505f90565b8116612d205780613103565b9092506020813d60201161504b575b8161503360209383612754565b810103126102125761504490614e30565b915f615000565b3d9150615026565b81168015614fc65791505090565b90915060c0813d60c0116150d9575b8161507d60c09383612754565b810103126102125761508e81614e30565b5061509b60208201614f57565b5060408101519083821682036102125760a0816150bd60606150d19401614f57565b506150ca60808201614e30565b5001614f57565b50905f614fc0565b3d9150615070565b6150e9613c86565b80519081156150f9576020012090565b50507fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1005480156151265790565b507fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47090565b615153613d8d565b8051908115615163576020012090565b50507fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1015480156151265790565b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08411615214579160209360809260ff5f9560405194855216868401526040830152606082015282805260015afa156121ba575f5173ffffffffffffffffffffffffffffffffffffffff81161561520a57905f905f90565b505f906001905f90565b5050505f916003919056fea264697066735822122062c67af76306dae11e2ff1ae7b212190cd5be2b4ededdea01c6c81c5f0b577ae64736f6c63430008170033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000000000000000000000000000000000e8d4a510000000000000000000000000007777777f279eba3d3ad8f4e708545291a6fdba8b0000000000000000000000007a810dcd0f8d83b20212326813db6ef7e9fd030c
-----Decoded View---------------
Arg [0] : _sparkValue (uint256): 1000000000000
Arg [1] : _protocolRewards (address): 0x7777777F279eba3d3Ad8F4E708545291A6fDBA8B
Arg [2] : _zoraRecipient (address): 0x7A810DCd0f8d83B20212326813Db6EF7E9FD030c
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000000000000000000000000000000000e8d4a51000
Arg [1] : 0000000000000000000000007777777f279eba3d3ad8f4e708545291a6fdba8b
Arg [2] : 0000000000000000000000007a810dcd0f8d83b20212326813db6ef7e9fd030c
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.