Latest 25 from a total of 10,896 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Create With Sign... | 143990151 | 60 days ago | IN | 0.00069 ETH | 0.000000109756 | ||||
| Create With Sign... | 143904296 | 62 days ago | IN | 0.00069 ETH | 0.000000092167 | ||||
| Create With Sign... | 143871246 | 63 days ago | IN | 0.00169 ETH | 0.000000075122 | ||||
| Create With Sign... | 143829402 | 64 days ago | IN | 0.00069 ETH | 0.000000082133 | ||||
| Create With Sign... | 143806539 | 64 days ago | IN | 0.00069 ETH | 0.000000075244 | ||||
| Create With Sign... | 143770620 | 65 days ago | IN | 0.00069 ETH | 0.000000054738 | ||||
| Create With Sign... | 143763929 | 65 days ago | IN | 0.00069 ETH | 0.000000157582 | ||||
| Create With Sign... | 143725990 | 66 days ago | IN | 0.00069 ETH | 0.000000151532 | ||||
| Create With Sign... | 143699418 | 67 days ago | IN | 0.00069 ETH | 0.000000068083 | ||||
| Create With Sign... | 143699387 | 67 days ago | IN | 0.00069 ETH | 0.0000000672 | ||||
| Create With Sign... | 143699367 | 67 days ago | IN | 0.00069 ETH | 0.000000071082 | ||||
| Create With Sign... | 143699339 | 67 days ago | IN | 0.00069 ETH | 0.00000006562 | ||||
| Create With Sign... | 143690033 | 67 days ago | IN | 0.00069 ETH | 0.000000082452 | ||||
| Create With Sign... | 143687538 | 67 days ago | IN | 0.00069 ETH | 0.000000793364 | ||||
| Create With Sign... | 143679499 | 67 days ago | IN | 0.00069 ETH | 0.000000010335 | ||||
| Create With Sign... | 143679466 | 67 days ago | IN | 0.00069 ETH | 0.000000008516 | ||||
| Create With Sign... | 143654577 | 68 days ago | IN | 0.00069 ETH | 0.00000001495 | ||||
| Create With Sign... | 143652050 | 68 days ago | IN | 0.00069 ETH | 0.000000075976 | ||||
| Create With Sign... | 143651856 | 68 days ago | IN | 0.00069 ETH | 0.000000080051 | ||||
| Create With Sign... | 143638672 | 68 days ago | IN | 0.00069 ETH | 0.00000010296 | ||||
| Create With Sign... | 143634811 | 68 days ago | IN | 0.00069 ETH | 0.000000068819 | ||||
| Create With Sign... | 143573280 | 70 days ago | IN | 0.00069 ETH | 0.000000069878 | ||||
| Create With Sign... | 143458619 | 72 days ago | IN | 0.00069 ETH | 0.000000106474 | ||||
| Create With Sign... | 143449011 | 72 days ago | IN | 0.00069 ETH | 0.000000650181 | ||||
| Create With Sign... | 143427850 | 73 days ago | IN | 0.00069 ETH | 0.000000085711 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 143990151 | 60 days ago | 0.00069 ETH | ||||
| 143990151 | 60 days ago | Contract Creation | 0 ETH | |||
| 143904296 | 62 days ago | 0.00069 ETH | ||||
| 143904296 | 62 days ago | Contract Creation | 0 ETH | |||
| 143871246 | 63 days ago | 0.00169 ETH | ||||
| 143871246 | 63 days ago | Contract Creation | 0 ETH | |||
| 143829402 | 64 days ago | 0.00069 ETH | ||||
| 143829402 | 64 days ago | Contract Creation | 0 ETH | |||
| 143806539 | 64 days ago | 0.00069 ETH | ||||
| 143806539 | 64 days ago | Contract Creation | 0 ETH | |||
| 143770620 | 65 days ago | 0.00069 ETH | ||||
| 143770620 | 65 days ago | Contract Creation | 0 ETH | |||
| 143763929 | 65 days ago | 0.00069 ETH | ||||
| 143763929 | 65 days ago | Contract Creation | 0 ETH | |||
| 143725990 | 66 days ago | 0.00069 ETH | ||||
| 143725990 | 66 days ago | Contract Creation | 0 ETH | |||
| 143699418 | 67 days ago | 0.00069 ETH | ||||
| 143699418 | 67 days ago | Contract Creation | 0 ETH | |||
| 143699387 | 67 days ago | 0.00069 ETH | ||||
| 143699387 | 67 days ago | Contract Creation | 0 ETH | |||
| 143699367 | 67 days ago | 0.00069 ETH | ||||
| 143699367 | 67 days ago | Contract Creation | 0 ETH | |||
| 143699339 | 67 days ago | 0.00069 ETH | ||||
| 143699339 | 67 days ago | Contract Creation | 0 ETH | |||
| 143690033 | 67 days ago | 0.00069 ETH |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
WritingEditionsFactory
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.19; /// > [[[[[[[[[[[ Imports ]]]]]]]]]]] import "./WritingEditions.sol"; import "./interface/IWritingEditionsFactory.sol"; import "../observability/Observability.sol"; import "../lib/Ownable.sol"; /// > [[[[[[[[[[[ External Library Imports ]]]]]]]]]]] import "openzeppelin-contracts/contracts/proxy/Clones.sol"; import "openzeppelin-contracts/contracts/security/ReentrancyGuard.sol"; /** * @title WritingEditionsFactory * @notice The WritingEditionsFactory contract deploys WritingEdition clones. * A WritingEdition is a token that supports ERC721: Non-Fungible Token Standard. * The factory supports deploying clones with account signatures for counterfactual * contract deployments when minting a token, otherwise colloquially known as "lazy minting". * @author MirrorXYZ * @custom:security-contact [email protected] */ contract WritingEditionsFactory is Ownable, ReentrancyGuard, IWritingEditionsFactoryEvents, IWritingEditionsFactory, IObservabilityEvents { /// @notice Version. uint8 public immutable override VERSION = 3; /// @notice Observability contract for data processing. address public immutable override o11y; /// @notice Treasury configuration. address public immutable override treasuryConfiguration; /// @notice Transaction Reentrancy Guard. bool public override guardOn; /// > [[[[[[[[[[[ Deployments ]]]]]]]]]]] /// @notice Writing edition implementation. address public override implementation; /// @dev Store when a salt is used. mapping(bytes32 => bool) public override salts; /// @dev Max limit. uint256 public override maxLimit; /// > [[[[[[[[[[[ Description ]]]]]]]]]]] /// @notice Base URI for clone description. string public constant override baseDescriptionURI = "https://mirror.xyz/"; /// > [[[[[[[[[[[ Constructor ]]]]]]]]]]] /// @notice Stores treasury, generates domain-separator, and deploys /// observability and implementation contracts. /// @param _treasuryConfiguration Mirror treasury configuration. /// @param _o11y Observability contract. /// @param _maxLimit Max edition limit. /// @param _guardOn Guard status. constructor( address _owner, address _treasuryConfiguration, address _o11y, uint256 _maxLimit, bool _guardOn ) Ownable(_owner) { require( _treasuryConfiguration != address(0), "must set treasury config" ); // Store treasury configuration. treasuryConfiguration = _treasuryConfiguration; // Set implementation contract. implementation = address( new WritingEditions(address(this), _treasuryConfiguration, _o11y) ); // Set observability contract. o11y = _o11y; // slither-disable-next-line reentrancy-no-eth IObservability(_o11y).emitFactoryImplementationSet( // oldImplementation address(0), // newImplementation implementation ); // Store guard status. guardOn = _guardOn; // Store max limit. maxLimit = _maxLimit; } /// > [[[[[[[[[[[ View functions ]]]]]]]]]]] /// @notice Generates the address that a clone will be deployed to. /// @param _implementation the WritingEditions address. /// @param salt the entropy used by create2 for generatating a deterministic address. function predictDeterministicAddress( address _implementation, bytes32 salt ) external view override returns (address) { return Clones.predictDeterministicAddress( _implementation, salt, address(this) ); } /// > [[[[[[[[[[[ Limit ]]]]]]]]]]] function setLimit(uint256 _maxLimit) external override onlyOwner { // slither-disable-next-line reentrancy-no-eth IObservability(o11y).emitFactoryLimitSet( // oldLimit maxLimit, // newLimit _maxLimit ); // Store max limit. maxLimit = _maxLimit; } /// > [[[[[[[[[[[ Guard ]]]]]]]]]]] function setGuard(bool _guardOn) external override onlyOwner { // slither-disable-next-line reentrancy-no-eth IObservability(o11y).emitFactoryGuardSet(_guardOn); // Store guard status. guardOn = _guardOn; } /// > [[[[[[[[[[[ Implementation ]]]]]]]]]]] function setImplementation( address _implementation ) external override onlyOwner { // slither-disable-next-line reentrancy-no-eth IObservability(o11y).emitFactoryImplementationSet( // oldImplementation implementation, // newImplementation _implementation ); // Store implementation. implementation = _implementation; } /// > [[[[[[[[[[[ Deployment functions ]]]]]]]]]]] /// @notice Deploy a new writing edition clone with the sender as the owner. /// @param edition edition parameters used to deploy the clone. function create( WritingEditions.WritingEdition memory edition ) external override returns (address clone) { clone = _deployCloneAndInitialize( msg.sender, edition, address(0), "", address(0) ); } /// @dev Deploy a new writing edition clone with a signature provided by `owner`. /// @param owner owner of the clone. /// @param edition edition parameters used to deploy the clone. /// @param v signature value. /// @param r signature value. /// @param s signature value. /// @param tokenRecipient account that will receive the first minted token. /// @param message message sent with the token purchase, not stored. function createWithSignature( address owner, WritingEditions.WritingEdition memory edition, uint8 v, bytes32 r, bytes32 s, address tokenRecipient, string memory message, address mintReferral ) external payable override nonReentrant returns (address clone) { // Generate salt from parameters. bytes32 salt = keccak256( abi.encode(owner, edition.name, edition.symbol, edition.nonce) ); // If the clone has been deployed, purchase instead of deploying. if (salts[salt]) { clone = Clones.predictDeterministicAddress( implementation, salt, address(this) ); require(clone.code.length > 0, "invalid clone address"); // slither-disable-next-line unused-return WritingEditions(clone).purchase{value: msg.value}( tokenRecipient, message, mintReferral ); } else { salts[salt] = true; clone = _deployCloneAndInitializeWithSignature( owner, edition, v, r, s, tokenRecipient, message, mintReferral ); } } /// > [[[[[[[[[[[ Tributary ]]]]]]]]]]] /// @notice Set a new tributary. /// @param _tributary new tributary. function setTributary(address clone, address _tributary) external override { require(msg.sender == Ownable(clone).owner(), "unauthorized"); address tributaryRegistry = _getTributaryRegistry(); // slither-disable-next-line reentrancy-no-eth IObservability(o11y).emitTributarySet( // clone clone, // oldTributary ITributaryRegistry(tributaryRegistry).producerToTributary(clone), // newTributary _tributary ); ITributaryRegistry(tributaryRegistry).setTributary(clone, _tributary); } /// > [[[[[[[[[[[ Internal functions ]]]]]]]]]]] /// @dev Deploys a clone and calls the initialize function. Additionally, /// this function calls `registerTributary` on the tributary registry, if /// one is set. function _deployCloneAndInitialize( address owner, WritingEditions.WritingEdition memory edition, address tokenRecipient, string memory message, address mintReferral ) internal returns (address clone) { clone = _validateEditionAndDeployClone(owner, edition); // Initialize clone. WritingEditions(clone).initialize{value: msg.value}( owner, edition, tokenRecipient, message, mintReferral, guardOn ); } function _deployCloneAndInitializeWithSignature( address owner, WritingEditions.WritingEdition memory edition, uint8 v, bytes32 r, bytes32 s, address tokenRecipient, string memory message, address mintReferral ) internal returns (address clone) { bytes32 salt = keccak256( abi.encode(owner, edition.name, edition.symbol, edition.nonce) ); clone = _validateEditionAndDeployClone(owner, edition); // Initialize clone with signature. WritingEditions(clone).initializeWithSignature{value: msg.value}( owner, salt, abi.encodePacked(r, s, v), edition, tokenRecipient, message, mintReferral, guardOn, msg.sender ); } function _validateEditionAndDeployClone( address owner, WritingEditions.WritingEdition memory edition ) internal returns (address clone) { require( edition.fundingRecipient != address(0), "must specify recipient" ); // Assert limit is valid. If maxLimit is zero allow any limit. require( maxLimit == 0 || (edition.limit > 0 && edition.limit <= maxLimit), "invalid limit" ); clone = Clones.cloneDeterministic( implementation, keccak256( abi.encode(owner, edition.name, edition.symbol, edition.nonce) ) ); // Register clones _before_ initializing. address tributaryRegistry = _getTributaryRegistry(); if (tributaryRegistry != address(0)) { IObservability(o11y).emitTributarySet( // clone clone, // oldTributary address(0), // newTributary edition.fundingRecipient ); ITributaryRegistry(tributaryRegistry).setTributary( clone, edition.fundingRecipient ); } IObservability(o11y).emitDeploymentEvent(owner, clone); } function _getTributaryRegistry() internal returns (address) { return ITreasuryConfiguration(treasuryConfiguration).tributaryRegistry(); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; /// > [[[[[[[[[[[ Imports ]]]]]]]]]]] import "./interface/IWritingEditions.sol"; import "./interface/IWritingEditionsFactory.sol"; import "../ERC7015/ERC7015.sol"; import "../observability/interface/IObservability.sol"; import "../fee-configuration/interface/IFeeConfiguration.sol"; import "../renderer/interface/IRenderer.sol"; import "../treasury/Rewards.sol"; import "../treasury/interface/ITreasuryConfiguration.sol"; import "../treasury/interface/ITreasury.sol"; import "../treasury/interface/ITributaryRegistry.sol"; import "../lib/Ownable.sol"; import "../lib/ERC721/ERC721.sol"; import "../lib/ERC165/ERC165.sol"; import "../lib/ERC721/interface/IERC721.sol"; import "../lib/ERC2981/interface/IERC2981.sol"; import "../lib/transaction-reentrancy-guard/TransactionReentrancyGuard.sol"; /// > [[[[[[[[[[[ External Library Imports ]]]]]]]]]]] import "openzeppelin-contracts/contracts/security/ReentrancyGuard.sol"; import "openzeppelin-contracts/contracts/utils/Base64.sol"; import "openzeppelin-contracts/contracts/utils/Strings.sol"; /** * @title WritingEditions * @author MirrorXYZ * @custom:security-contact [email protected] */ contract WritingEditions is ERC7015, Ownable, TransactionReentrancyGuard, ReentrancyGuard, ERC721, IERC721Metadata, IERC2981, IWritingEditions, IWritingEditionEvents, IObservabilityEvents { /// > [[[[[[[[[[[ Version ]]]]]]]]]]] /// @notice Version. uint8 public immutable override VERSION = 3; /// > [[[[[[[[[[[ Authorization ]]]]]]]]]]] /// @notice Address that deploys and initializes clones. address public immutable override factory; /// > [[[[[[[[[[[ Configuration ]]]]]]]]]]] /// @notice Address for Mirror treasury configuration. address public immutable override treasuryConfiguration; /// @notice Address for Mirror's observability contract. address public immutable override o11y; /// > [[[[[[[[[[[ ERC721 Metadata ]]]]]]]]]]] /// @notice Token name. string public override name; /// @notice Token symbol. string public override symbol; /// @notice Base URI for description. string internal _baseDescriptionURI; /// > [[[[[[[[[[[ Token Data ]]]]]]]]]]] /// @notice Total supply of editions. Used to calculate next tokenId. uint256 public override totalSupply; /// @notice Token text content, stored in Arweave. string public override contentURI; /// @notice Token image content, stored in IPFS. string public override imageURI; /// @notice Token price, set by the owner. uint256 public override price; /// @notice Token limit, set by the owner. uint256 public override limit; /// @notice Account that will receive funds from sales. address public override fundingRecipient; /// > [[[[[[[[[[[ Royalty Info (ERC2981) ]]]]]]]]]]] /// @notice Account that will receive royalties. address public override royaltyRecipient; /// @notice Royalty basis points. uint256 public override royaltyBPS; /// > [[[[[[[[[[[ Rendering ]]]]]]]]]]] /// @notice Address for a rendering contract, if set, calls to /// `tokenURI(uint256)` are forwarded to this address. address public override renderer; /// > [[[[[[[[[[[ Rewards ]]]]]]]]]]] /// @notice Address for the first minter if they are not the creator. address public override firstMinter; /// > [[[[[[[[[[[ Constructor ]]]]]]]]]]] /// @notice Implementation logic for clones. /// @param _factory the factory contract deploying clones with this implementation. /// @param _treasuryConfiguration Mirror treasury configuration. /// @param _o11y contract for observability. constructor( address _factory, address _treasuryConfiguration, address _o11y ) Ownable(address(0)) TransactionReentrancyGuard(true) ERC7015("WritingEditions", "1") { // Assert not the zero-address. require(_factory != address(0), "must set factory"); // Store factory. factory = _factory; // Assert not the zero-address. require( _treasuryConfiguration != address(0), "must set treasury configuration" ); // Store treasury configuration. treasuryConfiguration = _treasuryConfiguration; // Assert not the zero-address. require(_o11y != address(0), "must set observability"); // Store observability. o11y = _o11y; } /// > [[[[[[[[[[[ Initializing ]]]]]]]]]]] /// @notice Initialize a clone with a signature by storing edition parameters. /// Called only by the factory. Mints the first edition to `tokenRecipient`. /// @param _creator the token creator /// @param structHash the salt parameter for ERC7015 /// @param signature the signature of the creator /// @param edition edition parameters used to deploy the clone. /// @param tokenRecipient account that will receive the first minted token. /// @param message message sent with the token purchase, not stored. function initializeWithSignature( address _creator, bytes32 structHash, bytes calldata signature, WritingEdition memory edition, address tokenRecipient, string memory message, address mintReferral, bool _guardOn, address sender ) external payable override nonReentrant { _validateSignature(_creator, structHash, signature); // Set first minter. if (sender != _creator) { firstMinter = sender; } _initialize( _creator, edition, tokenRecipient, message, mintReferral, _guardOn ); } /// @notice Initialize a clone by storing edition parameters. Called only /// by the factory. Mints the first edition to `tokenRecipient`. /// @param _creator owner of the clone. /// @param edition edition parameters used to deploy the clone. /// @param tokenRecipient account that will receive the first minted token. /// @param message message sent with the token purchase, not stored. function initialize( address _creator, WritingEdition memory edition, address tokenRecipient, string memory message, address mintReferral, bool _guardOn ) external payable override nonReentrant { _initialize( _creator, edition, tokenRecipient, message, mintReferral, _guardOn ); } function _initialize( address _creator, WritingEdition memory edition, address tokenRecipient, string memory message, address mintReferral, bool _guardOn ) internal { // Only factory can call this function. require(msg.sender == factory, "unauthorized caller"); // Store ERC721 metadata. name = edition.name; symbol = edition.symbol; // Store edition data. imageURI = edition.imageURI; contentURI = edition.contentURI; price = edition.price; limit = edition.limit; fundingRecipient = edition.fundingRecipient; renderer = edition.renderer; // Store owner. _setInitialOwner(_creator); // Store guard status. _setGuard(_guardOn); // Mint initial token to recipient if (tokenRecipient != address(0)) { _purchase(tokenRecipient, message, mintReferral); } } /// @notice Base description URI. function baseDescriptionURI() external view override returns (string memory) { return _getBaseDescriptionURI(); } /// @notice Token description. function description() public view override returns (string memory) { return string( abi.encodePacked( _getBaseDescriptionURI(), Strings.toString(block.chainid), "/", _addressToString(address(this)) ) ); } /// > [[[[[[[[[[[ View Functions ]]]]]]]]]]] /// @notice Helper function to get owners for a list of tokenIds. /// @dev Could revert if `tokenIds` is too long. /// @param tokenIds a list of token-ids to check ownership of. /// @return owners a list of token-id owners, address(0) if token is not minted function ownerOf( uint256[] memory tokenIds ) external view override returns (address[] memory owners) { owners = new address[](tokenIds.length); for (uint256 i = 0; i < tokenIds.length; i++) { owners[i] = _owners[tokenIds[i]]; } } /// > [[[[[[[[[[[ Funding Recipient ]]]]]]]]]]] /// @notice Set a new funding recipient. /// @param _fundingRecipient new funding recipient. function setFundingRecipient( address _fundingRecipient ) external override onlyOwner { // slither-disable-next-line reentrancy-no-eth IObservability(o11y).emitFundingRecipientSet( // oldFundingRecipient fundingRecipient, // newFundingRecipient _fundingRecipient ); fundingRecipient = _fundingRecipient; } /// > [[[[[[[[[[[ Price ]]]]]]]]]]] /// @notice Set a new price. /// @param _price new price. function setPrice(uint256 _price) external override onlyOwner { // slither-disable-next-line reentrancy-no-eth IObservability(o11y).emitPriceSet( // oldPrice price, // newPrice _price ); price = _price; } /// @notice Set a new base description URI. /// @param newBaseDescriptionURI new base description URI function setBaseDescriptionURI( string memory newBaseDescriptionURI ) external override onlyOwner { // slither-disable-next-line reentrancy-no-eth IObservability(o11y).emitBaseDescriptionURISet( // oldDescriptionURI _getBaseDescriptionURI(), // oldDescriptionURI newBaseDescriptionURI ); _baseDescriptionURI = newBaseDescriptionURI; } /// @notice Turn Transaction Level Reentrancy Guard on/off. function toggleGuard() external override onlyOwner { _toggleGuard(); } /// > [[[[[[[[[[[ Purchase ]]]]]]]]]]] /// @notice Purchase a token. /// @param tokenRecipient the account to receive the token. /// @param message an optional message during purchase, not stored. /// @param mintReferral the account that referred the purchase. /// @return tokenId the id of the minted token. function purchase( address tokenRecipient, string memory message, address mintReferral ) external payable override guard nonReentrant returns (uint256 tokenId) { return _purchase(tokenRecipient, message, mintReferral); } /// > [[[[[[[[[[[ Mint ]]]]]]]]]]] /// @notice Mint an edition /// @dev throws if called by a non-owner /// @param tokenRecipient the account to receive the edition function mint( address tokenRecipient ) external override onlyOwner returns (uint256 tokenId) { tokenId = _getTokenIdAndMint(tokenRecipient); } /// > [[[[[[[[[[[ Limit ]]]]]]]]]]] /// @notice Allows the owner to set a global limit on the total supply /// @dev throws if attempting to increase the limit /// @param newLimit new mint limit. function setLimit(uint256 newLimit) external override onlyOwner { // Enforce that the limit should only ever decrease once set. require( newLimit >= totalSupply && (limit == 0 || newLimit < limit), "limit must be < than current limit" ); // Announce the change in limit. // slither-disable-next-line reentrancy-no-eth IObservability(o11y).emitWritingEditionLimitSet( // oldLimit limit, // newLimit newLimit ); // Update the limit. limit = newLimit; } /// @notice Set the limit to the last minted tokenId. function setMaxLimit() external override onlyOwner { // Announce the change in limit. // slither-disable-next-line reentrancy-no-eth IObservability(o11y).emitWritingEditionLimitSet( // oldLimit limit, // newLimit totalSupply ); // Update the limit. limit = totalSupply; } /// > [[[[[[[[[[[ ERC2981 Methods ]]]]]]]]]]] /// @notice Called with the sale price to determine how much royalty // is owed and to whom /// @param _tokenId - the NFT asset queried for royalty information /// @param _salePrice - the sale price of the NFT asset specified by _tokenId /// @return receiver - address of who should be sent the royalty payment /// @return royaltyAmount - the royalty payment amount for _salePrice function royaltyInfo( uint256 _tokenId, uint256 _salePrice ) external view override returns (address receiver, uint256 royaltyAmount) { receiver = royaltyRecipient; royaltyAmount = (_salePrice * royaltyBPS) / 10_000; } /// @notice Get royalties information. /// @param royaltyRecipient_ the address that will receive royalties /// @param royaltyBPS_ the royalty amount in basis points (bps) function setRoyaltyInfo( address royaltyRecipient_, uint256 royaltyBPS_ ) external override onlyOwner { require( royaltyBPS_ <= 10_000, "bps must be less than or equal to 10,000" ); // slither-disable-next-line reentrancy-no-eth IObservability(o11y).emitRoyaltyChange( // oldRoyaltyRecipient royaltyRecipient, // oldRoyaltyBPS royaltyBPS, // newRoyaltyRecipient royaltyRecipient_, // newRoyaltyBPS royaltyBPS_ ); royaltyRecipient = royaltyRecipient_; royaltyBPS = royaltyBPS_; } /// > [[[[[[[[[[[ Rendering Methods ]]]]]]]]]]] /// @notice Set the renderer address /// @dev Throws if renderer is not the zero address /// @param _renderer contract responsible for rendering tokens. function setRenderer(address _renderer) external override onlyOwner { require(renderer == address(0), "renderer already set"); renderer = _renderer; IObservability(o11y).emitRendererSet( // renderer _renderer ); } /// @notice Get contract metadata /// @dev If a renderer is set, attempt return the renderer's metadata. function contractURI() external view override returns (string memory) { if (renderer != address(0)) { // slither-disable-next-line unused-return try IRenderer(renderer).contractURI() returns ( // slither-disable-next-line uninitialized-local string memory result ) { return result; } catch { // Fallback if the renderer does not implement contractURI return _generateContractURI(); } } return _generateContractURI(); } /// @notice Get `tokenId` URI or data /// @dev If a renderer is set, call renderer's tokenURI /// @param tokenId The tokenId used to request data function tokenURI( uint256 tokenId ) external view override returns (string memory) { require(_exists(tokenId), "ERC721: query for nonexistent token"); if (renderer != address(0)) { return IRenderer(renderer).tokenURI(tokenId); } // slither-disable-next-line uninitialized-local bytes memory editionNumber; if (limit != 0) { editionNumber = abi.encodePacked("/", Strings.toString(limit)); } string memory json = Base64.encode( bytes( string( abi.encodePacked( '{"name": "', _escapeQuotes(name), " ", Strings.toString(tokenId), editionNumber, '", "description": "', _escapeQuotes(description()), '", "content": "ar://', contentURI, '", "image": "ipfs://', imageURI, '", "attributes":[{ "trait_type": "Serial", "value": ', Strings.toString(tokenId), "}] }" ) ) ) ); return string(abi.encodePacked("data:application/json;base64,", json)); } /// > [[[[[[[[[[[ IERC165 Method ]]]]]]]]]]] /// @param interfaceId The interface identifier, as specified in ERC-165 function supportsInterface( bytes4 interfaceId ) public pure override returns (bool) { return interfaceId == type(IERC721).interfaceId || interfaceId == type(IERC721Metadata).interfaceId || interfaceId == type(IERC165).interfaceId || interfaceId == type(IERC2981).interfaceId; } /// > [[[[[[[[[[[ Internal Functions ]]]]]]]]]]] function _generateContractURI() internal view returns (string memory) { string memory json = Base64.encode( bytes( string( abi.encodePacked( '{"name": "', _escapeQuotes(name), '", "description": "', _escapeQuotes(description()), '", "content": "ar://', contentURI, '", "image": "ipfs://', imageURI, '", "seller_fee_basis_points": ', Strings.toString(royaltyBPS), ', "fee_recipient": "', _addressToString(royaltyRecipient), '", "external_link": "', _getBaseDescriptionURI(), '"}' ) ) ) ); return string(abi.encodePacked("data:application/json;base64,", json)); } /// @dev Emit a transfer event from observability contract. function _beforeTokenTransfer( address from, address to, uint256 tokenId ) internal virtual override { IObservability(o11y).emitTransferEvent(from, to, tokenId); } function _purchase( address tokenRecipient, string memory message, address mintReferral ) internal returns (uint256 tokenId) { // Mint token, and get a tokenId. tokenId = _getTokenIdAndMint(tokenRecipient); // Get fee. uint256 flatFeeAmount = 0; address feeConfiguration = _getFeeConfiguration(); if ( feeConfiguration != address(0) && IFeeConfiguration(feeConfiguration).flatFeeOn() ) { // Calculate the fee on the current balance flatFeeAmount = IFeeConfiguration(feeConfiguration).flatFeeAmount(); } // Emit event through observability contract. IObservability(o11y).emitWritingEditionPurchased( // tokenId tokenId, // recipient tokenRecipient, // price price, // message message, // flatFeeAmount flatFeeAmount ); _withdraw(fundingRecipient, msg.value, flatFeeAmount, mintReferral); } function _getFeeConfiguration() internal returns (address) { return ITreasuryConfiguration(treasuryConfiguration).feeConfiguration(); } function _getTributaryRegistry() internal returns (address) { return ITreasuryConfiguration(treasuryConfiguration).tributaryRegistry(); } /// @dev Withdraws `amount` to `fundsRecipient`. Sends fee to treasury // if fees are on. function _withdraw( address fundsRecipient, uint256 amount, uint256 flatFeeAmount, address mintReferral ) internal { // If the fee is not zero, attempt to send it to the treasury. // If the treasury is not set, do not pay the fee. address treasury = ITreasuryConfiguration(treasuryConfiguration) .treasury(); if (flatFeeAmount != 0 && treasury != address(0)) { require(amount == price + flatFeeAmount, "invalid amount"); ( uint256 creatorReward, uint256 fee, uint256 mintReferralReward, uint256 firstMinterReward ) = Rewards.getRewards( mintReferral, price == 0, // free mint? flatFeeAmount ); if (firstMinter == address(0)) { // If the first minter is the creator, send them first minter reward. _sendEther( payable(fundsRecipient), price + creatorReward + firstMinterReward ); } else { _sendEther(payable(firstMinter), firstMinterReward); _sendEther(payable(fundsRecipient), price + creatorReward); } // Send fee to treasury. _sendEther(payable(treasury), fee); emit RewardsDistributed( creatorReward, fee, mintReferralReward, firstMinterReward, fundsRecipient, // creator mintReferral, firstMinter ); // Send referral reward to referral. if (mintReferralReward != 0) { _sendEther(payable(mintReferral), mintReferralReward); } } else { require(amount == price, "invalid amount"); _sendEther(payable(fundsRecipient), amount); } } function _sendEther(address payable recipient, uint256 amount) internal { // Ensure sufficient balance. require(address(this).balance >= amount, "insufficient balance"); // Send the value. // slither-disable-next-line low-level-calls (bool success, ) = recipient.call{value: amount, gas: gasleft()}(""); require(success, "recipient reverted"); } /// @dev Mints and returns tokenId function _getTokenIdAndMint( address tokenRecipient ) internal returns (uint256 tokenId) { // Increment totalSupply to get next id and store tokenId. tokenId = ++totalSupply; // check that there are still tokens available to purchase // zero and max uint256 represent infinite minting require( limit == 0 || limit == type(uint256).max || tokenId < limit + 1, "sold out" ); // mint a new token for the tokenRecipient, using the `tokenId`. _mint(tokenRecipient, tokenId); } function _getBaseDescriptionURI() internal view returns (string memory) { return bytes(_baseDescriptionURI).length == 0 ? IWritingEditionsFactory(factory).baseDescriptionURI() : _baseDescriptionURI; } // https://ethereum.stackexchange.com/questions/8346/convert-address-to-string/8447#8447 function _addressToString(address x) internal pure returns (string memory) { bytes memory s = new bytes(40); for (uint256 i = 0; i < 20; i++) { bytes1 b = bytes1( uint8(uint256(uint160(x)) / (2 ** (8 * (19 - i)))) ); bytes1 hi = bytes1(uint8(b) / 16); bytes1 lo = bytes1(uint8(b) - 16 * uint8(hi)); s[2 * i] = _char(hi); s[2 * i + 1] = _char(lo); } return string(abi.encodePacked("0x", s)); } function _char(bytes1 b) internal pure returns (bytes1 c) { if (uint8(b) < 10) return bytes1(uint8(b) + 0x30); else return bytes1(uint8(b) + 0x57); } function _escapeQuotes( string memory str ) internal pure returns (string memory) { bytes memory strBytes = bytes(str); uint8 quotesCount = 0; for (uint8 i = 0; i < strBytes.length; i++) { if (strBytes[i] == '"') { quotesCount++; } } if (quotesCount > 0) { bytes memory escapedBytes = new bytes( strBytes.length + (quotesCount) ); uint256 index; for (uint8 i = 0; i < strBytes.length; i++) { if (strBytes[i] == '"') { escapedBytes[index++] = "\\"; } escapedBytes[index++] = strBytes[i]; } return string(escapedBytes); } return str; } }
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
/// > [[[[[[[[[[[ Imports ]]]]]]]]]]]
import "./IWritingEditions.sol";
interface IWritingEditionsFactoryEvents {
event NewImplementation(
address indexed oldImplementation,
address indexed newImplementation
);
event EditionsDeployed(
address indexed owner,
address indexed clone,
address indexed implementation
);
}
interface IWritingEditionsFactory {
function VERSION() external view returns (uint8);
function implementation() external view returns (address);
function o11y() external view returns (address);
function treasuryConfiguration() external view returns (address);
function guardOn() external view returns (bool);
function salts(bytes32 salt) external view returns (bool);
function maxLimit() external view returns (uint256);
function baseDescriptionURI() external view returns (string memory);
function predictDeterministicAddress(
address implementation_,
bytes32 salt
) external view returns (address);
function setLimit(uint256 _maxLimit) external;
function setGuard(bool _guardOn) external;
function setImplementation(address _implementation) external;
function create(
IWritingEditions.WritingEdition memory edition_
) external returns (address clone);
function createWithSignature(
address owner_,
IWritingEditions.WritingEdition memory edition_,
uint8 v,
bytes32 r,
bytes32 s,
address recipient,
string memory message,
address mintReferral
) external payable returns (address clone);
function setTributary(address clone, address _tributary) external;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
import "./interface/IObservability.sol";
/**
* @title Observability
* @author MirrorXYZ
*/
contract Observability is IObservability, IObservabilityEvents {
/// > [[[[[[[[[[[ Factory functions ]]]]]]]]]]]
function emitDeploymentEvent(
address owner,
address clone
) external override {
emit CloneDeployed(msg.sender, owner, clone);
}
function emitTributarySet(
address clone,
address oldTributary,
address newTributary
) external override {
emit TributarySet(msg.sender, clone, oldTributary, newTributary);
}
function emitFactoryLimitSet(
uint256 oldLimit,
uint256 newLimit
) external override {
emit FactoryLimitSet(msg.sender, oldLimit, newLimit);
}
function emitFactoryGuardSet(bool guard) external override {
emit FactoryGuardSet(guard);
}
function emitFactoryImplementationSet(
address oldImplementation,
address newImplementation
) external override {
emit FactoryImplementationSet(
msg.sender,
oldImplementation,
newImplementation
);
}
/// > [[[[[[[[[[[ Clone functions ]]]]]]]]]]]
function emitWritingEditionPurchased(
uint256 tokenId,
address recipient,
uint256 price,
string memory message,
uint256 flatFeeAmount
) external override {
emit WritingEditionPurchased(
msg.sender,
tokenId,
recipient,
price,
message,
flatFeeAmount
);
}
function emitTransferEvent(
address from,
address to,
uint256 tokenId
) external override {
emit Transfer(msg.sender, from, to, tokenId);
}
function emitRoyaltyChange(
address oldRoyaltyRecipient,
uint256 oldRoyaltyBPS,
address newRoyaltyRecipient,
uint256 newRoyaltyBPS
) external override {
emit RoyaltyChange(
msg.sender,
oldRoyaltyRecipient,
oldRoyaltyBPS,
newRoyaltyRecipient,
newRoyaltyBPS
);
}
function emitRendererSet(address renderer) external override {
emit RendererSet(msg.sender, renderer);
}
function emitWritingEditionLimitSet(
uint256 oldLimit,
uint256 newLimit
) external override {
emit WritingEditionLimitSet(msg.sender, oldLimit, newLimit);
}
function emitPriceSet(
uint256 oldPrice,
uint256 newPrice
) external override {
emit PriceSet(msg.sender, oldPrice, newPrice);
}
function emitFundingRecipientSet(
address oldFundingRecipient,
address newFundingRecipient
) external override {
emit FundingRecipientSet(
msg.sender,
oldFundingRecipient,
newFundingRecipient
);
}
function emitBaseDescriptionURISet(
string memory oldBaseDescriptionURI,
string memory newBaseDescriptionURI
) external override {
emit BaseDescriptionURISet(
msg.sender,
oldBaseDescriptionURI,
newBaseDescriptionURI
);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
interface IOwnableEvents {
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
}
interface IOwnable {
function transferOwnership(address nextOwner_) external;
function cancelOwnershipTransfer() external;
function acceptOwnership() external;
function renounceOwnership() external;
function isOwner() external view returns (bool);
function isNextOwner() external view returns (bool);
}
contract Ownable is IOwnable, IOwnableEvents {
address public owner;
address private nextOwner;
/// > [[[[[[[[[[[ Modifiers ]]]]]]]]]]]
modifier onlyOwner() {
require(isOwner(), "caller is not the owner.");
_;
}
modifier onlyNextOwner() {
require(isNextOwner(), "current owner must set caller as next owner.");
_;
}
/// @notice Initialize contract by setting the initial owner.
constructor(address owner_) {
_setInitialOwner(owner_);
}
/// @notice Initiate ownership transfer by setting nextOwner.
function transferOwnership(address nextOwner_) external override onlyOwner {
require(nextOwner_ != address(0), "Next owner is the zero address.");
nextOwner = nextOwner_;
}
/// @notice Cancel ownership transfer by deleting nextOwner.
function cancelOwnershipTransfer() external override onlyOwner {
delete nextOwner;
}
/// @notice Accepts ownership transfer by setting owner.
function acceptOwnership() external override onlyNextOwner {
delete nextOwner;
owner = msg.sender;
emit OwnershipTransferred(owner, msg.sender);
}
/// @notice Renounce ownership by setting owner to zero address.
function renounceOwnership() external override onlyOwner {
_renounceOwnership();
}
/// @notice Returns true if the caller is the current owner.
function isOwner() public view override returns (bool) {
return msg.sender == owner;
}
/// @notice Returns true if the caller is the next owner.
function isNextOwner() public view override returns (bool) {
return msg.sender == nextOwner;
}
/// > [[[[[[[[[[[ Internal Functions ]]]]]]]]]]]
function _setOwner(address previousOwner, address newOwner) internal {
owner = newOwner;
emit OwnershipTransferred(previousOwner, owner);
}
function _setInitialOwner(address newOwner) internal {
owner = newOwner;
emit OwnershipTransferred(address(0), newOwner);
}
function _renounceOwnership() internal {
emit OwnershipTransferred(owner, address(0));
owner = address(0);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/Clones.sol)
pragma solidity ^0.8.0;
/**
* @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
* deploying minimal proxy contracts, also known as "clones".
*
* > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
* > a minimal bytecode implementation that delegates all calls to a known, fixed address.
*
* The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
* (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
* deterministic method.
*
* _Available since v3.4._
*/
library Clones {
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create opcode, which should never revert.
*/
function clone(address implementation) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
// of the `implementation` address with the bytecode before the address.
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create(0, 0x09, 0x37)
}
require(instance != address(0), "ERC1167: create failed");
}
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create2 opcode and a `salt` to deterministically deploy
* the clone. Using the same `implementation` and `salt` multiple time will revert, since
* the clones cannot be deployed twice at the same address.
*/
function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
// of the `implementation` address with the bytecode before the address.
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create2(0, 0x09, 0x37, salt)
}
require(instance != address(0), "ERC1167: create2 failed");
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(add(ptr, 0x38), deployer)
mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
mstore(add(ptr, 0x14), implementation)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
mstore(add(ptr, 0x58), salt)
mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
predicted := keccak256(add(ptr, 0x43), 0x55)
}
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(
address implementation,
bytes32 salt
) internal view returns (address predicted) {
return predictDeterministicAddress(implementation, salt, address(this));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
interface IWritingEditionEvents {
event RoyaltyChange(
address indexed oldRoyaltyRecipient,
uint256 oldRoyaltyBPS,
address indexed newRoyaltyRecipient,
uint256 newRoyaltyBPS
);
event RendererSet(address indexed renderer);
event WritingEditionLimitSet(uint256 oldLimit, uint256 newLimit);
event PriceSet(uint256 price);
event RewardsDistributed(
uint256 creatorReward,
uint256 fee,
uint256 mintReferralReward,
uint256 firstMinterReward,
address creator,
address mintReferral,
address firstMinter
);
}
interface IWritingEditions {
struct WritingEdition {
string name;
string symbol;
string description;
string imageURI;
string contentURI;
uint256 price;
uint256 limit;
address fundingRecipient;
address renderer;
uint256 nonce;
}
function VERSION() external view returns (uint8);
function factory() external returns (address);
function treasuryConfiguration() external returns (address);
function o11y() external returns (address);
function baseDescriptionURI() external view returns (string memory);
function description() external view returns (string memory);
function totalSupply() external view returns (uint256);
function price() external view returns (uint256);
function limit() external view returns (uint256);
function contentURI() external view returns (string memory);
function imageURI() external view returns (string memory);
function fundingRecipient() external returns (address);
function royaltyRecipient() external returns (address);
function royaltyBPS() external returns (uint256);
function renderer() external view returns (address);
function firstMinter() external view returns (address);
function ownerOf(
uint256[] memory tokenIds
) external view returns (address[] memory owners);
function initializeWithSignature(
address _creator,
bytes32 structHash,
bytes calldata signature,
WritingEdition memory edition,
address tokenRecipient,
string memory message,
address mintReferral,
bool _guardOn,
address sender
) external payable;
function initialize(
address _creator,
WritingEdition memory edition,
address recipient,
string memory message,
address mintReferral,
bool _guard
) external payable;
function setFundingRecipient(address fundingRecipient_) external;
function setPrice(uint256 price_) external;
function setBaseDescriptionURI(string memory _baseDescriptionURI) external;
function setLimit(uint256 limit_) external;
function setMaxLimit() external;
function setRoyaltyInfo(
address royaltyRecipient_,
uint256 royaltyPercentage_
) external;
function toggleGuard() external;
function purchase(
address tokenRecipient,
string memory message,
address mintReferral
) external payable returns (uint256 tokenId);
function mint(address tokenRecipient) external returns (uint256 tokenId);
function setRenderer(address renderer_) external;
function contractURI() external view returns (string memory);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "openzeppelin-contracts/contracts/utils/cryptography/EIP712.sol";
import "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol";
import "openzeppelin-contracts/contracts/interfaces/IERC1271.sol";
import "solady/utils/LibString.sol";
/// @notice ERC-7015: NFT Creator Attribution
/// @notice https://eips.ethereum.org/EIPS/eip-7015
/// @author indreams.eth
abstract contract ERC7015 is EIP712 {
error InvalidCreatorAttributionSignature();
error NameAndVersionTooLong();
error InvalidCreator();
event CreatorAttribution(
bytes32 structHash,
string domainName,
string version,
address creator,
bytes signature
);
/// @notice Define magic value to verify smart contract signatures (ERC1271).
bytes4 internal constant MAGIC_VALUE =
bytes4(keccak256("isValidSignature(bytes32,bytes)"));
bytes32 public constant TYPEHASH =
keccak256("TokenCreation(bytes32 structHash)");
bytes32 public immutable packedDomainNameAndVersion;
constructor(
string memory name,
string memory version
) EIP712(name, version) {
packedDomainNameAndVersion = LibString.packTwo(name, version);
if (packedDomainNameAndVersion == bytes32(0))
revert NameAndVersionTooLong();
}
function _validateSignature(
address creator,
bytes32 structHash,
bytes calldata signature
) internal {
if (!_isValid(structHash, creator, signature))
revert InvalidCreatorAttributionSignature();
(string memory _domainName, string memory _domainVersion) = LibString
.unpackTwo(packedDomainNameAndVersion);
emit CreatorAttribution(
structHash,
_domainName,
_domainVersion,
creator,
signature
);
}
function _isValid(
bytes32 structHash,
address signer,
bytes calldata signature
) internal view returns (bool) {
if (signer == address(0)) revert InvalidCreator();
bytes32 digest = _hashTypedDataV4(
keccak256(abi.encode(TYPEHASH, structHash))
);
// If the signer is a contract, attempt to validate the
// signature using EIP-1271.
if (signer.code.length != 0) {
// slither-disable-next-line unused-return
try IERC1271(signer).isValidSignature(digest, signature) returns (
// slither-disable-next-line uninitialized-local
bytes4 magicValue
) {
return MAGIC_VALUE == magicValue;
} catch {
return false;
}
}
address recoveredSigner = ECDSA.recover(digest, signature);
return recoveredSigner == signer;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
interface IObservabilityEvents {
/// > [[[[[[[[[[[ Factory events ]]]]]]]]]]]
event CloneDeployed(
address indexed factory,
address indexed owner,
address indexed clone
);
event TributarySet(
address indexed factory,
address indexed clone,
address oldTributary,
address indexed newTributary
);
event FactoryLimitSet(
address indexed factory,
uint256 oldLimit,
uint256 newLimit
);
event FactoryGuardSet(bool guard);
event FactoryImplementationSet(
address indexed factory,
address indexed oldImplementation,
address indexed newImplementation
);
/// > [[[[[[[[[[[ Clone events ]]]]]]]]]]]
event WritingEditionPurchased(
address indexed clone,
uint256 tokenId,
address indexed recipient,
uint256 price,
string message,
uint256 flatFeeAmount
);
event Transfer(
address indexed clone,
address indexed from,
address indexed to,
uint256 tokenId
);
event RoyaltyChange(
address indexed clone,
address indexed oldRoyaltyRecipient,
uint256 oldRoyaltyBPS,
address indexed newRoyaltyRecipient,
uint256 newRoyaltyBPS
);
event RendererSet(address indexed clone, address indexed renderer);
event WritingEditionLimitSet(
address indexed clone,
uint256 oldLimit,
uint256 newLimit
);
event PriceSet(address indexed clone, uint256 oldLimit, uint256 newLimit);
event FundingRecipientSet(
address indexed clone,
address indexed oldFundingRecipient,
address indexed newFundingRecipient
);
event BaseDescriptionURISet(
address indexed clone,
string oldBaseDescriptionURI,
string newBaseDescriptionURI
);
}
interface IObservability {
function emitDeploymentEvent(address owner, address clone) external;
function emitTributarySet(
address clone,
address oldTributary,
address newTributary
) external;
function emitFactoryGuardSet(bool guard) external;
function emitFactoryImplementationSet(
address oldImplementation,
address newImplementation
) external;
function emitFactoryLimitSet(uint256 oldLimit, uint256 newLimit) external;
function emitTransferEvent(
address from,
address to,
uint256 tokenId
) external;
function emitWritingEditionPurchased(
uint256 tokenId,
address recipient,
uint256 price,
string memory message,
uint256 flatFeeAmount
) external;
function emitRoyaltyChange(
address oldRoyaltyRecipient,
uint256 oldRoyaltyBPS,
address newRoyaltyRecipient,
uint256 newRoyaltyBPS
) external;
function emitRendererSet(address renderer) external;
function emitWritingEditionLimitSet(
uint256 oldLimit,
uint256 newLimit
) external;
function emitFundingRecipientSet(
address oldFundingRecipient,
address newFundingRecipient
) external;
function emitPriceSet(uint256 oldPrice, uint256 newPrice) external;
function emitBaseDescriptionURISet(
string memory oldBaseDescriptionURI,
string memory newBaseDescriptionURI
) external;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
interface IFeeConfigurationEvents {
event FeeSwitch(bool on);
event FlatFeeSwitch(bool on);
event MinimumFee(uint16 fee);
event MaximumFee(uint16 fee);
event FlatFeeAmount(uint256 fee);
}
interface IFeeConfiguration {
function on() external returns (bool);
function flatFeeOn() external returns (bool);
function flatFeeAmount() external returns (uint256);
function maximumFee() external returns (uint16);
function minimumFee() external returns (uint16);
function switchFee() external;
function setMinimumFee(uint16 newFee) external;
function setMaximumFee(uint16 newFe) external;
function valid(uint16 feeBPS) external view returns (bool);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
interface IRenderer {
function tokenURI(uint256 tokenId) external view returns (string calldata);
function contractURI() external view returns (string calldata);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
/**
* @title Rewards
* @notice Calculates rewards for free and paid mints
*/
library Rewards {
error FeeTooLowError();
/// @notice 40% creator reward for free mint
uint256 constant _FREE_CREATOR_REWARD_PERCENTAGE = 40_00;
/// @notice 10% mint referral reward for free mint
uint256 constant _FREE_MINT_REFERRAL_REWARD_PERCENTAGE = 10_00;
/// @notice 30% fee for free mint
uint256 constant _FREE_FEE_PERCENTAGE = 30_00;
/// @notice 20% first minter reward for free mint
uint256 constant _FREE_FIRST_MINTER_REWARD_PECENTAGE = 20_00;
/// @notice 25% creator referral reward for paid mint
uint256 constant _PAID_MINT_REFERRAL_REWARD_PERCENTAGE = 25_00;
/// @notice 50% mint referral reward for paid mint
uint256 constant _PAID_FEE_PERCENTAGE = 50_00;
/// @notice 25% fee for paid mint
uint256 constant _PAID_FIRST_MINTER_REWARD_PERCENTAGE = 25_00;
function getRewards(
address mintReferral,
bool freeMint,
uint256 flatFeeAmount
) external pure returns (uint256, uint256, uint256, uint256) {
if (flatFeeAmount < 10) revert FeeTooLowError();
if (freeMint) {
return _getFreeMintRewards(mintReferral, flatFeeAmount);
}
return _getPaidMintRewards(mintReferral, flatFeeAmount);
}
function _getFreeMintRewards(
address mintReferral,
uint256 flatFeeAmount
) internal pure returns (uint256, uint256, uint256, uint256) {
uint256 mintReferralReward = 0;
uint256 creatorReward = (flatFeeAmount *
_FREE_CREATOR_REWARD_PERCENTAGE) / 100_00;
uint256 firstMinterReward = (flatFeeAmount *
_FREE_FIRST_MINTER_REWARD_PECENTAGE) / 100_00;
uint256 fee = (flatFeeAmount * _FREE_FEE_PERCENTAGE) / 100_00;
if (mintReferral != address(0))
mintReferralReward =
(flatFeeAmount * _FREE_MINT_REFERRAL_REWARD_PERCENTAGE) /
100_00;
else
fee +=
(flatFeeAmount * _FREE_MINT_REFERRAL_REWARD_PERCENTAGE) /
100_00;
return (creatorReward, fee, mintReferralReward, firstMinterReward);
}
function _getPaidMintRewards(
address mintReferral,
uint256 flatFeeAmount
) internal pure returns (uint256, uint256, uint256, uint256) {
uint256 mintReferralReward = 0;
uint256 fee = (flatFeeAmount * _PAID_FEE_PERCENTAGE) / 100_00;
uint256 firstMinterReward = (flatFeeAmount *
_PAID_FIRST_MINTER_REWARD_PERCENTAGE) / 100_00;
if (mintReferral != address(0))
mintReferralReward =
(flatFeeAmount * _PAID_MINT_REFERRAL_REWARD_PERCENTAGE) /
100_00;
else
fee +=
(flatFeeAmount * _PAID_MINT_REFERRAL_REWARD_PERCENTAGE) /
100_00;
return (0, fee, mintReferralReward, firstMinterReward);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
interface ITreasuryConfigurationEvents {
event TreasurySet(address indexed treasury, address indexed newTreasury);
event TributaryRegistrySet(
address indexed tributaryRegistry,
address indexed newTributaryRegistry
);
event DistributionSet(
address indexed distribution,
address indexed newDistribution
);
event FeeConfigurationSet(
address indexed feeConfiguration,
address indexed newFeeConfiguration
);
}
interface ITreasuryConfiguration {
function treasury() external returns (address payable);
function tributaryRegistry() external returns (address);
function distribution() external returns (address);
function feeConfiguration() external returns (address);
function setTreasury(address payable newTreasury) external;
function setTributaryRegistry(address newTributaryRegistry) external;
function setDistribution(address newDistribution) external;
function setFeeConfiguration(address newFeeConfiguration) external;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
interface ITreasuryEvents {
event Transfer(address indexed from, address indexed to, uint256 value);
event ERC20Transfer(
address indexed token,
address indexed from,
address indexed to,
uint256 amount
);
event ERC721Transfer(
address indexed token,
address indexed from,
address indexed to,
uint256 tokenId
);
}
interface ITreasury {
struct Call {
// The target of the transaction.
address target;
// The value passed into the transaction.
uint96 value;
// Any data passed with the call.
bytes data;
}
function treasuryConfiguration() external view returns (address);
function transferFunds(address payable to, uint256 value) external;
function transferERC20(
address token,
address to,
uint256 value
) external;
function transferERC721(
address token,
address from,
address to,
uint256 tokenId
) external;
function contributeWithTributary(address tributary) external payable;
function contribute(uint256 amount) external payable;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
interface ITributaryRegistry {
function allowedRegistrar(address account) external view returns (bool);
function producerToTributary(address producer)
external
view
returns (address tributary);
function singletonProducer(address producer) external view returns (bool);
function addRegistrar(address registrar) external;
function removeRegistrar(address registrar) external;
function addSingletonProducer(address producer) external;
function removeSingletonProducer(address producer) external;
function setTributary(address producer, address newTributary) external;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
import "./interface/IERC721.sol";
import "../ERC165/ERC165.sol";
/**
* Based on: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol
*/
contract ERC721 is ERC165, IERC721, IERC721Events {
mapping(uint256 => address) internal _owners;
mapping(address => uint256) internal _balances;
mapping(uint256 => address) private _tokenApprovals;
mapping(address => mapping(address => bool)) private _operatorApprovals;
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override
returns (bool)
{
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
function balanceOf(address owner)
external
view
virtual
override
returns (uint256)
{
require(
owner != address(0),
"ERC721: balance query for the zero address"
);
return _balances[owner];
}
function ownerOf(uint256 tokenId) external view virtual returns (address) {
return _ownerOf(tokenId);
}
function _ownerOf(uint256 tokenId) internal view returns (address) {
address owner = _owners[tokenId];
require(
owner != address(0),
"ERC721: owner query for nonexistent token"
);
return owner;
}
function approve(address to, uint256 tokenId) external virtual override {
address owner = _ownerOf(tokenId);
require(to != owner, "ERC721: approval to current owner");
require(
msg.sender == owner || isApprovedForAll(owner, msg.sender),
"ERC721: approve caller is not owner nor approved for all"
);
_approve(to, tokenId);
}
function getApproved(uint256 tokenId)
public
view
virtual
override
returns (address)
{
require(
_exists(tokenId),
"ERC721: approved query for nonexistent token"
);
return _tokenApprovals[tokenId];
}
function setApprovalForAll(address operator, bool approved)
external
virtual
override
{
require(operator != msg.sender, "ERC721: approve to caller");
_operatorApprovals[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function isApprovedForAll(address owner, address operator)
public
view
virtual
override
returns (bool)
{
return _operatorApprovals[owner][operator];
}
function transferFrom(
address from,
address to,
uint256 tokenId
) external virtual override {
//solhint-disable-next-line max-line-length
require(
_isApprovedOrOwner(msg.sender, tokenId),
"ERC721: transfer caller is not owner nor approved"
);
_transfer(from, to, tokenId);
}
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external virtual override {
_safeTransferFrom(from, to, tokenId, "");
}
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory _data
) external virtual override {
_safeTransferFrom(from, to, tokenId, _data);
}
function _safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory _data
) internal virtual {
require(
_isApprovedOrOwner(msg.sender, tokenId),
"ERC721: transfer caller is not owner nor approved"
);
_safeTransfer(from, to, tokenId, _data);
}
function _safeTransfer(
address from,
address to,
uint256 tokenId,
bytes memory _data
) internal virtual {
_transfer(from, to, tokenId);
require(
_checkOnERC721Received(from, to, tokenId, _data),
"ERC721: transfer to non ERC721Receiver implementer"
);
}
function _exists(uint256 tokenId) internal view virtual returns (bool) {
return _owners[tokenId] != address(0);
}
function _isApprovedOrOwner(address spender, uint256 tokenId)
internal
view
virtual
returns (bool)
{
require(
_exists(tokenId),
"ERC721: operator query for nonexistent token"
);
address owner = _ownerOf(tokenId);
return (spender == owner ||
getApproved(tokenId) == spender ||
isApprovedForAll(owner, spender));
}
function _safeMint(address to, uint256 tokenId) internal virtual {
_safeMint(to, tokenId, "");
}
function _safeMint(
address to,
uint256 tokenId,
bytes memory _data
) internal virtual {
_mint(to, tokenId);
require(
_checkOnERC721Received(address(0), to, tokenId, _data),
"ERC721: transfer to non ERC721Receiver implementer"
);
}
function _mint(address to, uint256 tokenId) internal virtual {
require(to != address(0), "ERC721: mint to the zero address");
require(!_exists(tokenId), "ERC721: token already minted");
_beforeTokenTransfer(address(0), to, tokenId);
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(address(0), to, tokenId);
}
function _burn(uint256 tokenId) internal virtual {
address owner = _ownerOf(tokenId);
_beforeTokenTransfer(owner, address(0), tokenId);
// Clear approvals
_approve(address(0), tokenId);
_balances[owner] -= 1;
delete _owners[tokenId];
emit Transfer(owner, address(0), tokenId);
}
function _transfer(
address from,
address to,
uint256 tokenId
) internal virtual {
require(
_ownerOf(tokenId) == from,
"ERC721: transfer of token that is not own"
);
require(to != address(0), "ERC721: transfer to the zero address");
_beforeTokenTransfer(from, to, tokenId);
// Clear approvals from the previous owner
_approve(address(0), tokenId);
_balances[from] -= 1;
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
}
function _approve(address to, uint256 tokenId) internal virtual {
_tokenApprovals[tokenId] = to;
emit Approval(_ownerOf(tokenId), to, tokenId);
}
function _checkOnERC721Received(
address from,
address to,
uint256 tokenId,
bytes memory _data
) private returns (bool) {
if (to.code.length > 0) {
// slither-disable-next-line unused-return
try
IERC721Receiver(to).onERC721Received(
msg.sender,
from,
tokenId,
_data
)
returns (bytes4 retval) {
return retval == IERC721Receiver(to).onERC721Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
revert(
"ERC721: transfer to non ERC721Receiver implementer"
);
} else {
// solhint-disable-next-line no-inline-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
} else {
return true;
}
}
function _beforeTokenTransfer(
address from,
address to,
uint256 tokenId
) internal virtual {}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override
returns (bool)
{
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
interface IERC721 {
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
function approve(address to, uint256 tokenId) external;
function getApproved(uint256 tokenId)
external
view
returns (address operator);
function setApprovalForAll(address operator, bool _approved) external;
function isApprovedForAll(address owner, address operator)
external
view
returns (bool);
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
}
interface IERC721Events {
event Transfer(
address indexed from,
address indexed to,
uint256 indexed tokenId
);
event Approval(
address indexed owner,
address indexed approved,
uint256 indexed tokenId
);
event ApprovalForAll(
address indexed owner,
address indexed operator,
bool approved
);
}
interface IERC721Metadata {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function tokenURI(uint256 tokenId) external view returns (string memory);
}
interface IERC721Burnable is IERC721 {
function burn(uint256 tokenId) external;
}
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
interface IERC721Royalties {
function getFeeRecipients(uint256 id)
external
view
returns (address payable[] memory);
function getFeeBps(uint256 id) external view returns (uint256[] memory);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
/**
* @title IERC2981
* @notice Interface for the NFT Royalty Standard
*/
interface IERC2981 {
// / bytes4(keccak256("royaltyInfo(uint256,uint256)")) == 0x2a55205a
/**
* @notice Called with the sale price to determine how much royalty
* is owed and to whom.
* @param _tokenId - the NFT asset queried for royalty information
* @param _salePrice - the sale price of the NFT asset specified by _tokenId
* @return receiver - address of who should be sent the royalty payment
* @return royaltyAmount - the royalty payment amount for _salePrice
*/
function royaltyInfo(uint256 _tokenId, uint256 _salePrice)
external
view
returns (address receiver, uint256 royaltyAmount);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
/**
* @title TransactionReentrancyGuard
* @notice Transaction level reentrancy guard, used to prevent calling a
* function multiple times in the same transaction, e.g. minting a token
* using a multicall contract. The guard accesses a storage variable twice
* and compares the gas used, taking advantage of EIP-2929 cold/warm storage
* read costs.
*
* From EIP-2929: Gas cost increases for state access opcodes:
* "For SLOAD, if the (address, storage_key) pair (where address
* is the address of the contract whose storage is being read) is not yet
* in accessed_storage_keys, charge COLD_SLOAD_COST gas and add the pair
* to accessed_storage_keys. If the pair is already in accessed_storage_keys,
* charge WARM_STORAGE_READ_COST gas."
*
* Implementation was forked from bertani.eth, after a thread involving these
* anon solidity giga-brains: [at]rage_pit, [at]transmissions11, 0age.eth.
*/
contract TransactionReentrancyGuard {
bool public guardOn;
uint256 internal GUARD = 1;
/// > [[[[[[[[[[[ Modifiers ]]]]]]]]]]]
modifier guard() {
// If guard is on, run guard.
if (guardOn) {
_guard();
}
_;
}
constructor(bool _guardOn) {
_setGuard(_guardOn);
}
function _guard() internal view {
// Store current gas left.
uint256 t0 = gasleft();
// Load GUARD from storage.
uint256 g = GUARD;
// Store current gas left.
uint256 t1 = gasleft();
// Load GUARD from storage.
uint256 m = GUARD;
// Assert the cost of acessing `g` is greater than the
// cost of accessing `m`, which implies the first SLOAD
// was charged COLD_SLOAD_COST and the second SLOAD was
// charged WARM_STORAGE_READ_COST. Hence this is the first
// time the function has been called.
require(t1 - gasleft() < t0 - t1);
}
function _toggleGuard() internal {
_setGuard(!guardOn);
}
function _setGuard(bool _guardOn) internal {
guardOn = _guardOn;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Base64.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides a set of functions to operate with Base64 strings.
*
* _Available since v4.5._
*/
library Base64 {
/**
* @dev Base64 Encoding/Decoding Table
*/
string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/**
* @dev Converts a `bytes` to its Bytes64 `string` representation.
*/
function encode(bytes memory data) internal pure returns (string memory) {
/**
* Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
* https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
*/
if (data.length == 0) return "";
// Loads the table into memory
string memory table = _TABLE;
// Encoding takes 3 bytes chunks of binary data from `bytes` data parameter
// and split into 4 numbers of 6 bits.
// The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up
// - `data.length + 2` -> Round up
// - `/ 3` -> Number of 3-bytes chunks
// - `4 *` -> 4 characters for each chunk
string memory result = new string(4 * ((data.length + 2) / 3));
/// @solidity memory-safe-assembly
assembly {
// Prepare the lookup table (skip the first "length" byte)
let tablePtr := add(table, 1)
// Prepare result pointer, jump over length
let resultPtr := add(result, 32)
// Run over the input, 3 bytes at a time
for {
let dataPtr := data
let endPtr := add(data, mload(data))
} lt(dataPtr, endPtr) {
} {
// Advance 3 bytes
dataPtr := add(dataPtr, 3)
let input := mload(dataPtr)
// To write each character, shift the 3 bytes (18 bits) chunk
// 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
// and apply logical AND with 0x3F which is the number of
// the previous character in the ASCII table prior to the Base64 Table
// The result is then added to the table to get the character to write,
// and finally write it in the result pointer but with a left shift
// of 256 (1 byte) - 8 (1 ASCII char) = 248 bits
mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
}
// When data `bytes` is not exactly 3 bytes long
// it is padded with `=` characters at the end
switch mod(mload(data), 3)
case 1 {
mstore8(sub(resultPtr, 1), 0x3d)
mstore8(sub(resultPtr, 2), 0x3d)
}
case 2 {
mstore8(sub(resultPtr, 1), 0x3d)
}
}
return result;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/Math.sol";
import "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(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) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol)
pragma solidity ^0.8.8;
import "./ECDSA.sol";
import "../ShortStrings.sol";
import "../../interfaces/IERC5267.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
*
* The encoding specified in the EIP is very generic, and such a generic 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 their contracts 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.
*
* _Available since v3.4._
*
* @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
*/
abstract contract EIP712 is IERC5267 {
using ShortStrings for *;
bytes32 private constant _TYPE_HASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
// Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
// invalidate the cached domain separator if the chain id changes.
bytes32 private immutable _cachedDomainSeparator;
uint256 private immutable _cachedChainId;
address private immutable _cachedThis;
bytes32 private immutable _hashedName;
bytes32 private immutable _hashedVersion;
ShortString private immutable _name;
ShortString private immutable _version;
string private _nameFallback;
string private _versionFallback;
/**
* @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].
*/
constructor(string memory name, string memory version) {
_name = name.toShortStringWithFallback(_nameFallback);
_version = version.toShortStringWithFallback(_versionFallback);
_hashedName = keccak256(bytes(name));
_hashedVersion = keccak256(bytes(version));
_cachedChainId = block.chainid;
_cachedDomainSeparator = _buildDomainSeparator();
_cachedThis = address(this);
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
return _cachedDomainSeparator;
} else {
return _buildDomainSeparator();
}
}
function _buildDomainSeparator() private view returns (bytes32) {
return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, 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 ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
}
/**
* @dev See {EIP-5267}.
*
* _Available since v4.9._
*/
function eip712Domain()
public
view
virtual
override
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
return (
hex"0f", // 01111
_name.toStringWithFallback(_nameFallback),
_version.toStringWithFallback(_versionFallback),
block.chainid,
address(this),
bytes32(0),
new uint256[](0)
);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.0;
import "../Strings.sol";
/**
* @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,
InvalidSignatureV // Deprecated in v4.8
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode 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 {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]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
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);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode 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 {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
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]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
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.
*
* _Available since v4.2._
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
// 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);
}
// 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);
}
return (signer, RecoverError.NoError);
}
/**
* @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) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32")
mstore(0x1c, hash)
message := keccak256(0x00, 0x3c)
}
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, "\x19\x01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
data := keccak256(ptr, 0x42)
}
}
/**
* @dev Returns an Ethereum Signed Data with intended validator, created from a
* `validator` and `data` according to the version 0 of EIP-191.
*
* See {recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x00", validator, data));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC1271 standard signature validation method for
* contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
*
* _Available since v4.1._
*/
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
pragma solidity ^0.8.4;
/// @notice Library for converting numbers into strings and other string operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
library LibString {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The `length` of the output is too small to contain all the hex digits.
error HexLengthInsufficient();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The constant returned when the `search` is not found in the string.
uint256 internal constant NOT_FOUND = type(uint256).max;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* DECIMAL OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the base 10 decimal representation of `value`.
function toString(uint256 value) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
// The maximum value of a uint256 contains 78 digits (1 byte per digit), but
// we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
// We will need 1 word for the trailing zeros padding, 1 word for the length,
// and 3 words for a maximum of 78 digits.
str := add(mload(0x40), 0x80)
// Update the free memory pointer to allocate.
mstore(0x40, add(str, 0x20))
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end of the memory to calculate the length later.
let end := str
let w := not(0) // Tsk.
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let temp := value } 1 {} {
str := add(str, w) // `sub(str, 1)`.
// Write the character to the pointer.
// The ASCII index of the '0' character is 48.
mstore8(str, add(48, mod(temp, 10)))
// Keep dividing `temp` until zero.
temp := div(temp, 10)
if iszero(temp) { break }
}
let length := sub(end, str)
// Move the pointer 32 bytes leftwards to make room for the length.
str := sub(str, 0x20)
// Store the length.
mstore(str, length)
}
}
/// @dev Returns the base 10 decimal representation of `value`.
function toString(int256 value) internal pure returns (string memory str) {
if (value >= 0) {
return toString(uint256(value));
}
unchecked {
str = toString(uint256(-value));
}
/// @solidity memory-safe-assembly
assembly {
// We still have some spare memory space on the left,
// as we have allocated 3 words (96 bytes) for up to 78 digits.
let length := mload(str) // Load the string length.
mstore(str, 0x2d) // Store the '-' character.
str := sub(str, 1) // Move back the string pointer by a byte.
mstore(str, add(length, 1)) // Update the string length.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HEXADECIMAL OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the hexadecimal representation of `value`,
/// left-padded to an input length of `length` bytes.
/// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
/// giving a total length of `length * 2 + 2` bytes.
/// Reverts if `length` is too small for the output to contain all the digits.
function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value, length);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write the length.
}
}
/// @dev Returns the hexadecimal representation of `value`,
/// left-padded to an input length of `length` bytes.
/// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
/// giving a total length of `length * 2` bytes.
/// Reverts if `length` is too small for the output to contain all the digits.
function toHexStringNoPrefix(uint256 value, uint256 length)
internal
pure
returns (string memory str)
{
/// @solidity memory-safe-assembly
assembly {
// We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes
// for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
// We add 0x20 to the total and round down to a multiple of 0x20.
// (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))
// Allocate the memory.
mstore(0x40, add(str, 0x20))
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end to calculate the length later.
let end := str
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
let start := sub(str, add(length, length))
let w := not(1) // Tsk.
let temp := value
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for {} 1 {} {
str := add(str, w) // `sub(str, 2)`.
mstore8(add(str, 1), mload(and(temp, 15)))
mstore8(str, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(xor(str, start)) { break }
}
if temp {
// Store the function selector of `HexLengthInsufficient()`.
mstore(0x00, 0x2194895a)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Compute the string's length.
let strLength := sub(end, str)
// Move the pointer and write the length.
str := sub(str, 0x20)
mstore(str, strLength)
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
/// As address are 20 bytes long, the output will left-padded to have
/// a length of `20 * 2 + 2` bytes.
function toHexString(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x".
/// The output excludes leading "0" from the `toHexString` output.
/// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`.
function toMinimalHexString(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
let strLength := add(mload(str), 2) // Compute the length.
mstore(add(str, o), 0x3078) // Write the "0x" prefix, accounting for leading zero.
str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero.
mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output excludes leading "0" from the `toHexStringNoPrefix` output.
/// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`.
function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
let strLength := mload(str) // Get the length.
str := add(str, o) // Move the pointer, accounting for leading zero.
mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is encoded using 2 hexadecimal digits per byte.
/// As address are 20 bytes long, the output will left-padded to have
/// a length of `20 * 2` bytes.
function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
// We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
// 0x02 bytes for the prefix, and 0x40 bytes for the digits.
// The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
str := add(mload(0x40), 0x80)
// Allocate the memory.
mstore(0x40, add(str, 0x20))
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end to calculate the length later.
let end := str
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
let w := not(1) // Tsk.
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let temp := value } 1 {} {
str := add(str, w) // `sub(str, 2)`.
mstore8(add(str, 1), mload(and(temp, 15)))
mstore8(str, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(temp) { break }
}
// Compute the string's length.
let strLength := sub(end, str)
// Move the pointer and write the length.
str := sub(str, 0x20)
mstore(str, strLength)
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
/// and the alphabets are capitalized conditionally according to
/// https://eips.ethereum.org/EIPS/eip-55
function toHexStringChecksummed(address value) internal pure returns (string memory str) {
str = toHexString(value);
/// @solidity memory-safe-assembly
assembly {
let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
let o := add(str, 0x22)
let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
let t := shl(240, 136) // `0b10001000 << 240`
for { let i := 0 } 1 {} {
mstore(add(i, i), mul(t, byte(i, hashed)))
i := add(i, 1)
if eq(i, 20) { break }
}
mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
o := add(o, 0x20)
mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
function toHexString(address value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexStringNoPrefix(address value) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
str := mload(0x40)
// Allocate the memory.
// We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
// 0x02 bytes for the prefix, and 0x28 bytes for the digits.
// The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
mstore(0x40, add(str, 0x80))
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
str := add(str, 2)
mstore(str, 40)
let o := add(str, 0x20)
mstore(add(o, 40), 0)
value := shl(96, value)
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let i := 0 } 1 {} {
let p := add(o, add(i, i))
let temp := byte(i, value)
mstore8(add(p, 1), mload(and(temp, 15)))
mstore8(p, mload(shr(4, temp)))
i := add(i, 1)
if eq(i, 20) { break }
}
}
}
/// @dev Returns the hex encoded string from the raw bytes.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexString(bytes memory raw) internal pure returns (string memory str) {
str = toHexStringNoPrefix(raw);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write the length.
}
}
/// @dev Returns the hex encoded string from the raw bytes.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
let length := mload(raw)
str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
mstore(str, add(length, length)) // Store the length of the output.
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
let o := add(str, 0x20)
let end := add(raw, length)
for {} iszero(eq(raw, end)) {} {
raw := add(raw, 1)
mstore8(add(o, 1), mload(and(mload(raw), 15)))
mstore8(o, mload(and(shr(4, mload(raw)), 15)))
o := add(o, 2)
}
mstore(o, 0) // Zeroize the slot after the string.
mstore(0x40, add(o, 0x20)) // Allocate the memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* RUNE STRING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the number of UTF characters in the string.
function runeCount(string memory s) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
if mload(s) {
mstore(0x00, div(not(0), 255))
mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)
let o := add(s, 0x20)
let end := add(o, mload(s))
for { result := 1 } 1 { result := add(result, 1) } {
o := add(o, byte(0, mload(shr(250, mload(o)))))
if iszero(lt(o, end)) { break }
}
}
}
}
/// @dev Returns if this string is a 7-bit ASCII string.
/// (i.e. all characters codes are in [0..127])
function is7BitASCII(string memory s) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
let mask := shl(7, div(not(0), 255))
result := 1
let n := mload(s)
if n {
let o := add(s, 0x20)
let end := add(o, n)
let last := mload(end)
mstore(end, 0)
for {} 1 {} {
if and(mask, mload(o)) {
result := 0
break
}
o := add(o, 0x20)
if iszero(lt(o, end)) { break }
}
mstore(end, last)
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTE STRING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// For performance and bytecode compactness, all indices of the following operations
// are byte (ASCII) offsets, not UTF character offsets.
/// @dev Returns `subject` all occurrences of `search` replaced with `replacement`.
function replace(string memory subject, string memory search, string memory replacement)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
let searchLength := mload(search)
let replacementLength := mload(replacement)
subject := add(subject, 0x20)
search := add(search, 0x20)
replacement := add(replacement, 0x20)
result := add(mload(0x40), 0x20)
let subjectEnd := add(subject, subjectLength)
if iszero(gt(searchLength, subjectLength)) {
let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)
let h := 0
if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(search)
for {} 1 {} {
let t := mload(subject)
// Whether the first `searchLength % 32` bytes of
// `subject` and `search` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(subject, searchLength), h)) {
mstore(result, t)
result := add(result, 1)
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
// Copy the `replacement` one word at a time.
for { let o := 0 } 1 {} {
mstore(add(result, o), mload(add(replacement, o)))
o := add(o, 0x20)
if iszero(lt(o, replacementLength)) { break }
}
result := add(result, replacementLength)
subject := add(subject, searchLength)
if searchLength {
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
mstore(result, t)
result := add(result, 1)
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
}
}
let resultRemainder := result
result := add(mload(0x40), 0x20)
let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))
// Copy the rest of the string one word at a time.
for {} lt(subject, subjectEnd) {} {
mstore(resultRemainder, mload(subject))
resultRemainder := add(resultRemainder, 0x20)
subject := add(subject, 0x20)
}
result := sub(result, 0x20)
let last := add(add(result, 0x20), k) // Zeroize the slot after the string.
mstore(last, 0)
mstore(0x40, add(last, 0x20)) // Allocate the memory.
mstore(result, k) // Store the length.
}
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from left to right, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function indexOf(string memory subject, string memory search, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
for { let subjectLength := mload(subject) } 1 {} {
if iszero(mload(search)) {
if iszero(gt(from, subjectLength)) {
result := from
break
}
result := subjectLength
break
}
let searchLength := mload(search)
let subjectStart := add(subject, 0x20)
result := not(0) // Initialize to `NOT_FOUND`.
subject := add(subjectStart, from)
let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(add(search, 0x20))
if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }
if iszero(lt(searchLength, 0x20)) {
for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
if eq(keccak256(subject, searchLength), h) {
result := sub(subject, subjectStart)
break
}
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
for {} 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
result := sub(subject, subjectStart)
break
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
}
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from left to right.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function indexOf(string memory subject, string memory search)
internal
pure
returns (uint256 result)
{
result = indexOf(subject, search, 0);
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from right to left, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function lastIndexOf(string memory subject, string memory search, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
for {} 1 {} {
result := not(0) // Initialize to `NOT_FOUND`.
let searchLength := mload(search)
if gt(searchLength, mload(subject)) { break }
let w := result
let fromMax := sub(mload(subject), searchLength)
if iszero(gt(fromMax, from)) { from := fromMax }
let end := add(add(subject, 0x20), w)
subject := add(add(subject, 0x20), from)
if iszero(gt(subject, end)) { break }
// As this function is not too often used,
// we shall simply use keccak256 for smaller bytecode size.
for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
if eq(keccak256(subject, searchLength), h) {
result := sub(subject, add(end, 1))
break
}
subject := add(subject, w) // `sub(subject, 1)`.
if iszero(gt(subject, end)) { break }
}
break
}
}
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from right to left.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function lastIndexOf(string memory subject, string memory search)
internal
pure
returns (uint256 result)
{
result = lastIndexOf(subject, search, uint256(int256(-1)));
}
/// @dev Returns whether `subject` starts with `search`.
function startsWith(string memory subject, string memory search)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
let searchLength := mload(search)
// Just using keccak256 directly is actually cheaper.
// forgefmt: disable-next-item
result := and(
iszero(gt(searchLength, mload(subject))),
eq(
keccak256(add(subject, 0x20), searchLength),
keccak256(add(search, 0x20), searchLength)
)
)
}
}
/// @dev Returns whether `subject` ends with `search`.
function endsWith(string memory subject, string memory search)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
let searchLength := mload(search)
let subjectLength := mload(subject)
// Whether `search` is not longer than `subject`.
let withinRange := iszero(gt(searchLength, subjectLength))
// Just using keccak256 directly is actually cheaper.
// forgefmt: disable-next-item
result := and(
withinRange,
eq(
keccak256(
// `subject + 0x20 + max(subjectLength - searchLength, 0)`.
add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),
searchLength
),
keccak256(add(search, 0x20), searchLength)
)
)
}
}
/// @dev Returns `subject` repeated `times`.
function repeat(string memory subject, uint256 times)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
if iszero(or(iszero(times), iszero(subjectLength))) {
subject := add(subject, 0x20)
result := mload(0x40)
let output := add(result, 0x20)
for {} 1 {} {
// Copy the `subject` one word at a time.
for { let o := 0 } 1 {} {
mstore(add(output, o), mload(add(subject, o)))
o := add(o, 0x20)
if iszero(lt(o, subjectLength)) { break }
}
output := add(output, subjectLength)
times := sub(times, 1)
if iszero(times) { break }
}
mstore(output, 0) // Zeroize the slot after the string.
let resultLength := sub(output, add(result, 0x20))
mstore(result, resultLength) // Store the length.
// Allocate the memory.
mstore(0x40, add(result, add(resultLength, 0x20)))
}
}
}
/// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function slice(string memory subject, uint256 start, uint256 end)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
if iszero(gt(subjectLength, end)) { end := subjectLength }
if iszero(gt(subjectLength, start)) { start := subjectLength }
if lt(start, end) {
result := mload(0x40)
let resultLength := sub(end, start)
mstore(result, resultLength)
subject := add(subject, start)
let w := not(0x1f)
// Copy the `subject` one word at a time, backwards.
for { let o := and(add(resultLength, 0x1f), w) } 1 {} {
mstore(add(result, o), mload(add(subject, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
// Zeroize the slot after the string.
mstore(add(add(result, 0x20), resultLength), 0)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, add(result, and(add(resultLength, 0x3f), w)))
}
}
}
/// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
/// `start` is a byte offset.
function slice(string memory subject, uint256 start)
internal
pure
returns (string memory result)
{
result = slice(subject, start, uint256(int256(-1)));
}
/// @dev Returns all the indices of `search` in `subject`.
/// The indices are byte offsets.
function indicesOf(string memory subject, string memory search)
internal
pure
returns (uint256[] memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
let searchLength := mload(search)
if iszero(gt(searchLength, subjectLength)) {
subject := add(subject, 0x20)
search := add(search, 0x20)
result := add(mload(0x40), 0x20)
let subjectStart := subject
let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)
let h := 0
if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(search)
for {} 1 {} {
let t := mload(subject)
// Whether the first `searchLength % 32` bytes of
// `subject` and `search` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(subject, searchLength), h)) {
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
// Append to `result`.
mstore(result, sub(subject, subjectStart))
result := add(result, 0x20)
// Advance `subject` by `searchLength`.
subject := add(subject, searchLength)
if searchLength {
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
}
let resultEnd := result
// Assign `result` to the free memory pointer.
result := mload(0x40)
// Store the length of `result`.
mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))
// Allocate memory for result.
// We allocate one more word, so this array can be recycled for {split}.
mstore(0x40, add(resultEnd, 0x20))
}
}
}
/// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.
function split(string memory subject, string memory delimiter)
internal
pure
returns (string[] memory result)
{
uint256[] memory indices = indicesOf(subject, delimiter);
/// @solidity memory-safe-assembly
assembly {
let w := not(0x1f)
let indexPtr := add(indices, 0x20)
let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
mstore(add(indicesEnd, w), mload(subject))
mstore(indices, add(mload(indices), 1))
let prevIndex := 0
for {} 1 {} {
let index := mload(indexPtr)
mstore(indexPtr, 0x60)
if iszero(eq(index, prevIndex)) {
let element := mload(0x40)
let elementLength := sub(index, prevIndex)
mstore(element, elementLength)
// Copy the `subject` one word at a time, backwards.
for { let o := and(add(elementLength, 0x1f), w) } 1 {} {
mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
// Zeroize the slot after the string.
mstore(add(add(element, 0x20), elementLength), 0)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))
// Store the `element` into the array.
mstore(indexPtr, element)
}
prevIndex := add(index, mload(delimiter))
indexPtr := add(indexPtr, 0x20)
if iszero(lt(indexPtr, indicesEnd)) { break }
}
result := indices
if iszero(mload(delimiter)) {
result := add(indices, 0x20)
mstore(result, sub(mload(indices), 2))
}
}
}
/// @dev Returns a concatenated string of `a` and `b`.
/// Cheaper than `string.concat()` and does not de-align the free memory pointer.
function concat(string memory a, string memory b)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let w := not(0x1f)
result := mload(0x40)
let aLength := mload(a)
// Copy `a` one word at a time, backwards.
for { let o := and(add(aLength, 0x20), w) } 1 {} {
mstore(add(result, o), mload(add(a, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
let bLength := mload(b)
let output := add(result, aLength)
// Copy `b` one word at a time, backwards.
for { let o := and(add(bLength, 0x20), w) } 1 {} {
mstore(add(output, o), mload(add(b, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
let totalLength := add(aLength, bLength)
let last := add(add(result, 0x20), totalLength)
// Zeroize the slot after the string.
mstore(last, 0)
// Stores the length.
mstore(result, totalLength)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, and(add(last, 0x1f), w))
}
}
/// @dev Returns a copy of the string in either lowercase or UPPERCASE.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function toCase(string memory subject, bool toUpper)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let length := mload(subject)
if length {
result := add(mload(0x40), 0x20)
subject := add(subject, 1)
let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)
let w := not(0)
for { let o := length } 1 {} {
o := add(o, w)
let b := and(0xff, mload(add(subject, o)))
mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))
if iszero(o) { break }
}
result := mload(0x40)
mstore(result, length) // Store the length.
let last := add(add(result, 0x20), length)
mstore(last, 0) // Zeroize the slot after the string.
mstore(0x40, add(last, 0x20)) // Allocate the memory.
}
}
}
/// @dev Returns a string from a small bytes32 string.
/// `smallString` must be null terminated, or behavior will be undefined.
function fromSmallString(bytes32 smallString) internal pure returns (string memory result) {
if (smallString == bytes32(0)) return result;
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let n := 0
for {} 1 {} {
n := add(n, 1)
if iszero(byte(n, smallString)) { break } // Scan for '\0'.
}
mstore(result, n)
let o := add(result, 0x20)
mstore(o, smallString)
mstore(add(o, n), 0)
mstore(0x40, add(result, 0x40))
}
}
/// @dev Returns a lowercased copy of the string.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function lower(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, false);
}
/// @dev Returns an UPPERCASED copy of the string.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function upper(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, true);
}
/// @dev Escapes the string to be used within HTML tags.
function escapeHTML(string memory s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
let end := add(s, mload(s))
result := add(mload(0x40), 0x20)
// Store the bytes of the packed offsets and strides into the scratch space.
// `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.
mstore(0x1f, 0x900094)
mstore(0x08, 0xc0000000a6ab)
// Store ""&'<>" into the scratch space.
mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))
for {} iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
// Not in `["\"","'","&","<",">"]`.
if iszero(and(shl(c, 1), 0x500000c400000000)) {
mstore8(result, c)
result := add(result, 1)
continue
}
let t := shr(248, mload(c))
mstore(result, mload(and(t, 0x1f)))
result := add(result, shr(5, t))
}
let last := result
mstore(last, 0) // Zeroize the slot after the string.
result := mload(0x40)
mstore(result, sub(last, add(result, 0x20))) // Store the length.
mstore(0x40, add(last, 0x20)) // Allocate the memory.
}
}
/// @dev Escapes the string to be used within double-quotes in a JSON.
/// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.
function escapeJSON(string memory s, bool addDoubleQuotes)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let end := add(s, mload(s))
result := add(mload(0x40), 0x20)
if addDoubleQuotes {
mstore8(result, 34)
result := add(1, result)
}
// Store "\\u0000" in scratch space.
// Store "0123456789abcdef" in scratch space.
// Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`.
// into the scratch space.
mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)
// Bitmask for detecting `["\"","\\"]`.
let e := or(shl(0x22, 1), shl(0x5c, 1))
for {} iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
if iszero(lt(c, 0x20)) {
if iszero(and(shl(c, 1), e)) {
// Not in `["\"","\\"]`.
mstore8(result, c)
result := add(result, 1)
continue
}
mstore8(result, 0x5c) // "\\".
mstore8(add(result, 1), c)
result := add(result, 2)
continue
}
if iszero(and(shl(c, 1), 0x3700)) {
// Not in `["\b","\t","\n","\f","\d"]`.
mstore8(0x1d, mload(shr(4, c))) // Hex value.
mstore8(0x1e, mload(and(c, 15))) // Hex value.
mstore(result, mload(0x19)) // "\\u00XX".
result := add(result, 6)
continue
}
mstore8(result, 0x5c) // "\\".
mstore8(add(result, 1), mload(add(c, 8)))
result := add(result, 2)
}
if addDoubleQuotes {
mstore8(result, 34)
result := add(1, result)
}
let last := result
mstore(last, 0) // Zeroize the slot after the string.
result := mload(0x40)
mstore(result, sub(last, add(result, 0x20))) // Store the length.
mstore(0x40, add(last, 0x20)) // Allocate the memory.
}
}
/// @dev Escapes the string to be used within double-quotes in a JSON.
function escapeJSON(string memory s) internal pure returns (string memory result) {
result = escapeJSON(s, false);
}
/// @dev Returns whether `a` equals `b`.
function eq(string memory a, string memory b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
}
}
/// @dev Returns whether `a` equals `b`. For small strings up to 32 bytes.
/// `b` must be null terminated, or behavior will be undefined.
function eqs(string memory a, bytes32 b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
// These should be evaluated on compile time, as far as possible.
let x := and(b, add(not(b), 1))
let r := or(shl(8, iszero(b)), shl(7, iszero(iszero(shr(128, x)))))
r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
result := gt(eq(mload(a), sub(32, shr(3, r))), shr(r, xor(b, mload(add(a, 0x20)))))
}
}
/// @dev Packs a single string with its length into a single word.
/// Returns `bytes32(0)` if the length is zero or greater than 31.
function packOne(string memory a) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
// We don't need to zero right pad the string,
// since this is our own custom non-standard packing scheme.
result :=
mul(
// Load the length and the bytes.
mload(add(a, 0x1f)),
// `length != 0 && length < 32`. Abuses underflow.
// Assumes that the length is valid and within the block gas limit.
lt(sub(mload(a), 1), 0x1f)
)
}
}
/// @dev Unpacks a string packed using {packOne}.
/// Returns the empty string if `packed` is `bytes32(0)`.
/// If `packed` is not an output of {packOne}, the output behavior is undefined.
function unpackOne(bytes32 packed) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
// Grab the free memory pointer.
result := mload(0x40)
// Allocate 2 words (1 for the length, 1 for the bytes).
mstore(0x40, add(result, 0x40))
// Zeroize the length slot.
mstore(result, 0)
// Store the length and bytes.
mstore(add(result, 0x1f), packed)
// Right pad with zeroes.
mstore(add(add(result, 0x20), mload(result)), 0)
}
}
/// @dev Packs two strings with their lengths into a single word.
/// Returns `bytes32(0)` if combined length is zero or greater than 30.
function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let aLength := mload(a)
// We don't need to zero right pad the strings,
// since this is our own custom non-standard packing scheme.
result :=
mul(
// Load the length and the bytes of `a` and `b`.
or(
shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),
mload(sub(add(b, 0x1e), aLength))
),
// `totalLength != 0 && totalLength < 31`. Abuses underflow.
// Assumes that the lengths are valid and within the block gas limit.
lt(sub(add(aLength, mload(b)), 1), 0x1e)
)
}
}
/// @dev Unpacks strings packed using {packTwo}.
/// Returns the empty strings if `packed` is `bytes32(0)`.
/// If `packed` is not an output of {packTwo}, the output behavior is undefined.
function unpackTwo(bytes32 packed)
internal
pure
returns (string memory resultA, string memory resultB)
{
/// @solidity memory-safe-assembly
assembly {
// Grab the free memory pointer.
resultA := mload(0x40)
resultB := add(resultA, 0x40)
// Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.
mstore(0x40, add(resultB, 0x40))
// Zeroize the length slots.
mstore(resultA, 0)
mstore(resultB, 0)
// Store the lengths and bytes.
mstore(add(resultA, 0x1f), packed)
mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
// Right pad with zeroes.
mstore(add(add(resultA, 0x20), mload(resultA)), 0)
mstore(add(add(resultB, 0x20), mload(resultB)), 0)
}
}
/// @dev Directly returns `a` without copying.
function directReturn(string memory a) internal pure {
assembly {
// Assumes that the string does not start from the scratch space.
let retStart := sub(a, 0x20)
let retSize := add(mload(a), 0x40)
// Right pad with zeroes. Just in case the string is produced
// by a method that doesn't zero right pad.
mstore(add(retStart, retSize), 0)
// Store the return offset.
mstore(retStart, 0x20)
// End the transaction, returning the string.
return(retStart, retSize)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/ShortStrings.sol)
pragma solidity ^0.8.8;
import "./StorageSlot.sol";
// | string | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA |
// | length | 0x BB |
type ShortString is bytes32;
/**
* @dev This library provides functions to convert short memory strings
* into a `ShortString` type that can be used as an immutable variable.
*
* Strings of arbitrary length can be optimized using this library if
* they are short enough (up to 31 bytes) by packing them with their
* length (1 byte) in a single EVM word (32 bytes). Additionally, a
* fallback mechanism can be used for every other case.
*
* Usage example:
*
* ```solidity
* contract Named {
* using ShortStrings for *;
*
* ShortString private immutable _name;
* string private _nameFallback;
*
* constructor(string memory contractName) {
* _name = contractName.toShortStringWithFallback(_nameFallback);
* }
*
* function name() external view returns (string memory) {
* return _name.toStringWithFallback(_nameFallback);
* }
* }
* ```
*/
library ShortStrings {
// Used as an identifier for strings longer than 31 bytes.
bytes32 private constant _FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;
error StringTooLong(string str);
error InvalidShortString();
/**
* @dev Encode a string of at most 31 chars into a `ShortString`.
*
* This will trigger a `StringTooLong` error is the input string is too long.
*/
function toShortString(string memory str) internal pure returns (ShortString) {
bytes memory bstr = bytes(str);
if (bstr.length > 31) {
revert StringTooLong(str);
}
return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
}
/**
* @dev Decode a `ShortString` back to a "normal" string.
*/
function toString(ShortString sstr) internal pure returns (string memory) {
uint256 len = byteLength(sstr);
// using `new string(len)` would work locally but is not memory safe.
string memory str = new string(32);
/// @solidity memory-safe-assembly
assembly {
mstore(str, len)
mstore(add(str, 0x20), sstr)
}
return str;
}
/**
* @dev Return the length of a `ShortString`.
*/
function byteLength(ShortString sstr) internal pure returns (uint256) {
uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
if (result > 31) {
revert InvalidShortString();
}
return result;
}
/**
* @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
*/
function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
if (bytes(value).length < 32) {
return toShortString(value);
} else {
StorageSlot.getStringSlot(store).value = value;
return ShortString.wrap(_FALLBACK_SENTINEL);
}
}
/**
* @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
*/
function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
return toString(value);
} else {
return store;
}
}
/**
* @dev Return the length of a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
*
* WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
* actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
*/
function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
return byteLength(value);
} else {
return bytes(store).length;
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol)
pragma solidity ^0.8.0;
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 v4.9.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.0;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
* _Available since v4.9 for `string`, `bytes`._
*/
library 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
}
}
}{
"remappings": [
"solmate/=lib/solmate/src/",
"ds-test/=lib/ds-test/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"multicall/=lib/multicall/src/",
"forge-std/=lib/forge-std/src/",
"solady/=lib/solady/src/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"openzeppelin/=lib/openzeppelin-contracts/contracts/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "paris",
"libraries": {
"src/treasury/Rewards.sol": {
"Rewards": "0x77bcbbb7783ea8c43ae1893e35c76daf7ec153e3"
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_treasuryConfiguration","type":"address"},{"internalType":"address","name":"_o11y","type":"address"},{"internalType":"uint256","name":"_maxLimit","type":"uint256"},{"internalType":"bool","name":"_guardOn","type":"bool"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"clone","type":"address"},{"indexed":false,"internalType":"string","name":"oldBaseDescriptionURI","type":"string"},{"indexed":false,"internalType":"string","name":"newBaseDescriptionURI","type":"string"}],"name":"BaseDescriptionURISet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"factory","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"clone","type":"address"}],"name":"CloneDeployed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"clone","type":"address"},{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"EditionsDeployed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"guard","type":"bool"}],"name":"FactoryGuardSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"factory","type":"address"},{"indexed":true,"internalType":"address","name":"oldImplementation","type":"address"},{"indexed":true,"internalType":"address","name":"newImplementation","type":"address"}],"name":"FactoryImplementationSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"factory","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldLimit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newLimit","type":"uint256"}],"name":"FactoryLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"clone","type":"address"},{"indexed":true,"internalType":"address","name":"oldFundingRecipient","type":"address"},{"indexed":true,"internalType":"address","name":"newFundingRecipient","type":"address"}],"name":"FundingRecipientSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldImplementation","type":"address"},{"indexed":true,"internalType":"address","name":"newImplementation","type":"address"}],"name":"NewImplementation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"clone","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldLimit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newLimit","type":"uint256"}],"name":"PriceSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"clone","type":"address"},{"indexed":true,"internalType":"address","name":"renderer","type":"address"}],"name":"RendererSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"clone","type":"address"},{"indexed":true,"internalType":"address","name":"oldRoyaltyRecipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldRoyaltyBPS","type":"uint256"},{"indexed":true,"internalType":"address","name":"newRoyaltyRecipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"newRoyaltyBPS","type":"uint256"}],"name":"RoyaltyChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"clone","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"factory","type":"address"},{"indexed":true,"internalType":"address","name":"clone","type":"address"},{"indexed":false,"internalType":"address","name":"oldTributary","type":"address"},{"indexed":true,"internalType":"address","name":"newTributary","type":"address"}],"name":"TributarySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"clone","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldLimit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newLimit","type":"uint256"}],"name":"WritingEditionLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"clone","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"string","name":"message","type":"string"},{"indexed":false,"internalType":"uint256","name":"flatFeeAmount","type":"uint256"}],"name":"WritingEditionPurchased","type":"event"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"baseDescriptionURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"string","name":"imageURI","type":"string"},{"internalType":"string","name":"contentURI","type":"string"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"},{"internalType":"address","name":"fundingRecipient","type":"address"},{"internalType":"address","name":"renderer","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"}],"internalType":"struct IWritingEditions.WritingEdition","name":"edition","type":"tuple"}],"name":"create","outputs":[{"internalType":"address","name":"clone","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"string","name":"imageURI","type":"string"},{"internalType":"string","name":"contentURI","type":"string"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"},{"internalType":"address","name":"fundingRecipient","type":"address"},{"internalType":"address","name":"renderer","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"}],"internalType":"struct IWritingEditions.WritingEdition","name":"edition","type":"tuple"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"address","name":"tokenRecipient","type":"address"},{"internalType":"string","name":"message","type":"string"},{"internalType":"address","name":"mintReferral","type":"address"}],"name":"createWithSignature","outputs":[{"internalType":"address","name":"clone","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"guardOn","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isNextOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"o11y","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_implementation","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"predictDeterministicAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"salts","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_guardOn","type":"bool"}],"name":"setGuard","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_implementation","type":"address"}],"name":"setImplementation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxLimit","type":"uint256"}],"name":"setLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"clone","type":"address"},{"internalType":"address","name":"_tributary","type":"address"}],"name":"setTributary","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nextOwner_","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"treasuryConfiguration","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60e060405260036080523480156200001657600080fd5b50604051620073c3380380620073c3833981016040819052620000399162000227565b846200004581620001b1565b5060016002556001600160a01b038416620000a65760405162461bcd60e51b815260206004820152601860248201527f6d7573742073657420747265617375727920636f6e6669670000000000000000604482015260640160405180910390fd5b6001600160a01b03841660c052604051309085908590620000c790620001fc565b6001600160a01b03938416815291831660208301529091166040820152606001604051809103906000f08015801562000104573d6000803e3d6000fd5b5060038054610100600160a81b0319166101006001600160a01b039384168102919091179182905585831660a08190526040516322fd9ed760e01b815260006004820152919092049092166024830152906322fd9ed790604401600060405180830381600087803b1580156200017957600080fd5b505af11580156200018e573d6000803e3d6000fd5b50506003805460ff19169315159390931790925550506005555062000296915050565b600080546001600160a01b0319166001600160a01b03831690811782556040519091907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350565b6156c58062001cfe83390190565b80516001600160a01b03811681146200022257600080fd5b919050565b600080600080600060a086880312156200024057600080fd5b6200024b866200020a565b94506200025b602087016200020a565b93506200026b604087016200020a565b925060608601519150608086015180151581146200028857600080fd5b809150509295509295909350565b60805160a05160c051611a07620002f7600039600081816103900152610dc80152600081816102bf0152818161053e015281816105ff0152818161083f01528181610b6f015281816111150152611204015260006104540152611a076000f3fe6080604052600436106101355760003560e01c806379ba5097116100ab578063bee0ec0b1161006f578063bee0ec0b1461037e578063d784d426146103b2578063e6d283a6146103d2578063ed459df214610402578063f2fde38b14610422578063ffa1ad741461044257600080fd5b806379ba5097146102f65780638da5cb5b1461030b5780638f32d59b1461032b578063a56600631461034b578063ae3605ed1461036b57600080fd5b80633cb70c2d116100fd5780633cb70c2d146101f25780635c60da1b1461023e57806364d44b1e146102635780636f19d0b614610283578063705bf368146102ad578063715018a6146102e157600080fd5b8063089dc13b1461013a5780631a861d261461017757806323452b9c1461019b57806327ea6f2b146101b2578063360d0fad146101d2575b600080fd5b34801561014657600080fd5b5061015a61015536600461151c565b610488565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561018357600080fd5b5061018d60055481565b60405190815260200161016e565b3480156101a757600080fd5b506101b06104ae565b005b3480156101be57600080fd5b506101b06101cd366004611559565b6104f3565b3480156101de57600080fd5b5061015a6101ed366004611572565b6105aa565b3480156101fe57600080fd5b506102316040518060400160405280601381526020017268747470733a2f2f6d6972726f722e78797a2f60681b81525081565b60405161016e91906115e4565b34801561024a57600080fd5b5060035461015a9061010090046001600160a01b031681565b34801561026f57600080fd5b506101b061027e3660046115f7565b6105be565b34801561028f57600080fd5b5060035461029d9060ff1681565b604051901515815260200161016e565b3480156102b957600080fd5b5061015a7f000000000000000000000000000000000000000000000000000000000000000081565b3480156102ed57600080fd5b506101b0610677565b34801561030257600080fd5b506101b06106ab565b34801561031757600080fd5b5060005461015a906001600160a01b031681565b34801561033757600080fd5b506000546001600160a01b0316331461029d565b34801561035757600080fd5b506101b0610366366004611619565b610762565b61015a610379366004611652565b610987565b34801561038a57600080fd5b5061015a7f000000000000000000000000000000000000000000000000000000000000000081565b3480156103be57600080fd5b506101b06103cd366004611719565b610b15565b3480156103de57600080fd5b5061029d6103ed366004611559565b60046020526000908152604090205460ff1681565b34801561040e57600080fd5b506001546001600160a01b0316331461029d565b34801561042e57600080fd5b506101b061043d366004611719565b610bf4565b34801561044e57600080fd5b506104767f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff909116815260200161016e565b60006104a833836000604051806020016040528060008152506000610c96565b92915050565b6000546001600160a01b031633146104e15760405162461bcd60e51b81526004016104d890611736565b60405180910390fd5b600180546001600160a01b0319169055565b6000546001600160a01b0316331461051d5760405162461bcd60e51b81526004016104d890611736565b600554604051639ea75f2760e01b81526004810191909152602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690639ea75f2790604401600060405180830381600087803b15801561058a57600080fd5b505af115801561059e573d6000803e3d6000fd5b50505060059190915550565b60006105b7838330610d1e565b9392505050565b6000546001600160a01b031633146105e85760405162461bcd60e51b81526004016104d890611736565b6040516302f848e760e11b815281151560048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906305f091ce90602401600060405180830381600087803b15801561064b57600080fd5b505af115801561065f573d6000803e3d6000fd5b50506003805460ff1916931515939093179092555050565b6000546001600160a01b031633146106a15760405162461bcd60e51b81526004016104d890611736565b6106a9610d7a565b565b6001546001600160a01b0316331461071a5760405162461bcd60e51b815260206004820152602c60248201527f63757272656e74206f776e6572206d757374207365742063616c6c657220617360448201526b103732bc3a1037bbb732b91760a11b60648201526084016104d8565b600180546001600160a01b0319908116909155600080543392168217815560405182917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3565b816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107c4919061176d565b6001600160a01b0316336001600160a01b0316146108135760405162461bcd60e51b815260206004820152600c60248201526b1d5b985d5d1a1bdc9a5e995960a21b60448201526064016104d8565b600061081d610dc4565b6040516352ba8c2b60e11b81526001600160a01b0385811660048301529192507f00000000000000000000000000000000000000000000000000000000000000008216916332c7791691869185169063a575185690602401602060405180830381865afa158015610892573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b6919061176d565b6040516001600160e01b031960e085901b1681526001600160a01b03928316600482015290821660248201529085166044820152606401600060405180830381600087803b15801561090757600080fd5b505af115801561091b573d6000803e3d6000fd5b505060405163a566006360e01b81526001600160a01b03868116600483015285811660248301528416925063a56600639150604401600060405180830381600087803b15801561096a57600080fd5b505af115801561097e573d6000803e3d6000fd5b50505050505050565b6000610991610e4f565b60008989600001518a602001518b61012001516040516020016109b7949392919061178a565b60408051601f1981840301815291815281516020928301206000818152600490935291205490915060ff1615610ad257600354610a039061010090046001600160a01b03168230610d1e565b91506000826001600160a01b03163b11610a575760405162461bcd60e51b8152602060048201526015602482015274696e76616c696420636c6f6e65206164647265737360581b60448201526064016104d8565b6040516321a6e7dd60e11b81526001600160a01b0383169063434dcfba903490610a89908990899089906004016117d2565b60206040518083038185885af1158015610aa7573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610acc9190611807565b50610afe565b6000818152600460205260409020805460ff19166001179055610afb8a8a8a8a8a8a8a8a610ea6565b91505b50610b096001600255565b98975050505050505050565b6000546001600160a01b03163314610b3f5760405162461bcd60e51b81526004016104d890611736565b6003546040516322fd9ed760e01b81526101009091046001600160a01b03908116600483015282811660248301527f000000000000000000000000000000000000000000000000000000000000000016906322fd9ed790604401600060405180830381600087803b158015610bb357600080fd5b505af1158015610bc7573d6000803e3d6000fd5b5050600380546001600160a01b0390941661010002610100600160a81b0319909416939093179092555050565b6000546001600160a01b03163314610c1e5760405162461bcd60e51b81526004016104d890611736565b6001600160a01b038116610c745760405162461bcd60e51b815260206004820152601f60248201527f4e657874206f776e657220697320746865207a65726f20616464726573732e0060448201526064016104d8565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000610ca28686610fbd565b60035460405162c76e0b60e11b81529192506001600160a01b0383169163018edc16913491610ce3918b918b918b918b918b9160ff909116906004016118f7565b6000604051808303818588803b158015610cfc57600080fd5b505af1158015610d10573d6000803e3d6000fd5b505050505095945050505050565b60405160388101919091526f5af43d82803e903d91602b57fd5bf3ff60248201526014810192909252733d602d80600a3d3981f3363d3d373d3d3d363d73825260588201526037600c8201206078820152605560439091012090565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d6811b6f6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610e26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e4a919061176d565b905090565b6002805403610ea05760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016104d8565b60028055565b6000808989600001518a602001518b6101200151604051602001610ecd949392919061178a565b604051602081830303815290604052805190602001209050610eef8a8a610fbd565b9150816001600160a01b031663240e6f89348c848b8b8e604051602001610f3693929190928352602083019190915260f81b6001600160f81b031916604082015260410190565b6040516020818303038152906040528e8b8b8b600360009054906101000a900460ff16336040518b63ffffffff1660e01b8152600401610f7e9998979695949392919061194f565b6000604051808303818588803b158015610f9757600080fd5b505af1158015610fab573d6000803e3d6000fd5b50505050505098975050505050505050565b60e08101516000906001600160a01b03166110135760405162461bcd60e51b81526020600482015260166024820152751b5d5cdd081cdc1958da599e481c9958da5c1a595b9d60521b60448201526064016104d8565b6005541580611037575060008260c0015111801561103757506005548260c0015111155b6110735760405162461bcd60e51b815260206004820152600d60248201526c1a5b9d985b1a59081b1a5b5a5d609a1b60448201526064016104d8565b60035482516020808501516101208601516040516110c59561010090046001600160a01b0316946110aa948a94919391920161178a565b60405160208183030381529060405280519060200120611267565b905060006110d1610dc4565b90506001600160a01b038116156111dd5760e0830151604051631963bc8b60e11b81526001600160a01b0384811660048301526000602483015291821660448201527f0000000000000000000000000000000000000000000000000000000000000000909116906332c7791690606401600060405180830381600087803b15801561115b57600080fd5b505af115801561116f573d6000803e3d6000fd5b5050505060e083015160405163a566006360e01b81526001600160a01b03848116600483015291821660248201529082169063a566006390604401600060405180830381600087803b1580156111c457600080fd5b505af11580156111d8573d6000803e3d6000fd5b505050505b6040516306e9fe9960e41b81526001600160a01b03858116600483015283811660248301527f00000000000000000000000000000000000000000000000000000000000000001690636e9fe99090604401600060405180830381600087803b15801561124857600080fd5b505af115801561125c573d6000803e3d6000fd5b505050505092915050565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c176000526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760096000f590506001600160a01b0381166104a85760405162461bcd60e51b815260206004820152601760248201527f455243313136373a2063726561746532206661696c656400000000000000000060448201526064016104d8565b634e487b7160e01b600052604160045260246000fd5b604051610140810167ffffffffffffffff8111828210171561133e5761133e611304565b60405290565b600082601f83011261135557600080fd5b813567ffffffffffffffff8082111561137057611370611304565b604051601f8301601f19908116603f0116810190828211818310171561139857611398611304565b816040528381528660208588010111156113b157600080fd5b836020870160208301376000602085830101528094505050505092915050565b6001600160a01b03811681146113e657600080fd5b50565b80356113f4816113d1565b919050565b6000610140828403121561140c57600080fd5b61141461131a565b9050813567ffffffffffffffff8082111561142e57600080fd5b61143a85838601611344565b8352602084013591508082111561145057600080fd5b61145c85838601611344565b6020840152604084013591508082111561147557600080fd5b61148185838601611344565b6040840152606084013591508082111561149a57600080fd5b6114a685838601611344565b606084015260808401359150808211156114bf57600080fd5b506114cc84828501611344565b60808301525060a082013560a082015260c082013560c08201526114f260e083016113e9565b60e08201526101006115058184016113e9565b818301525061012080830135818301525092915050565b60006020828403121561152e57600080fd5b813567ffffffffffffffff81111561154557600080fd5b611551848285016113f9565b949350505050565b60006020828403121561156b57600080fd5b5035919050565b6000806040838503121561158557600080fd5b8235611590816113d1565b946020939093013593505050565b6000815180845260005b818110156115c4576020818501810151868301820152016115a8565b506000602082860101526020601f19601f83011685010191505092915050565b6020815260006105b7602083018461159e565b60006020828403121561160957600080fd5b813580151581146105b757600080fd5b6000806040838503121561162c57600080fd5b8235611637816113d1565b91506020830135611647816113d1565b809150509250929050565b600080600080600080600080610100898b03121561166f57600080fd5b883561167a816113d1565b9750602089013567ffffffffffffffff8082111561169757600080fd5b6116a38c838d016113f9565b985060408b0135915060ff821682146116bb57600080fd5b81975060608b0135965060808b013595506116d860a08c016113e9565b945060c08b01359150808211156116ee57600080fd5b506116fb8b828c01611344565b92505061170a60e08a016113e9565b90509295985092959890939650565b60006020828403121561172b57600080fd5b81356105b7816113d1565b60208082526018908201527f63616c6c6572206973206e6f7420746865206f776e65722e0000000000000000604082015260600190565b60006020828403121561177f57600080fd5b81516105b7816113d1565b6001600160a01b03851681526080602082018190526000906117ae9083018661159e565b82810360408401526117c0818661159e565b91505082606083015295945050505050565b600060018060a01b038086168352606060208401526117f4606084018661159e565b9150808416604084015250949350505050565b60006020828403121561181957600080fd5b5051919050565b600061014082518185526118368286018261159e565b91505060208301518482036020860152611850828261159e565b9150506040830151848203604086015261186a828261159e565b91505060608301518482036060860152611884828261159e565b9150506080830151848203608086015261189e828261159e565b91505060a083015160a085015260c083015160c085015260e08301516118cf60e08601826001600160a01b03169052565b50610100838101516001600160a01b0316908501526101209283015192909301919091525090565b600060018060a01b03808916835260c0602084015261191960c0840189611820565b81881660408501528381036060850152611933818861159e565b959091166080840152505090151560a090910152949350505050565b6001600160a01b038a81168252602082018a90526101206040830181905260009161197c8483018c61159e565b91508382036060850152611990828b611820565b9150808916608085015283820360a08501526119ac828961159e565b96811660c085015294151560e08401525050911661010090910152969550505050505056fea2646970667358221220994653b8c585513601d17ed8f59a59c1d00c08ad547d70fe216ece47cf44764e64736f6c6343000813003361020060405260016004556003610180523480156200001d57600080fd5b50604051620056c5380380620056c58339810160408190526200004091620003c6565b600160006040518060400160405280600f81526020016e57726974696e6745646974696f6e7360881b815250604051806040016040528060018152602001603160f81b81525081816200009e600083620002e160201b90919060201c565b61012052620000af816001620002e1565b61014052815160208084019190912060e052815190820120610100524660a0526200013d60e05161010051604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201529081019290925260608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b60805250503060c05281518151818303601e9081015183860151601f85900360031b1b1760001992909301919091011002610160819052620001925760405163fa05c4d760e01b815260040160405180910390fd5b50620001a09050816200031a565b506003805460ff60a01b1916600160a01b831515021790555060016005556001600160a01b0383166200020d5760405162461bcd60e51b815260206004820152601060248201526f6d7573742073657420666163746f727960801b60448201526064015b60405180910390fd5b6001600160a01b038084166101a05282166200026c5760405162461bcd60e51b815260206004820152601f60248201527f6d7573742073657420747265617375727920636f6e66696775726174696f6e00604482015260640162000204565b6001600160a01b038083166101c0528116620002cb5760405162461bcd60e51b815260206004820152601660248201527f6d75737420736574206f62736572766162696c69747900000000000000000000604482015260640162000204565b6001600160a01b03166101e05250620005f69050565b60006020835110156200030157620002f98362000366565b905062000314565b816200030e8482620004b5565b5060ff90505b92915050565b600280546001600160a01b0319166001600160a01b0383169081179091556040516000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350565b600080829050601f8151111562000394578260405163305a27a960e01b815260040162000204919062000581565b8051620003a182620005d1565b179392505050565b80516001600160a01b0381168114620003c157600080fd5b919050565b600080600060608486031215620003dc57600080fd5b620003e784620003a9565b9250620003f760208501620003a9565b91506200040760408501620003a9565b90509250925092565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200043b57607f821691505b6020821081036200045c57634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620004b057600081815260208120601f850160051c810160208610156200048b5750805b601f850160051c820191505b81811015620004ac5782815560010162000497565b5050505b505050565b81516001600160401b03811115620004d157620004d162000410565b620004e981620004e2845462000426565b8462000462565b602080601f831160018114620005215760008415620005085750858301515b600019600386901b1c1916600185901b178555620004ac565b600085815260208120601f198616915b82811015620005525788860151825594840194600190910190840162000531565b5085821015620005715787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600060208083528351808285015260005b81811015620005b05785810183015185820160400152820162000592565b506000604082860101526040601f19601f8301168501019250505092915050565b805160208083015191908110156200045c5760001960209190910360031b1b16919050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e051614fdb620006ea600039600081816106ca01528181610e7801528181610fb801528181611259015281816115850152818161162a0152818161181101528181611b92015281816125ef0152612e350152600081816108d10152818161304301526130c901526000818161090501528181611deb01526123f101526000610a370152600081816109c301526122ba015260006114ea015260006114c001526000613959015260006139310152600061388c015260006138b6015260006138e00152614fdb6000f3fe6080604052600436106103505760003560e01c806370a08231116101c6578063a8d13a29116100f7578063e2e784d511610095578063ebc905571161006f578063ebc90557146109b1578063ed459df2146109e5578063f2fde38b14610a05578063ffa1ad7414610a2557600080fd5b8063e2e784d51461095c578063e8a3d4851461097c578063e985e9c51461099157600080fd5b8063bee0ec0b116100d1578063bee0ec0b146108bf578063c45a0155146108f3578063c7381da314610927578063c87b56dd1461093c57600080fd5b8063a8d13a2914610874578063b88d4fde14610889578063b9c9d93a146108a957600080fd5b80638f32d59b116101645780639c1fd6e01161013e5780639c1fd6e014610808578063a035b1fe14610828578063a22cb4651461083e578063a4d66daf1461085e57600080fd5b80638f32d59b146107b357806391b7f5ed146107d357806395d89b41146107f357600080fd5b806379ba5097116101a057806379ba50971461073657806384b0196e1461074b5780638ada6b0f146107735780638da5cb5b1461079357600080fd5b806370a08231146106ec578063715018a61461070c5780637284e4161461072157600080fd5b806327ea6f2b116102a05780634c00de821161023e5780636352211e116102185780636352211e146106575780636a627842146106775780636f19d0b614610697578063705bf368146106b857600080fd5b80634c00de82146106025780634dcd14b21461062257806356d3163d1461063757600080fd5b80633cb70c2d1161027a5780633cb70c2d1461058d57806342842e0e146105a2578063434dcfba146105c257806347591135146105d557600080fd5b806327ea6f2b146104fa5780632a55205a1461051a5780633b639e6f1461055957600080fd5b8063135d088d1161030d57806323452b9c116102e757806323452b9c1461049257806323b872dd146104a7578063240e6f89146104c7578063241d9651146104da57600080fd5b8063135d088d1461043957806318160ddd1461044e5780631bb534ba1461047257600080fd5b8063018edc161461035557806301ffc9a71461036a57806306fdde031461039f578063081812fc146103c1578063095ea7b3146103f957806309cf81c714610419575b600080fd5b610368610363366004613e4f565b610a6b565b005b34801561037657600080fd5b5061038a610385366004613f15565b610a93565b60405190151581526020015b60405180910390f35b3480156103ab57600080fd5b506103b4610b00565b6040516103969190613f82565b3480156103cd57600080fd5b506103e16103dc366004613f95565b610b8e565b6040516001600160a01b039091168152602001610396565b34801561040557600080fd5b50610368610414366004613fae565b610c28565b34801561042557600080fd5b506016546103e1906001600160a01b031681565b34801561044557600080fd5b506103b4610d3d565b34801561045a57600080fd5b50610464600d5481565b604051908152602001610396565b34801561047e57600080fd5b506012546103e1906001600160a01b031681565b34801561049e57600080fd5b50610368610d4a565b3480156104b357600080fd5b506103686104c2366004613fda565b610d86565b6103686104d536600461405c565b610db7565b3480156104e657600080fd5b506103686104f5366004614147565b610e24565b34801561050657600080fd5b50610368610515366004613f95565b610ef9565b34801561052657600080fd5b5061053a610535366004614164565b611024565b604080516001600160a01b039093168352602083019190915201610396565b34801561056557600080fd5b506104647f2ef51e411d530420ce5b1e9bccba7925a7d50a82a8ec0b09d8d096e59ebb9f7b81565b34801561059957600080fd5b506103b461105a565b3480156105ae57600080fd5b506103686105bd366004613fda565b611069565b6104646105d0366004614186565b611084565b3480156105e157600080fd5b506105f56105f03660046141e9565b6110c7565b604051610396919061428e565b34801561060e57600080fd5b506013546103e1906001600160a01b031681565b34801561062e57600080fd5b506103b461119c565b34801561064357600080fd5b50610368610652366004614147565b6111a9565b34801561066357600080fd5b506103e1610672366004613f95565b6112ba565b34801561068357600080fd5b50610464610692366004614147565b6112c5565b3480156106a357600080fd5b5060035461038a90600160a01b900460ff1681565b3480156106c457600080fd5b506103e17f000000000000000000000000000000000000000000000000000000000000000081565b3480156106f857600080fd5b50610464610707366004614147565b6112fb565b34801561071857600080fd5b50610368611382565b34801561072d57600080fd5b506103b46113b6565b34801561074257600080fd5b506103686113f8565b34801561075757600080fd5b506107606114b2565b60405161039697969594939291906142db565b34801561077f57600080fd5b506015546103e1906001600160a01b031681565b34801561079f57600080fd5b506002546103e1906001600160a01b031681565b3480156107bf57600080fd5b506002546001600160a01b0316331461038a565b3480156107df57600080fd5b506103686107ee366004613f95565b61153a565b3480156107ff57600080fd5b506103b46115f1565b34801561081457600080fd5b50610368610823366004614371565b6115fe565b34801561083457600080fd5b5061046460105481565b34801561084a57600080fd5b506103686108593660046143a5565b6116c2565b34801561086a57600080fd5b5061046460115481565b34801561088057600080fd5b50610368611786565b34801561089557600080fd5b506103686108a43660046143de565b6117b8565b3480156108b557600080fd5b5061046460145481565b3480156108cb57600080fd5b506103e17f000000000000000000000000000000000000000000000000000000000000000081565b3480156108ff57600080fd5b506103e17f000000000000000000000000000000000000000000000000000000000000000081565b34801561093357600080fd5b506103686117ca565b34801561094857600080fd5b506103b4610957366004613f95565b611887565b34801561096857600080fd5b50610368610977366004613fae565b611ac9565b34801561098857600080fd5b506103b4611c16565b34801561099d57600080fd5b5061038a6109ac36600461445d565b611cb7565b3480156109bd57600080fd5b506104647f000000000000000000000000000000000000000000000000000000000000000081565b3480156109f157600080fd5b506003546001600160a01b0316331461038a565b348015610a1157600080fd5b50610368610a20366004614147565b611ce5565b348015610a3157600080fd5b50610a597f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff9091168152602001610396565b610a73611d87565b610a81868686868686611de0565b610a8b6001600555565b505050505050565b60006001600160e01b031982166380ac58cd60e01b1480610ac457506001600160e01b03198216635b5e139f60e01b145b80610adf57506001600160e01b031982166301ffc9a760e01b145b80610afa57506001600160e01b0319821663152a902d60e11b145b92915050565b600a8054610b0d9061448b565b80601f0160208091040260200160405190810160405280929190818152602001828054610b399061448b565b8015610b865780601f10610b5b57610100808354040283529160200191610b86565b820191906000526020600020905b815481529060010190602001808311610b6957829003601f168201915b505050505081565b6000818152600660205260408120546001600160a01b0316610c0c5760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b60648201526084015b60405180910390fd5b506000908152600860205260409020546001600160a01b031690565b6000610c3382611f13565b9050806001600160a01b0316836001600160a01b031603610ca05760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b6064820152608401610c03565b336001600160a01b0382161480610cbc5750610cbc8133611cb7565b610d2e5760405162461bcd60e51b815260206004820152603860248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f7760448201527f6e6572206e6f7220617070726f76656420666f7220616c6c00000000000000006064820152608401610c03565b610d388383611f8a565b505050565b600f8054610b0d9061448b565b6002546001600160a01b03163314610d745760405162461bcd60e51b8152600401610c03906144bf565b600380546001600160a01b0319169055565b610d903382611ff8565b610dac5760405162461bcd60e51b8152600401610c03906144f6565b610d388383836120cf565b610dbf611d87565b610dcb8a8a8a8a61227a565b896001600160a01b0316816001600160a01b031614610e0057601680546001600160a01b0319166001600160a01b0383161790555b610e0e8a8787878787611de0565b610e186001600555565b50505050505050505050565b6002546001600160a01b03163314610e4e5760405162461bcd60e51b8152600401610c03906144bf565b60125460405163f0091f4f60e01b81526001600160a01b03918216600482015282821660248201527f00000000000000000000000000000000000000000000000000000000000000009091169063f0091f4f90604401600060405180830381600087803b158015610ebe57600080fd5b505af1158015610ed2573d6000803e3d6000fd5b5050601280546001600160a01b0319166001600160a01b0394909416939093179092555050565b6002546001600160a01b03163314610f235760405162461bcd60e51b8152600401610c03906144bf565b600d548110158015610f4057506011541580610f40575060115481105b610f975760405162461bcd60e51b815260206004820152602260248201527f6c696d6974206d757374206265203c207468616e2063757272656e74206c696d6044820152611a5d60f21b6064820152608401610c03565b60115460405163271c9e1f60e01b81526004810191909152602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063271c9e1f90604401600060405180830381600087803b15801561100457600080fd5b505af1158015611018573d6000803e3d6000fd5b50505060119190915550565b6013546014546001600160a01b039091169060009061271090611047908561455d565b611051919061458a565b90509250929050565b6060611064612349565b905090565b610d3883838360405180602001604052806000815250612475565b600354600090600160a01b900460ff16156110a1576110a16124a7565b6110a9611d87565b6110b48484846124d8565b90506110c06001600555565b9392505050565b606081516001600160401b038111156110e2576110e2613c20565b60405190808252806020026020018201604052801561110b578160200160208202803683370190505b50905060005b825181101561119657600660008483815181106111305761113061459e565b6020026020010151815260200190815260200160002060009054906101000a90046001600160a01b031682828151811061116c5761116c61459e565b6001600160a01b03909216602092830291909101909101528061118e816145b4565b915050611111565b50919050565b600e8054610b0d9061448b565b6002546001600160a01b031633146111d35760405162461bcd60e51b8152600401610c03906144bf565b6015546001600160a01b0316156112235760405162461bcd60e51b81526020600482015260146024820152731c995b99195c995c88185b1c9958591e481cd95d60621b6044820152606401610c03565b601580546001600160a01b0319166001600160a01b03838116918217909255604051631445778560e21b815260048101919091527f000000000000000000000000000000000000000000000000000000000000000090911690635115de1490602401600060405180830381600087803b15801561129f57600080fd5b505af11580156112b3573d6000803e3d6000fd5b5050505050565b6000610afa82611f13565b6002546000906001600160a01b031633146112f25760405162461bcd60e51b8152600401610c03906144bf565b610afa8261267f565b60006001600160a01b0382166113665760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604482015269726f206164647265737360b01b6064820152608401610c03565b506001600160a01b031660009081526007602052604090205490565b6002546001600160a01b031633146113ac5760405162461bcd60e51b8152600401610c03906144bf565b6113b4612702565b565b60606113c0612349565b6113c94661274c565b6113d2306127de565b6040516020016113e4939291906145e9565b604051602081830303815290604052905090565b6003546001600160a01b031633146114675760405162461bcd60e51b815260206004820152602c60248201527f63757272656e74206f776e6572206d757374207365742063616c6c657220617360448201526b103732bc3a1037bbb732b91760a11b6064820152608401610c03565b600380546001600160a01b0319908116909155600280543392168217905560405181907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3565b6000606080828080836114e57f000000000000000000000000000000000000000000000000000000000000000083612947565b6115107f00000000000000000000000000000000000000000000000000000000000000006001612947565b60408051600080825260208201909252600f60f81b9b939a50919850469750309650945092509050565b6002546001600160a01b031633146115645760405162461bcd60e51b8152600401610c03906144bf565b60105460405163509d812560e11b81526004810191909152602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a13b024a90604401600060405180830381600087803b1580156115d157600080fd5b505af11580156115e5573d6000803e3d6000fd5b50505060109190915550565b600b8054610b0d9061448b565b6002546001600160a01b031633146116285760405162461bcd60e51b8152600401610c03906144bf565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fc0ed66a61165f612349565b836040518363ffffffff1660e01b815260040161167d929190614639565b600060405180830381600087803b15801561169757600080fd5b505af11580156116ab573d6000803e3d6000fd5b5050505080600c90816116be91906146ad565b5050565b336001600160a01b0383160361171a5760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610c03565b3360008181526009602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b6002546001600160a01b031633146117b05760405162461bcd60e51b8152600401610c03906144bf565b6113b46129f2565b6117c484848484612475565b50505050565b6002546001600160a01b031633146117f45760405162461bcd60e51b8152600401610c03906144bf565b601154600d5460405163271c9e1f60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169263271c9e1f9261184d92600401918252602082015260400190565b600060405180830381600087803b15801561186757600080fd5b505af115801561187b573d6000803e3d6000fd5b5050600d546011555050565b6000818152600660205260409020546060906001600160a01b03166118fa5760405162461bcd60e51b815260206004820152602360248201527f4552433732313a20717565727920666f72206e6f6e6578697374656e7420746f60448201526235b2b760e91b6064820152608401610c03565b6015546001600160a01b03161561197c5760155460405163c87b56dd60e01b8152600481018490526001600160a01b039091169063c87b56dd90602401600060405180830381865afa158015611954573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610afa919081019061476c565b60606011546000146119b55761199360115461274c565b6040516020016119a391906147d9565b60405160208183030381529060405290505b6000611a9e611a4d600a80546119ca9061448b565b80601f01602080910402602001604051908101604052809291908181526020018280546119f69061448b565b8015611a435780601f10611a1857610100808354040283529160200191611a43565b820191906000526020600020905b815481529060010190602001808311611a2657829003601f168201915b5050505050612a09565b611a568661274c565b84611a67611a626113b6565b612a09565b600e600f611a748b61274c565b604051602001611a8a9796959493929190614875565b604051602081830303815290604052612bbf565b905080604051602001611ab191906149d1565b60405160208183030381529060405292505050919050565b6002546001600160a01b03163314611af35760405162461bcd60e51b8152600401610c03906144bf565b612710811115611b565760405162461bcd60e51b815260206004820152602860248201527f627073206d757374206265206c657373207468616e206f7220657175616c207460448201526706f2031302c3030360c41b6064820152608401610c03565b601354601454604051630ba1ba8360e01b81526001600160a01b03928316600482015260248101919091528382166044820152606481018390527f000000000000000000000000000000000000000000000000000000000000000090911690630ba1ba8390608401600060405180830381600087803b158015611bd857600080fd5b505af1158015611bec573d6000803e3d6000fd5b5050601380546001600160a01b0319166001600160a01b0395909516949094179093555060145550565b6015546060906001600160a01b031615611caf57601560009054906101000a90046001600160a01b03166001600160a01b031663e8a3d4856040518163ffffffff1660e01b8152600401600060405180830381865afa925050508015611c9e57506040513d6000823e601f3d908101601f19168201604052611c9b919081019061476c565b60015b611caa57611064612d11565b919050565b611064612d11565b6001600160a01b03918216600090815260096020908152604080832093909416825291909152205460ff1690565b6002546001600160a01b03163314611d0f5760405162461bcd60e51b8152600401610c03906144bf565b6001600160a01b038116611d655760405162461bcd60e51b815260206004820152601f60248201527f4e657874206f776e657220697320746865207a65726f20616464726573732e006044820152606401610c03565b600380546001600160a01b0319166001600160a01b0392909216919091179055565b600260055403611dd95760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610c03565b6002600555565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611e4e5760405162461bcd60e51b81526020600482015260136024820152723ab730baba3437b934bd32b21031b0b63632b960691b6044820152606401610c03565b8451600a90611e5d90826146ad565b506020850151600b90611e7090826146ad565b506060850151600f90611e8390826146ad565b506080850151600e90611e9690826146ad565b5060a085015160105560c085015160115560e0850151601280546001600160a01b039283166001600160a01b03199182161790915561010087015160158054919093169116179055611ee786612d9d565b611ef081612de9565b6001600160a01b03841615610a8b57611f0a8484846124d8565b50505050505050565b6000818152600660205260408120546001600160a01b031680610afa5760405162461bcd60e51b815260206004820152602960248201527f4552433732313a206f776e657220717565727920666f72206e6f6e657869737460448201526832b73a103a37b5b2b760b91b6064820152608401610c03565b600081815260086020526040902080546001600160a01b0319166001600160a01b0384169081179091558190611fbf82611f13565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000818152600660205260408120546001600160a01b03166120715760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b6064820152608401610c03565b600061207c83611f13565b9050806001600160a01b0316846001600160a01b031614806120b75750836001600160a01b03166120ac84610b8e565b6001600160a01b0316145b806120c757506120c78185611cb7565b949350505050565b826001600160a01b03166120e282611f13565b6001600160a01b03161461214a5760405162461bcd60e51b815260206004820152602960248201527f4552433732313a207472616e73666572206f6620746f6b656e2074686174206960448201526839903737ba1037bbb760b91b6064820152608401610c03565b6001600160a01b0382166121ac5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610c03565b6121b7838383612e07565b6121c2600082611f8a565b6001600160a01b03831660009081526007602052604081208054600192906121eb908490614a16565b90915550506001600160a01b0382166000908152600760205260408120805460019290612219908490614a29565b909155505060008181526006602052604080822080546001600160a01b0319166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b61228683858484612e8d565b6122a35760405163120d188f60e31b815260040160405180910390fd5b6040805160808101825260008082529181018281527f0000000000000000000000000000000000000000000000000000000000000000601f830152815182016020018051605f84015283905280518201606001839052829190915091507f06c5a80e592816bd4f60093568e69affa68b5e378a189b2f59a1121703de47de85838389888860405161233996959493929190614a65565b60405180910390a1505050505050565b6060600c80546123589061448b565b1590506123ef57600c805461236c9061448b565b80601f01602080910402602001604051908101604052809291908181526020018280546123989061448b565b80156123e55780601f106123ba576101008083540402835291602001916123e5565b820191906000526020600020905b8154815290600101906020018083116123c857829003601f168201915b5050505050905090565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316633cb70c2d6040518163ffffffff1660e01b8152600401600060405180830381865afa15801561244d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611064919081019061476c565b61247f3383611ff8565b61249b5760405162461bcd60e51b8152600401610c03906144f6565b6117c48484848461300c565b60005a60045490915060005a6004549091506124c38285614a16565b5a6124ce9084614a16565b106117c457600080fd5b60006124e38461267f565b90506000806124f061303f565b90506001600160a01b038116158015906125695750806001600160a01b031663da8b5a566040518163ffffffff1660e01b81526004016020604051808303816000875af1158015612545573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125699190614ac1565b156125d557806001600160a01b031663ff0c44da6040518163ffffffff1660e01b81526004016020604051808303816000875af11580156125ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125d29190614ade565b91505b60105460405163ca9d19eb60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163ca9d19eb9161262b9187918b91908b908990600401614af7565b600060405180830381600087803b15801561264557600080fd5b505af1158015612659573d6000803e3d6000fd5b505060125461267692506001600160a01b031690503484876130c5565b50509392505050565b6000600d60008154612690906145b4565b918290555060115490915015806126aa5750600019601154145b806126c157506011546126be906001614a29565b81105b6126f85760405162461bcd60e51b81526020600482015260086024820152671cdbdb19081bdd5d60c21b6044820152606401610c03565b611caa828261338d565b6002546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600280546001600160a01b0319169055565b60606000612759836134db565b60010190506000816001600160401b0381111561277857612778613c20565b6040519080825280601f01601f1916602001820160405280156127a2576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a85049450846127ac57509392505050565b60408051602880825260608281019093526000919060208201818036833701905050905060005b601481101561291e57600061281b826013614a16565b61282690600861455d565b612831906002614c1a565b612844906001600160a01b03871661458a565b60f81b9050600060108260f81c61285b9190614c26565b60f81b905060008160f81c60106128729190614c48565b8360f81c6128809190614c6b565b60f81b905061288e826135b3565b8561289a86600261455d565b815181106128aa576128aa61459e565b60200101906001600160f81b031916908160001a9053506128ca816135b3565b856128d686600261455d565b6128e1906001614a29565b815181106128f1576128f161459e565b60200101906001600160f81b031916908160001a9053505050508080612916906145b4565b915050612805565b50806040516020016129309190614c84565b604051602081830303815290604052915050919050565b606060ff83146129615761295a836135e9565b9050610afa565b81805461296d9061448b565b80601f01602080910402602001604051908101604052809291908181526020018280546129999061448b565b80156129e65780601f106129bb576101008083540402835291602001916129e6565b820191906000526020600020905b8154815290600101906020018083116129c957829003601f168201915b50505050509050610afa565b6003546113b490600160a01b900460ff1615612de9565b6060816000805b82518160ff161015612a6a57828160ff1681518110612a3157612a3161459e565b01602001516001600160f81b031916601160f91b03612a585781612a5481614cae565b9250505b80612a6281614cae565b915050612a10565b5060ff811615612bb75760008160ff168351612a869190614a29565b6001600160401b03811115612a9d57612a9d613c20565b6040519080825280601f01601f191660200182016040528015612ac7576020820181803683370190505b5090506000805b84518160ff161015612bac57848160ff1681518110612aef57612aef61459e565b01602001516001600160f81b031916601160f91b03612b4257601760fa1b8383612b18816145b4565b945081518110612b2a57612b2a61459e565b60200101906001600160f81b031916908160001a9053505b848160ff1681518110612b5757612b5761459e565b01602001516001600160f81b0319168383612b71816145b4565b945081518110612b8357612b8361459e565b60200101906001600160f81b031916908160001a90535080612ba481614cae565b915050612ace565b509095945050505050565b509192915050565b60608151600003612bde57505060408051602081019091526000815290565b6000604051806060016040528060408152602001614f666040913990506000600384516002612c0d9190614a29565b612c17919061458a565b612c2290600461455d565b6001600160401b03811115612c3957612c39613c20565b6040519080825280601f01601f191660200182016040528015612c63576020820181803683370190505b509050600182016020820185865187015b80821015612ccf576003820191508151603f8160121c168501518453600184019350603f81600c1c168501518453600184019350603f8160061c168501518453600184019350603f8116850151845350600183019250612c74565b5050600386510660018114612ceb5760028114612cfe57612d06565b603d6001830353603d6002830353612d06565b603d60018303535b509195945050505050565b60606000612d75612d28600a80546119ca9061448b565b612d33611a626113b6565b600e600f612d4260145461274c565b601354612d57906001600160a01b03166127de565b612d5f612349565b604051602001611a8a9796959493929190614ccd565b905080604051602001612d8891906149d1565b60405160208183030381529060405291505090565b600280546001600160a01b0319166001600160a01b0383169081179091556040516000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350565b60038054911515600160a01b0260ff60a01b19909216919091179055565b60405163536a4ee560e11b81526001600160a01b0384811660048301528381166024830152604482018390527f0000000000000000000000000000000000000000000000000000000000000000169063a6d49dca90606401600060405180830381600087803b158015612e7957600080fd5b505af1158015611f0a573d6000803e3d6000fd5b60006001600160a01b038416612eb6576040516332db94d160e21b815260040160405180910390fd5b604080517f2ef51e411d530420ce5b1e9bccba7925a7d50a82a8ec0b09d8d096e59ebb9f7b6020820152908101869052600090612f0b9060600160405160208183030381529060405280519060200120613628565b90506001600160a01b0385163b15612fb057604051630b135d3f60e11b81526001600160a01b03861690631626ba7e90612f4d90849088908890600401614e1d565b602060405180830381865afa925050508015612f86575060408051601f3d908101601f19168201909252612f8391810190614e37565b60015b612f945760009150506120c7565b6001600160e01b031916630b135d3f60e11b1491506120c79050565b6000612ff28286868080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061365592505050565b6001600160a01b0387811691161492505050949350505050565b6130178484846120cf565b61302384848484613679565b6117c45760405162461bcd60e51b8152600401610c0390614e54565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166398c47e8c6040518163ffffffff1660e01b81526004016020604051808303816000875af11580156130a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110649190614ea6565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166361d027b36040518163ffffffff1660e01b81526004016020604051808303816000875af1158015613127573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061314b9190614ea6565b9050821580159061316457506001600160a01b03811615155b1561334157826010546131779190614a29565b84146131b65760405162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a5908185b5bdd5b9d60921b6044820152606401610c03565b60105460405163136171d360e21b81526001600160a01b038416600482015290156024820152604481018490526000908190819081907377bcbbb7783ea8c43ae1893e35c76daf7ec153e390634d85c74c90606401608060405180830381865af4158015613228573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061324c9190614ec3565b601654939750919550935091506001600160a01b031661328d576132888982866010546132799190614a29565b6132839190614a29565b613777565b6132b5565b6016546132a3906001600160a01b031682613777565b6132b589856010546132839190614a29565b6132bf8584613777565b6016546040805186815260208101869052808201859052606081018490526001600160a01b038c8116608083015289811660a083015290921660c0830152517f8b181338417e2725cdc77ffb698b4e26f936882171329a7de03b0e3d3f8266849181900360e00190a18115613338576133388683613777565b505050506112b3565b60105484146133835760405162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a5908185b5bdd5b9d60921b6044820152606401610c03565b6112b38585613777565b6001600160a01b0382166133e35760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610c03565b6000818152600660205260409020546001600160a01b0316156134485760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610c03565b61345460008383612e07565b6001600160a01b038216600090815260076020526040812080546001929061347d908490614a29565b909155505060008181526006602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b831061351a5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef81000000008310613546576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061356457662386f26fc10000830492506010015b6305f5e100831061357c576305f5e100830492506008015b612710831061359057612710830492506004015b606483106135a2576064830492506002015b600a8310610afa5760010192915050565b6000600a60f883901c10156135da576135d160f883901c6030614ef9565b60f81b92915050565b6135d160f883901c6057614ef9565b606060006135f683613857565b604080516020808252818301909252919250600091906020820181803683375050509182525060208101929092525090565b6000610afa61363561387f565b8360405161190160f01b8152600281019290925260228201526042902090565b600080600061366485856139aa565b91509150613671816139ef565b509392505050565b60006001600160a01b0384163b1561376f57604051630a85bd0160e11b81526001600160a01b0385169063150b7a02906136bd903390899088908890600401614f12565b6020604051808303816000875af19250505080156136f8575060408051601f3d908101601f191682019092526136f591810190614e37565b60015b613755573d808015613726576040519150601f19603f3d011682016040523d82523d6000602084013e61372b565b606091505b50805160000361374d5760405162461bcd60e51b8152600401610c0390614e54565b805181602001fd5b6001600160e01b031916630a85bd0160e11b1490506120c7565b5060016120c7565b804710156137be5760405162461bcd60e51b8152602060048201526014602482015273696e73756666696369656e742062616c616e636560601b6044820152606401610c03565b6000826001600160a01b0316825a6040519091906000818181858888f193505050503d806000811461380c576040519150601f19603f3d011682016040523d82523d6000602084013e613811565b606091505b5050905080610d385760405162461bcd60e51b81526020600482015260126024820152711c9958da5c1a595b9d081c995d995c9d195960721b6044820152606401610c03565b600060ff8216601f811115610afa57604051632cd44ac360e21b815260040160405180910390fd5b6000306001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161480156138d857507f000000000000000000000000000000000000000000000000000000000000000046145b1561390257507f000000000000000000000000000000000000000000000000000000000000000090565b611064604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f0000000000000000000000000000000000000000000000000000000000000000918101919091527f000000000000000000000000000000000000000000000000000000000000000060608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b60008082516041036139e05760208301516040840151606085015160001a6139d487828585613b3c565b945094505050506139e8565b506000905060025b9250929050565b6000816004811115613a0357613a03614f4f565b03613a0b5750565b6001816004811115613a1f57613a1f614f4f565b03613a6c5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610c03565b6002816004811115613a8057613a80614f4f565b03613acd5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610c03565b6003816004811115613ae157613ae1614f4f565b03613b395760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610c03565b50565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115613b735750600090506003613bf7565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015613bc7573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116613bf057600060019250925050613bf7565b9150600090505b94509492505050565b6001600160a01b0381168114613b3957600080fd5b8035611caa81613c00565b634e487b7160e01b600052604160045260246000fd5b60405161014081016001600160401b0381118282101715613c5957613c59613c20565b60405290565b604051601f8201601f191681016001600160401b0381118282101715613c8757613c87613c20565b604052919050565b60006001600160401b03821115613ca857613ca8613c20565b50601f01601f191660200190565b6000613cc9613cc484613c8f565b613c5f565b9050828152838383011115613cdd57600080fd5b828260208301376000602084830101529392505050565b600082601f830112613d0557600080fd5b6110c083833560208501613cb6565b60006101408284031215613d2757600080fd5b613d2f613c36565b905081356001600160401b0380821115613d4857600080fd5b613d5485838601613cf4565b83526020840135915080821115613d6a57600080fd5b613d7685838601613cf4565b60208401526040840135915080821115613d8f57600080fd5b613d9b85838601613cf4565b60408401526060840135915080821115613db457600080fd5b613dc085838601613cf4565b60608401526080840135915080821115613dd957600080fd5b50613de684828501613cf4565b60808301525060a082013560a082015260c082013560c0820152613e0c60e08301613c15565b60e0820152610100613e1f818401613c15565b818301525061012080830135818301525092915050565b8015158114613b3957600080fd5b8035611caa81613e36565b60008060008060008060c08789031215613e6857600080fd5b8635613e7381613c00565b955060208701356001600160401b0380821115613e8f57600080fd5b613e9b8a838b01613d14565b965060408901359150613ead82613c00565b90945060608801359080821115613ec357600080fd5b50613ed089828a01613cf4565b9350506080870135613ee181613c00565b915060a0870135613ef181613e36565b809150509295509295509295565b6001600160e01b031981168114613b3957600080fd5b600060208284031215613f2757600080fd5b81356110c081613eff565b60005b83811015613f4d578181015183820152602001613f35565b50506000910152565b60008151808452613f6e816020860160208601613f32565b601f01601f19169290920160200192915050565b6020815260006110c06020830184613f56565b600060208284031215613fa757600080fd5b5035919050565b60008060408385031215613fc157600080fd5b8235613fcc81613c00565b946020939093013593505050565b600080600060608486031215613fef57600080fd5b8335613ffa81613c00565b9250602084013561400a81613c00565b929592945050506040919091013590565b60008083601f84011261402d57600080fd5b5081356001600160401b0381111561404457600080fd5b6020830191508360208285010111156139e857600080fd5b6000806000806000806000806000806101208b8d03121561407c57600080fd5b6140858b613c15565b995060208b0135985060408b01356001600160401b03808211156140a857600080fd5b6140b48e838f0161401b565b909a50985060608d01359150808211156140cd57600080fd5b6140d98e838f01613d14565b97506140e760808e01613c15565b965060a08d01359150808211156140fd57600080fd5b5061410a8d828e01613cf4565b94505061411960c08c01613c15565b925061412760e08c01613e44565b91506141366101008c01613c15565b90509295989b9194979a5092959850565b60006020828403121561415957600080fd5b81356110c081613c00565b6000806040838503121561417757600080fd5b50508035926020909101359150565b60008060006060848603121561419b57600080fd5b83356141a681613c00565b925060208401356001600160401b038111156141c157600080fd5b6141cd86828701613cf4565b92505060408401356141de81613c00565b809150509250925092565b600060208083850312156141fc57600080fd5b82356001600160401b038082111561421357600080fd5b818501915085601f83011261422757600080fd5b81358181111561423957614239613c20565b8060051b915061424a848301613c5f565b818152918301840191848101908884111561426457600080fd5b938501935b8385101561428257843582529385019390850190614269565b98975050505050505050565b6020808252825182820181905260009190848201906040850190845b818110156142cf5783516001600160a01b0316835292840192918401916001016142aa565b50909695505050505050565b60ff60f81b881681526000602060e0818401526142fb60e084018a613f56565b838103604085015261430d818a613f56565b606085018990526001600160a01b038816608086015260a0850187905284810360c0860152855180825283870192509083019060005b8181101561435f57835183529284019291840191600101614343565b50909c9b505050505050505050505050565b60006020828403121561438357600080fd5b81356001600160401b0381111561439957600080fd5b6120c784828501613cf4565b600080604083850312156143b857600080fd5b82356143c381613c00565b915060208301356143d381613e36565b809150509250929050565b600080600080608085870312156143f457600080fd5b84356143ff81613c00565b9350602085013561440f81613c00565b92506040850135915060608501356001600160401b0381111561443157600080fd5b8501601f8101871361444257600080fd5b61445187823560208401613cb6565b91505092959194509250565b6000806040838503121561447057600080fd5b823561447b81613c00565b915060208301356143d381613c00565b600181811c9082168061449f57607f821691505b60208210810361119657634e487b7160e01b600052602260045260246000fd5b60208082526018908201527f63616c6c6572206973206e6f7420746865206f776e65722e0000000000000000604082015260600190565b60208082526031908201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6040820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610afa57610afa614547565b634e487b7160e01b600052601260045260246000fd5b60008261459957614599614574565b500490565b634e487b7160e01b600052603260045260246000fd5b6000600182016145c6576145c6614547565b5060010190565b600081516145df818560208601613f32565b9290920192915050565b600084516145fb818460208901613f32565b84519083019061460f818360208901613f32565b602f60f81b9101908152835161462c816001840160208801613f32565b0160010195945050505050565b60408152600061464c6040830185613f56565b828103602084015261465e8185613f56565b95945050505050565b601f821115610d3857600081815260208120601f850160051c8101602086101561468e5750805b601f850160051c820191505b81811015610a8b5782815560010161469a565b81516001600160401b038111156146c6576146c6613c20565b6146da816146d4845461448b565b84614667565b602080601f83116001811461470f57600084156146f75750858301515b600019600386901b1c1916600185901b178555610a8b565b600085815260208120601f198616915b8281101561473e5788860151825594840194600190910190840161471f565b508582101561475c5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60006020828403121561477e57600080fd5b81516001600160401b0381111561479457600080fd5b8201601f810184136147a557600080fd5b80516147b3613cc482613c8f565b8181528560208385010111156147c857600080fd5b61465e826020830160208601613f32565b602f60f81b8152600082516147f5816001850160208701613f32565b9190910160010192915050565b6000815461480f8161448b565b60018281168015614827576001811461483c5761486b565b60ff198416875282151583028701945061486b565b8560005260208060002060005b858110156148625781548a820152908401908201614849565b50505082870194505b5050505092915050565b693d913730b6b2911d101160b11b8152875160009061489b81600a850160208d01613f32565b600160fd1b600a9184019182015288516148bc81600b840160208d01613f32565b88519101906148d281600b840160208c01613f32565b72111610113232b9b1b934b83a34b7b7111d101160691b600b9290910191820152865161490681601e840160208b01613f32565b73222c2022636f6e74656e74223a202261723a2f2f60601b601e92909101918201526149356032820187614802565b73222c2022696d616765223a2022697066733a2f2f60601b815290506149c36149b36149ad6149676014850189614802565b7f222c202261747472696275746573223a5b7b202274726169745f74797065223a8152730101129b2b934b0b6111610113b30b63ab2911d160651b602082015260340190565b866145cd565b637d5d207d60e01b815260040190565b9a9950505050505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c000000815260008251614a0981601d850160208701613f32565b91909101601d0192915050565b81810381811115610afa57610afa614547565b80820180821115610afa57610afa614547565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b86815260a060208201526000614a7e60a0830188613f56565b8281036040840152614a908188613f56565b6001600160a01b038716606085015283810360808501529050614ab4818587614a3c565b9998505050505050505050565b600060208284031215614ad357600080fd5b81516110c081613e36565b600060208284031215614af057600080fd5b5051919050565b85815260018060a01b038516602082015283604082015260a060608201526000614b2460a0830185613f56565b90508260808301529695505050505050565b600181815b80851115614b71578160001904821115614b5757614b57614547565b80851615614b6457918102915b93841c9390800290614b3b565b509250929050565b600082614b8857506001610afa565b81614b9557506000610afa565b8160018114614bab5760028114614bb557614bd1565b6001915050610afa565b60ff841115614bc657614bc6614547565b50506001821b610afa565b5060208310610133831016604e8410600b8410161715614bf4575081810a610afa565b614bfe8383614b36565b8060001904821115614c1257614c12614547565b029392505050565b60006110c08383614b79565b600060ff831680614c3957614c39614574565b8060ff84160491505092915050565b60ff8181168382160290811690818114614c6457614c64614547565b5092915050565b60ff8281168282160390811115610afa57610afa614547565b61060f60f31b815260008251614ca1816002850160208701613f32565b9190910160020192915050565b600060ff821660ff8103614cc457614cc4614547565b60010192915050565b693d913730b6b2911d101160b11b81528751600090614cf381600a850160208d01613f32565b72111610113232b9b1b934b83a34b7b7111d101160691b600a918401918201528851614d2681601d840160208d01613f32565b73222c2022636f6e74656e74223a202261723a2f2f60601b601d9290910191820152614d556031820189614802565b73222c2022696d616765223a2022697066733a2f2f60601b81529050614d7e6014820188614802565b90507f222c202273656c6c65725f6665655f62617369735f706f696e7473223a20000081528551614db681601e840160208a01613f32565b731610113332b2afb932b1b4b834b2b73a111d101160611b601e92909101918201526149c3614e0f6149ad614dee60328501896145cd565b741116101132bc3a32b93730b62fb634b735911d101160591b815260150190565b61227d60f01b815260020190565b83815260406020820152600061465e604083018486614a3c565b600060208284031215614e4957600080fd5b81516110c081613eff565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b600060208284031215614eb857600080fd5b81516110c081613c00565b60008060008060808587031215614ed957600080fd5b505082516020840151604085015160609095015191969095509092509050565b60ff8181168382160190811115610afa57610afa614547565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090614f4590830184613f56565b9695505050505050565b634e487b7160e01b600052602160045260246000fdfe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa2646970667358221220cb7e837f88fa7481fe3fa18d8b035389e972d63b2a6e6bd8fa67f823ce6c622164736f6c63430008130033000000000000000000000000ae6fdda940155d090e3a3de9b090ad6a392f761a000000000000000000000000004d8438f4016a96f2d6ddc17808f4e40b47cde60000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec07200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
Deployed Bytecode
0x6080604052600436106101355760003560e01c806379ba5097116100ab578063bee0ec0b1161006f578063bee0ec0b1461037e578063d784d426146103b2578063e6d283a6146103d2578063ed459df214610402578063f2fde38b14610422578063ffa1ad741461044257600080fd5b806379ba5097146102f65780638da5cb5b1461030b5780638f32d59b1461032b578063a56600631461034b578063ae3605ed1461036b57600080fd5b80633cb70c2d116100fd5780633cb70c2d146101f25780635c60da1b1461023e57806364d44b1e146102635780636f19d0b614610283578063705bf368146102ad578063715018a6146102e157600080fd5b8063089dc13b1461013a5780631a861d261461017757806323452b9c1461019b57806327ea6f2b146101b2578063360d0fad146101d2575b600080fd5b34801561014657600080fd5b5061015a61015536600461151c565b610488565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561018357600080fd5b5061018d60055481565b60405190815260200161016e565b3480156101a757600080fd5b506101b06104ae565b005b3480156101be57600080fd5b506101b06101cd366004611559565b6104f3565b3480156101de57600080fd5b5061015a6101ed366004611572565b6105aa565b3480156101fe57600080fd5b506102316040518060400160405280601381526020017268747470733a2f2f6d6972726f722e78797a2f60681b81525081565b60405161016e91906115e4565b34801561024a57600080fd5b5060035461015a9061010090046001600160a01b031681565b34801561026f57600080fd5b506101b061027e3660046115f7565b6105be565b34801561028f57600080fd5b5060035461029d9060ff1681565b604051901515815260200161016e565b3480156102b957600080fd5b5061015a7f0000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec07281565b3480156102ed57600080fd5b506101b0610677565b34801561030257600080fd5b506101b06106ab565b34801561031757600080fd5b5060005461015a906001600160a01b031681565b34801561033757600080fd5b506000546001600160a01b0316331461029d565b34801561035757600080fd5b506101b0610366366004611619565b610762565b61015a610379366004611652565b610987565b34801561038a57600080fd5b5061015a7f000000000000000000000000004d8438f4016a96f2d6ddc17808f4e40b47cde681565b3480156103be57600080fd5b506101b06103cd366004611719565b610b15565b3480156103de57600080fd5b5061029d6103ed366004611559565b60046020526000908152604090205460ff1681565b34801561040e57600080fd5b506001546001600160a01b0316331461029d565b34801561042e57600080fd5b506101b061043d366004611719565b610bf4565b34801561044e57600080fd5b506104767f000000000000000000000000000000000000000000000000000000000000000381565b60405160ff909116815260200161016e565b60006104a833836000604051806020016040528060008152506000610c96565b92915050565b6000546001600160a01b031633146104e15760405162461bcd60e51b81526004016104d890611736565b60405180910390fd5b600180546001600160a01b0319169055565b6000546001600160a01b0316331461051d5760405162461bcd60e51b81526004016104d890611736565b600554604051639ea75f2760e01b81526004810191909152602481018290527f0000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec0726001600160a01b031690639ea75f2790604401600060405180830381600087803b15801561058a57600080fd5b505af115801561059e573d6000803e3d6000fd5b50505060059190915550565b60006105b7838330610d1e565b9392505050565b6000546001600160a01b031633146105e85760405162461bcd60e51b81526004016104d890611736565b6040516302f848e760e11b815281151560048201527f0000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec0726001600160a01b0316906305f091ce90602401600060405180830381600087803b15801561064b57600080fd5b505af115801561065f573d6000803e3d6000fd5b50506003805460ff1916931515939093179092555050565b6000546001600160a01b031633146106a15760405162461bcd60e51b81526004016104d890611736565b6106a9610d7a565b565b6001546001600160a01b0316331461071a5760405162461bcd60e51b815260206004820152602c60248201527f63757272656e74206f776e6572206d757374207365742063616c6c657220617360448201526b103732bc3a1037bbb732b91760a11b60648201526084016104d8565b600180546001600160a01b0319908116909155600080543392168217815560405182917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3565b816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107c4919061176d565b6001600160a01b0316336001600160a01b0316146108135760405162461bcd60e51b815260206004820152600c60248201526b1d5b985d5d1a1bdc9a5e995960a21b60448201526064016104d8565b600061081d610dc4565b6040516352ba8c2b60e11b81526001600160a01b0385811660048301529192507f0000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec0728216916332c7791691869185169063a575185690602401602060405180830381865afa158015610892573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b6919061176d565b6040516001600160e01b031960e085901b1681526001600160a01b03928316600482015290821660248201529085166044820152606401600060405180830381600087803b15801561090757600080fd5b505af115801561091b573d6000803e3d6000fd5b505060405163a566006360e01b81526001600160a01b03868116600483015285811660248301528416925063a56600639150604401600060405180830381600087803b15801561096a57600080fd5b505af115801561097e573d6000803e3d6000fd5b50505050505050565b6000610991610e4f565b60008989600001518a602001518b61012001516040516020016109b7949392919061178a565b60408051601f1981840301815291815281516020928301206000818152600490935291205490915060ff1615610ad257600354610a039061010090046001600160a01b03168230610d1e565b91506000826001600160a01b03163b11610a575760405162461bcd60e51b8152602060048201526015602482015274696e76616c696420636c6f6e65206164647265737360581b60448201526064016104d8565b6040516321a6e7dd60e11b81526001600160a01b0383169063434dcfba903490610a89908990899089906004016117d2565b60206040518083038185885af1158015610aa7573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610acc9190611807565b50610afe565b6000818152600460205260409020805460ff19166001179055610afb8a8a8a8a8a8a8a8a610ea6565b91505b50610b096001600255565b98975050505050505050565b6000546001600160a01b03163314610b3f5760405162461bcd60e51b81526004016104d890611736565b6003546040516322fd9ed760e01b81526101009091046001600160a01b03908116600483015282811660248301527f0000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec07216906322fd9ed790604401600060405180830381600087803b158015610bb357600080fd5b505af1158015610bc7573d6000803e3d6000fd5b5050600380546001600160a01b0390941661010002610100600160a81b0319909416939093179092555050565b6000546001600160a01b03163314610c1e5760405162461bcd60e51b81526004016104d890611736565b6001600160a01b038116610c745760405162461bcd60e51b815260206004820152601f60248201527f4e657874206f776e657220697320746865207a65726f20616464726573732e0060448201526064016104d8565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000610ca28686610fbd565b60035460405162c76e0b60e11b81529192506001600160a01b0383169163018edc16913491610ce3918b918b918b918b918b9160ff909116906004016118f7565b6000604051808303818588803b158015610cfc57600080fd5b505af1158015610d10573d6000803e3d6000fd5b505050505095945050505050565b60405160388101919091526f5af43d82803e903d91602b57fd5bf3ff60248201526014810192909252733d602d80600a3d3981f3363d3d373d3d3d363d73825260588201526037600c8201206078820152605560439091012090565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b60007f000000000000000000000000004d8438f4016a96f2d6ddc17808f4e40b47cde66001600160a01b031663d6811b6f6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610e26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e4a919061176d565b905090565b6002805403610ea05760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016104d8565b60028055565b6000808989600001518a602001518b6101200151604051602001610ecd949392919061178a565b604051602081830303815290604052805190602001209050610eef8a8a610fbd565b9150816001600160a01b031663240e6f89348c848b8b8e604051602001610f3693929190928352602083019190915260f81b6001600160f81b031916604082015260410190565b6040516020818303038152906040528e8b8b8b600360009054906101000a900460ff16336040518b63ffffffff1660e01b8152600401610f7e9998979695949392919061194f565b6000604051808303818588803b158015610f9757600080fd5b505af1158015610fab573d6000803e3d6000fd5b50505050505098975050505050505050565b60e08101516000906001600160a01b03166110135760405162461bcd60e51b81526020600482015260166024820152751b5d5cdd081cdc1958da599e481c9958da5c1a595b9d60521b60448201526064016104d8565b6005541580611037575060008260c0015111801561103757506005548260c0015111155b6110735760405162461bcd60e51b815260206004820152600d60248201526c1a5b9d985b1a59081b1a5b5a5d609a1b60448201526064016104d8565b60035482516020808501516101208601516040516110c59561010090046001600160a01b0316946110aa948a94919391920161178a565b60405160208183030381529060405280519060200120611267565b905060006110d1610dc4565b90506001600160a01b038116156111dd5760e0830151604051631963bc8b60e11b81526001600160a01b0384811660048301526000602483015291821660448201527f0000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec072909116906332c7791690606401600060405180830381600087803b15801561115b57600080fd5b505af115801561116f573d6000803e3d6000fd5b5050505060e083015160405163a566006360e01b81526001600160a01b03848116600483015291821660248201529082169063a566006390604401600060405180830381600087803b1580156111c457600080fd5b505af11580156111d8573d6000803e3d6000fd5b505050505b6040516306e9fe9960e41b81526001600160a01b03858116600483015283811660248301527f0000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec0721690636e9fe99090604401600060405180830381600087803b15801561124857600080fd5b505af115801561125c573d6000803e3d6000fd5b505050505092915050565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c176000526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760096000f590506001600160a01b0381166104a85760405162461bcd60e51b815260206004820152601760248201527f455243313136373a2063726561746532206661696c656400000000000000000060448201526064016104d8565b634e487b7160e01b600052604160045260246000fd5b604051610140810167ffffffffffffffff8111828210171561133e5761133e611304565b60405290565b600082601f83011261135557600080fd5b813567ffffffffffffffff8082111561137057611370611304565b604051601f8301601f19908116603f0116810190828211818310171561139857611398611304565b816040528381528660208588010111156113b157600080fd5b836020870160208301376000602085830101528094505050505092915050565b6001600160a01b03811681146113e657600080fd5b50565b80356113f4816113d1565b919050565b6000610140828403121561140c57600080fd5b61141461131a565b9050813567ffffffffffffffff8082111561142e57600080fd5b61143a85838601611344565b8352602084013591508082111561145057600080fd5b61145c85838601611344565b6020840152604084013591508082111561147557600080fd5b61148185838601611344565b6040840152606084013591508082111561149a57600080fd5b6114a685838601611344565b606084015260808401359150808211156114bf57600080fd5b506114cc84828501611344565b60808301525060a082013560a082015260c082013560c08201526114f260e083016113e9565b60e08201526101006115058184016113e9565b818301525061012080830135818301525092915050565b60006020828403121561152e57600080fd5b813567ffffffffffffffff81111561154557600080fd5b611551848285016113f9565b949350505050565b60006020828403121561156b57600080fd5b5035919050565b6000806040838503121561158557600080fd5b8235611590816113d1565b946020939093013593505050565b6000815180845260005b818110156115c4576020818501810151868301820152016115a8565b506000602082860101526020601f19601f83011685010191505092915050565b6020815260006105b7602083018461159e565b60006020828403121561160957600080fd5b813580151581146105b757600080fd5b6000806040838503121561162c57600080fd5b8235611637816113d1565b91506020830135611647816113d1565b809150509250929050565b600080600080600080600080610100898b03121561166f57600080fd5b883561167a816113d1565b9750602089013567ffffffffffffffff8082111561169757600080fd5b6116a38c838d016113f9565b985060408b0135915060ff821682146116bb57600080fd5b81975060608b0135965060808b013595506116d860a08c016113e9565b945060c08b01359150808211156116ee57600080fd5b506116fb8b828c01611344565b92505061170a60e08a016113e9565b90509295985092959890939650565b60006020828403121561172b57600080fd5b81356105b7816113d1565b60208082526018908201527f63616c6c6572206973206e6f7420746865206f776e65722e0000000000000000604082015260600190565b60006020828403121561177f57600080fd5b81516105b7816113d1565b6001600160a01b03851681526080602082018190526000906117ae9083018661159e565b82810360408401526117c0818661159e565b91505082606083015295945050505050565b600060018060a01b038086168352606060208401526117f4606084018661159e565b9150808416604084015250949350505050565b60006020828403121561181957600080fd5b5051919050565b600061014082518185526118368286018261159e565b91505060208301518482036020860152611850828261159e565b9150506040830151848203604086015261186a828261159e565b91505060608301518482036060860152611884828261159e565b9150506080830151848203608086015261189e828261159e565b91505060a083015160a085015260c083015160c085015260e08301516118cf60e08601826001600160a01b03169052565b50610100838101516001600160a01b0316908501526101209283015192909301919091525090565b600060018060a01b03808916835260c0602084015261191960c0840189611820565b81881660408501528381036060850152611933818861159e565b959091166080840152505090151560a090910152949350505050565b6001600160a01b038a81168252602082018a90526101206040830181905260009161197c8483018c61159e565b91508382036060850152611990828b611820565b9150808916608085015283820360a08501526119ac828961159e565b96811660c085015294151560e08401525050911661010090910152969550505050505056fea2646970667358221220994653b8c585513601d17ed8f59a59c1d00c08ad547d70fe216ece47cf44764e64736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000ae6fdda940155d090e3a3de9b090ad6a392f761a000000000000000000000000004d8438f4016a96f2d6ddc17808f4e40b47cde60000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec07200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
-----Decoded View---------------
Arg [0] : _owner (address): 0xae6FdDA940155D090E3A3DE9b090aD6A392F761a
Arg [1] : _treasuryConfiguration (address): 0x004D8438f4016A96F2D6dDc17808F4e40b47cdE6
Arg [2] : _o11y (address): 0x3F2408693cc2E0C8E0bb68f039cEB6DEac0EC072
Arg [3] : _maxLimit (uint256): 0
Arg [4] : _guardOn (bool): True
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 000000000000000000000000ae6fdda940155d090e3a3de9b090ad6a392f761a
Arg [1] : 000000000000000000000000004d8438f4016a96f2d6ddc17808f4e40b47cde6
Arg [2] : 0000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec072
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000001
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.