Feature Tip: Add private address tag to any address under My Name Tag !
Source Code
Token Contract
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 25 from a total of 1,141 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Set Approval For... | 24248808 | 10 hrs ago | IN | 0 ETH | 0.00009694 | ||||
| Set Approval For... | 24248619 | 11 hrs ago | IN | 0 ETH | 0.0000048 | ||||
| Safe Transfer Fr... | 24248606 | 11 hrs ago | IN | 0 ETH | 0.00013358 | ||||
| Set Approval For... | 24240906 | 36 hrs ago | IN | 0 ETH | 0.00011879 | ||||
| Set Approval For... | 24240799 | 37 hrs ago | IN | 0 ETH | 0.00006755 | ||||
| Set Approval For... | 24228917 | 3 days ago | IN | 0 ETH | 0.00010634 | ||||
| Transfer From | 24227897 | 3 days ago | IN | 0 ETH | 0.00000345 | ||||
| Transfer From | 24227883 | 3 days ago | IN | 0 ETH | 0.00000345 | ||||
| Set Approval For... | 24194869 | 7 days ago | IN | 0 ETH | 0.00005391 | ||||
| Set Approval For... | 24194819 | 7 days ago | IN | 0 ETH | 0.00009422 | ||||
| Set Approval For... | 24191552 | 8 days ago | IN | 0 ETH | 0.00010083 | ||||
| Safe Transfer Fr... | 24182851 | 9 days ago | IN | 0 ETH | 0.00009063 | ||||
| Safe Transfer Fr... | 24182848 | 9 days ago | IN | 0 ETH | 0.00013849 | ||||
| Set Approval For... | 24179732 | 10 days ago | IN | 0 ETH | 0.00009464 | ||||
| Set Approval For... | 24178916 | 10 days ago | IN | 0 ETH | 0.00009443 | ||||
| Safe Transfer Fr... | 24178899 | 10 days ago | IN | 0 ETH | 0.00013018 | ||||
| Set Approval For... | 24176440 | 10 days ago | IN | 0 ETH | 0.00006139 | ||||
| Set Approval For... | 24176367 | 10 days ago | IN | 0 ETH | 0.00013034 | ||||
| Set Approval For... | 24175359 | 10 days ago | IN | 0 ETH | 0.00009442 | ||||
| Safe Transfer Fr... | 24169599 | 11 days ago | IN | 0 ETH | 0.00013901 | ||||
| Set Approval For... | 24160261 | 12 days ago | IN | 0 ETH | 0.0000014 | ||||
| Set Approval For... | 24155695 | 13 days ago | IN | 0 ETH | 0.00009467 | ||||
| Set Approval For... | 24150709 | 14 days ago | IN | 0 ETH | 0.0000205 | ||||
| Safe Transfer Fr... | 24150699 | 14 days ago | IN | 0 ETH | 0.00000964 | ||||
| Set Approval For... | 24148602 | 14 days ago | IN | 0 ETH | 0.0000298 |
Latest 11 internal transactions
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x600b5981 | 23521212 | 102 days ago | Contract Creation | 0 ETH | |||
| 0x600b5981 | 23515621 | 103 days ago | Contract Creation | 0 ETH | |||
| 0x600b5981 | 23515617 | 103 days ago | Contract Creation | 0 ETH | |||
| 0x600b5981 | 23515595 | 103 days ago | Contract Creation | 0 ETH | |||
| 0x600b5981 | 23371355 | 123 days ago | Contract Creation | 0 ETH | |||
| 0x600b5981 | 23370487 | 123 days ago | Contract Creation | 0 ETH | |||
| 0x600b5981 | 23370462 | 123 days ago | Contract Creation | 0 ETH | |||
| 0x600b5981 | 23370456 | 123 days ago | Contract Creation | 0 ETH | |||
| 0x600b5981 | 23370449 | 123 days ago | Contract Creation | 0 ETH | |||
| 0x600b5981 | 23370444 | 123 days ago | Contract Creation | 0 ETH | |||
| 0x61010060 | 23326849 | 129 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
GenArt721CoreV3_Curated_Flex
Compiler Version
v0.8.22+commit.4fc1097e
Optimization Enabled:
Yes with 10 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.22;
// Created By: Art Blocks Inc.
import {GenArt721CoreV3_Engine_Flex} from "./engine/V3/GenArt721CoreV3_Engine_Flex.sol";
import {EngineConfiguration} from "./interfaces/v0.8.x/IGenArt721CoreContractV3_Engine.sol";
import "./libs/v0.8.x/Bytes32Strings.sol";
/**
* @title Art Blocks Curated ERC-721 core contract, V3.2 Flex (ONCHAIN only)
* @author Art Blocks Inc.
* @notice This contract derives from the Art Blocks Engine Flex contract and adds
* some functionality to support curation of projects by Art Blocks.
* It also performs initialization of the contract in the constructor, because
* the contract is not intended to use a clone proxy pattern, and is intended
* to be deployed without any required follow-on calls to initialize.
* Constraints are added to the contract to only allow ONCHAIN dependencies,
* ensuring that the Art Blocks Curated projects are only dependent on onchain
* data, consistent with previous Art Blocks Curated contracts.
* ----------------------------------------------------------------------------
* See the Art Blocks Engine Flex contract for additional applicable documentation.
*/
contract GenArt721CoreV3_Curated_Flex is GenArt721CoreV3_Engine_Flex {
using Bytes32Strings for bytes32;
/// override patch version of this core contract
// @dev this is a constant value that is used to override the inherited core version CORE_VERSION
bytes32 private constant _CORE_VERSION_OVERRIDE = "v3.2.7";
// @dev overridden core version is returned instead of the inherited core version
function coreVersion() external pure override returns (string memory) {
return _CORE_VERSION_OVERRIDE.toString();
}
/// metadata pointer to the previous associated Flagship Artblocks core contracts
// @dev not defined as constant because constant address arrays are not yet implemented in Solidity
address[] public PREVIOUS_ART_BLOCKS_CONTRACTS = [
0x059EDD72Cd353dF5106D2B9cC5ab83a52287aC3a,
0xa7d8d9ef8D8Ce8992Df33D8b8CF4Aebabd5bD270,
0x99a9B7c1116f9ceEB1652de04d5969CcE509B069,
0xAB0000000000aa06f89B268D604a9c1C41524Ac6
];
/// Curation registry for Flagship projects, managed by Art Blocks
address public artblocksCurationRegistryAddress;
bool public constant IS_FLAGSHIP = true;
/**
* @notice Construct a new curated Art Blocks ERC-721 core contract.
* Performs all contract initialization in the constructor.
* @param engineConfiguration Configuration for the engine contract.
* note: parameter `engineConfiguration.newSuperAdminAddress` is not used or operated on in this contract.
* @param adminACLContract_ Address of the admin ACL contract.
* @param defaultBaseURIHost Default base URI host for token URIs, e.g. "https://token.artblocks.io/" for mainnet
* @param bytecodeStorageReaderContract_ Address of the bytecode storage reader contract to be used by this
* contract.
*/
constructor(
EngineConfiguration memory engineConfiguration,
address adminACLContract_,
string memory defaultBaseURIHost,
address bytecodeStorageReaderContract_
) GenArt721CoreV3_Engine_Flex() {
// input validation generally performed by the engine factory
_onlyNonZeroAddress(engineConfiguration.renderProviderAddress);
_onlyNonZeroAddress(engineConfiguration.randomizerContract);
_onlyNonZeroAddress(adminACLContract_);
_onlyNonZeroAddress(bytecodeStorageReaderContract_);
require(
bytes(defaultBaseURIHost).length > 0,
"GenArt721CoreV3_Curated_Flex: defaultBaseURIHost must be non-empty"
);
// input validation specific to the curated contract
require(
!engineConfiguration.autoApproveArtistSplitProposals,
"GenArt721CoreV3_Curated_Flex: autoApproveArtistSplitProposals must be false"
);
// @dev artblocks is listed as render provider, no party should be platform provider
require(
engineConfiguration.nullPlatformProvider,
"GenArt721CoreV3_Curated_Flex: nullPlatformProvider must be true"
);
require(
!engineConfiguration.allowArtistProjectActivation,
"GenArt721CoreV3_Curated_Flex: allowArtistProjectActivation must be false"
);
// @dev previous artblocks contracts exists, so only starting project ID > 0
require(
engineConfiguration.startingProjectId > 0,
"GenArt721CoreV3_Curated_Flex: startingProjectId must be greater than 0"
);
// initialize the contract
_initialize({
engineConfiguration: engineConfiguration,
adminACLContract_: adminACLContract_,
defaultBaseURIHost: defaultBaseURIHost,
bytecodeStorageReaderContract_: bytecodeStorageReaderContract_
});
// override default base URI to be curated format instead of Engine (no address required for curated)
_updateDefaultBaseURI(defaultBaseURIHost);
}
/**
* @notice Override of the Engine contract's initialize function.
* Immediately reverts, as initialization of this Curated contract is performed in the constructor.
*/
function initialize(
EngineConfiguration memory /*engineConfiguration*/,
address /*adminACLContract_*/,
string memory /*defaultBaseURIHost*/,
address /*bytecodeStorageReaderContract_*/
) external pure override {
// revert - initialization for this contract is performed in the constructor
// @dev note that the initialize function would revert without this override, but it is included for clarity
revert(
"GenArt721CoreV3_Curated_Flex: contract initialized in constructor"
);
}
/**
* @notice Updates reference to Art Blocks Curation Registry contract.
* @param _artblocksCurationRegistryAddress Address of Art Blocks Curation Registry contract.
*/
function updateArtblocksCurationRegistryAddress(
address _artblocksCurationRegistryAddress
) external {
_onlyAdminACL(this.updateArtblocksCurationRegistryAddress.selector);
artblocksCurationRegistryAddress = _artblocksCurationRegistryAddress;
emit ArtBlocksCurationRegistryContractUpdated(
_artblocksCurationRegistryAddress
);
}
function updateProjectExternalAssetDependency(
uint256 _projectId,
uint256 _index,
string memory _cidOrData,
ExternalAssetDependencyType _dependencyType
) public override {
require(
_dependencyType == ExternalAssetDependencyType.ONCHAIN,
"GenArt721CoreV3_Curated_Flex: Curated dependency type must be ONCHAIN"
);
super.updateProjectExternalAssetDependency({
_projectId: _projectId,
_index: _index,
_cidOrData: _cidOrData,
_dependencyType: _dependencyType
});
}
function addProjectExternalAssetDependency(
uint256 _projectId,
string memory _cidOrData,
ExternalAssetDependencyType _dependencyType
) public override {
require(
_dependencyType == ExternalAssetDependencyType.ONCHAIN,
"GenArt721CoreV3_Curated_Flex: Curated dependency type must be ONCHAIN"
);
super.addProjectExternalAssetDependency({
_projectId: _projectId,
_cidOrData: _cidOrData,
_dependencyType: _dependencyType
});
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC2981.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";
/**
* @dev Interface for the NFT Royalty Standard.
*
* A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
* support for royalty payments across all NFT marketplaces and ecosystem participants.
*/
interface IERC2981 is IERC165 {
/**
* @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
* exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
*/
function royaltyInfo(
uint256 tokenId,
uint256 salePrice
) external view returns (address receiver, uint256 royaltyAmount);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Metadata.sol)
pragma solidity ^0.8.20;
import {IERC721} from "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Metadata is IERC721 {
/**
* @dev Returns the token collection name.
*/
function name() external view returns (string memory);
/**
* @dev Returns the token collection symbol.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) external view returns (string memory);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
* {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.20;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be
* reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.22;
// Created By: Art Blocks Inc.
import "../../interfaces/v0.8.x/IRandomizer_V3CoreBase.sol";
import "../../interfaces/v0.8.x/IAdminACLV0_Extended.sol";
import "../../interfaces/v0.8.x/IGenArt721CoreContractV3_Engine_Flex.sol";
import {IGenArt721CoreContractV3_ProjectFinance} from "../../interfaces/v0.8.x/IGenArt721CoreContractV3_ProjectFinance.sol";
import "../../interfaces/v0.8.x/IGenArt721CoreContractExposesHashSeed.sol";
import "../../interfaces/v0.8.x/IDependencyRegistryCompatibleV0.sol";
import {ISplitProviderV0} from "../../interfaces/v0.8.x/ISplitProviderV0.sol";
import {IBytecodeStorageReader_Base} from "../../interfaces/v0.8.x/IBytecodeStorageReader_Base.sol";
import "@openzeppelin-5.0/contracts/utils/Strings.sol";
import "@openzeppelin-5.0/contracts/access/Ownable.sol";
import {IERC2981} from "@openzeppelin-5.0/contracts/interfaces/IERC2981.sol";
import "../../libs/v0.8.x/ERC721_PackedHashSeedV1.sol";
import {BytecodeStorageWriter, BytecodeStorageReader} from "../../libs/v0.8.x/BytecodeStorageV2.sol";
import {V3FlexLib} from "../../libs/v0.8.x/V3FlexLib.sol";
import "../../libs/v0.8.x/Bytes32Strings.sol";
/**
* @title Art Blocks Engine Flex ERC-721 core contract, V3.
* @author Art Blocks Inc.
* @notice Privileged Roles and Ownership:
* This contract is designed to be managed, with progressively limited powers
* as a project progresses from active to locked.
* Privileged roles and abilities are controlled by the admin ACL contract and
* artists. Both of these roles hold extensive power and can arbitrarily
* control and modify portions of projects, dependent upon project state. After
* a project is locked, important project metadata fields are locked including
* the project name, artist name, and script and display details. Edition size
* can never be increased.
* Care must be taken to ensure that the admin ACL contract and artist
* addresses are secure behind a multi-sig or other access control mechanism.
* ----------------------------------------------------------------------------
* The following functions are restricted to the Admin ACL contract:
* - updateArtblocksDependencyRegistryAddress
* - updateArtblocksOnChainGeneratorAddress
* - updateNextCoreContract
* - updateProviderSalesAddresses
* - updateProviderPrimarySalesPercentages (up to 100%)
* - updateProviderDefaultSecondarySalesBPS (up to 100%)
* - syncProviderSecondaryForProjectToDefaults
* - updateMinterContract
* - updateRandomizerAddress
* - toggleProjectIsActive (note: artist may be configured to activate projects)
* - addProject
* - forbidNewProjects (forever forbidding new projects)
* - updateDefaultBaseURI (used to initialize new project base URIs)
* - updateSplitProvider
* - updateBytecodeStorageReaderContract
* - updateIPFSGateway
* - updateArweaveGateway
* ----------------------------------------------------------------------------
* The following functions are restricted to either the Artist address or
* the Admin ACL contract, only when the project is not locked:
* - updateProjectName
* - updateProjectArtistName
* - updateProjectLicense
* - Change project script via addProjectScript, addProjectScriptCompressed,
* updateProjectScript, updateProjectScriptCompressed,
* and removeProjectLastScript
* - updateProjectScriptType
* - updateProjectAspectRatio
* ----------------------------------------------------------------------------
* The following functions are restricted to only the Artist address:
* - proposeArtistPaymentAddressesAndSplits (Note that this has to be accepted
* by adminAcceptArtistAddressesAndSplits to take effect, which is restricted
* to the Admin ACL contract, or the artist if the core contract owner has
* renounced ownership. Also note that a proposal will be automatically
* accepted if the artist only proposes changed payee percentages without
* modifying any payee addresses, or is only removing payee addresses, or
* if the global config `autoApproveArtistSplitProposals` is set to `true`.)
* - toggleProjectIsPaused (note the artist can still mint while paused)
* - updateProjectSecondaryMarketRoyaltyPercentage (up to
ARTIST_MAX_SECONDARY_ROYALTY_PERCENTAGE percent)
* - updateProjectWebsite
* - updateProjectMaxInvocations (to a number greater than or equal to the
* current number of invocations, and less than current project maximum
* invocations)
* - updateProjectBaseURI (controlling the base URI for tokens in the project)
* ----------------------------------------------------------------------------
* The following function is restricted to either the Admin ACL contract, or
* the Artist address if the core contract owner has renounced ownership:
* - adminAcceptArtistAddressesAndSplits
* - updateProjectArtistAddress (owner ultimately controlling the project and
* its and-on revenue, unless owner has renounced ownership)
* ----------------------------------------------------------------------------
* The following function is restricted to the artist when a project is
* unlocked, and only callable by Admin ACL contract when a project is locked:
* - updateProjectDescription
* ----------------------------------------------------------------------------
* The following functions for managing external asset dependencies are restricted
* to projects with external asset dependencies that are unlocked:
* - lockProjectExternalAssetDependencies
* - updateProjectExternalAssetDependency
* - updateProjectExternalAssetDependencyOnChainCompressed
* - updateProjectAssetDependencyOnChainAtAddress
* - removeProjectExternalAssetDependency
* - addProjectExternalAssetDependency
* - addProjectExternalAssetDependencyOnChainCompressed
* - addProjectAssetDependencyOnChainAtAddress
* ----------------------------------------------------------------------------
* The following function is restricted to owner calling directly:
* - transferOwnership
* - renounceOwnership
* ----------------------------------------------------------------------------
* The following configuration variables are set at time of contract deployment,
* and not modifiable thereafter (immutable after the point of deployment):
* - (bool) autoApproveArtistSplitProposals
* ----------------------------------------------------------------------------
* Additional admin and artist privileged roles may be described on minters,
* registries, and other contracts that may interact with this core contract.
*/
contract GenArt721CoreV3_Engine_Flex is
ERC721_PackedHashSeedV1,
Ownable,
IERC2981,
IDependencyRegistryCompatibleV0,
IGenArt721CoreContractV3_Engine_Flex,
IGenArt721CoreContractV3_ProjectFinance,
IGenArt721CoreContractExposesHashSeed
{
using BytecodeStorageWriter for string;
using BytecodeStorageWriter for bytes;
using Bytes32Strings for bytes32;
using Strings for uint256;
using Strings for address;
uint256 constant ONE_HUNDRED = 100;
uint256 constant ONE_MILLION = 1_000_000;
uint24 constant ONE_MILLION_UINT24 = 1_000_000;
uint256 constant FOUR_WEEKS_IN_SECONDS = 2_419_200;
uint8 constant AT_CHARACTER_CODE = uint8(bytes1("@")); // 0x40
// numeric constants
uint256 constant MAX_PROVIDER_SECONDARY_SALES_BPS = 10000; // 10_000 BPS = 100%
uint256 constant ARTIST_MAX_SECONDARY_ROYALTY_PERCENTAGE = 95; // 95%
/// pointer to next core contract associated with this contract
address public nextCoreContract;
/// Dependency registry managed by Art Blocks
address public artblocksDependencyRegistryAddress;
/// On chain generator managed by Art Blocks
address public artblocksOnChainGeneratorAddress;
/// ensure initialization can only be performed once
bool private _initialized;
/// current randomizer contract
IRandomizer_V3CoreBase public randomizerContract;
/// append-only array of all randomizer contract addresses ever used by
/// this contract
address[] private _historicalRandomizerAddresses;
/// admin ACL contract
IAdminACLV0 public adminACLContract;
struct Project {
uint24 invocations;
uint24 maxInvocations;
uint24 scriptCount;
// max uint64 ~= 1.8e19 sec ~= 570 billion years
uint64 completedTimestamp;
bool active;
bool paused;
string name;
string artist;
address descriptionAddress;
string website;
string license;
string projectBaseURI;
bytes32 scriptTypeAndVersion;
string aspectRatio;
// mapping from script index to address storing script in bytecode
mapping(uint256 => address) scriptBytecodeAddresses;
}
mapping(uint256 => Project) projects;
/// private mapping from project ID to project financial information. See
/// `projectIdToFinancials` getter for public access.
mapping(uint256 _projectId => ProjectFinance)
private _projectIdToFinancials;
/// hash of artist's proposed payment updates to be approved by admin
mapping(uint256 => bytes32) public proposedArtistAddressesAndSplitsHash;
/// The render provider payment address for all primary sales revenues
/// (packed)
address payable public renderProviderPrimarySalesAddress;
/// Percentage of primary sales revenue allocated to the render provider
/// (packed)
// packed uint: max of 100, max uint8 = 255
uint8 private _renderProviderPrimarySalesPercentage;
/// The platform provider payment address for all primary sales revenues
/// (packed)
address payable public platformProviderPrimarySalesAddress;
/// Percentage of primary sales revenue allocated to the platform provider
/// (packed)
// packed uint: max of 100, max uint8 = 255
uint8 private _platformProviderPrimarySalesPercentage;
/// @dev Note on "default" provider secondary values - the only way these can
/// be different on a per project basis is if admin updates these and then
/// does not call syncProviderSecondaryForProjectToDefaults for the project.
/// -----------------------------------------------------------------------
/// The default render provider payment address for all secondary sales royalty
/// revenues, for all new projects. Individual project payment info is defined
/// in each project's ProjectFinance struct.
/// Projects can be updated to this value by calling the
/// `syncProviderSecondaryForProjectToDefaults` function for each project.
address payable public defaultRenderProviderSecondarySalesAddress;
/// The default basis points allocated to render provider for all secondary
/// sales royalty revenues, for all new projects. Individual project
/// payment info is defined in each project's ProjectFinance struct.
/// Projects can be updated to this value by calling the
/// `syncProviderSecondaryForProjectToDefaults` function for each project.
uint256 public defaultRenderProviderSecondarySalesBPS;
/// The default platform provider payment address for all secondary sales royalty
/// revenues, for all new projects. Individual project payment info is defined
/// in each project's ProjectFinance struct.
/// Projects can be updated to this value by calling the
/// `syncProviderSecondaryForProjectToDefaults` function for each project.
address payable public defaultPlatformProviderSecondarySalesAddress;
/// The default basis points allocated to platform provider for all secondary
/// sales royalty revenues, for all new projects. Individual project
/// payment info is defined in each project's ProjectFinance struct.
/// Projects can be updated to this value by calling the
/// `syncProviderSecondaryForProjectToDefaults` function for each project.
uint256 public defaultPlatformProviderSecondarySalesBPS;
/// -----------------------------------------------------------------------
/// single minter allowed for this core contract
address public minterContract;
/// starting (initial) project ID on this contract configured
/// at time of deployment and intended to be immutable after initialization.
/// Not marked as immutable due to initialization requirements
/// under the ERC-1167 minimal proxy pattern, which necessitates
/// setting this value post-deployment.
uint256 public startingProjectId;
/// next project ID to be created
uint248 private _nextProjectId;
/// bool indicating if adding new projects is forbidden;
/// default behavior is to allow new projects
bool public newProjectsForbidden;
/// configuration variable set at time of deployment, intended to be
/// immutable after initialization, that determines whether or not
/// admin approval^ should be required to accept artist address change
/// proposals, or if these proposals should always auto-approve, as
/// determined by the business process requirements of the Engine
/// partner using this contract.
///
/// ^does not apply in the case where contract-ownership itself is revoked
/// Not marked as immutable due to initialization requirements
/// under the ERC-1167 minimal proxy pattern, which necessitates
/// setting this value post-deployment.
bool public autoApproveArtistSplitProposals;
/// configuration variable set at time of deployment, intended to be
/// immutable after initialization, that determines if platform provider
/// fees and addresses are always required to be set to zero.
/// Not marked as immutable due to initialization requirements
/// under the ERC-1167 minimal proxy pattern, which necessitates
/// setting this value post-deployment.
bool public nullPlatformProvider;
/// configuration variable set at time of deployment, intended to be
/// immutable after initialization, that determines if artists are allowed
/// to activate their own projects.
/// Not marked as immutable due to initialization requirements
/// under the ERC-1167 minimal proxy pattern, which necessitates
/// setting this value post-deployment.
bool public allowArtistProjectActivation;
/// version & type of this core contract
bytes32 constant CORE_VERSION = "v3.2.5";
function coreVersion() external pure virtual returns (string memory) {
return CORE_VERSION.toString();
}
bytes32 constant CORE_TYPE = "GenArt721CoreV3_Engine_Flex";
function coreType() external pure returns (string memory) {
return CORE_TYPE.toString();
}
/// default base URI to initialize all new project projectBaseURI values to
string public defaultBaseURI;
// ERC2981 royalty support and default royalty values
bytes4 private constant _INTERFACE_ID_ERC2981 = 0x2a55205a;
uint8 private constant _DEFAULT_ARTIST_SECONDARY_ROYALTY_PERCENTAGE = 5;
// royalty split provider
ISplitProviderV0 public splitProvider;
// bytecode storage reader contract; may be universal or specific version reader contract
IBytecodeStorageReader_Base public bytecodeStorageReaderContract;
/**
* @dev This constructor sets the owner to a non-functional address as a formality.
* It is only ever ran on the implementation contract. The `Ownable` constructor is
* called to satisfy the contract's inheritance requirements. This owner has no
* operational significance and should not be considered secure or meaningful.
* The true ownership will be set in the `initialize` function post-deployment to
* ensure correct owner management in the proxy architecture.
* Explicitly setting the owner to '0xdead' to indicate non-operational use.
*/
constructor() Ownable(0x000000000000000000000000000000000000dEaD) {}
function _onlyNonZeroAddress(address _address) internal pure {
if (_address == address(0)) {
revert GenArt721Error(ErrorCodes.OnlyNonZeroAddress);
}
}
function _onlyNonEmptyString(string memory _string) internal pure {
if (bytes(_string).length == 0) {
revert GenArt721Error(ErrorCodes.OnlyNonEmptyString);
}
}
function _onlyNonEmptyBytes(bytes memory _bytes) internal pure {
if (_bytes.length == 0) {
revert GenArt721Error(ErrorCodes.OnlyNonEmptyBytes);
}
}
function _onlyValidTokenId(uint256 _tokenId) internal view {
if (_ownerOf(_tokenId) == address(0)) {
revert GenArt721Error(ErrorCodes.TokenDoesNotExist);
}
}
function _onlyValidProjectId(uint256 _projectId) internal view {
if (_projectId < startingProjectId || _projectId >= _nextProjectId) {
revert GenArt721Error(ErrorCodes.ProjectDoesNotExist);
}
}
function _onlyUnlocked(uint256 _projectId) internal view {
// Note: calling `_projectUnlocked` enforces that the `_projectId`
// passed in is valid.`
if (!_projectUnlocked(_projectId)) {
revert GenArt721Error(ErrorCodes.OnlyUnlockedProjects);
}
}
function _onlyAdminACL(bytes4 _selector) internal {
if (!adminACLAllowed(msg.sender, address(this), _selector)) {
revert GenArt721Error(ErrorCodes.OnlyAdminACL);
}
}
function _onlyArtist(uint256 _projectId) internal view {
if (msg.sender != _projectIdToFinancials[_projectId].artistAddress) {
revert GenArt721Error(ErrorCodes.OnlyArtist);
}
}
function _onlyArtistOrAdminACL(
uint256 _projectId,
bytes4 _selector
) internal {
if (
!(msg.sender == _projectIdToFinancials[_projectId].artistAddress ||
adminACLAllowed(msg.sender, address(this), _selector))
) {
revert GenArt721Error(ErrorCodes.OnlyArtistOrAdminACL);
}
}
/**
* This modifier allows the artist of a project to call a function if the
* owner of the contract has renounced ownership. This is to allow the
* contract to continue to function if the owner decides to renounce
* ownership.
*/
function _onlyAdminACLOrRenouncedArtist(
uint256 _projectId,
bytes4 _selector
) internal {
// check if Admin ACL is allowed to call this function
if (adminACLAllowed(msg.sender, address(this), _selector)) {
return;
}
// check if the owner has renounced ownership and the caller is the
// artist of the project
if (
owner() == address(0) &&
msg.sender == _projectIdToFinancials[_projectId].artistAddress
) {
return;
}
// neither of the above conditions were met, revert
revert GenArt721Error(ErrorCodes.OnlyAdminACLOrRenouncedArtist);
}
/**
* @notice Initializes the contract with the provided `engineConfiguration`.
* This function should be called atomically, immediately after deployment.
* Only callable once. Validation on `engineConfiguration` is performed by caller.
* @dev This function is intentionally unpermissioned to allow for the
* initialization of the contract post-deployment. It is expected that this
* function will be called atomically by the factory contract that deploys this
* contract, after which it will be initialized and uncallable.
* @param engineConfiguration EngineConfiguration to configure the contract with.
* note: parameter `engineConfiguration.newSuperAdminAddress` is not used or operated on in this contract.
* @param adminACLContract_ Address of admin access control contract, to be
* set as contract owner.
* @param defaultBaseURIHost Base URI prefix to initialize default base URI with.
* @param bytecodeStorageReaderContract_ Address of the bytecode storage reader contract.
*/
function initialize(
EngineConfiguration memory engineConfiguration,
address adminACLContract_,
string memory defaultBaseURIHost,
address bytecodeStorageReaderContract_
) external virtual {
// @dev internal function call so derived contracts have access to initialization logic
_initialize({
engineConfiguration: engineConfiguration,
adminACLContract_: adminACLContract_,
defaultBaseURIHost: defaultBaseURIHost,
bytecodeStorageReaderContract_: bytecodeStorageReaderContract_
});
}
/**
* @notice Updates preferredIPFSGateway to `_gateway`.
*/
function updateIPFSGateway(string calldata _gateway) public {
_onlyAdminACL(this.updateIPFSGateway.selector);
V3FlexLib.updateIPFSGateway({_gateway: _gateway});
}
/**
* @notice Updates preferredArweaveGateway to `_gateway`.
*/
function updateArweaveGateway(string calldata _gateway) public {
_onlyAdminACL(this.updateArweaveGateway.selector);
V3FlexLib.updateArweaveGateway({_gateway: _gateway});
}
/**
* @notice Locks external asset dependencies for project `_projectId`.
*/
function lockProjectExternalAssetDependencies(uint256 _projectId) external {
_onlyArtistOrAdminACL(
_projectId,
this.lockProjectExternalAssetDependencies.selector
);
V3FlexLib.lockProjectExternalAssetDependencies({
_projectId: _projectId
});
}
/**
* @notice Updates external asset dependency for project `_projectId`.
* @dev public virtual to allow for derived contracts to override this function
* @param _projectId Project to be updated.
* @param _index Asset index.
* @param _cidOrData Field that contains the CID of the dependency if IPFS or ARWEAVE,
* empty string of ONCHAIN, or a string representation of the Art Blocks Dependency
* Registry's `dependencyNameAndVersion` if ART_BLOCKS_DEPENDENCY_REGISTRY.
* @param _dependencyType Asset dependency type.
* 0 - IPFS
* 1 - ARWEAVE
* 2 - ONCHAIN
* 3 - ART_BLOCKS_DEPENDENCY_REGISTRY
*/
function updateProjectExternalAssetDependency(
uint256 _projectId,
uint256 _index,
string memory _cidOrData,
ExternalAssetDependencyType _dependencyType
) public virtual {
_onlyArtistOrAdminACL(
_projectId,
this.updateProjectExternalAssetDependency.selector
);
V3FlexLib.updateProjectExternalAssetDependency({
_projectId: _projectId,
_index: _index,
_cidOrData: _cidOrData,
_dependencyType: _dependencyType
});
}
/**
* @notice Updates external asset dependency for project `_projectId` of type
* ONCHAIN using on-chain compression. The string should be compressed using
* `getCompressed`.
* This function stores the string in a compressed format on-chain.
* For reads, the compressed script is decompressed on-chain, ensuring the
* original text is reconstructed without external dependencies.
* @dev _compressedString in memory to minimize bytecode size.
* @param _projectId Project to be updated.
* @param _index Asset index.
* @param _compressedString Pre-compressed string asset to be added.
*/
function updateProjectExternalAssetDependencyOnChainCompressed(
uint256 _projectId,
uint256 _index,
bytes memory _compressedString
) external {
_onlyArtistOrAdminACL(
_projectId,
this.updateProjectExternalAssetDependencyOnChainCompressed.selector
);
V3FlexLib.updateProjectExternalAssetDependencyOnChainCompressed({
_projectId: _projectId,
_index: _index,
_compressedString: _compressedString
});
}
/**
* @notice Updates external asset dependency for project `_projectId` at
* index `_index`, with data at BytecodeStorage-compatible address
* `_assetAddress`.
* @dev This function may point to any BytecodeStorageReader-compatible
* on-chain asset, including a web3call contract.
* @param _projectId Project to be updated.
* @param _index Asset index.
* @param _assetAddress Address of the on-chain asset.
*/
function updateProjectAssetDependencyOnChainAtAddress(
uint256 _projectId,
uint256 _index,
address _assetAddress
) external {
_onlyArtistOrAdminACL(
_projectId,
this.updateProjectAssetDependencyOnChainAtAddress.selector
);
V3FlexLib.updateProjectAssetDependencyOnChainAtAddress({
_projectId: _projectId,
_index: _index,
_assetAddress: _assetAddress
});
}
/**
* @notice Removes external asset dependency for project `_projectId` at index `_index`.
* As of v3.2, only allow removal of dependency at last index, for UX purposes.
* @param _projectId Project to be updated.
* @param _index Asset index
*/
function removeProjectExternalAssetDependency(
uint256 _projectId,
uint256 _index
) external {
_onlyArtistOrAdminACL(
_projectId,
this.removeProjectExternalAssetDependency.selector
);
V3FlexLib.removeProjectExternalAssetDependency({
_projectId: _projectId,
_index: _index
});
}
/**
* @notice Adds external asset dependency for project `_projectId`.
* @dev public virtual to allow for derived contracts to override this function
* @param _projectId Project to be updated.
* @param _cidOrData Field that contains the CID of the dependency if IPFS or ARWEAVE,
* empty string of ONCHAIN, or a string representation of the Art Blocks Dependency
* Registry's `dependencyNameAndVersion` if ART_BLOCKS_DEPENDENCY_REGISTRY.
* @param _dependencyType Asset dependency type.
* 0 - IPFS
* 1 - ARWEAVE
* 2 - ONCHAIN
* 3 - ART_BLOCKS_DEPENDENCY_REGISTRY
*/
function addProjectExternalAssetDependency(
uint256 _projectId,
string memory _cidOrData,
ExternalAssetDependencyType _dependencyType
) public virtual {
_onlyArtistOrAdminACL(
_projectId,
this.addProjectExternalAssetDependency.selector
);
V3FlexLib.addProjectExternalAssetDependency({
_projectId: _projectId,
_cidOrData: _cidOrData,
_dependencyType: _dependencyType
});
}
/**
* @notice Adds external asset dependency for project `_projectId` of type
* ONCHAIN using on-chain compression. The string should be compressed using
* `getCompressed`.
* This function stores the string in a compressed format on-chain.
* For reads, the compressed script is decompressed on-chain, ensuring the
* original text is reconstructed without external dependencies.
* @dev _compressedString in memory to minimize bytecode size.
* @param _projectId Project to be updated.
* @param _compressedString Pre-compressed string asset to be added.
*/
function addProjectExternalAssetDependencyOnChainCompressed(
uint256 _projectId,
bytes memory _compressedString
) external {
_onlyArtistOrAdminACL(
_projectId,
this.addProjectExternalAssetDependencyOnChainCompressed.selector
);
V3FlexLib.addProjectExternalAssetDependencyOnChainCompressed({
_projectId: _projectId,
_compressedString: _compressedString
});
}
/**
* @notice Adds an on-chain external asset dependency for project
* `_projectId`, with data at BytecodeStorage-compatible address
* `_assetAddress`.
* @dev This function may point to any BytecodeStorageReader-compatible
* on-chain asset, including a web3call contract.
* @param _projectId Project to be updated.
* @param _assetAddress Address of the BytecodeStorageReader-compatible on-chain asset.
*/
function addProjectAssetDependencyOnChainAtAddress(
uint256 _projectId,
address _assetAddress
) external {
_onlyArtistOrAdminACL(
_projectId,
this.addProjectAssetDependencyOnChainAtAddress.selector
);
V3FlexLib.addProjectAssetDependencyOnChainAtAddress({
_projectId: _projectId,
_assetAddress: _assetAddress
});
}
/**
* @notice Mints a token from project `_projectId` and sets the
* token's owner to `_to`. Hash may or may not be assigned to the token
* during the mint transaction, depending on the randomizer contract.
* @param _to Address to be the minted token's owner.
* @param _projectId Project ID to mint a token on.
* @param _by Purchaser of minted token.
* @return _tokenId The ID of the minted token.
* @dev sender must be the allowed minterContract
* @dev name of function is optimized for gas usage
*/
function mint_Ecf(
address _to,
uint256 _projectId,
address _by
) external returns (uint256 _tokenId) {
// CHECKS
if (msg.sender != minterContract) {
revert GenArt721Error(ErrorCodes.OnlyMinterContract);
}
Project storage project = projects[_projectId];
// load invocations into memory
uint24 invocationsBefore = project.invocations;
uint24 invocationsAfter;
unchecked {
// invocationsBefore guaranteed <= maxInvocations <= 1_000_000,
// 1_000_000 << max uint24, so no possible overflow
invocationsAfter = invocationsBefore + 1;
}
uint24 maxInvocations = project.maxInvocations;
if (invocationsBefore >= maxInvocations) {
revert GenArt721Error(ErrorCodes.MaxInvocationsReached);
}
if (
!(project.active ||
_by == _projectIdToFinancials[_projectId].artistAddress)
) {
revert GenArt721Error(ErrorCodes.ProjectMustExistAndBeActive);
}
if (
project.paused &&
_by != _projectIdToFinancials[_projectId].artistAddress
) {
revert GenArt721Error(ErrorCodes.PurchasesPaused);
}
// EFFECTS
// increment project's invocations
project.invocations = invocationsAfter;
uint256 thisTokenId;
unchecked {
// invocationsBefore is uint24 << max uint256. In production use,
// _projectId * ONE_MILLION must be << max uint256, otherwise
// tokenIdToProjectId function become invalid.
// Therefore, no risk of overflow
thisTokenId = (_projectId * ONE_MILLION) + invocationsBefore;
}
// mark project as completed if hit max invocations
if (invocationsAfter == maxInvocations) {
_completeProject(_projectId);
}
// INTERACTIONS
_mint(_to, thisTokenId);
// token hash is updated by the randomizer contract on V3
randomizerContract.assignTokenHash(thisTokenId);
// Do not need to also log `projectId` in event, as the `projectId` for
// a given token can be derived from the `tokenId` with:
// projectId = tokenId / 1_000_000
emit Mint(_to, thisTokenId);
return thisTokenId;
}
/**
* @notice Sets the hash seed for a given token ID `_tokenId`.
* May only be called by the current randomizer contract.
* May only be called for tokens that have not already been assigned a
* non-zero hash.
* @param _tokenId Token ID to set the hash for.
* @param _hashSeed Hash seed to set for the token ID. Only last 12 bytes
* will be used.
* @dev gas-optimized function name because called during mint sequence
* @dev if a separate event is required when the token hash is set, e.g.
* for indexing purposes, it must be emitted by the randomizer. This is to
* minimize gas when minting.
*/
function setTokenHash_8PT(uint256 _tokenId, bytes32 _hashSeed) external {
_onlyValidTokenId(_tokenId);
OwnerAndHashSeed storage ownerAndHashSeed = _ownersAndHashSeeds[
_tokenId
];
if (msg.sender != address(randomizerContract)) {
revert GenArt721Error(ErrorCodes.OnlyRandomizer);
}
if (ownerAndHashSeed.hashSeed != bytes12(0)) {
revert GenArt721Error(ErrorCodes.TokenHashAlreadySet);
}
if (_hashSeed == bytes12(0)) {
revert GenArt721Error(ErrorCodes.NoZeroHashSeed);
}
ownerAndHashSeed.hashSeed = bytes12(_hashSeed);
}
/**
* @notice Allows owner (AdminACL) to revoke ownership of the contract.
* Note that the contract is intended to continue to function after the
* owner renounces ownership, but no new projects will be able to be added.
* Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the
* owner/AdminACL contract. The same is true for any dependent contracts
* that also integrate with the owner/AdminACL contract (e.g. potentially
* minter suite contracts, registry contracts, etc.).
* After renouncing ownership, artists will be in control of updates to
* their payment addresses and splits (see modifier
* onlyAdminACLOrRenouncedArtist`).
* While there is no currently intended reason to call this method based on
* typical Engine partner business practices, this method exists to allow
* artists to continue to maintain the limited set of contract
* functionality that exists post-project-lock in an environment in which
* there is no longer an admin maintaining this smart contract.
* @dev This function is intended to be called directly by the AdminACL,
* not by an address allowed by the AdminACL contract.
*/
function renounceOwnership() public override onlyOwner {
// broadcast that new projects are no longer allowed (if not already)
_forbidNewProjects();
// renounce ownership viw Ownable
Ownable.renounceOwnership();
}
/**
* @notice Updates reference to next core contract, associated with this contract.
* @param _nextCoreContract Address of the next core contract
*/
function updateNextCoreContract(address _nextCoreContract) external {
_onlyAdminACL(this.updateNextCoreContract.selector);
nextCoreContract = _nextCoreContract;
emit PlatformUpdated(
bytes32(uint256(PlatformUpdatedFields.FIELD_NEXT_CORE_CONTRACT))
);
}
/**
* @notice Updates reference to Art Blocks Dependency Registry contract.
* @param _artblocksDependencyRegistryAddress Address of new Dependency
* Registry.
*/
function updateArtblocksDependencyRegistryAddress(
address _artblocksDependencyRegistryAddress
) external {
_onlyAdminACL(this.updateArtblocksDependencyRegistryAddress.selector);
_onlyNonZeroAddress(_artblocksDependencyRegistryAddress);
artblocksDependencyRegistryAddress = _artblocksDependencyRegistryAddress;
emit PlatformUpdated(
bytes32(
uint256(
PlatformUpdatedFields
.FIELD_ARTBLOCKS_DEPENDENCY_REGISTRY_ADDRESS
)
)
);
}
/**
* @notice Updates reference to Art Blocks On Chain Generator contract.
* @param _artblocksOnChainGeneratorAddress Address of new on chain generator.
*/
function updateArtblocksOnChainGeneratorAddress(
address _artblocksOnChainGeneratorAddress
) external {
_onlyAdminACL(this.updateArtblocksOnChainGeneratorAddress.selector);
_onlyNonZeroAddress(_artblocksOnChainGeneratorAddress);
artblocksOnChainGeneratorAddress = _artblocksOnChainGeneratorAddress;
emit PlatformUpdated(
bytes32(
uint256(
PlatformUpdatedFields
.FIELD_ARTBLOCKS_ON_CHAIN_GENERATOR_ADDRESS
)
)
);
}
/**
* @notice Updates sales addresses for the platform and render providers to
* the input parameters.
* note: This does not update splitter contracts for all projects on
* this core contract. If updated splitter contracts are desired, they must be
* updated after this update via the `syncProviderSecondaryForProjectToDefaults` function.
* @param _renderProviderPrimarySalesAddress Address of new primary sales
* payment address.
* @param _defaultRenderProviderSecondarySalesAddress Default address of new secondary sales
* payment address.
* @param _platformProviderPrimarySalesAddress Address of new primary sales
* payment address.
* @param _defaultPlatformProviderSecondarySalesAddress Default address of new secondary sales
* payment address.
*/
function updateProviderSalesAddresses(
address payable _renderProviderPrimarySalesAddress,
address payable _defaultRenderProviderSecondarySalesAddress,
address payable _platformProviderPrimarySalesAddress,
address payable _defaultPlatformProviderSecondarySalesAddress
) external {
_onlyAdminACL(this.updateProviderSalesAddresses.selector);
_onlyNonZeroAddress(_renderProviderPrimarySalesAddress);
_onlyNonZeroAddress(_defaultRenderProviderSecondarySalesAddress);
// @dev checks on platform provider addresses performed in _updateProviderSalesAddresses
_updateProviderSalesAddresses(
_renderProviderPrimarySalesAddress,
_defaultRenderProviderSecondarySalesAddress,
_platformProviderPrimarySalesAddress,
_defaultPlatformProviderSecondarySalesAddress
);
}
/**
* @notice Updates the render and platform provider primary sales revenue percentage to
* the provided inputs.
* If contract is configured to have a null platform provider, the platform provider
* primary sales percentage must be set to zero.
* @param renderProviderPrimarySalesPercentage_ New primary sales revenue % for the render provider
* @param platformProviderPrimarySalesPercentage_ New primary sales revenue % for the platform provider
* percentage.
*/
function updateProviderPrimarySalesPercentages(
uint256 renderProviderPrimarySalesPercentage_,
uint256 platformProviderPrimarySalesPercentage_
) external {
_onlyAdminACL(this.updateProviderPrimarySalesPercentages.selector);
// require no platform provider payment if null platform provider
if (
nullPlatformProvider && platformProviderPrimarySalesPercentage_ != 0
) {
revert GenArt721Error(ErrorCodes.OnlyNullPlatformProvider);
}
// Validate that the sum of the proposed %s, does not exceed 100%.
if (
(renderProviderPrimarySalesPercentage_ +
platformProviderPrimarySalesPercentage_) > ONE_HUNDRED
) {
revert GenArt721Error(ErrorCodes.OverMaxSumOfPercentages);
}
// Casting to `uint8` here is safe due check above, which does not allow
// overflow as of solidity version ^0.8.0.
_renderProviderPrimarySalesPercentage = uint8(
renderProviderPrimarySalesPercentage_
);
_platformProviderPrimarySalesPercentage = uint8(
platformProviderPrimarySalesPercentage_
);
emit PlatformUpdated(
bytes32(
uint256(
PlatformUpdatedFields
.FIELD_PROVIDER_PRIMARY_SALES_PERCENTAGES
)
)
);
}
/**
* @notice Updates default render and platform provider secondary sales royalty
* Basis Points to the provided inputs.
* If contract is configured to have a null platform provider, the platform provider
* secondary sales BPS must be set to zero.
* note: This does not update splitter contracts for all projects on
* this core contract. If updated splitter contracts are desired, they must be
* updated after this update via the `syncProviderSecondaryForProjectToDefaults` function.
* @param _defaultRenderProviderSecondarySalesBPS New default secondary sales royalty Basis
* points.
* @param _defaultPlatformProviderSecondarySalesBPS New default secondary sales royalty Basis
* points.
* @dev Due to secondary royalties being ultimately enforced via social
* consensus, no hard upper limit is imposed on the BPS value, other than
* <= 100% royalty, which would not make mathematical sense. Realistically,
* changing this value is expected to either never occur, or be a rare
* occurrence.
*/
function updateProviderDefaultSecondarySalesBPS(
uint256 _defaultRenderProviderSecondarySalesBPS,
uint256 _defaultPlatformProviderSecondarySalesBPS
) external {
_onlyAdminACL(this.updateProviderDefaultSecondarySalesBPS.selector);
// require no platform provider payment if null platform provider
if (
nullPlatformProvider &&
_defaultPlatformProviderSecondarySalesBPS != 0
) {
revert GenArt721Error(ErrorCodes.OnlyNullPlatformProvider);
}
// Validate that the sum of the proposed provider BPS, does not exceed 10_000 BPS.
if (
_defaultRenderProviderSecondarySalesBPS +
_defaultPlatformProviderSecondarySalesBPS >
MAX_PROVIDER_SECONDARY_SALES_BPS
) {
revert GenArt721Error(ErrorCodes.OverMaxSumOfBPS);
}
defaultRenderProviderSecondarySalesBPS = _defaultRenderProviderSecondarySalesBPS;
defaultPlatformProviderSecondarySalesBPS = _defaultPlatformProviderSecondarySalesBPS;
emit PlatformUpdated(
bytes32(
uint256(
PlatformUpdatedFields.FIELD_PROVIDER_SECONDARY_SALES_BPS
)
)
);
}
/**
* @notice Updates minter to `_address`.
* @param _address Address of new minter.
*/
function updateMinterContract(address _address) external {
_onlyAdminACL(this.updateMinterContract.selector);
_onlyNonZeroAddress(_address);
_updateMinterContract(_address);
}
/**
* @notice Updates randomizer to `_randomizerAddress`.
* @param _randomizerAddress Address of new randomizer.
*/
function updateRandomizerAddress(address _randomizerAddress) external {
_onlyAdminACL(this.updateRandomizerAddress.selector);
_onlyNonZeroAddress(_randomizerAddress);
_updateRandomizerAddress(_randomizerAddress);
}
/**
* @notice Updates split provider address to `_splitProviderAddress`.
* Reverts if `_splitProviderAddress` is zero address.
* @param _splitProviderAddress New split provider address.
*/
function updateSplitProvider(address _splitProviderAddress) external {
_onlyAdminACL(this.updateSplitProvider.selector);
_updateSplitProvider(_splitProviderAddress);
}
/**
* @notice Updates bytecode storage reader contract to `_bytecodeStorageReaderContract`.
* Reverts if `_bytecodeStorageReaderContract` is zero address.
* Updating the active Bytecode Storage Reader contract may affect the ability to read
* data related to existing projects. Care should be taken to ensure that the new
* contract is compatible with the existing project data.
* @param _bytecodeStorageReaderContract New bytecode storage reader contract address.
*/
function updateBytecodeStorageReaderContract(
address _bytecodeStorageReaderContract
) external {
_onlyAdminACL(this.updateBytecodeStorageReaderContract.selector);
_onlyNonZeroAddress(_bytecodeStorageReaderContract);
_updateBytecodeStorageReaderContract(_bytecodeStorageReaderContract);
}
/**
* @notice Toggles project `_projectId` as active/inactive.
* @param _projectId Project ID to be toggled.
*/
function toggleProjectIsActive(uint256 _projectId) external {
if (allowArtistProjectActivation) {
_onlyArtistOrAdminACL(
_projectId,
this.toggleProjectIsActive.selector
);
} else {
_onlyAdminACL(this.toggleProjectIsActive.selector);
}
_onlyValidProjectId(_projectId);
projects[_projectId].active = !projects[_projectId].active;
emit ProjectUpdated(
_projectId,
bytes32(uint256(ProjectUpdatedFields.FIELD_PROJECT_ACTIVE))
);
}
/**
* @notice Artist proposes updated set of artist address, additional payee
* addresses, and percentage splits for project `_projectId`. Addresses and
* percentages do not have to all be changed, but they must all be defined
* as a complete set.
* Note that if the artist is only proposing a change to the payee percentage
* splits, without modifying the payee addresses, the proposal will be
* automatically approved and the new splits will become active immediately.
* Automatic approval will also be granted if the artist is only removing
* additional payee addresses, without adding any new ones.
* Also note that if `autoApproveArtistSplitProposals` is true, proposals
* will always be auto-approved, regardless of what is being changed.
* Also note that if the artist is proposing sending funds to the zero
* address, this function will revert and the proposal will not be created.
* @param _projectId Project ID.
* @param _artistAddress Artist address that controls the project, and may
* receive payments.
* @param _additionalPayeePrimarySales Address that may receive a
* percentage split of the artist's primary sales revenue.
* @param _additionalPayeePrimarySalesPercentage Percent of artist's
* portion of primary sale revenue that will be split to address
* `_additionalPayeePrimarySales`.
* @param _additionalPayeeSecondarySales Address that may receive a percentage
* split of the secondary sales royalties.
* @param _additionalPayeeSecondarySalesPercentage Percent of artist's portion
* of secondary sale royalties that will be split to address
* `_additionalPayeeSecondarySales`.
* @dev `_artistAddress` must be a valid address (non-zero-address), but it
* is intentionally allowable for `_additionalPayee{Primary,Secondaary}Sales`
* and their associated percentages to be zero'd out by the controlling artist.
*/
function proposeArtistPaymentAddressesAndSplits(
uint256 _projectId,
address payable _artistAddress,
address payable _additionalPayeePrimarySales,
uint256 _additionalPayeePrimarySalesPercentage,
address payable _additionalPayeeSecondarySales,
uint256 _additionalPayeeSecondarySalesPercentage
) external {
_onlyValidProjectId(_projectId);
_onlyArtist(_projectId);
_onlyNonZeroAddress(_artistAddress);
ProjectFinance storage projectFinance = _projectIdToFinancials[
_projectId
];
// checks
if (
_additionalPayeePrimarySalesPercentage > ONE_HUNDRED ||
_additionalPayeeSecondarySalesPercentage > ONE_HUNDRED
) {
revert GenArt721Error(ErrorCodes.MaxOf100Percent);
}
if (
_additionalPayeePrimarySalesPercentage > 0 &&
_additionalPayeePrimarySales == address(0)
) {
revert GenArt721Error(ErrorCodes.PrimaryPayeeIsZeroAddress);
}
if (
_additionalPayeeSecondarySalesPercentage > 0 &&
_additionalPayeeSecondarySales == address(0)
) {
revert GenArt721Error(ErrorCodes.SecondaryPayeeIsZeroAddress);
}
// effects
// emit event for off-chain indexing
// note: always emit a proposal event, even in the pathway of
// automatic approval, to simplify indexing expectations
emit ProposedArtistAddressesAndSplits(
_projectId,
_artistAddress,
_additionalPayeePrimarySales,
_additionalPayeePrimarySalesPercentage,
_additionalPayeeSecondarySales,
_additionalPayeeSecondarySalesPercentage
);
// automatically accept if no proposed addresses modifications, or if
// the proposal only removes payee addresses, or if contract is set to
// always auto-approve.
// store proposal hash on-chain, only if not automatic accept
bool automaticAccept = autoApproveArtistSplitProposals;
if (!automaticAccept) {
// block scope to avoid stack too deep error
bool artistUnchanged = _artistAddress ==
projectFinance.artistAddress;
bool additionalPrimaryUnchangedOrRemoved = (_additionalPayeePrimarySales ==
projectFinance.additionalPayeePrimarySales) ||
(_additionalPayeePrimarySales == address(0));
bool additionalSecondaryUnchangedOrRemoved = (_additionalPayeeSecondarySales ==
projectFinance.additionalPayeeSecondarySales) ||
(_additionalPayeeSecondarySales == address(0));
automaticAccept =
artistUnchanged &&
additionalPrimaryUnchangedOrRemoved &&
additionalSecondaryUnchangedOrRemoved;
}
if (automaticAccept) {
// clear any previously proposed values
proposedArtistAddressesAndSplitsHash[_projectId] = bytes32(0);
// update storage
// artist address can change during automatic accept if
// autoApproveArtistSplitProposals is true
projectFinance.artistAddress = _artistAddress;
projectFinance
.additionalPayeePrimarySales = _additionalPayeePrimarySales;
// safe to cast as uint8 as max is 100%, max uint8 is 255
projectFinance.additionalPayeePrimarySalesPercentage = uint8(
_additionalPayeePrimarySalesPercentage
);
projectFinance
.additionalPayeeSecondarySales = _additionalPayeeSecondarySales;
// safe to cast as uint8 as max is 100%, max uint8 is 255
projectFinance.additionalPayeeSecondarySalesPercentage = uint8(
_additionalPayeeSecondarySalesPercentage
);
// assign project's splitter
// @dev only call after all previous storage updates
_assignSplitter(_projectId);
// emit event for off-chain indexing
emit AcceptedArtistAddressesAndSplits(_projectId);
} else {
proposedArtistAddressesAndSplitsHash[_projectId] = keccak256(
abi.encode(
_artistAddress,
_additionalPayeePrimarySales,
_additionalPayeePrimarySalesPercentage,
_additionalPayeeSecondarySales,
_additionalPayeeSecondarySalesPercentage
)
);
}
}
/**
* @notice Admin accepts a proposed set of updated artist address,
* additional payee addresses, and percentage splits for project
* `_projectId`. Addresses and percentages do not have to all be changed,
* but they must all be defined as a complete set.
* @param _projectId Project ID.
* @param _artistAddress Artist address that controls the project, and may
* receive payments.
* @param _additionalPayeePrimarySales Address that may receive a
* percentage split of the artist's primary sales revenue.
* @param _additionalPayeePrimarySalesPercentage Percent of artist's
* portion of primary sale revenue that will be split to address
* `_additionalPayeePrimarySales`.
* @param _additionalPayeeSecondarySales Address that may receive a percentage
* split of the secondary sales royalties.
* @param _additionalPayeeSecondarySalesPercentage Percent of artist's portion
* of secondary sale royalties that will be split to address
* `_additionalPayeeSecondarySales`.
* @dev this must be called by the Admin ACL contract, and must only accept
* the most recent proposed values for a given project (validated on-chain
* by comparing the hash of the proposed and accepted values).
* @dev `_artistAddress` must be a valid address (non-zero-address), but it
* is intentionally allowable for `_additionalPayee{Primary,Secondaary}Sales`
* and their associated percentages to be zero'd out by the controlling artist.
*/
function adminAcceptArtistAddressesAndSplits(
uint256 _projectId,
address payable _artistAddress,
address payable _additionalPayeePrimarySales,
uint256 _additionalPayeePrimarySalesPercentage,
address payable _additionalPayeeSecondarySales,
uint256 _additionalPayeeSecondarySalesPercentage
) external {
_onlyValidProjectId(_projectId);
_onlyAdminACLOrRenouncedArtist(
_projectId,
this.adminAcceptArtistAddressesAndSplits.selector
);
_onlyNonZeroAddress(_artistAddress);
// checks
if (
proposedArtistAddressesAndSplitsHash[_projectId] !=
keccak256(
abi.encode(
_artistAddress,
_additionalPayeePrimarySales,
_additionalPayeePrimarySalesPercentage,
_additionalPayeeSecondarySales,
_additionalPayeeSecondarySalesPercentage
)
)
) {
revert GenArt721Error(ErrorCodes.MustMatchArtistProposal);
}
// effects
ProjectFinance storage projectFinance = _projectIdToFinancials[
_projectId
];
projectFinance.artistAddress = _artistAddress;
projectFinance
.additionalPayeePrimarySales = _additionalPayeePrimarySales;
projectFinance.additionalPayeePrimarySalesPercentage = uint8(
_additionalPayeePrimarySalesPercentage
);
projectFinance
.additionalPayeeSecondarySales = _additionalPayeeSecondarySales;
projectFinance.additionalPayeeSecondarySalesPercentage = uint8(
_additionalPayeeSecondarySalesPercentage
);
// clear proposed values
proposedArtistAddressesAndSplitsHash[_projectId] = bytes32(0);
// assign project's splitter
// @dev only call after all previous storage updates
_assignSplitter(_projectId);
// emit event for off-chain indexing
emit AcceptedArtistAddressesAndSplits(_projectId);
}
/**
* @notice Updates artist of project `_projectId` to `_artistAddress`.
* This is to only be used in the event that the artist address is
* compromised or sanctioned.
* @param _projectId Project ID.
* @param _artistAddress New artist address.
*/
function updateProjectArtistAddress(
uint256 _projectId,
address payable _artistAddress
) external {
_onlyValidProjectId(_projectId);
_onlyAdminACLOrRenouncedArtist(
_projectId,
this.updateProjectArtistAddress.selector
);
_onlyNonZeroAddress(_artistAddress);
_projectIdToFinancials[_projectId].artistAddress = _artistAddress;
// assign project's splitter
// @dev only call after all previous storage updates
_assignSplitter(_projectId);
emit ProjectUpdated(
_projectId,
bytes32(uint256(ProjectUpdatedFields.FIELD_PROJECT_ARTIST_ADDRESS))
);
}
/**
* @notice Toggles paused state of project `_projectId`.
* @param _projectId Project ID to be toggled.
*/
function toggleProjectIsPaused(uint256 _projectId) external {
_onlyArtist(_projectId);
projects[_projectId].paused = !projects[_projectId].paused;
emit ProjectUpdated(
_projectId,
bytes32(uint256(ProjectUpdatedFields.FIELD_PROJECT_PAUSED))
);
}
/**
* @notice Adds new project `_projectName` by `_artistAddress`.
* @param _projectName Project name.
* @param _artistAddress Artist's address.
* @dev token price now stored on minter
*/
function addProject(
string memory _projectName,
address payable _artistAddress
) external {
_onlyAdminACL(this.addProject.selector);
_onlyNonEmptyString(_projectName);
_onlyNonZeroAddress(_artistAddress);
if (newProjectsForbidden) {
revert GenArt721Error(ErrorCodes.NewProjectsForbidden);
}
uint256 projectId = _nextProjectId;
ProjectFinance storage projectFinance = _projectIdToFinancials[
projectId
];
projectFinance.artistAddress = _artistAddress;
projects[projectId].name = _projectName;
projects[projectId].paused = true;
projects[projectId].maxInvocations = ONE_MILLION_UINT24;
projects[projectId].projectBaseURI = defaultBaseURI;
// assign default artist royalty to artist
projectFinance
.secondaryMarketRoyaltyPercentage = _DEFAULT_ARTIST_SECONDARY_ROYALTY_PERCENTAGE;
// copy default platform and render provider royalties to ProjectFinance
projectFinance
.platformProviderSecondarySalesAddress = defaultPlatformProviderSecondarySalesAddress;
projectFinance.platformProviderSecondarySalesBPS = uint16(
defaultPlatformProviderSecondarySalesBPS
);
projectFinance
.renderProviderSecondarySalesAddress = defaultRenderProviderSecondarySalesAddress;
projectFinance.renderProviderSecondarySalesBPS = uint16(
defaultRenderProviderSecondarySalesBPS
);
_nextProjectId = uint248(projectId) + 1;
// @dev emit initial project created event before splitter event
emit ProjectUpdated(
projectId,
bytes32(uint256(ProjectUpdatedFields.FIELD_PROJECT_CREATED))
);
// assign project's splitter
// @dev only call after all previous storage updates
_assignSplitter(projectId);
}
/**
* @notice Forever forbids new projects from being added to this contract.
*/
function forbidNewProjects() external {
_onlyAdminACL(this.forbidNewProjects.selector);
if (newProjectsForbidden) {
revert GenArt721Error(ErrorCodes.NewProjectsAlreadyForbidden);
}
_forbidNewProjects();
}
/**
* @notice Updates name of project `_projectId` to be `_projectName`.
* @param _projectId Project ID.
* @param _projectName New project name.
*/
function updateProjectName(
uint256 _projectId,
string memory _projectName
) external {
_onlyUnlocked(_projectId);
_onlyArtistOrAdminACL(_projectId, this.updateProjectName.selector);
_onlyNonEmptyString(_projectName);
projects[_projectId].name = _projectName;
emit ProjectUpdated(
_projectId,
bytes32(uint256(ProjectUpdatedFields.FIELD_PROJECT_NAME))
);
}
/**
* @notice Updates artist name for project `_projectId` to be
* `_projectArtistName`.
* @dev allows admin to update after project is locked, due to our
* experiences of artist name changes being requested post-lock.
* @param _projectId Project ID.
* @param _projectArtistName New artist name.
*/
function updateProjectArtistName(
uint256 _projectId,
string memory _projectArtistName
) external {
// if unlocked, only artist may update, if locked, only admin may update
// @dev valid project checked in _projectUnlocked function
if (_projectUnlocked(_projectId)) {
if (
msg.sender != _projectIdToFinancials[_projectId].artistAddress
) {
revert GenArt721Error(ErrorCodes.OnlyArtistOrAdminIfLocked);
}
} else {
if (
!adminACLAllowed(
msg.sender,
address(this),
this.updateProjectArtistName.selector
)
) {
revert GenArt721Error(ErrorCodes.OnlyArtistOrAdminIfLocked);
}
}
_onlyNonEmptyString(_projectArtistName);
projects[_projectId].artist = _projectArtistName;
emit ProjectUpdated(
_projectId,
bytes32(uint256(ProjectUpdatedFields.FIELD_PROJECT_ARTIST_NAME))
);
}
/**
* @notice Updates artist secondary market royalties for project
* `_projectId` to be `_secondaryMarketRoyalty` percent.
* This deploys a new splitter contract if needed.
* This DOES NOT include the secondary market royalty percentages collected
* by the issuing platform; it is only the total percentage of royalties
* that will be split to artist and additionalSecondaryPayee.
* @param _projectId Project ID.
* @param _secondaryMarketRoyalty Percent of secondary sales revenue that will
* be split to artist and additionalSecondaryPayee. This must be less than
* or equal to ARTIST_MAX_SECONDARY_ROYALTY_PERCENTAGE percent.
*/
function updateProjectSecondaryMarketRoyaltyPercentage(
uint256 _projectId,
uint256 _secondaryMarketRoyalty
) external {
_onlyArtist(_projectId);
if (_secondaryMarketRoyalty > ARTIST_MAX_SECONDARY_ROYALTY_PERCENTAGE) {
revert GenArt721Error(ErrorCodes.OverMaxSecondaryRoyaltyPercentage);
}
_projectIdToFinancials[_projectId]
.secondaryMarketRoyaltyPercentage = uint8(_secondaryMarketRoyalty);
// assign project's splitter
// @dev only call after all previous storage updates
_assignSplitter(_projectId);
emit ProjectUpdated(
_projectId,
bytes32(
uint256(
ProjectUpdatedFields
.FIELD_PROJECT_SECONDARY_MARKET_ROYALTY_PERCENTAGE
)
)
);
}
/**
* @notice Updates platform and render provider secondary market royalty addresses
* and BPS to the contract-level default values for project `_projectId`.
* This updates the splitter parameters on the existing splitter for the project.
* Reverts if called by a non-admin address.
* @param _projectId Project ID.
*/
function syncProviderSecondaryForProjectToDefaults(
uint256 _projectId
) external {
_onlyAdminACL(this.syncProviderSecondaryForProjectToDefaults.selector);
_onlyValidProjectId(_projectId);
ProjectFinance storage projectFinance = _projectIdToFinancials[
_projectId
];
// update project finance for project in storage
projectFinance
.platformProviderSecondarySalesAddress = defaultPlatformProviderSecondarySalesAddress;
projectFinance.platformProviderSecondarySalesBPS = uint16(
defaultPlatformProviderSecondarySalesBPS
);
projectFinance
.renderProviderSecondarySalesAddress = defaultRenderProviderSecondarySalesAddress;
projectFinance.renderProviderSecondarySalesBPS = uint16(
defaultRenderProviderSecondarySalesBPS
);
emit ProjectUpdated(
_projectId,
bytes32(
uint256(
ProjectUpdatedFields
.FIELD_PROJECT_PROVIDER_SECONDARY_FINANCIALS
)
)
);
// assign project's splitter
// @dev only call after all previous storage updates
_assignSplitter(_projectId);
}
/**
* @notice Updates description of project `_projectId`.
* Only artist may call when unlocked, only admin may call when locked.
* Note: The BytecodeStorage library is used to store the description to
* reduce initial upload cost, however, even minor edits will require an
* expensive, entirely new bytecode storage contract to be deployed instead
* of relatively cheap updates to already-warm storage slots. This results
* in an increased gas cost for minor edits to the description after the
* initial upload, but an overall decrease in gas cost for projects with
* less than ~3-5 edits (depending on the length of the description).
* @param _projectId Project ID.
* @param _projectDescription New project description.
*/
function updateProjectDescription(
uint256 _projectId,
string memory _projectDescription
) external {
// checks
// if unlocked, only artist may update, if locked, only admin may update
if (_projectUnlocked(_projectId)) {
if (
msg.sender != _projectIdToFinancials[_projectId].artistAddress
) {
revert GenArt721Error(ErrorCodes.OnlyArtistOrAdminIfLocked);
}
} else {
if (
!adminACLAllowed(
msg.sender,
address(this),
this.updateProjectDescription.selector
)
) {
revert GenArt721Error(ErrorCodes.OnlyArtistOrAdminIfLocked);
}
}
// effects
// store description in contract bytecode, replacing reference address from
// the old storage description with the newly created one
projects[_projectId].descriptionAddress = _projectDescription
.writeToBytecode();
emit ProjectUpdated(
_projectId,
bytes32(uint256(ProjectUpdatedFields.FIELD_PROJECT_DESCRIPTION))
);
}
/**
* @notice Updates website of project `_projectId` to be `_projectWebsite`.
* @param _projectId Project ID.
* @param _projectWebsite New project website.
* @dev It is intentionally allowed for this to be set to the empty string.
*/
function updateProjectWebsite(
uint256 _projectId,
string memory _projectWebsite
) external {
_onlyArtist(_projectId);
projects[_projectId].website = _projectWebsite;
emit ProjectUpdated(
_projectId,
bytes32(uint256(ProjectUpdatedFields.FIELD_PROJECT_WEBSITE))
);
}
/**
* @notice Updates license for project `_projectId`.
* @param _projectId Project ID.
* @param _projectLicense New project license.
*/
function updateProjectLicense(
uint256 _projectId,
string memory _projectLicense
) external {
_onlyUnlocked(_projectId);
_onlyArtistOrAdminACL(_projectId, this.updateProjectLicense.selector);
_onlyNonEmptyString(_projectLicense);
projects[_projectId].license = _projectLicense;
emit ProjectUpdated(
_projectId,
bytes32(uint256(ProjectUpdatedFields.FIELD_PROJECT_LICENSE))
);
}
/**
* @notice Updates maximum invocations for project `_projectId` to
* `_maxInvocations`. Maximum invocations may only be decreased by the
* artist, and must be greater than or equal to current invocations.
* New projects are created with maximum invocations of 1 million by
* default.
* @param _projectId Project ID.
* @param _maxInvocations New maximum invocations.
*/
function updateProjectMaxInvocations(
uint256 _projectId,
uint24 _maxInvocations
) external {
_onlyArtist(_projectId);
// CHECKS
Project storage project = projects[_projectId];
uint256 _invocations = project.invocations;
if (_maxInvocations >= project.maxInvocations) {
revert GenArt721Error(ErrorCodes.OnlyMaxInvocationsDecrease);
}
if (_maxInvocations < _invocations) {
revert GenArt721Error(ErrorCodes.OnlyGteInvocations);
}
// EFFECTS
project.maxInvocations = _maxInvocations;
emit ProjectUpdated(
_projectId,
bytes32(uint256(ProjectUpdatedFields.FIELD_PROJECT_MAX_INVOCATIONS))
);
// register completed timestamp if action completed the project
if (_maxInvocations == _invocations) {
_completeProject(_projectId);
}
}
/**
* @notice Adds a script to project `_projectId`.
* @param _projectId Project to be updated.
* @param _script Script to be added. Required to be a non-empty string,
* but no further validation is performed.
*/
function addProjectScript(
uint256 _projectId,
string memory _script
) external {
_onlyUnlocked(_projectId);
_onlyArtistOrAdminACL(_projectId, this.addProjectScript.selector);
_onlyNonEmptyString(_script);
Project storage project = projects[_projectId];
// store script in contract bytecode
project.scriptBytecodeAddresses[project.scriptCount] = _script
.writeToBytecode();
project.scriptCount = project.scriptCount + 1;
emit ProjectUpdated(
_projectId,
bytes32(uint256(ProjectUpdatedFields.FIELD_PROJECT_SCRIPT))
);
}
/**
* @notice Adds a pre-compressed script to project `_projectId`. The script
* should be compressed using `getCompressed`. This function stores the script
* in a compressed format on-chain. For reads, the compressed script is
* decompressed on-chain, ensuring the original text is reconstructed without
* external dependencies.
* @param _projectId Project to be updated.
* @param _compressedScript Pre-compressed script to be added.
* Required to be non-empty, but no further validation is performed.
*/
function addProjectScriptCompressed(
uint256 _projectId,
bytes memory _compressedScript
) external {
_onlyUnlocked(_projectId);
_onlyArtistOrAdminACL(
_projectId,
this.addProjectScriptCompressed.selector
);
_onlyNonEmptyBytes(_compressedScript);
Project storage project = projects[_projectId];
// store compressed script in contract bytecode
project.scriptBytecodeAddresses[project.scriptCount] = _compressedScript
.writeToBytecodeCompressed();
project.scriptCount = project.scriptCount + 1;
emit ProjectUpdated(
_projectId,
bytes32(uint256(ProjectUpdatedFields.FIELD_PROJECT_SCRIPT))
);
}
/**
* @notice Updates script for project `_projectId` at script ID `_scriptId`.
* @param _projectId Project to be updated.
* @param _scriptId Script ID to be updated.
* @param _script The updated script value. Required to be a non-empty
* string, but no further validation is performed.
*/
function updateProjectScript(
uint256 _projectId,
uint256 _scriptId,
string memory _script
) external {
_onlyUnlocked(_projectId);
_onlyArtistOrAdminACL(_projectId, this.updateProjectScript.selector);
_onlyNonEmptyString(_script);
Project storage project = projects[_projectId];
if (_scriptId >= project.scriptCount) {
revert GenArt721Error(ErrorCodes.ScriptIdOutOfRange);
}
// store script in contract bytecode, replacing reference address from
// the old storage contract with the newly created one
project.scriptBytecodeAddresses[_scriptId] = _script.writeToBytecode();
emit ProjectUpdated(
_projectId,
bytes32(uint256(ProjectUpdatedFields.FIELD_PROJECT_SCRIPT))
);
}
/**
* @notice Updates script for project `_projectId` at script ID `_scriptId`
* with a pre-compressed script. The script should be compressed using
* `getCompressed`. This function stores the script in a compressed format
* on-chain. For reads, the compressed script is decompressed on-chain, ensuring
* the original text is reconstructed without external dependencies.
* @param _projectId Project to be updated.
* @param _scriptId Script ID to be updated.
* @param _compressedScript The updated pre-compressed script value.
* Required to be non-empty, but no further validation is performed.
*/
function updateProjectScriptCompressed(
uint256 _projectId,
uint256 _scriptId,
bytes memory _compressedScript
) external {
_onlyUnlocked(_projectId);
_onlyArtistOrAdminACL(
_projectId,
this.updateProjectScriptCompressed.selector
);
_onlyNonEmptyBytes(_compressedScript);
Project storage project = projects[_projectId];
if (_scriptId >= project.scriptCount) {
revert GenArt721Error(ErrorCodes.ScriptIdOutOfRange);
}
// store script in contract bytecode, replacing reference address from
// the old storage contract with the newly created one
project.scriptBytecodeAddresses[_scriptId] = _compressedScript
.writeToBytecodeCompressed();
emit ProjectUpdated(
_projectId,
bytes32(uint256(ProjectUpdatedFields.FIELD_PROJECT_SCRIPT))
);
}
/**
* @notice Removes last script from project `_projectId`.
* @param _projectId Project to be updated.
*/
function removeProjectLastScript(uint256 _projectId) external {
_onlyUnlocked(_projectId);
_onlyArtistOrAdminACL(
_projectId,
this.removeProjectLastScript.selector
);
Project storage project = projects[_projectId];
if (project.scriptCount == 0) {
revert GenArt721Error(ErrorCodes.NoScriptsToRemove);
}
// delete reference to old storage contract address
delete project.scriptBytecodeAddresses[project.scriptCount - 1];
unchecked {
project.scriptCount = project.scriptCount - 1;
}
emit ProjectUpdated(
_projectId,
bytes32(uint256(ProjectUpdatedFields.FIELD_PROJECT_SCRIPT))
);
}
/**
* @notice Updates script type for project `_projectId`.
* @param _projectId Project to be updated.
* @param _scriptTypeAndVersion Script type and version e.g. "[email protected]",
* as bytes32 encoded string.
*/
function updateProjectScriptType(
uint256 _projectId,
bytes32 _scriptTypeAndVersion
) external {
_onlyUnlocked(_projectId);
_onlyArtistOrAdminACL(
_projectId,
this.updateProjectScriptType.selector
);
Project storage project = projects[_projectId];
// require exactly one @ symbol in _scriptTypeAndVersion
if (
!_scriptTypeAndVersion.containsExactCharacterQty(
AT_CHARACTER_CODE,
uint8(1)
)
) {
revert GenArt721Error(ErrorCodes.ScriptTypeAndVersionFormat);
}
project.scriptTypeAndVersion = _scriptTypeAndVersion;
emit ProjectUpdated(
_projectId,
bytes32(uint256(ProjectUpdatedFields.FIELD_PROJECT_SCRIPT_TYPE))
);
}
/**
* @notice Updates project's aspect ratio.
* @param _projectId Project to be updated.
* @param _aspectRatio Aspect ratio to be set. Intended to be string in the
* format of a decimal, e.g. "1" for square, "1.77777778" for 16:9, etc.,
* allowing for a maximum of 10 digits and one (optional) decimal separator.
*/
function updateProjectAspectRatio(
uint256 _projectId,
string memory _aspectRatio
) external {
_onlyUnlocked(_projectId);
_onlyArtistOrAdminACL(
_projectId,
this.updateProjectAspectRatio.selector
);
_onlyNonEmptyString(_aspectRatio);
// Perform more detailed input validation for aspect ratio.
bytes memory aspectRatioBytes = bytes(_aspectRatio);
uint256 bytesLength = aspectRatioBytes.length;
if (bytesLength > 11) {
revert GenArt721Error(ErrorCodes.AspectRatioTooLong);
}
bool hasSeenDecimalSeparator = false;
bool hasSeenNumber = false;
for (uint256 i; i < bytesLength; i++) {
bytes1 character = aspectRatioBytes[i];
// Allow as many #s as desired.
if (character >= 0x30 && character <= 0x39) {
// 9-0
// We need to ensure there is at least 1 `9-0` occurrence.
hasSeenNumber = true;
continue;
}
if (character == 0x2E) {
// .
// Allow no more than 1 `.` occurrence.
if (!hasSeenDecimalSeparator) {
hasSeenDecimalSeparator = true;
continue;
}
}
revert GenArt721Error(ErrorCodes.AspectRatioImproperFormat);
}
if (!hasSeenNumber) {
revert GenArt721Error(ErrorCodes.AspectRatioNoNumbers);
}
projects[_projectId].aspectRatio = _aspectRatio;
emit ProjectUpdated(
_projectId,
bytes32(uint256(ProjectUpdatedFields.FIELD_PROJECT_ASPECT_RATIO))
);
}
/**
* @notice Updates base URI for project `_projectId` to `_newBaseURI`.
* This is the controlling base URI for all tokens in the project. The
* contract-level defaultBaseURI is only used when initializing new
* projects.
* @param _projectId Project to be updated.
* @param _newBaseURI New base URI.
*/
function updateProjectBaseURI(
uint256 _projectId,
string memory _newBaseURI
) external {
_onlyArtist(_projectId);
_onlyNonEmptyString(_newBaseURI);
projects[_projectId].projectBaseURI = _newBaseURI;
emit ProjectUpdated(
_projectId,
bytes32(uint256(ProjectUpdatedFields.FIELD_PROJECT_BASE_URI))
);
}
/**
* @notice Updates default base URI to `_defaultBaseURI`. The
* contract-level defaultBaseURI is only used when initializing new
* projects. Token URIs are determined by their project's `projectBaseURI`.
* @param _defaultBaseURI New default base URI.
*/
function updateDefaultBaseURI(string memory _defaultBaseURI) external {
_onlyAdminACL(this.updateDefaultBaseURI.selector);
_onlyNonEmptyString(_defaultBaseURI);
_updateDefaultBaseURI(_defaultBaseURI);
}
/**
* @notice Next project ID to be created on this contract.
* @return uint256 Next project ID.
*/
function nextProjectId() external view returns (uint256) {
return _nextProjectId;
}
/**
* @notice Returns token hash for token ID `_tokenId`. Returns null if hash
* has not been set.
* @param _tokenId Token ID to be queried.
* @return bytes32 Token hash.
* @dev token hash is the keccak256 hash of the stored hash seed
*/
function tokenIdToHash(uint256 _tokenId) external view returns (bytes32) {
bytes12 _hashSeed = _ownersAndHashSeeds[_tokenId].hashSeed;
if (_hashSeed == 0) {
return 0;
}
return keccak256(abi.encode(_hashSeed));
}
/**
* @notice Returns token hash **seed** for token ID `_tokenId`. Returns
* null if hash seed has not been set. The hash seed id the bytes12 value
* which is hashed to produce the token hash.
* @param _tokenId Token ID to be queried.
* @return bytes12 Token hash seed.
* @dev token hash seed is keccak256 hashed to give the token hash
*/
function tokenIdToHashSeed(
uint256 _tokenId
) external view returns (bytes12) {
return _ownersAndHashSeeds[_tokenId].hashSeed;
}
/**
* @notice View function returning the render provider portion of
* primary sales, in percent.
* @return uint256 The render provider portion of primary sales,
* in percent.
*/
function renderProviderPrimarySalesPercentage()
external
view
returns (uint256)
{
return _renderProviderPrimarySalesPercentage;
}
/**
* @notice View function returning the platform provider portion of
* primary sales, in percent.
* @return uint256 The platform provider portion of primary sales,
* in percent.
*/
function platformProviderPrimarySalesPercentage()
external
view
returns (uint256)
{
return _platformProviderPrimarySalesPercentage;
}
/**
* @notice View function returning Artist's address for project
* `_projectId`.
* @param _projectId Project ID to be queried.
* @return address Artist's address.
*/
function projectIdToArtistAddress(
uint256 _projectId
) external view returns (address payable) {
return _projectIdToFinancials[_projectId].artistAddress;
}
/**
* @notice View function returning Artist's secondary market royalty
* percentage for project `_projectId`.
* This does not include render/platform providers portions of secondary
* market royalties.
* @param _projectId Project ID to be queried.
* @return uint256 Artist's secondary market royalty percentage.
*/
function projectIdToSecondaryMarketRoyaltyPercentage(
uint256 _projectId
) external view returns (uint256) {
return
_projectIdToFinancials[_projectId].secondaryMarketRoyaltyPercentage;
}
/**
* @notice View function returning project financial details for project
* `_projectId`.
* @param _projectId Project ID to be queried.
* @return ProjectFinance Project financial details.
*/
function projectIdToFinancials(
uint256 _projectId
) external view returns (ProjectFinance memory) {
return _projectIdToFinancials[_projectId];
}
/**
* @notice Returns project details for project `_projectId`.
* @param _projectId Project to be queried.
* @return projectName Name of project
* @return artist Artist of project
* @return description Project description
* @return website Project website
* @return license Project license
* @dev this function was named projectDetails prior to V3 core contract.
*/
function projectDetails(
uint256 _projectId
)
external
view
returns (
string memory projectName,
string memory artist,
string memory description,
string memory website,
string memory license
)
{
Project storage project = projects[_projectId];
projectName = project.name;
artist = project.artist;
address projectDescriptionBytecodeAddress = project.descriptionAddress;
if (projectDescriptionBytecodeAddress == address(0)) {
description = "";
} else {
description = _readFromBytecode(projectDescriptionBytecodeAddress);
}
website = project.website;
license = project.license;
}
/**
* @notice Returns project state data for project `_projectId`.
* @param _projectId Project to be queried
* @return invocations Current number of invocations
* @return maxInvocations Maximum allowed invocations
* @return active Boolean representing if project is currently active
* @return paused Boolean representing if project is paused
* @return completedTimestamp zero if project not complete, otherwise
* timestamp of project completion.
* @return locked Boolean representing if project is locked
* @dev price and currency info are located on minter contracts
*/
function projectStateData(
uint256 _projectId
)
external
view
returns (
uint256 invocations,
uint256 maxInvocations,
bool active,
bool paused,
uint256 completedTimestamp,
bool locked
)
{
Project storage project = projects[_projectId];
invocations = project.invocations;
maxInvocations = project.maxInvocations;
active = project.active;
paused = project.paused;
completedTimestamp = project.completedTimestamp;
locked = !_projectUnlocked(_projectId);
}
/**
* @notice Returns script information for project `_projectId`.
* @param _projectId Project to be queried.
* @return scriptTypeAndVersion Project's script type and version
* (e.g. "p5js(atSymbol)1.0.0")
* @return aspectRatio Aspect ratio of project (e.g. "1" for square,
* "1.77777778" for 16:9, etc.)
* @return scriptCount Count of scripts for project
*/
function projectScriptDetails(
uint256 _projectId
)
external
view
override(IGenArt721CoreContractV3_Base, IDependencyRegistryCompatibleV0)
returns (
string memory scriptTypeAndVersion,
string memory aspectRatio,
uint256 scriptCount
)
{
Project storage project = projects[_projectId];
scriptTypeAndVersion = project.scriptTypeAndVersion.toString();
aspectRatio = project.aspectRatio;
scriptCount = project.scriptCount;
}
/**
* @notice Returns address with bytecode containing project script for
* project `_projectId` at script index `_index`.
*/
function projectScriptBytecodeAddressByIndex(
uint256 _projectId,
uint256 _index
) external view returns (address) {
return projects[_projectId].scriptBytecodeAddresses[_index];
}
/**
* @notice Returns the compressed form of a string in bytes using solady LibZip's flz compress algorithm. The bytes output from this function are intended to be used as input to `addProjectScriptCompressed` and `updateProjectScriptCompressed`.
* @param _script Script to be compressed. Required to be a non-empty string, but no further validaton is performed.
* @return bytes compressed bytes
*/
function getCompressed(
string memory _script
) external pure returns (bytes memory) {
_onlyNonEmptyString(_script);
// @dev want a potentially version-specific compression algorithm, so use version-specific library here
return BytecodeStorageReader.getCompressed(_script);
}
/**
* @notice Returns script for project `_projectId` at script index `_index`.
* @param _projectId Project to be queried.
* @param _index Index of script to be queried.
*/
function projectScriptByIndex(
uint256 _projectId,
uint256 _index
) external view returns (string memory) {
Project storage project = projects[_projectId];
// If trying to access an out-of-index script, return the empty string.
if (_index >= project.scriptCount) {
return "";
}
return _readFromBytecode(project.scriptBytecodeAddresses[_index]);
}
/**
* @notice Returns base URI for project `_projectId`.
* @param _projectId Project to be queried.
* @return projectBaseURI Base URI for project
*/
function projectURIInfo(
uint256 _projectId
) external view returns (string memory projectBaseURI) {
projectBaseURI = projects[_projectId].projectBaseURI;
}
/**
* @notice Backwards-compatible (pre-V3) function returning if `_minter` is
* minterContract.
* @param _minter Address to be queried.
* @return bool Boolean representing if `_minter` is minterContract.
*/
function isMintWhitelisted(address _minter) external view returns (bool) {
return (minterContract == _minter);
}
/**
* @notice Gets qty of randomizers in history of all randomizers used by
* this core contract. If a randomizer is switched away from then back to,
* it will show up in the history twice.
* @return randomizerHistoryCount Count of randomizers in history
*/
function numHistoricalRandomizers() external view returns (uint256) {
return _historicalRandomizerAddresses.length;
}
/**
* @notice Gets address of randomizer at index `_index` in history of all
* randomizers used by this core contract. Index is zero-based.
* @param _index Historical index of randomizer to be queried.
* @return randomizerAddress Address of randomizer at index `_index`.
* @dev If a randomizer is switched away from and then switched back to, it
* will show up in the history twice.
*/
function getHistoricalRandomizerAt(
uint256 _index
) external view returns (address) {
if (_index >= _historicalRandomizerAddresses.length) {
revert GenArt721Error(ErrorCodes.IndexOutOfBounds);
}
return _historicalRandomizerAddresses[_index];
}
/**
* @notice Gets ERC-2981 royalty information for token with ID `_tokenId`
* and sale price `_salePrice`.
* @param _tokenId Token ID to be queried for royalty information
* @param _salePrice the sale price of the NFT asset specified by _tokenId
* @return receiver address that should be sent the royalty payment
* @return royaltyAmount the royalty payment amount for `_salePrice
* @dev reverts if invalid _tokenId
*/
function royaltyInfo(
uint256 _tokenId,
uint256 _salePrice
) external view returns (address receiver, uint256 royaltyAmount) {
_onlyValidTokenId(_tokenId);
// populate receiver with project's royalty splitter
// @dev royalty splitter created upon project creation, so will always exist
// for valid token ID
uint256 projectId = tokenIdToProjectId(_tokenId);
ProjectFinance storage projectFinance = _projectIdToFinancials[
projectId
];
receiver = projectFinance.royaltySplitter;
// populate royaltyAmount with calculated royalty amount
// @dev important to cast to uint256 before multiplying to avoid overflow
uint256 totalRoyaltyBPS = (100 *
uint256(projectFinance.secondaryMarketRoyaltyPercentage)) +
projectFinance.platformProviderSecondarySalesBPS +
projectFinance.renderProviderSecondarySalesBPS;
// @dev totalRoyaltyBPS guaranteed to be <= 10,000,
if (totalRoyaltyBPS > 10_000) {
revert GenArt721Error(ErrorCodes.OverMaxSumOfBPS);
}
// @dev overflow automatically checked in solidity 0.8
// @dev totalRoyaltyBPS guaranteed to be <= 10_000,
// so overflow only possible with unreasonably high _salePrice values near uint256 max
royaltyAmount = (_salePrice * totalRoyaltyBPS) / 10_000;
}
/**
* @notice View function that returns appropriate revenue splits between
* different render provider, platform provider, Artist, and Artist's
* additional primary sales payee given a sale price of `_price` on
* project `_projectId`.
* This always returns four revenue amounts and four addresses, but if a
* revenue is zero for either Artist or additional payee, the corresponding
* address returned will also be null (for gas optimization).
* Does not account for refund if user overpays for a token (minter should
* handle a refund of the difference, if appropriate).
* Some minters may have alternative methods of splitting payments, in
* which case they should implement their own payment splitting logic.
* @param _projectId Project ID to be queried.
* @param _price Sale price of token.
* @return renderProviderRevenue_ amount of revenue to be sent to the
* render provider
* @return renderProviderAddress_ address to send render provider revenue to
* @return platformProviderRevenue_ amount of revenue to be sent to the
* platform provider
* @return platformProviderAddress_ address to send platform provider revenue to
* @return artistRevenue_ amount of revenue to be sent to Artist
* @return artistAddress_ address to send Artist revenue to. Will be null
* if no revenue is due to artist (gas optimization).
* @return additionalPayeePrimaryRevenue_ amount of revenue to be sent to
* additional payee for primary sales
* @return additionalPayeePrimaryAddress_ address to send Artist's
* additional payee for primary sales revenue to. Will be null if no
* revenue is due to additional payee for primary sales (gas optimization).
* @dev this always returns four addresses and four revenues, but if the
* revenue is zero, the corresponding address will be address(0). It is up
* to the contract performing the revenue split to handle this
* appropriately.
*/
function getPrimaryRevenueSplits(
uint256 _projectId,
uint256 _price
)
external
view
returns (
uint256 renderProviderRevenue_,
address payable renderProviderAddress_,
uint256 platformProviderRevenue_,
address payable platformProviderAddress_,
uint256 artistRevenue_,
address payable artistAddress_,
uint256 additionalPayeePrimaryRevenue_,
address payable additionalPayeePrimaryAddress_
)
{
ProjectFinance storage projectFinance = _projectIdToFinancials[
_projectId
];
// calculate revenues – this is a three-way split between the
// render provider, the platform provider, and the artist, and
// is safe to perform this given that in the case of loss of
// precision Solidity will round down.
uint256 projectFunds = _price;
renderProviderRevenue_ =
(_price * uint256(_renderProviderPrimarySalesPercentage)) /
ONE_HUNDRED;
// renderProviderRevenue_ percentage is always <=100, so guaranteed to never underflow
projectFunds -= renderProviderRevenue_;
platformProviderRevenue_ =
(_price * uint256(_platformProviderPrimarySalesPercentage)) /
ONE_HUNDRED;
// platformProviderRevenue_ percentage is always <=100, so guaranteed to never underflow
projectFunds -= platformProviderRevenue_;
additionalPayeePrimaryRevenue_ =
(projectFunds *
projectFinance.additionalPayeePrimarySalesPercentage) /
ONE_HUNDRED;
// projectIdToAdditionalPayeePrimarySalesPercentage is always
// <=100, so guaranteed to never underflow
artistRevenue_ = projectFunds - additionalPayeePrimaryRevenue_;
// set addresses from storage
renderProviderAddress_ = renderProviderPrimarySalesAddress;
platformProviderAddress_ = platformProviderPrimarySalesAddress;
if (artistRevenue_ > 0) {
artistAddress_ = projectFinance.artistAddress;
}
if (additionalPayeePrimaryRevenue_ > 0) {
additionalPayeePrimaryAddress_ = projectFinance
.additionalPayeePrimarySales;
}
}
/**
* @notice Returns external asset dependency for project `_projectId` at index `_index`.
* If the dependencyType is ONCHAIN, the `data` field will contain the read BytecodeStorage data and `cid`
* will be an empty string. Conversly, for any other dependencyType, the `data` field will be an empty string
* and the `bytecodeAddress` will point to the zero address.
* @dev If an ONCHAIN dependency is a web3call, the `data` field will be returned per the BytecodeStorageReader
* contract's active spec, which at the time of this writing is the special string "#web3call_contract#".
* If the dependencyType is ART_BLOCKS_DEPENDENCY_REGISTRY, the `cid` field will contain the string
* representation of the dependencyNameAndVersion bytes32 value stored in the dependency registry (
* at public address `artblocksDependencyRegistryAddress`)
* @param _projectId Project to be queried.
* @param _index Index of external asset dependency to be queried.
* @return ExternalAssetDependencyWithData External asset dependency for project `_projectId` at index `_index`.
*/
function projectExternalAssetDependencyByIndex(
uint256 _projectId,
uint256 _index
) external view returns (ExternalAssetDependencyWithData memory) {
return
V3FlexLib.projectExternalAssetDependencyByIndex({
_projectId: _projectId,
_index: _index,
_bytecodeStorageReaderContract: bytecodeStorageReaderContract
});
}
/**
* @notice Returns external asset dependency count for project `_projectId` at index `_index`.
* @param _projectId Project to be queried.
* @return uint256 Count of external asset dependencies for project `_projectId`.
*/
function projectExternalAssetDependencyCount(
uint256 _projectId
) external view returns (uint256) {
return
V3FlexLib.projectExternalAssetDependencyCount({
_projectId: _projectId
});
}
/**
* @notice Returns the preferred IPFS gateway for the platform.
* @return string Preferred IPFS gateway for the platform.
*/
function preferredIPFSGateway() external view returns (string memory) {
return V3FlexLib.preferredIPFSGateway();
}
/**
* @notice Returns the preferred Arweave gateway for the platform.
* @return string Preferred Arweave gateway for the platform.
*/
function preferredArweaveGateway() external view returns (string memory) {
return V3FlexLib.preferredArweaveGateway();
}
/**
* @notice Backwards-compatible (pre-V3) getter returning contract admin
* @return address Address of contract admin (same as owner)
*/
function admin() external view returns (address) {
return owner();
}
/**
* @notice Gets the project ID for a given `_tokenId`.
* @param _tokenId Token ID to be queried.
* @return _projectId Project ID for given `_tokenId`.
*/
function tokenIdToProjectId(
uint256 _tokenId
) public pure returns (uint256 _projectId) {
return _tokenId / ONE_MILLION;
}
/**
* @notice Convenience function that returns whether `_sender` is allowed
* to call function with selector `_selector` on contract `_contract`, as
* determined by this contract's current Admin ACL contract. Expected use
* cases include minter contracts checking if caller is allowed to call
* admin-gated functions on minter contracts.
* @param _sender Address of the sender calling function with selector
* `_selector` on contract `_contract`.
* @param _contract Address of the contract being called by `_sender`.
* @param _selector Function selector of the function being called by
* `_sender`.
* @return bool Whether `_sender` is allowed to call function with selector
* `_selector` on contract `_contract`.
* @dev assumes the Admin ACL contract is the owner of this contract, which
* is expected to always be true.
* @dev adminACLContract is expected to either be null address (if owner
* has renounced ownership), or conform to IAdminACLV0 interface. Check for
* null address first to avoid revert when admin has renounced ownership.
*/
function adminACLAllowed(
address _sender,
address _contract,
bytes4 _selector
) public returns (bool) {
return
owner() != address(0) &&
adminACLContract.allowed(_sender, _contract, _selector);
}
/**
* @notice Returns contract owner. Set to deployer's address by default on
* contract deployment.
* @return address Address of contract owner.
* @dev ref: https://docs.openzeppelin.com/contracts/4.x/api/access#Ownable
* @dev owner role was called `admin` prior to V3 core contract
*/
function owner()
public
view
override(Ownable, IGenArt721CoreContractV3_Base)
returns (address)
{
return Ownable.owner();
}
/**
* @notice Gets token URI for token ID `_tokenId`.
* @param _tokenId Token ID to be queried.
* @return string URI of token ID `_tokenId`.
* @dev token URIs are the concatenation of the project base URI and the
* token ID.
*/
function tokenURI(
uint256 _tokenId
) public view override returns (string memory) {
_onlyValidTokenId(_tokenId);
string memory _projectBaseURI = projects[tokenIdToProjectId(_tokenId)]
.projectBaseURI;
return string.concat(_projectBaseURI, _tokenId.toString());
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(
bytes4 interfaceId
)
public
view
virtual
override(ERC721_PackedHashSeedV1, IERC165)
returns (bool)
{
return
interfaceId == _INTERFACE_ID_ERC2981 ||
super.supportsInterface(interfaceId);
}
/**
* @notice Forbids new projects from being created
* @dev only performs operation and emits event if contract is not already
* forbidding new projects.
*/
function _forbidNewProjects() internal {
if (!newProjectsForbidden) {
newProjectsForbidden = true;
emit PlatformUpdated(
bytes32(
uint256(PlatformUpdatedFields.FIELD_NEW_PROJECTS_FORBIDDEN)
)
);
}
}
/**
* @notice Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
* @param newOwner New owner.
* @dev owner role was called `admin` prior to V3 core contract.
* @dev Overrides and wraps OpenZeppelin's _transferOwnership function to
* also update adminACLContract for improved introspection.
*/
function _transferOwnership(address newOwner) internal override {
Ownable._transferOwnership(newOwner);
adminACLContract = IAdminACLV0(newOwner);
}
/**
* @notice Updates sales addresses for the platform and render providers to
* the input parameters.
* Reverts if invalid platform provider addresses are provided given the
* contract's immutably configured nullPlatformProvider state.
* Does not check render provider addresses in any way.
* @param _renderProviderPrimarySalesAddress Address of new primary sales
* payment address.
* @param _defaultRenderProviderSecondarySalesAddress Address of new secondary sales
* payment address.
* @param _platformProviderPrimarySalesAddress Address of new primary sales
* payment address.
* @param _defaultPlatformProviderSecondarySalesAddress Address of new secondary sales
* payment address.
*/
function _updateProviderSalesAddresses(
address _renderProviderPrimarySalesAddress,
address _defaultRenderProviderSecondarySalesAddress,
address _platformProviderPrimarySalesAddress,
address _defaultPlatformProviderSecondarySalesAddress
) internal {
if (nullPlatformProvider) {
// require null platform provider address
if (
_platformProviderPrimarySalesAddress != address(0) ||
_defaultPlatformProviderSecondarySalesAddress != address(0)
) {
revert GenArt721Error(ErrorCodes.OnlyNullPlatformProvider);
}
} else {
_onlyNonZeroAddress(_platformProviderPrimarySalesAddress);
_onlyNonZeroAddress(_defaultPlatformProviderSecondarySalesAddress);
}
platformProviderPrimarySalesAddress = payable(
_platformProviderPrimarySalesAddress
);
defaultPlatformProviderSecondarySalesAddress = payable(
_defaultPlatformProviderSecondarySalesAddress
);
renderProviderPrimarySalesAddress = payable(
_renderProviderPrimarySalesAddress
);
defaultRenderProviderSecondarySalesAddress = payable(
_defaultRenderProviderSecondarySalesAddress
);
emit PlatformUpdated(
bytes32(
uint256(PlatformUpdatedFields.FIELD_PROVIDER_SALES_ADDRESSES)
)
);
}
/**
* @notice Updates minter address to `_minterAddress`.
* @param _minterAddress New minter address.
* @dev Note that this method does not check that the input address is
* not `address(0)`, as it is expected that callers of this method should
* perform input validation where applicable.
*/
function _updateMinterContract(address _minterAddress) internal {
minterContract = _minterAddress;
emit MinterUpdated(_minterAddress);
}
/**
* @notice Updates randomizer address to `_randomizerAddress`.
* @param _randomizerAddress New randomizer address.
* @dev Note that this method does not check that the input address is
* not `address(0)`, as it is expected that callers of this method should
* perform input validation where applicable.
*/
function _updateRandomizerAddress(address _randomizerAddress) internal {
randomizerContract = IRandomizer_V3CoreBase(_randomizerAddress);
// populate historical randomizer array
_historicalRandomizerAddresses.push(_randomizerAddress);
emit PlatformUpdated(
bytes32(uint256(PlatformUpdatedFields.FIELD_RANDOMIZER_ADDRESS))
);
}
/**
* @notice Updates split provider address to `_splitProviderAddress`.
* Reverts if `_splitProviderAddress` is the zero address.
* @param _splitProviderAddress New split provider address.
* @dev Note that this method does not check that the input address is
* not `address(0)`, as it is expected that callers of this method should
* perform input validation where applicable.
*/
function _updateSplitProvider(address _splitProviderAddress) internal {
// require non-zero split provider address
_onlyNonZeroAddress(_splitProviderAddress);
splitProvider = ISplitProviderV0(_splitProviderAddress);
emit PlatformUpdated(
bytes32(uint256(PlatformUpdatedFields.FIELD_SPLIT_PROVIDER))
);
}
/**
* @notice Update the bytecode storage reader contract address, and emit corresponding event.
* @param _bytecodeStorageReaderContract New bytecode storage reader contract address.
*/
function _updateBytecodeStorageReaderContract(
address _bytecodeStorageReaderContract
) internal {
bytecodeStorageReaderContract = IBytecodeStorageReader_Base(
_bytecodeStorageReaderContract
);
emit PlatformUpdated(
bytes32(
uint256(PlatformUpdatedFields.FIELD_BYTECODE_STORAGE_READER)
)
);
}
/**
* @notice internal function to update a splitter contract for a project,
* based on the project's financials in this contract's storage.
* @dev Warning: this function uses storage reads to get the project's
* financials, so ensure storage has been updated before calling this
* @dev This function includes a trusted interaction that is entrusted to
* not reenter this contract.
* @param projectId Project ID to be updated.
*/
function _assignSplitter(uint256 projectId) internal {
ProjectFinance storage projectFinance = _projectIdToFinancials[
projectId
];
// assign project's royalty splitter
// @dev loads values from storage, so need to ensure storage has been updated
address royaltySplitter = splitProvider.getOrCreateSplitter(
ISplitProviderV0.SplitInputs({
platformProviderSecondarySalesAddress: projectFinance
.platformProviderSecondarySalesAddress,
platformProviderSecondarySalesBPS: projectFinance
.platformProviderSecondarySalesBPS,
renderProviderSecondarySalesAddress: projectFinance
.renderProviderSecondarySalesAddress,
renderProviderSecondarySalesBPS: projectFinance
.renderProviderSecondarySalesBPS,
artistTotalRoyaltyPercentage: projectFinance
.secondaryMarketRoyaltyPercentage,
artist: projectFinance.artistAddress,
additionalPayee: projectFinance.additionalPayeeSecondarySales,
additionalPayeePercentage: projectFinance
.additionalPayeeSecondarySalesPercentage
})
);
projectFinance.royaltySplitter = royaltySplitter;
emit ProjectRoyaltySplitterUpdated({
projectId: projectId,
royaltySplitter: royaltySplitter
});
}
/**
* @notice Updates default base URI to `_defaultBaseURI`.
* When new projects are added, their `projectBaseURI` is automatically
* initialized to `_defaultBaseURI`.
* @param _defaultBaseURI New default base URI.
* @dev Note that this method does not check that the input string is not
* the empty string, as it is expected that callers of this method should
* perform input validation where applicable.
*/
function _updateDefaultBaseURI(string memory _defaultBaseURI) internal {
defaultBaseURI = _defaultBaseURI;
emit PlatformUpdated(
bytes32(uint256(PlatformUpdatedFields.FIELD_DEFAULT_BASE_URI))
);
}
/**
* @notice Internal function to complete a project.
* @param _projectId Project ID to be completed.
*/
function _completeProject(uint256 _projectId) internal {
projects[_projectId].completedTimestamp = uint64(block.timestamp);
emit ProjectUpdated(
_projectId,
bytes32(uint256(ProjectUpdatedFields.FIELD_PROJECT_COMPLETED))
);
}
/**
* @notice Initializes the contract with the provided `engineConfiguration`.
* This function should be called atomically, immediately after deployment.
* Only callable once. Validation on `engineConfiguration` is performed by caller.
* @param engineConfiguration EngineConfiguration to configure the contract with.
* note: parameter `engineConfiguration.newSuperAdminAddress` is not used or operated on in this contract.
* @param adminACLContract_ Address of admin access control contract, to be
* set as contract owner.
* @param defaultBaseURIHost Base URI prefix to initialize default base URI with.
* @param bytecodeStorageReaderContract_ Address of bytecode storage reader contract.
*/
function _initialize(
EngineConfiguration memory engineConfiguration,
address adminACLContract_,
string memory defaultBaseURIHost,
address bytecodeStorageReaderContract_
) internal {
// can only be initialized once
if (_initialized) {
revert GenArt721Error(ErrorCodes.ContractInitialized);
}
// immediately mark as initialized
_initialized = true;
// @dev assume renderProviderAddress, randomizer, and AdminACL non-zero
// checks on platform provider addresses performed in _updateProviderSalesAddresses
// initialize default sales revenue percentages and basis points
_renderProviderPrimarySalesPercentage = 10;
defaultRenderProviderSecondarySalesBPS = 250;
_platformProviderPrimarySalesPercentage = engineConfiguration
.nullPlatformProvider
? 0
: 10;
defaultPlatformProviderSecondarySalesBPS = engineConfiguration
.nullPlatformProvider
? 0
: 250;
// set token name and token symbol
ERC721_PackedHashSeedV1.initialize(
engineConfiguration.tokenName,
engineConfiguration.tokenSymbol
);
// update minter if populated
if (engineConfiguration.minterFilterAddress != address(0)) {
_updateMinterContract(engineConfiguration.minterFilterAddress);
}
_updateSplitProvider(engineConfiguration.splitProviderAddress);
_updateBytecodeStorageReaderContract(bytecodeStorageReaderContract_);
// setup immutable `autoApproveArtistSplitProposals` config
autoApproveArtistSplitProposals = engineConfiguration
.autoApproveArtistSplitProposals;
// setup immutable `nullPlatformProvider` config
nullPlatformProvider = engineConfiguration.nullPlatformProvider;
// setup immutable `allowArtistProjectActivation` config
allowArtistProjectActivation = engineConfiguration
.allowArtistProjectActivation;
// record contracts starting project ID
// casting-up is safe
startingProjectId = uint256(engineConfiguration.startingProjectId);
// @dev nullPlatformProvider must be set before calling _updateProviderSalesAddresses
_updateProviderSalesAddresses(
engineConfiguration.renderProviderAddress,
engineConfiguration.renderProviderAddress,
engineConfiguration.platformProviderAddress,
engineConfiguration.platformProviderAddress
);
_updateRandomizerAddress(engineConfiguration.randomizerContract);
// set AdminACL management contract as owner
_transferOwnership(adminACLContract_);
// initialize default base URI
_updateDefaultBaseURI(
string.concat(defaultBaseURIHost, address(this).toHexString(), "/")
);
// initialize next project ID
_nextProjectId = engineConfiguration.startingProjectId;
emit PlatformUpdated(
bytes32(uint256(PlatformUpdatedFields.FIELD_NEXT_PROJECT_ID))
);
// @dev This contract is registered on the core registry in a
// subsequent call by the factory.
}
/**
* @notice Internal function that returns whether a project is unlocked.
* Projects automatically lock four weeks after they are completed.
* Projects are considered completed when they have been invoked the
* maximum number of times.
* @param _projectId Project ID to be queried.
* @return bool true if project is unlocked, false otherwise.
* @dev This also enforces that the `_projectId` passed in is valid.
*/
function _projectUnlocked(uint256 _projectId) internal view returns (bool) {
_onlyValidProjectId(_projectId);
uint256 projectCompletedTimestamp = projects[_projectId]
.completedTimestamp;
bool projectOpen = projectCompletedTimestamp == 0;
return
projectOpen ||
(block.timestamp - projectCompletedTimestamp <
FOUR_WEEKS_IN_SECONDS);
}
/**
* @notice Helper for calling bytecodeStorageReaderContract reader method;
* added for bytecode size reduction purposes.
*/
function _readFromBytecode(
address _address
) internal view returns (string memory) {
return bytecodeStorageReaderContract.readFromBytecode(_address);
}
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
import "./IAdminACLV0.sol";
interface IAdminACLV0_Extended is IAdminACLV0 {
/**
* @notice Allows superAdmin change the superAdmin address.
* @param _newSuperAdmin The new superAdmin address.
* @param _genArt721CoreAddressesToUpdate Array of genArt721Core
* addresses to update to the new superAdmin, for indexing purposes only.
* @dev this function is gated to only superAdmin address.
*/
function changeSuperAdmin(
address _newSuperAdmin,
address[] calldata _genArt721CoreAddressesToUpdate
) external;
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
interface IAdminACLV0 {
/**
* @notice Token ID `_tokenId` minted to `_to`.
* @param previousSuperAdmin The previous superAdmin address.
* @param newSuperAdmin The new superAdmin address.
* @param genArt721CoreAddressesToUpdate Array of genArt721Core
* addresses to update to the new superAdmin, for indexing purposes only.
*/
event SuperAdminTransferred(
address indexed previousSuperAdmin,
address indexed newSuperAdmin,
address[] genArt721CoreAddressesToUpdate
);
/// Type of the Admin ACL contract, e.g. "AdminACLV0"
function AdminACLType() external view returns (string memory);
/// super admin address
function superAdmin() external view returns (address);
/**
* @notice Calls transferOwnership on other contract from this contract.
* This is useful for updating to a new AdminACL contract.
* @dev this function should be gated to only superAdmin-like addresses.
*/
function transferOwnershipOn(
address _contract,
address _newAdminACL
) external;
/**
* @notice Calls renounceOwnership on other contract from this contract.
* @dev this function should be gated to only superAdmin-like addresses.
*/
function renounceOwnershipOn(address _contract) external;
/**
* @notice Checks if sender `_sender` is allowed to call function with selector
* `_selector` on contract `_contract`.
*/
function allowed(
address _sender,
address _contract,
bytes4 _selector
) external returns (bool);
}// SPDX-License-Identifier: LGPL-3.0-only
// Creatd By: Art Blocks Inc.
pragma solidity ^0.8.0;
/**
* @title Art Blocks Script Storage Library - Minimal Interface for Reader Contracts
* @notice This interface defines the minimal expected read function(s) for a Bytecode Storage Reader contract.
*/
interface IBytecodeStorageReader_Base {
/**
* @notice Read a string from a data contract deployed via BytecodeStorage.
* @dev may also support reading additional stored data formats in the future.
* @param address_ address of contract deployed via BytecodeStorage to be read
* @return data The string data stored at the specific address.
*/
function readFromBytecode(
address address_
) external view returns (string memory data);
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.19;
interface IDependencyRegistryCompatibleV0 {
/// Dependency registry managed by Art Blocks
function artblocksDependencyRegistryAddress()
external
view
returns (address);
/**
* @notice Returns script information for project `_projectId`.
* @param _projectId Project to be queried.
* @return scriptTypeAndVersion Project's script type and version
* (e.g. "p5js(atSymbol)1.0.0")
* @return aspectRatio Aspect ratio of project (e.g. "1" for square,
* "1.77777778" for 16:9, etc.)
* @return scriptCount Count of scripts for project
*/
function projectScriptDetails(
uint256 _projectId
)
external
view
returns (
string memory scriptTypeAndVersion,
string memory aspectRatio,
uint256 scriptCount
);
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
interface IGenArt721CoreContractExposesHashSeed {
// function to read the hash-seed for a given tokenId
function tokenIdToHashSeed(
uint256 _tokenId
) external view returns (bytes12);
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
import "./IAdminACLV0.sol";
import "./IGenArt721CoreProjectScriptV1.sol";
/**
* @title This interface is intended to house interface items that are common
* across all GenArt721CoreContractV3 flagship and derivative implementations.
* This interface extends the IManifold royalty interface in order to
* add support the Royalty Registry by default.
* @author Art Blocks Inc.
*/
interface IGenArt721CoreContractV3_Base is IGenArt721CoreProjectScriptV1 {
// This interface emits generic events that contain fields that indicate
// which parameter has been updated. This is sufficient for application
// state management, while also simplifying the contract and indexing code.
// This was done as an alternative to having custom events that emit what
// field-values have changed for each event, given that changed values can
// be introspected by indexers due to the design of this smart contract
// exposing these state changes via publicly viewable fields.
/**
* @notice Event emitted when the Art Blocks Curation Registry contract is updated.
* @dev only utilized by subset of V3 core contracts (Art Blocks Curated contracts)
* @param artblocksCurationRegistryAddress Address of Art Blocks Curation Registry contract.
*/
event ArtBlocksCurationRegistryContractUpdated(
address indexed artblocksCurationRegistryAddress
);
/**
* @notice Project's royalty splitter was updated to `_splitter`.
* @dev New event in v3.2
* @param projectId The project ID.
* @param royaltySplitter The new splitter address to receive royalties.
*/
event ProjectRoyaltySplitterUpdated(
uint256 indexed projectId,
address indexed royaltySplitter
);
// The following fields are used to indicate which contract-level parameter
// has been updated in the `PlatformUpdated` event:
// @dev only append to the end of this enum in the case of future updates
enum PlatformUpdatedFields {
FIELD_NEXT_PROJECT_ID, // 0
FIELD_NEW_PROJECTS_FORBIDDEN, // 1
FIELD_DEFAULT_BASE_URI, // 2
FIELD_RANDOMIZER_ADDRESS, // 3
FIELD_NEXT_CORE_CONTRACT, // 4
FIELD_ARTBLOCKS_DEPENDENCY_REGISTRY_ADDRESS, // 5
FIELD_ARTBLOCKS_ON_CHAIN_GENERATOR_ADDRESS, // 6
FIELD_PROVIDER_SALES_ADDRESSES, // 7
FIELD_PROVIDER_PRIMARY_SALES_PERCENTAGES, // 8
FIELD_PROVIDER_SECONDARY_SALES_BPS, // 9
FIELD_SPLIT_PROVIDER, // 10
FIELD_BYTECODE_STORAGE_READER // 11
}
// The following fields are used to indicate which project-level parameter
// has been updated in the `ProjectUpdated` event:
// @dev only append to the end of this enum in the case of future updates
enum ProjectUpdatedFields {
FIELD_PROJECT_COMPLETED, // 0
FIELD_PROJECT_ACTIVE, // 1
FIELD_PROJECT_ARTIST_ADDRESS, // 2
FIELD_PROJECT_PAUSED, // 3
FIELD_PROJECT_CREATED, // 4
FIELD_PROJECT_NAME, // 5
FIELD_PROJECT_ARTIST_NAME, // 6
FIELD_PROJECT_SECONDARY_MARKET_ROYALTY_PERCENTAGE, // 7
FIELD_PROJECT_DESCRIPTION, // 8
FIELD_PROJECT_WEBSITE, // 9
FIELD_PROJECT_LICENSE, // 10
FIELD_PROJECT_MAX_INVOCATIONS, // 11
FIELD_PROJECT_SCRIPT, // 12
FIELD_PROJECT_SCRIPT_TYPE, // 13
FIELD_PROJECT_ASPECT_RATIO, // 14
FIELD_PROJECT_BASE_URI, // 15
FIELD_PROJECT_PROVIDER_SECONDARY_FINANCIALS // 16
}
/**
* @notice Error codes for the GenArt721 contract. Used by the GenArt721Error
* custom error.
* @dev only append to the end of this enum in the case of future updates
*/
enum ErrorCodes {
OnlyNonZeroAddress, // 0
OnlyNonEmptyString, // 1
OnlyNonEmptyBytes, // 2
TokenDoesNotExist, // 3
ProjectDoesNotExist, // 4
OnlyUnlockedProjects, // 5
OnlyAdminACL, // 6
OnlyArtist, // 7
OnlyArtistOrAdminACL, // 8
OnlyAdminACLOrRenouncedArtist, // 9
OnlyMinterContract, // 10
MaxInvocationsReached, // 11
ProjectMustExistAndBeActive, // 12
PurchasesPaused, // 13
OnlyRandomizer, // 14
TokenHashAlreadySet, // 15
NoZeroHashSeed, // 16
OverMaxSumOfPercentages, // 17
IndexOutOfBounds, // 18
OverMaxSumOfBPS, // 19
MaxOf100Percent, // 20
PrimaryPayeeIsZeroAddress, // 21
SecondaryPayeeIsZeroAddress, // 22
MustMatchArtistProposal, // 23
NewProjectsForbidden, // 24
NewProjectsAlreadyForbidden, // 25
OnlyArtistOrAdminIfLocked, // 26
OverMaxSecondaryRoyaltyPercentage, // 27
OnlyMaxInvocationsDecrease, // 28
OnlyGteInvocations, // 29
ScriptIdOutOfRange, // 30
NoScriptsToRemove, // 31
ScriptTypeAndVersionFormat, // 32
AspectRatioTooLong, // 33
AspectRatioNoNumbers, // 34
AspectRatioImproperFormat, // 35
OnlyNullPlatformProvider, // 36
ContractInitialized // 37
}
/**
* @notice Emits an error code `_errorCode` in the GenArt721Error event.
* @dev Emitting error codes instead of error strings saves significant
* contract bytecode size, allowing for more contract functionality within
* the 24KB contract size limit.
* @param _errorCode The error code to emit. See ErrorCodes enum.
*/
error GenArt721Error(ErrorCodes _errorCode);
/**
* @notice Token ID `_tokenId` minted to `_to`.
*/
event Mint(address indexed _to, uint256 indexed _tokenId);
/**
* @notice currentMinter updated to `_currentMinter`.
* @dev Implemented starting with V3 core
*/
event MinterUpdated(address indexed _currentMinter);
/**
* @notice Platform updated on bytes32-encoded field `_field`.
*/
event PlatformUpdated(bytes32 indexed _field);
/**
* @notice Project ID `_projectId` updated on bytes32-encoded field
* `_update`.
*/
event ProjectUpdated(uint256 indexed _projectId, bytes32 indexed _update);
event ProposedArtistAddressesAndSplits(
uint256 indexed _projectId,
address _artistAddress,
address _additionalPayeePrimarySales,
uint256 _additionalPayeePrimarySalesPercentage,
address _additionalPayeeSecondarySales,
uint256 _additionalPayeeSecondarySalesPercentage
);
event AcceptedArtistAddressesAndSplits(uint256 indexed _projectId);
// version and type of the core contract
// coreVersion is a string of the form "0.x.y"
function coreVersion() external view returns (string memory);
// coreType is a string of the form "GenArt721CoreV3"
function coreType() external view returns (string memory);
// owner (pre-V3 was named admin) of contract
// this is expected to be an Admin ACL contract for V3
function owner() external view returns (address);
// Admin ACL contract for V3, will be at the address owner()
function adminACLContract() external returns (IAdminACLV0);
// backwards-compatible (pre-V3) admin - equal to owner()
function admin() external view returns (address);
/**
* Function determining if _sender is allowed to call function with
* selector _selector on contract `_contract`. Intended to be used with
* peripheral contracts such as minters, as well as internally by the
* core contract itself.
*/
function adminACLAllowed(
address _sender,
address _contract,
bytes4 _selector
) external returns (bool);
/// getter function of public variable
function startingProjectId() external view returns (uint256);
// getter function of public variable
function nextProjectId() external view returns (uint256);
// getter function of public mapping
function tokenIdToProjectId(
uint256 tokenId
) external view returns (uint256 projectId);
// @dev this is not available in V0
function isMintWhitelisted(address minter) external view returns (bool);
function projectIdToArtistAddress(
uint256 _projectId
) external view returns (address payable);
function projectIdToSecondaryMarketRoyaltyPercentage(
uint256 _projectId
) external view returns (uint256);
function projectURIInfo(
uint256 _projectId
) external view returns (string memory projectBaseURI);
// @dev new function in V3
function projectStateData(
uint256 _projectId
)
external
view
returns (
uint256 invocations,
uint256 maxInvocations,
bool active,
bool paused,
uint256 completedTimestamp,
bool locked
);
function projectDetails(
uint256 _projectId
)
external
view
returns (
string memory projectName,
string memory artist,
string memory description,
string memory website,
string memory license
);
function projectScriptDetails(
uint256 _projectId
)
external
view
returns (
string memory scriptTypeAndVersion,
string memory aspectRatio,
uint256 scriptCount
);
function projectScriptByIndex(
uint256 _projectId,
uint256 _index
) external view returns (string memory);
function tokenIdToHash(uint256 _tokenId) external view returns (bytes32);
// function to set a token's hash (must be guarded)
function setTokenHash_8PT(uint256 _tokenId, bytes32 _hash) external;
// @dev gas-optimized signature in V3 for `mint`
function mint_Ecf(
address _to,
uint256 _projectId,
address _by
) external returns (uint256 tokenId);
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
import "./IAdminACLV0.sol";
import "./IGenArt721CoreContractV3_Engine.sol";
/**
* @title This interface is intended to house interface items that are common
* across all GenArt721CoreContractV3 Engine Flex and derivative implementations.
* @author Art Blocks Inc.
*/
interface IGenArt721CoreContractV3_Engine_Flex is
IGenArt721CoreContractV3_Engine
{
/**
* @notice When an external asset dependency is updated or added, this event is emitted.
* @param _projectId The project ID of the project that was updated.
* @param _index The index of the external asset dependency that was updated.
* @param _cid Field that contains the CID of the dependency if IPFS or ARWEAVE, empty string of ONCHAIN, or a string representation
* of the Art Blocks Dependency Registry's `dependencyNameAndVersion` if ART_BLOCKS_DEPENDENCY_REGISTRY.
* @param _dependencyType The type of the external asset dependency.
* @param _externalAssetDependencyCount The number of external asset dependencies.
*/
event ExternalAssetDependencyUpdated(
uint256 indexed _projectId,
uint256 indexed _index,
string _cid,
ExternalAssetDependencyType _dependencyType,
uint24 _externalAssetDependencyCount
);
/**
* @notice The project id `_projectId` has had an external asset dependency removed at index `_index`.
*/
event ExternalAssetDependencyRemoved(
uint256 indexed _projectId,
uint256 indexed _index
);
/**
* @notice The preferred gateway for dependency type `_dependencyType` has been updated to `_gatewayAddress`.
*/
event GatewayUpdated(
ExternalAssetDependencyType indexed _dependencyType,
string _gatewayAddress
);
/**
* @notice The project id `_projectId` has had all external asset dependencies locked.
* @dev This is a one-way operation. Once locked, the external asset dependencies cannot be updated.
*/
event ProjectExternalAssetDependenciesLocked(uint256 indexed _projectId);
/**
* @notice An external asset dependency type. Can be one of IPFS, ARWEAVE, ONCHAIN, or ART_BLOCKS_DEPENDENCY_REGISTRY.
*/
enum ExternalAssetDependencyType {
IPFS,
ARWEAVE,
ONCHAIN,
ART_BLOCKS_DEPENDENCY_REGISTRY
}
/**
* @notice Project storage that relate to Flex data.
*/
struct ProjectFlex {
bool externalAssetDependenciesLocked;
uint24 externalAssetDependencyCount;
mapping(uint256 => ExternalAssetDependency) externalAssetDependencies;
}
/**
* @notice An external asset dependency, without the retrieved data of any ONCHAIN assets. This reflects what is
* stored in this contract's storage.
* @param CID field that contains the CID of the dependency if IPFS or ARWEAVE, empty string of ONCHAIN, or a string representation
* of the Art Blocks Dependency Registry's `dependencyNameAndVersion` if ART_BLOCKS_DEPENDENCY_REGISTRY.
* @param dependencyType field that contains the type of the dependency.
* @param bytecodeAddress field that contains the address of the bytecode for this dependency if ONCHAIN, null address otherwise.
*/
struct ExternalAssetDependency {
string cid;
ExternalAssetDependencyType dependencyType;
address bytecodeAddress;
}
/**
* @notice An external asset dependency with data. This is a convenience struct.
* @param CID field that contains the CID of the dependency if IPFS or ARWEAVE, empty string of ONCHAIN, or a string representation
* of the Art Blocks Dependency Registry's `dependencyNameAndVersion` if ART_BLOCKS_DEPENDENCY_REGISTRY.
* @param dependencyType field that contains the type of the dependency.
* @param bytecodeAddress field that contains the address of the bytecode for this dependency if ONCHAIN, null address otherwise.
* @param data field that contains the data retrieved from this bytecode address if ONCHAIN, empty string otherwise.
*/
struct ExternalAssetDependencyWithData {
string cid;
ExternalAssetDependencyType dependencyType;
address bytecodeAddress;
string data;
}
// preferredIPFSGateway is a url string
function preferredIPFSGateway() external view returns (string memory);
// preferredArweaveGateway is a url string
function preferredArweaveGateway() external view returns (string memory);
// updates the preferred IPFS gateway
function updateIPFSGateway(string calldata _gateway) external;
// updates the preferred Arweave gateway
function updateArweaveGateway(string calldata _gateway) external;
// locks the external asset dependencies for a project
function lockProjectExternalAssetDependencies(uint256 _projectId) external;
// updates the external asset dependency for a project at a given index
function updateProjectExternalAssetDependency(
uint256 _projectId,
uint256 _index,
string memory _cidOrData,
ExternalAssetDependencyType _dependencyType
) external;
// adds an external asset dependency for a project
function addProjectExternalAssetDependency(
uint256 _projectId,
string memory _cidOrData,
ExternalAssetDependencyType _dependencyType
) external;
// removes an external asset dependency for a project at a given index
function removeProjectExternalAssetDependency(
uint256 _projectId,
uint256 _index
) external;
// getter function for project external asset dependencies
function projectExternalAssetDependencyByIndex(
uint256 _projectId,
uint256 _index
) external view returns (ExternalAssetDependencyWithData memory);
// getter function project external asset dependency count
function projectExternalAssetDependencyCount(
uint256 _projectId
) external view returns (uint256);
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
import "./IAdminACLV0.sol";
import "./IGenArt721CoreContractV3_Base.sol";
import "./ISplitProviderV0.sol";
/**
* @notice Struct representing Engine contract configuration.
* @param tokenName Name of token.
* @param tokenSymbol Token symbol.
* @param renderProviderAddress address to send render provider revenue to
* @param randomizerContract Randomizer contract.
* @param splitProviderAddress Address to use as royalty splitter provider for the contract.
* @param minterFilterAddress Address of the Minter Filter to set as the Minter
* on the contract.
* @param startingProjectId The initial next project ID.
* @param autoApproveArtistSplitProposals Whether or not to always
* auto-approve proposed artist split updates.
* @param nullPlatformProvider Enforce always setting zero platform provider fees and addresses.
* @param allowArtistProjectActivation Allow artist to activate their own projects.
* @dev _startingProjectId should be set to a value much, much less than
* max(uint248), but an explicit input type of `uint248` is used as it is
* safer to cast up to `uint256` than it is to cast down for the purposes
* of setting `_nextProjectId`.
*/
struct EngineConfiguration {
string tokenName;
string tokenSymbol;
address renderProviderAddress;
address platformProviderAddress;
address newSuperAdminAddress;
address randomizerContract;
address splitProviderAddress;
address minterFilterAddress;
uint248 startingProjectId;
bool autoApproveArtistSplitProposals;
bool nullPlatformProvider;
bool allowArtistProjectActivation;
}
interface IGenArt721CoreContractV3_Engine is IGenArt721CoreContractV3_Base {
// @dev new function in V3.2
/**
* @notice Initializes the contract with the provided `engineConfiguration`.
* This function should be called atomically, immediately after deployment.
* Only callable once. Validation on `engineConfiguration` is performed by caller.
* @param engineConfiguration EngineConfiguration to configure the contract with.
* @param adminACLContract_ Address of admin access control contract, to be
* set as contract owner.
* @param defaultBaseURIHost Base URI prefix to initialize default base URI with.
* @param bytecodeStorageReaderContract_ Address of the bytecode storage reader contract.
*/
function initialize(
EngineConfiguration calldata engineConfiguration,
address adminACLContract_,
string memory defaultBaseURIHost,
address bytecodeStorageReaderContract_
) external;
// @dev new function in V3
function getPrimaryRevenueSplits(
uint256 _projectId,
uint256 _price
)
external
view
returns (
uint256 renderProviderRevenue_,
address payable renderProviderAddress_,
uint256 platformProviderRevenue_,
address payable platformProviderAddress_,
uint256 artistRevenue_,
address payable artistAddress_,
uint256 additionalPayeePrimaryRevenue_,
address payable additionalPayeePrimaryAddress_
);
// @dev The render provider primary sales payment address
function renderProviderPrimarySalesAddress()
external
view
returns (address payable);
// @dev The platform provider primary sales payment address
function platformProviderPrimarySalesAddress()
external
view
returns (address payable);
// @dev Percentage of primary sales allocated to the render provider
function renderProviderPrimarySalesPercentage()
external
view
returns (uint256);
// @dev Percentage of primary sales allocated to the platform provider
function platformProviderPrimarySalesPercentage()
external
view
returns (uint256);
/** @notice The default render provider payment address for all secondary sales royalty
* revenues, for all new projects. Individual project payment info is defined
* in each project's ProjectFinance struct.
* @return The default render provider payment address for secondary sales royalties.
*/
function defaultRenderProviderSecondarySalesAddress()
external
view
returns (address payable);
/** @notice The default platform provider payment address for all secondary sales royalty
* revenues, for all new projects. Individual project payment info is defined
* in each project's ProjectFinance struct.
* @return The default platform provider payment address for secondary sales royalties.
*/
function defaultPlatformProviderSecondarySalesAddress()
external
view
returns (address payable);
/** @notice The default render provider payment basis points for all secondary sales royalty
* revenues, for all new projects. Individual project payment info is defined
* in each project's ProjectFinance struct.
* @return The default render provider payment basis points for secondary sales royalties.
*/
function defaultRenderProviderSecondarySalesBPS()
external
view
returns (uint256);
/** @notice The default platform provider payment basis points for all secondary sales royalty
* revenues, for all new projects. Individual project payment info is defined
* in each project's ProjectFinance struct.
* @return The default platform provider payment basis points for secondary sales royalties.
*/
function defaultPlatformProviderSecondarySalesBPS()
external
view
returns (uint256);
/**
* @notice The address of the current split provider being used by the contract.
* @return The address of the current split provider.
*/
function splitProvider() external view returns (ISplitProviderV0);
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
import "./IAdminACLV0.sol";
/**
* @title This interface defines a project finance struct that is used in the
* GenArt721CoreContractV3 flagship and derivative implementations beginning
* with v3.2.0. This struct is intended to house all financial information
* related to a project, including royalties, artist splits, and platform
* provider splits.
* @author Art Blocks Inc.
*/
interface IGenArt721CoreContractV3_ProjectFinance {
/// packed struct containing project financial information
struct ProjectFinance {
address payable additionalPayeePrimarySales;
// packed uint: max of 95, max uint8 = 255
uint8 secondaryMarketRoyaltyPercentage;
address payable additionalPayeeSecondarySales;
// packed uint: max of 100, max uint8 = 255
uint8 additionalPayeeSecondarySalesPercentage;
address payable artistAddress;
// packed uint: max of 100, max uint8 = 255
uint8 additionalPayeePrimarySalesPercentage;
address platformProviderSecondarySalesAddress;
// packed uint: max of 10_000 max uint16 = 65_535
uint16 platformProviderSecondarySalesBPS;
address renderProviderSecondarySalesAddress;
// packed uint: max of 10_000 max uint16 = 65_535
uint16 renderProviderSecondarySalesBPS;
// address to send ERC-2981 royalties to
address royaltySplitter;
}
/**
* @notice View function returning project financial details for project
* `_projectId`.
* @param _projectId Project ID to be queried.
* @return ProjectFinance Project financial details.
*/
function projectIdToFinancials(
uint256 _projectId
) external view returns (ProjectFinance memory);
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.0;
/**
* @dev This interface is implemented by GenArt721CoreV3 and above
*/
interface IGenArt721CoreProjectScriptV1 {
function projectScriptDetails(
uint256 _projectId
)
external
view
returns (
string memory scriptTypeAndVersion,
string memory aspectRatio,
uint256 scriptCount
);
function projectScriptBytecodeAddressByIndex(
uint256 _projectId,
uint256 _index
) external view returns (address);
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc. to support the 0xSplits V2 integration
// Sourced from:
// - https://github.com/0xSplits/splits-contracts-monorepo/blob/main/packages/splits-v2/src/libraries/SplitV2.sol
// - https://github.com/0xSplits/splits-contracts-monorepo/blob/main/packages/splits-v2/src/splitters/SplitFactoryV2.sol
pragma solidity ^0.8.0;
interface ISplitFactoryV2 {
/* -------------------------------------------------------------------------- */
/* STRUCTS */
/* -------------------------------------------------------------------------- */
/**
* @notice Split struct
* @dev This struct is used to store the split information.
* @dev There are no hard caps on the number of recipients/totalAllocation/allocation unit. Thus the chain and its
* gas limits will dictate these hard caps. Please double check if the split you are creating can be distributed on
* the chain.
* @param recipients The recipients of the split.
* @param allocations The allocations of the split.
* @param totalAllocation The total allocation of the split.
* @param distributionIncentive The incentive for distribution. Limits max incentive to 6.5%.
*/
struct Split {
address[] recipients;
uint256[] allocations;
uint256 totalAllocation;
uint16 distributionIncentive;
}
/* -------------------------------------------------------------------------- */
/* FUNCTIONS */
/* -------------------------------------------------------------------------- */
/**
* @notice Create a new split with params and owner.
* @param _splitParams Params to create split with.
* @param _owner Owner of created split.
* @param _creator Creator of created split.
* @param _salt Salt for create2.
* @return split Address of the created split.
*/
function createSplitDeterministic(
Split calldata _splitParams,
address _owner,
address _creator,
bytes32 _salt
) external returns (address split);
/**
* @notice Predict the address of a new split and check if it is deployed.
* @param _splitParams Params to create split with.
* @param _owner Owner of created split.
* @param _salt Salt for create2.
*/
function isDeployed(
Split calldata _splitParams,
address _owner,
bytes32 _salt
) external view returns (address split, bool exists);
}// SPDX-License-Identifier: LGPL-3.0-only
// Creatd By: Art Blocks Inc.
pragma solidity ^0.8.0;
interface IRandomizer_V3CoreBase {
/**
* @notice This function is intended to be called by a core contract, and
* the core contract can be assured that the randomizer will call back to
* the calling contract to set the token hash seed for `_tokenId` via
* `setTokenHash_8PT`.
* @dev This function may revert if hash seed generation is improperly
* configured (for example, if in polyptych mode, but no hash seed has been
* previously configured).
* @dev This function is not specifically gated to any specific caller, but
* will only call back to the calling contract, `msg.sender`, to set the
* specified token's hash seed.
* A third party contract calling this function will not be able to set the
* token hash seed on a different core contract.
* @param _tokenId The token ID must be assigned a hash.
*/
function assignTokenHash(uint256 _tokenId) external;
}// SPDX-License-Identifier: LGPL-3.0-only
// Creatd By: Art Blocks Inc.
pragma solidity ^0.8.0;
import {ISplitFactoryV2} from "./integration-refs/splits-0x-v2/ISplitFactoryV2.sol";
interface ISplitProviderV0 {
/**
* @notice SplitInputs struct defines the inputs for requested splitters.
* It is defined in a way easily communicated from the Art Blocks GenArt721V3 contract,
* to allow for easy integration and minimal additional bytecode in the GenArt721V3 contract.
*/
struct SplitInputs {
address platformProviderSecondarySalesAddress;
uint16 platformProviderSecondarySalesBPS;
address renderProviderSecondarySalesAddress;
uint16 renderProviderSecondarySalesBPS;
uint8 artistTotalRoyaltyPercentage;
address artist;
address additionalPayee;
uint8 additionalPayeePercentage;
}
/**
* @notice Emitted when a new splitter contract is created.
* @param splitter address of the splitter contract
*/
event SplitterCreated(address indexed splitter);
/**
* @notice Gets or creates an immutable splitter contract at a deterministic address.
* Splits in the splitter contract are determined by the input split parameters,
* so we can safely create the splitter contract at a deterministic address (or use
* the existing splitter contract if it already exists at that address).
* @dev Uses the 0xSplits v2 implementation to create a splitter contract
* @param splitInputs The split input parameters.
* @return splitter The newly created splitter contract address.
*/
function getOrCreateSplitter(
SplitInputs calldata splitInputs
) external returns (address);
/**
* @notice Indicates the type of the contract, e.g. `SplitProviderV0`.
* @return type_ The type of the contract.
*/
function type_() external pure returns (bytes32);
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
import {LibZip} from "solady/src/utils/LibZip.sol";
pragma solidity ^0.8.0;
/**
* @title Art Blocks Script Storage Library
* @notice Utilize contract bytecode as persistent storage for large chunks of script string data.
* V2 includes optional on-chain compression/decompression via solady LibZip.
* This library is intended to have an external deployed copy that is released in the future,
* and, as such, has been designed to support both updated V2 and V1 (versioned, with purging removed)
* reads as well as backwards-compatible reads for both a) the unversioned "V0" storage contracts
* which were deployed by the original version of this libary and b) contracts that were deployed
* using one of the SSTORE2 implementations referenced below.
* For these pre-V1 storage contracts (which themselves did not have any explicit versioning semantics)
* backwards-compatible reads are optimistic, and only expected to work for contracts actually
* deployed by the original version of this library – and may fail ungracefully if attempted to be
* used to read from other contracts.
* This library is split into two components, intended to be updated in tandem, and thus included
* here in the same source file. One component is an internal library that is intended to be embedded
* directly into other contracts and provides all _write_ functionality. The other is a public library
* that is intended to be deployed as a standalone contract and provides all _read_ functionality.
*
* @author Art Blocks Inc.
* @author Modified from 0xSequence (https://github.com/0xsequence/sstore2/blob/master/contracts/SSTORE2.sol)
* @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol)
* @author Utilizes LibZip from solady (https://github.com/Vectorized/solady/blob/main/src/utils/LibZip.sol)
*
* @dev Compared to the above two rerferenced libraries, this contracts-as-storage implementation makes a few
* notably different design decisions:
* - uses the `string` data type for input/output on reads, rather than speaking in bytes directly,
* with an exception for optionally compressed data which are input as bytes
* - stores the "writer" address (library user) in the deployed contract bytes, which is useful for
* on-chain introspection and provenance purposes
* - stores a very simple versioning string in the deployed contract bytes, which captures the version
* of the library that was used to deploy the storage contract and useful for supporting future
* compatibility management as this library evolves (e.g. in response to EOF v1 migration plans)
* - stores a bool indicating if the stored data are compressed.
* Also, given that much of this library is written in assembly, this library makes use of a slightly
* different convention (when compared to the rest of the Art Blocks smart contract repo) around
* pre-defining return values in some cases in order to simplify need to directly memory manage these
* return values.
*/
/**
* @title Art Blocks Script Storage Library (Public, Reads)
* @author Art Blocks Inc.
* @notice The public library for reading from storage contracts. This library is intended to be deployed as a
* standalone contract, and provides all _read_ functionality.
*/
library BytecodeStorageReader {
// Define the set of known valid version strings that may be stored in the deployed storage contract bytecode
// note: These are all intentionally exactly 32-bytes and are null-terminated. Null-termination is used due
// to this being the standard expected formatting in common web3 tooling such as ethers.js. Please see
// the following for additional context: https://docs.ethers.org/v5/api/utils/strings/#Bytes32String
// Used for storage contracts that were deployed by an unknown source
bytes32 public constant UNKNOWN_VERSION_STRING =
"UNKNOWN_VERSION_STRING_________ ";
// Pre-dates versioning string, so this doesn't actually exist in any deployed contracts,
// but is useful for backwards-compatible semantics with original version of this library
bytes32 public constant V0_VERSION_STRING =
"BytecodeStorage_V0.0.0_________ ";
// The first versioned storage contract, deployed by an updated version of this library
bytes32 public constant V1_VERSION_STRING =
"BytecodeStorage_V1.0.0_________ ";
// The first versioned storage contract, deployed by an updated version of this library
bytes32 public constant V2_VERSION_STRING =
"BytecodeStorage_V2.0.0_________ ";
// The current version of this library.
bytes32 public constant CURRENT_VERSION = V2_VERSION_STRING;
//---------------------------------------------------------------------------------------------------------------//
// Starting Index | Size | Ending Index | Description //
//---------------------------------------------------------------------------------------------------------------//
// 0 | N/A | 0 | //
// 0 | 1 | 1 | single byte opcode for making the storage contract non-executable //
// 1 | 32 | 33 | the 32 byte slot used for storing a basic versioning string //
// 33 | 32 | 65 | the 32 bytes for storing the deploying contract's (0-padded) address //
// 65 | 1 | 66 | single byte indicating if the stored data are compressed //
//---------------------------------------------------------------------------------------------------------------//
// Define the offset for where the "meta bytes" end, and the "data bytes" begin. Note that this is a manually
// calculated value, and must be updated if the above table is changed. It is expected that tests will fail
// loudly if these values are not updated in-step with eachother.
uint256 private constant VERSION_OFFSET = 1;
uint256 private constant ADDRESS_OFFSET = 33;
uint256 private constant COMPRESSION_OFFSET = 65;
uint256 private constant DATA_OFFSET = 66;
// Define the set of known *historic* offset values for where the "meta bytes" end, and the "data bytes" begin.
// SSTORE2 deployed storage contracts take the general format of:
// concat(0x00, data)
// note: this is true for both variants of the SSTORE2 library
uint256 private constant SSTORE2_DATA_OFFSET = 1;
// V0 deployed storage contracts take the general format of:
// concat(gated-cleanup-logic, deployer-address, data)
uint256 private constant V0_ADDRESS_OFFSET = 72;
uint256 private constant V0_DATA_OFFSET = 104;
// V1 deployed storage contracts take the general format of:
// concat(invalid opcode, version, deployer-address, data)
uint256 private constant V1_ADDRESS_OFFSET = 33;
uint256 private constant V1_DATA_OFFSET = 65;
// V2 deployed storage contracts take the general format of:
// concat(invalid opcode, version, deployer-address, compression-bool, data)
uint256 private constant V2_ADDRESS_OFFSET = ADDRESS_OFFSET;
uint256 private constant V2_DATA_OFFSET = DATA_OFFSET;
/*//////////////////////////////////////////////////////////////
READ LOGIC
//////////////////////////////////////////////////////////////*/
/**
* @notice Read a string from contract bytecode
* @param _address address of deployed contract with bytecode stored in the V0 or V1 or V2 format
* @return data string read from contract bytecode
* @dev This function performs input validation that the contract to read is in an expected format
*/
function readFromBytecode(
address _address
) public view returns (string memory data) {
(
uint256 dataOffset,
bool isCompressed
) = _bytecodeDataOffsetAndIsCompressedAt(_address);
if (isCompressed) {
return
string(
LibZip.flzDecompress(
readBytesFromBytecode(_address, dataOffset)
)
);
} else {
return string(readBytesFromBytecode(_address, dataOffset));
}
}
/**
* @notice Read the bytes from contract bytecode that was written to the EVM using SSTORE2
* @param _address address of deployed contract with bytecode stored in the SSTORE2 format
* @return data bytes read from contract bytecode
* @dev This function performs no input validation on the provided contract,
* other than that there is content to read (but not that its a "storage contract")
*/
function readBytesFromSSTORE2Bytecode(
address _address
) public view returns (bytes memory data) {
return readBytesFromBytecode(_address, SSTORE2_DATA_OFFSET);
}
/**
* @notice Read the bytes from contract bytecode, with an explicitly provided starting offset
* @param _address address of deployed contract with bytecode stored in the V0 or V1 format
* @param _offset offset to read from in contract bytecode, explicitly provided (not calculated)
* @return data bytes read from contract bytecode
* @dev This function performs no input validation on the provided contract,
* other than that there is content to read (but not that its a "storage contract")
*/
function readBytesFromBytecode(
address _address,
uint256 _offset
) public view returns (bytes memory data) {
// get the size of the bytecode
uint256 bytecodeSize = _bytecodeSizeAt(_address);
// handle case where address contains code < _offset
if (bytecodeSize < _offset) {
revert("ContractAsStorage: Read Error");
}
// handle case where address contains code >= dataOffset
// decrement by dataOffset to account for header info
uint256 size;
unchecked {
size = bytecodeSize - _offset;
}
assembly {
// allocate free memory
data := mload(0x40)
// update free memory pointer
// use and(x, not(0x1f) as cheaper equivalent to sub(x, mod(x, 0x20)).
// adding 0x1f to size + logic above ensures the free memory pointer
// remains word-aligned, following the Solidity convention.
mstore(0x40, add(data, and(add(add(size, 0x20), 0x1f), not(0x1f))))
// store length of data in first 32 bytes
mstore(data, size)
// copy code to memory, excluding the deployer-address
extcodecopy(_address, add(data, 0x20), _offset, size)
}
}
/**
* @notice Get address for deployer for given contract bytecode
* @param _address address of deployed contract with bytecode stored in the V0 or V1 format
* @return writerAddress address read from contract bytecode
*/
function getWriterAddressForBytecode(
address _address
) public view returns (address) {
// get the size of the data
uint256 bytecodeSize = _bytecodeSizeAt(_address);
// the dataOffset for the bytecode
uint256 addressOffset = _bytecodeAddressOffsetAt(_address);
// handle case where address contains code < addressOffset + 32 (address takes a whole slot)
if (bytecodeSize < (addressOffset + 32)) {
revert("ContractAsStorage: Read Error");
}
assembly {
// allocate free memory
let writerAddress := mload(0x40)
// shift free memory pointer by one slot
mstore(0x40, add(mload(0x40), 0x20))
// copy the 32-byte address of the data contract writer to memory
// note: this relies on the assumption noted at the top-level of
// this file that the storage layout for the deployed
// contracts-as-storage contract looks like::
// | invalid opcode | version-string (unless v0) | deployer-address (padded) | data |
extcodecopy(
_address,
writerAddress,
addressOffset,
0x20 // full 32-bytes, as address is expected to be zero-padded
)
return(
writerAddress,
0x20 // return size is entire slot, as it is zero-padded
)
}
}
/**
* @notice Get version for given contract bytecode
* @param _address address of deployed contract with bytecode stored in the V0 or V1 format
* @return version version read from contract bytecode
*/
function getLibraryVersionForBytecode(
address _address
) public view returns (bytes32) {
return _bytecodeVersionAt(_address);
}
/**
* @notice Get if data are stored in compressed format for given contract bytecode
* @param _address address of deployed contract with bytecode stored in the V0, V1, or V2 format
* @return isCompressed boolean indicating if the stored data are compressed
*/
function getIsCompressedForBytecode(
address _address
) public view returns (bool) {
(, bool isCompressed) = _bytecodeDataOffsetAndIsCompressedAt(_address);
return isCompressed;
}
/**
* Utility function to get the compressed form of a message string using solady LibZip's
* flz compress algorithm.
* The compressed message is returned as bytes, which may be used as the input to
* the function `BytecodeStorageWriter.writeToBytecodeCompressed`.
* @param _data string to be compressed
* @return bytes compressed bytes
*/
function getCompressed(
string memory _data
) public pure returns (bytes memory) {
return LibZip.flzCompress(bytes(_data));
}
/*//////////////////////////////////////////////////////////////
INTERNAL HELPER LOGIC
//////////////////////////////////////////////////////////////*/
/**
* @notice Returns the size of the bytecode at address `_address`
* @param _address address that may or may not contain bytecode
* @return size size of the bytecode code at `_address`
*/
function _bytecodeSizeAt(
address _address
) private view returns (uint256 size) {
assembly {
size := extcodesize(_address)
}
if (size == 0) {
revert("ContractAsStorage: Read Error");
}
}
/**
* @notice Returns the offset of the data in the bytecode at address `_address`
* @param _address address that may or may not contain bytecode
* @return dataOffset offset of data in bytecode if a known version, otherwise 0
* @return isCompressed bool indicating if the stored data are compressed
*/
function _bytecodeDataOffsetAndIsCompressedAt(
address _address
) private view returns (uint256 dataOffset, bool isCompressed) {
bytes32 version = _bytecodeVersionAt(_address);
if (version == V2_VERSION_STRING) {
dataOffset = V2_DATA_OFFSET;
isCompressed = _isCompressedAt(_address, version);
} else if (version == V1_VERSION_STRING) {
dataOffset = V1_DATA_OFFSET;
// isCompressed remains false, as V1 contracts do not support compression
} else if (version == V0_VERSION_STRING) {
dataOffset = V0_DATA_OFFSET;
// isCompressed remains false, as V0 contracts do not support compression
} else {
// unknown version, revert
revert("ContractAsStorage: Unsupported Version");
}
}
/**
* @notice Returns the offset of the address in the bytecode at address `_address`
* @param _address address that may or may not contain bytecode
* @return addressOffset offset of address in bytecode if a known version, otherwise 0
*/
function _bytecodeAddressOffsetAt(
address _address
) private view returns (uint256 addressOffset) {
bytes32 version = _bytecodeVersionAt(_address);
if (version == V2_VERSION_STRING) {
addressOffset = V2_ADDRESS_OFFSET;
} else if (version == V1_VERSION_STRING) {
addressOffset = V1_ADDRESS_OFFSET;
} else if (version == V0_VERSION_STRING) {
addressOffset = V0_ADDRESS_OFFSET;
} else {
// unknown version, revert
revert("ContractAsStorage: Unsupported Version");
}
}
/**
* @notice Get version string for given contract bytecode
* @param _address address of deployed contract with bytecode stored in the V0 or V1 format
* @return version version string read from contract bytecode
*/
function _bytecodeVersionAt(
address _address
) private view returns (bytes32 version) {
// get the size of the data
uint256 bytecodeSize = _bytecodeSizeAt(_address);
// handle case where address contains code < minimum expected version string size,
// by returning early with the unknown version string
if (bytecodeSize < (VERSION_OFFSET + 32)) {
return UNKNOWN_VERSION_STRING;
}
assembly {
// allocate free memory
let versionString := mload(0x40)
// shift free memory pointer by one slot
mstore(0x40, add(mload(0x40), 0x20))
// copy the 32-byte version string of the bytecode library to memory
// note: this relies on the assumption noted at the top-level of
// this file that the storage layout for the deployed
// contracts-as-storage contract looks like:
// | invalid opcode | version-string (unless v0) | deployer-address (padded) | data |
extcodecopy(
_address,
versionString,
VERSION_OFFSET,
0x20 // 32-byte version string
)
// note: must check against literal strings, as Yul does not allow for
// dynamic strings in switch statements.
switch mload(versionString)
case "BytecodeStorage_V2.0.0_________ " {
version := V2_VERSION_STRING
}
case "BytecodeStorage_V1.0.0_________ " {
version := V1_VERSION_STRING
}
case 0x2060486000396000513314601057fe5b60013614601957fe5b6000357fff0000 {
// the v0 variant of this library pre-dates formal versioning w/ version strings,
// so we check the first 32 bytes of the execution bytecode itself which
// is static and known across all storage contracts deployed with the first version
// of this library.
version := V0_VERSION_STRING
}
default {
version := UNKNOWN_VERSION_STRING
}
}
}
/**
* @notice Get if stored data are compressed for given contract bytecode
* @param _address address of deployed contract with bytecode stored
* @param _version version string of the bytecode library used to deploy the contract at `_address`
* @return isCompressed bool indicating if the stored data are compressed
*/
function _isCompressedAt(
address _address,
bytes32 _version
) private view returns (bool isCompressed) {
// @dev if branch no coverage - unreachable as used, remains for redundant safety
if (_version == V0_VERSION_STRING || _version == V1_VERSION_STRING) {
// V0 and V1 and unknown contracts do not support compression
return false;
}
// @dev if branch no coverage - unreachable as used, remains for redundant safety
if (_version != V2_VERSION_STRING) {
// unsupported version, throw error
revert("ContractAsStorage: Unsupported Version");
}
// get the size of the data
uint256 bytecodeSize = _bytecodeSizeAt(_address);
// handle case where address contains code < minimum expected version string size,
// by returning early with false
if (bytecodeSize < (COMPRESSION_OFFSET + 1)) {
return false;
}
assembly {
// allocate free memory
let compressedByte := mload(0x40)
// shift free memory pointer by one slot
mstore(0x40, add(mload(0x40), 0x20))
// zero out word at compressedByte (solidity does not guarantee zeroed memory beyond free memory pointer)
mstore(compressedByte, 0x00)
// copy the 1-byte compressed flag of the bytecode library to memory
// note: this relies on the assumption noted at the top-level of
// this file that the storage layout for the deployed
// contracts-as-storage contract looks like:
// | invalid opcode | version-string (unless v0) | deployer-address (padded) | isCompressed | data |
extcodecopy(
_address,
compressedByte,
COMPRESSION_OFFSET,
0x1 // 1-byte version string
)
// check if the compressed flag is set
switch mload(compressedByte)
case 0x00 {
isCompressed := false
}
default {
isCompressed := true
}
}
}
}
/**
* @title Art Blocks Script Storage Library (Internal, Writes)
* @author Art Blocks Inc.
* @notice The internal library for writing to storage contracts. This library is intended to be deployed
* within library client contracts that use this library to perform _write_ operations on storage.
*/
library BytecodeStorageWriter {
/*//////////////////////////////////////////////////////////////
WRITE LOGIC
//////////////////////////////////////////////////////////////*/
/**
* @notice Write a string to contract bytecode
* @param _data string to be written to contract. No input validation is performed on this parameter.
* @return address_ address of deployed contract with bytecode stored in the V2 format
*/
function writeToBytecode(
string memory _data
) internal returns (address address_) {
// prefix bytecode with
bytes memory creationCode = abi.encodePacked(
//---------------------------------------------------------------------------------------------------------------//
// Opcode | Opcode + Arguments | Description | Stack View //
//---------------------------------------------------------------------------------------------------------------//
// a.) creation code returns all code in the contract except for the first 11 (0B in hex) bytes, as these 11
// bytes are the creation code itself which we do not want to store in the deployed storage contract result
//---------------------------------------------------------------------------------------------------------------//
// 0x60 | 0x60_0B | PUSH1 11 | codeOffset //
// 0x59 | 0x59 | MSIZE | 0 codeOffset //
// 0x81 | 0x81 | DUP2 | codeOffset 0 codeOffset //
// 0x38 | 0x38 | CODESIZE | codeSize codeOffset 0 codeOffset //
// 0x03 | 0x03 | SUB | (codeSize - codeOffset) 0 codeOffset //
// 0x80 | 0x80 | DUP | (codeSize - codeOffset) (codeSize - codeOffset) 0 codeOffset //
// 0x92 | 0x92 | SWAP3 | codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) //
// 0x59 | 0x59 | MSIZE | 0 codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) //
// 0x39 | 0x39 | CODECOPY | 0 (codeSize - codeOffset) //
// 0xF3 | 0xF3 | RETURN | //
//---------------------------------------------------------------------------------------------------------------//
// (11 bytes)
hex"60_0B_59_81_38_03_80_92_59_39_F3",
//---------------------------------------------------------------------------------------------------------------//
// b.) ensure that the deployed storage contract is non-executeable (first opcode is the `invalid` opcode)
//---------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------//
// 0xFE | 0xFE | INVALID | //
//---------------------------------------------------------------------------------------------------------------//
// (1 byte)
hex"FE",
//---------------------------------------------------------------------------------------------------------------//
// c.) store the version string, which is already represented as a 32-byte value
//---------------------------------------------------------------------------------------------------------------//
// (32 bytes)
BytecodeStorageReader.CURRENT_VERSION,
//---------------------------------------------------------------------------------------------------------------//
// d.) store the deploying-contract's address with 0-padding to fit a 20-byte address into a 32-byte slot
//---------------------------------------------------------------------------------------------------------------//
// (12 bytes)
hex"00_00_00_00_00_00_00_00_00_00_00_00",
// (20 bytes)
address(this),
//---------------------------------------------------------------------------------------------------------------//
// e.) store the bool indicating if the data is compressed. true for this function.
//---------------------------------------------------------------------------------------------------------------//
// (1 byte)
hex"00",
// uploaded data (stored as bytecode) comes last
_data
);
assembly {
// deploy a new contract with the generated creation code.
// start 32 bytes into creationCode to avoid copying the byte length.
address_ := create(0, add(creationCode, 0x20), mload(creationCode))
}
// address must be non-zero if contract was deployed successfully
require(address_ != address(0), "ContractAsStorage: Write Error");
}
/**
* @notice Write a string to contract bytecode, input as compressed bytes from solady LibZip
* @param _dataCompressed compressed bytes to be written to contract. No input validation is performed on this parameter.
* @return address_ address of deployed contract with bytecode stored in the V2 format
*/
function writeToBytecodeCompressed(
bytes memory _dataCompressed
) internal returns (address address_) {
// prefix bytecode with
bytes memory creationCode = abi.encodePacked(
//---------------------------------------------------------------------------------------------------------------//
// Opcode | Opcode + Arguments | Description | Stack View //
//---------------------------------------------------------------------------------------------------------------//
// a.) creation code returns all code in the contract except for the first 11 (0B in hex) bytes, as these 11
// bytes are the creation code itself which we do not want to store in the deployed storage contract result
//---------------------------------------------------------------------------------------------------------------//
// 0x60 | 0x60_0B | PUSH1 11 | codeOffset //
// 0x59 | 0x59 | MSIZE | 0 codeOffset //
// 0x81 | 0x81 | DUP2 | codeOffset 0 codeOffset //
// 0x38 | 0x38 | CODESIZE | codeSize codeOffset 0 codeOffset //
// 0x03 | 0x03 | SUB | (codeSize - codeOffset) 0 codeOffset //
// 0x80 | 0x80 | DUP | (codeSize - codeOffset) (codeSize - codeOffset) 0 codeOffset //
// 0x92 | 0x92 | SWAP3 | codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) //
// 0x59 | 0x59 | MSIZE | 0 codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) //
// 0x39 | 0x39 | CODECOPY | 0 (codeSize - codeOffset) //
// 0xF3 | 0xF3 | RETURN | //
//---------------------------------------------------------------------------------------------------------------//
// (11 bytes)
hex"60_0B_59_81_38_03_80_92_59_39_F3",
//---------------------------------------------------------------------------------------------------------------//
// b.) ensure that the deployed storage contract is non-executeable (first opcode is the `invalid` opcode)
//---------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------//
// 0xFE | 0xFE | INVALID | //
//---------------------------------------------------------------------------------------------------------------//
// (1 byte)
hex"FE",
//---------------------------------------------------------------------------------------------------------------//
// c.) store the version string, which is already represented as a 32-byte value
//---------------------------------------------------------------------------------------------------------------//
// (32 bytes)
BytecodeStorageReader.CURRENT_VERSION,
//---------------------------------------------------------------------------------------------------------------//
// d.) store the deploying-contract's address with 0-padding to fit a 20-byte address into a 32-byte slot
//---------------------------------------------------------------------------------------------------------------//
// (12 bytes)
hex"00_00_00_00_00_00_00_00_00_00_00_00",
// (20 bytes)
address(this),
//---------------------------------------------------------------------------------------------------------------//
// e.) store the bool indicating if the data is compressed. true for this function.
//---------------------------------------------------------------------------------------------------------------//
// (1 byte)
hex"01",
// uploaded compressed data (stored as bytecode) comes last
_dataCompressed
);
assembly {
// deploy a new contract with the generated creation code.
// start 32 bytes into creationCode to avoid copying the byte length.
address_ := create(0, add(creationCode, 0x20), mload(creationCode))
}
// address must be non-zero if contract was deployed successfully
require(address_ != address(0), "ContractAsStorage: Write Error");
}
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
// Inspired by: https://ethereum.stackexchange.com/a/123950/103422
pragma solidity ^0.8.0;
/**
* @dev Operations on bytes32 data type, dealing with conversion to string.
*/
library Bytes32Strings {
/**
* @notice Intended to convert a `bytes32`-encoded string literal to `string`.
* Trims zero padding to arrive at original string literal.
*/
function toString(
bytes32 source
) internal pure returns (string memory result) {
uint8 length;
while (source[length] != 0 && length < 32) {
length++;
}
assembly {
// free memory pointer
result := mload(0x40)
// update free memory pointer to new "memory end"
// (offset is 64-bytes: 32 for length, 32 for data)
mstore(0x40, add(result, 0x40))
// store length in first 32-byte memory slot
mstore(result, length)
// write actual data in second 32-byte memory slot
mstore(add(result, 0x20), source)
}
}
/**
* @notice Intended to check if a `bytes32`-encoded string contains a given
* character with UTF-8 character code `utf8CharCode exactly `targetQty`
* times. Does not support searching for multi-byte characters, only
* characters with UTF-8 character codes < 0x80.
*/
function containsExactCharacterQty(
bytes32 source,
uint8 utf8CharCode,
uint8 targetQty
) internal pure returns (bool) {
uint8 _occurrences;
uint8 i;
for (i; i < 32; ) {
uint8 _charCode = uint8(source[i]);
// if not a null byte, or a multi-byte UTF-8 character, check match
if (_charCode != 0 && _charCode < 0x80) {
if (_charCode == utf8CharCode) {
unchecked {
// no risk of overflow since max 32 iterations < max uin8=255
++_occurrences;
}
}
}
unchecked {
// no risk of overflow since max 32 iterations < max uin8=255
++i;
}
}
return _occurrences == targetQty;
}
/**
* @notice Converts string to bytes32.
* https://ethereum.stackexchange.com/a/9152
* @param source string to be converted.
* @return result bytes32 representation of string.
* @dev If string is longer than 32 bytes, it truncates to the right.
*/
function stringToBytes32(
string memory source
) internal pure returns (bytes32 result) {
bytes memory tempEmptyStringTest = bytes(source);
if (tempEmptyStringTest.length == 0) {
return 0x0;
}
assembly ("memory-safe") {
result := mload(add(source, 32))
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/ERC721.sol)
pragma solidity ^0.8.20;
import {IERC721} from "@openzeppelin-5.0/contracts/token/ERC721/IERC721.sol";
import {IERC721Receiver} from "@openzeppelin-5.0/contracts/token/ERC721/IERC721Receiver.sol";
import {IERC721Metadata} from "@openzeppelin-5.0/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import {Context} from "@openzeppelin-5.0/contracts/utils/Context.sol";
import {Strings} from "@openzeppelin-5.0/contracts/utils/Strings.sol";
import {IERC165, ERC165} from "@openzeppelin-5.0/contracts/utils/introspection/ERC165.sol";
import {IERC721Errors} from "@openzeppelin-5.0/contracts/interfaces/draft-IERC6093.sol";
/**
* @dev Forked version of the OpenZeppelin v5.0.1 ERC721 contract. Updated
* with an initialize function to ensure EIP 1167 compatibility. Utilizes
* a struct to pack owner and hash seed into a single storage slot.
* ---------------------
* @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
* the Metadata extension, but not including the Enumerable extension, which is available separately as
* {ERC721Enumerable}.
*/
abstract contract ERC721_PackedHashSeedV1 is
Context,
ERC165,
IERC721,
IERC721Metadata,
IERC721Errors
{
using Strings for uint256;
// Token name
string private _name;
// Token symbol
string private _symbol;
/// ensure initialization can only be performed once
bool private _initialized;
/// struct to pack a token owner and hash seed into same storage slot
struct OwnerAndHashSeed {
// 20 bytes for address of token's owner
address owner;
// remaining 12 bytes allocated to token hash seed
bytes12 hashSeed;
}
/// mapping of token ID to OwnerAndHashSeed
/// @dev visibility internal so inheriting contracts can access
mapping(uint256 tokenId => OwnerAndHashSeed) internal _ownersAndHashSeeds;
mapping(address owner => uint256) private _balances;
mapping(uint256 tokenId => address) private _tokenApprovals;
mapping(address owner => mapping(address operator => bool))
private _operatorApprovals;
// @dev constructor intentionally removed to allow for EIP 1167 compatibility,
// see `initialize` function for contract initialization
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(
bytes4 interfaceId
) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC721-balanceOf}.
*/
function balanceOf(address owner) public view virtual returns (uint256) {
if (owner == address(0)) {
revert ERC721InvalidOwner(address(0));
}
return _balances[owner];
}
/**
* @dev See {IERC721-ownerOf}.
*/
function ownerOf(uint256 tokenId) public view virtual returns (address) {
return _requireOwned(tokenId);
}
/**
* @dev See {IERC721Metadata-name}.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev See {IERC721Metadata-symbol}.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev See {IERC721Metadata-tokenURI}.
*/
function tokenURI(
uint256 tokenId
) public view virtual returns (string memory) {
_requireOwned(tokenId);
string memory baseURI = _baseURI();
return
bytes(baseURI).length > 0
? string.concat(baseURI, tokenId.toString())
: "";
}
/**
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, can be overridden in child contracts.
*/
function _baseURI() internal view virtual returns (string memory) {
return "";
}
/**
* @dev See {IERC721-approve}.
*/
function approve(address to, uint256 tokenId) public virtual {
_approve(to, tokenId, _msgSender());
}
/**
* @dev See {IERC721-getApproved}.
*/
function getApproved(
uint256 tokenId
) public view virtual returns (address) {
_requireOwned(tokenId);
return _getApproved(tokenId);
}
/**
* @dev See {IERC721-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual {
_setApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC721-isApprovedForAll}.
*/
function isApprovedForAll(
address owner,
address operator
) public view virtual returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev See {IERC721-transferFrom}.
*/
function transferFrom(
address from,
address to,
uint256 tokenId
) public virtual {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
// Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists
// (from != 0). Therefore, it is not needed to verify that the return value is not 0 here.
address previousOwner = _update(to, tokenId, _msgSender());
if (previousOwner != from) {
revert ERC721IncorrectOwner(from, tokenId, previousOwner);
}
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) public {
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory data
) public virtual {
transferFrom(from, to, tokenId);
_checkOnERC721Received(from, to, tokenId, data);
}
/**
* @notice Initializes the contract by setting a `name` and a `symbol` to the token collection.
* This function should be called atomically, immediately after deployment.
* Only callable once.
* @param name_ Name for the token collection.
* @param symbol_ Symbol for the token collection.
*/
function initialize(string memory name_, string memory symbol_) internal {
require(!_initialized, "Already initialized");
_name = name_;
_symbol = symbol_;
_initialized = true;
}
/**
* @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
*
* IMPORTANT: Any overrides to this function that add ownership of tokens not tracked by the
* core ERC721 logic MUST be matched with the use of {_increaseBalance} to keep balances
* consistent with ownership. The invariant to preserve is that for any address `a` the value returned by
* `balanceOf(a)` must be equal to the number of tokens such that `_ownerOf(tokenId)` is `a`.
*/
function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
return _ownersAndHashSeeds[tokenId].owner;
}
/**
* @dev Returns the approved address for `tokenId`. Returns 0 if `tokenId` is not minted.
*/
function _getApproved(
uint256 tokenId
) internal view virtual returns (address) {
return _tokenApprovals[tokenId];
}
/**
* @dev Returns whether `spender` is allowed to manage `owner`'s tokens, or `tokenId` in
* particular (ignoring whether it is owned by `owner`).
*
* WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
* assumption.
*/
function _isAuthorized(
address owner,
address spender,
uint256 tokenId
) internal view virtual returns (bool) {
return
spender != address(0) &&
(owner == spender ||
isApprovedForAll(owner, spender) ||
_getApproved(tokenId) == spender);
}
/**
* @dev Checks if `spender` can operate on `tokenId`, assuming the provided `owner` is the actual owner.
* Reverts if `spender` does not have approval from the provided `owner` for the given token or for all its assets
* the `spender` for the specific `tokenId`.
*
* WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
* assumption.
*/
function _checkAuthorized(
address owner,
address spender,
uint256 tokenId
) internal view virtual {
if (!_isAuthorized(owner, spender, tokenId)) {
if (owner == address(0)) {
revert ERC721NonexistentToken(tokenId);
} else {
revert ERC721InsufficientApproval(spender, tokenId);
}
}
}
/**
* @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
*
* NOTE: the value is limited to type(uint128).max. This protect against _balance overflow. It is unrealistic that
* a uint256 would ever overflow from increments when these increments are bounded to uint128 values.
*
* WARNING: Increasing an account's balance using this function tends to be paired with an override of the
* {_ownerOf} function to resolve the ownership of the corresponding tokens so that balances and ownership
* remain consistent with one another.
*/
function _increaseBalance(address account, uint128 value) internal virtual {
unchecked {
_balances[account] += value;
}
}
/**
* @dev Transfers `tokenId` from its current owner to `to`, or alternatively mints (or burns) if the current owner
* (or `to`) is the zero address. Returns the owner of the `tokenId` before the update.
*
* The `auth` argument is optional. If the value passed is non 0, then this function will check that
* `auth` is either the owner of the token, or approved to operate on the token (by the owner).
*
* Emits a {Transfer} event.
*
* NOTE: If overriding this function in a way that tracks balances, see also {_increaseBalance}.
*/
function _update(
address to,
uint256 tokenId,
address auth
) internal virtual returns (address) {
address from = _ownerOf(tokenId);
// Perform (optional) operator check
if (auth != address(0)) {
_checkAuthorized(from, auth, tokenId);
}
// Execute the update
if (from != address(0)) {
// Clear approval. No need to re-authorize or emit the Approval event
_approve(address(0), tokenId, address(0), false);
unchecked {
_balances[from] -= 1;
}
}
if (to != address(0)) {
unchecked {
_balances[to] += 1;
}
}
_ownersAndHashSeeds[tokenId].owner = to;
emit Transfer(from, to, tokenId);
return from;
}
/**
* @dev Mints `tokenId` and transfers it to `to`.
*
* WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
*
* Requirements:
*
* - `tokenId` must not exist.
* - `to` cannot be the zero address.
*
* Emits a {Transfer} event.
*/
function _mint(address to, uint256 tokenId) internal {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
address previousOwner = _update(to, tokenId, address(0));
if (previousOwner != address(0)) {
revert ERC721InvalidSender(address(0));
}
}
/**
* @dev Mints `tokenId`, transfers it to `to` and checks for `to` acceptance.
*
* Requirements:
*
* - `tokenId` must not exist.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeMint(address to, uint256 tokenId) internal {
_safeMint(to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/
function _safeMint(
address to,
uint256 tokenId,
bytes memory data
) internal virtual {
_mint(to, tokenId);
_checkOnERC721Received(address(0), to, tokenId, data);
}
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
* This is an internal function that does not check if the sender is authorized to operate on the token.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/
function _burn(uint256 tokenId) internal {
address previousOwner = _update(address(0), tokenId, address(0));
if (previousOwner == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
}
/**
* @dev Transfers `tokenId` from `from` to `to`.
* As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
*
* Emits a {Transfer} event.
*/
function _transfer(address from, address to, uint256 tokenId) internal {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
address previousOwner = _update(to, tokenId, address(0));
if (previousOwner == address(0)) {
revert ERC721NonexistentToken(tokenId);
} else if (previousOwner != from) {
revert ERC721IncorrectOwner(from, tokenId, previousOwner);
}
}
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking that contract recipients
* are aware of the ERC721 standard to prevent tokens from being forever locked.
*
* `data` is additional data, it has no specified format and it is sent in call to `to`.
*
* This internal function is like {safeTransferFrom} in the sense that it invokes
* {IERC721Receiver-onERC721Received} on the receiver, and can be used to e.g.
* implement alternative mechanisms to perform token transfer, such as signature-based.
*
* Requirements:
*
* - `tokenId` token must exist and be owned by `from`.
* - `to` cannot be the zero address.
* - `from` cannot be the zero address.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeTransfer(address from, address to, uint256 tokenId) internal {
_safeTransfer(from, to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeTransfer-address-address-uint256-}[`_safeTransfer`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/
function _safeTransfer(
address from,
address to,
uint256 tokenId,
bytes memory data
) internal virtual {
_transfer(from, to, tokenId);
_checkOnERC721Received(from, to, tokenId, data);
}
/**
* @dev Approve `to` to operate on `tokenId`
*
* The `auth` argument is optional. If the value passed is non 0, then this function will check that `auth` is
* either the owner of the token, or approved to operate on all tokens held by this owner.
*
* Emits an {Approval} event.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address to, uint256 tokenId, address auth) internal {
_approve(to, tokenId, auth, true);
}
/**
* @dev Variant of `_approve` with an optional flag to enable or disable the {Approval} event. The event is not
* emitted in the context of transfers.
*/
function _approve(
address to,
uint256 tokenId,
address auth,
bool emitEvent
) internal virtual {
// Avoid reading the owner unless necessary
if (emitEvent || auth != address(0)) {
address owner = _requireOwned(tokenId);
// We do not use _isAuthorized because single-token approvals should not be able to call approve
if (
auth != address(0) &&
owner != auth &&
!isApprovedForAll(owner, auth)
) {
revert ERC721InvalidApprover(auth);
}
if (emitEvent) {
emit Approval(owner, to, tokenId);
}
}
_tokenApprovals[tokenId] = to;
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Requirements:
* - operator can't be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function _setApprovalForAll(
address owner,
address operator,
bool approved
) internal virtual {
if (operator == address(0)) {
revert ERC721InvalidOperator(operator);
}
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
/**
* @dev Reverts if the `tokenId` doesn't have a current owner (it hasn't been minted, or it has been burned).
* Returns the owner.
*
* Overrides to ownership logic should be done to {_ownerOf}.
*/
function _requireOwned(uint256 tokenId) internal view returns (address) {
address owner = _ownerOf(tokenId);
if (owner == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
return owner;
}
/**
* @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target address. This will revert if the
* recipient doesn't accept the token transfer. The call is not executed if the target address is not a contract.
*
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param data bytes optional data to send along with the call
*/
function _checkOnERC721Received(
address from,
address to,
uint256 tokenId,
bytes memory data
) private {
if (to.code.length > 0) {
try
IERC721Receiver(to).onERC721Received(
_msgSender(),
from,
tokenId,
data
)
returns (bytes4 retval) {
if (retval != IERC721Receiver.onERC721Received.selector) {
revert ERC721InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
revert ERC721InvalidReceiver(to);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
import {IGenArt721CoreContractV3_Engine_Flex} from "../../interfaces/v0.8.x/IGenArt721CoreContractV3_Engine_Flex.sol";
import {IBytecodeStorageReader_Base} from "../../interfaces/v0.8.x/IBytecodeStorageReader_Base.sol";
import {BytecodeStorageWriter} from "./BytecodeStorageV2.sol";
/**
* @title Art Blocks V3 Engine Flex - External Helper Library
* @notice This library is designed to offload bytecode from the V3 Engine
* Flex contract. It implements logic that may be accessed via DELEGATECALL for
* operations related to the V3 Engine Flex contract.
* @author Art Blocks Inc.
*/
library V3FlexLib {
using BytecodeStorageWriter for string;
using BytecodeStorageWriter for bytes;
// For the purposes of this implementation, due to the limited scope and
// existing legacy infrastructure, the library emits the events
// defined in IGenArt721CoreContractV3_Engine_Flex.sol. The events are
// manually duplicated here
/**
* @notice When an external asset dependency is updated or added, this event is emitted.
* @param _projectId The project ID of the project that was updated.
* @param _index The index of the external asset dependency that was updated.
* @param _cid Field that contains the CID of the dependency if IPFS or ARWEAVE,
* empty string of ONCHAIN, or a string representation of the Art Blocks Dependency
* Registry's `dependencyNameAndVersion` if ART_BLOCKS_DEPENDENCY_REGISTRY.
* @param _dependencyType The type of the external asset dependency.
* @param _externalAssetDependencyCount The number of external asset dependencies.
*/
event ExternalAssetDependencyUpdated(
uint256 indexed _projectId,
uint256 indexed _index,
string _cid,
IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependencyType _dependencyType,
uint24 _externalAssetDependencyCount
);
/**
* @notice The project id `_projectId` has had an external asset dependency removed at index `_index`.
*/
event ExternalAssetDependencyRemoved(
uint256 indexed _projectId,
uint256 indexed _index
);
/**
* @notice The preferred gateway for dependency type `_dependencyType` has been updated to `_gatewayAddress`.
*/
event GatewayUpdated(
IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependencyType indexed _dependencyType,
string _gatewayAddress
);
/**
* @notice The project id `_projectId` has had all external asset dependencies locked.
* @dev This is a one-way operation. Once locked, the external asset dependencies cannot be updated.
*/
event ProjectExternalAssetDependenciesLocked(uint256 indexed _projectId);
// position of V3 Flex Lib storage, using a diamond storage pattern
// for this library
bytes32 constant V3_FLEX_LIB_STORAGE_POSITION =
keccak256("v3flexlib.storage");
// project-level variables
/**
* Struct used to store a project's currently configured price, and
* whether or not the price has been configured.
*/
struct FlexProjectData {
bool externalAssetDependenciesLocked;
uint24 externalAssetDependencyCount;
mapping(uint256 => IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependency) externalAssetDependencies;
}
// Diamond storage pattern is used in this library
struct V3FlexLibStorage {
string preferredIPFSGateway;
string preferredArweaveGateway;
mapping(uint256 projectId => FlexProjectData) flexProjectsData;
}
/**
* @notice Updates preferredIPFSGateway to `_gateway`.
* @param _gateway The new preferred IPFS gateway.
*/
function updateIPFSGateway(string calldata _gateway) external {
s().preferredIPFSGateway = _gateway;
emit GatewayUpdated({
_dependencyType: IGenArt721CoreContractV3_Engine_Flex
.ExternalAssetDependencyType
.IPFS,
_gatewayAddress: _gateway
});
}
/**
* @notice Updates preferredArweaveGateway to `_gateway`.
* @param _gateway The new preferred Arweave gateway.
*/
function updateArweaveGateway(string calldata _gateway) external {
s().preferredArweaveGateway = _gateway;
emit GatewayUpdated({
_dependencyType: IGenArt721CoreContractV3_Engine_Flex
.ExternalAssetDependencyType
.ARWEAVE,
_gatewayAddress: _gateway
});
}
/**
* @notice Locks external asset dependencies for project `_projectId`.
* Reverts if the external asset dependencies are already locked.
* @dev This is a one-way operation. Once locked, the external asset dependencies cannot be updated.
* @param _projectId Project to be locked.
*/
function lockProjectExternalAssetDependencies(uint256 _projectId) external {
FlexProjectData storage flexProjectData = getFlexProjectData(
_projectId
);
_onlyUnlockedProjectExternalAssetDependencies(flexProjectData);
flexProjectData.externalAssetDependenciesLocked = true;
emit ProjectExternalAssetDependenciesLocked(_projectId);
}
/**
* @notice Updates external asset dependency for project `_projectId`.
* @dev Making this an external function adds roughly 1% to the gas cost of adding an asset, but
* significantly reduces the bytecode of contracts using this library.
* @param _projectId Project to be updated.
* @param _index Asset index.
* @param _cidOrData Field that contains the CID of the dependency if IPFS or ARWEAVE,
* empty string of ONCHAIN, or a string representation of the Art Blocks Dependency
* Registry's `dependencyNameAndVersion` if ART_BLOCKS_DEPENDENCY_REGISTRY.
* @param _dependencyType Asset dependency type.
* 0 - IPFS
* 1 - ARWEAVE
* 2 - ONCHAIN
* 3 - ART_BLOCKS_DEPENDENCY_REGISTRY
*/
function updateProjectExternalAssetDependency(
uint256 _projectId,
uint256 _index,
string memory _cidOrData,
IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependencyType _dependencyType
) external {
FlexProjectData storage flexProjectData = getFlexProjectData(
_projectId
);
_onlyUnlockedProjectExternalAssetDependencies(flexProjectData);
uint24 assetCount = flexProjectData.externalAssetDependencyCount;
require(_index < assetCount, "Asset index out of range");
// @dev dependencyNameAndVersion are not validated against the dependency registry
// due to limitations of L1 reads on L2 networks at this time
IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependency
storage _oldDependency = flexProjectData.externalAssetDependencies[
_index
];
IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependencyType _oldDependencyType = _oldDependency
.dependencyType;
// update the asset's dependency type to new value in storage
flexProjectData
.externalAssetDependencies[_index]
.dependencyType = _dependencyType;
// if the incoming dependency type is onchain, we need to write the data to bytecode
if (
_dependencyType ==
IGenArt721CoreContractV3_Engine_Flex
.ExternalAssetDependencyType
.ONCHAIN
) {
if (
_oldDependencyType !=
IGenArt721CoreContractV3_Engine_Flex
.ExternalAssetDependencyType
.ONCHAIN
) {
// we only need to set the cid to an empty string if we are replacing an offchain asset
// an onchain asset will already have an empty cid
flexProjectData.externalAssetDependencies[_index].cid = "";
}
flexProjectData
.externalAssetDependencies[_index]
.bytecodeAddress = _cidOrData.writeToBytecode();
// we don't want to emit data, so we emit the cid as an empty string
_cidOrData = "";
} else {
// incoming dependency type is not ONCHAIN, so we set the cid directly with either
// the incoming cid or string representation of the dependencyNameAndVersion
flexProjectData.externalAssetDependencies[_index].cid = _cidOrData;
// clear any previously populated bytecode address
flexProjectData
.externalAssetDependencies[_index]
.bytecodeAddress = address(0);
}
emit ExternalAssetDependencyUpdated({
_projectId: _projectId,
_index: _index,
_cid: _cidOrData,
_dependencyType: _dependencyType,
_externalAssetDependencyCount: assetCount
});
}
/**
* @notice Updates external asset dependency for project `_projectId` of type
* ONCHAIN using on-chain compression. This function stores the string
* in a compressed format on-chain. For reads, the compressed script is
* decompressed on-chain, ensuring the original text is reconstructed without
* external dependencies.
* @param _projectId Project to be updated.
* @param _index Asset index.
* @param _compressedString Pre-compressed string asset to be added.
*/
function updateProjectExternalAssetDependencyOnChainCompressed(
uint256 _projectId,
uint256 _index,
bytes memory _compressedString
) external {
FlexProjectData storage flexProjectData = getFlexProjectData(
_projectId
);
_onlyUnlockedProjectExternalAssetDependencies(flexProjectData);
// check that the index is within the range of the asset count
uint24 assetCount = flexProjectData.externalAssetDependencyCount;
require(_index < assetCount, "Asset index out of range");
// EFFECTS
// overwrite the relevant fields of the previous asset, assigning bytecodeAddress directly
IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependency
storage currentDependency = flexProjectData
.externalAssetDependencies[_index];
currentDependency.cid = "";
currentDependency.dependencyType = IGenArt721CoreContractV3_Engine_Flex
.ExternalAssetDependencyType
.ONCHAIN;
currentDependency.bytecodeAddress = _compressedString
.writeToBytecodeCompressed();
// emit the event
emit ExternalAssetDependencyUpdated({
_projectId: _projectId,
_index: _index,
_cid: "",
_dependencyType: IGenArt721CoreContractV3_Engine_Flex
.ExternalAssetDependencyType
.ONCHAIN,
_externalAssetDependencyCount: assetCount
});
}
/**
* @notice Updates external asset dependency for project `_projectId` at
* index `_index`, with data at BytecodeStorage-compatible address
* `_assetAddress`.
* @dev This function may point to any BytecodeStorageReader-compatible
* on-chain asset, including a web3call contract.
* @param _projectId Project to be updated.
* @param _index Asset index.
* @param _assetAddress Address of the on-chain asset.
*/
function updateProjectAssetDependencyOnChainAtAddress(
uint256 _projectId,
uint256 _index,
address _assetAddress
) external {
// CHECKS
FlexProjectData storage flexProjectData = getFlexProjectData(
_projectId
);
_onlyUnlockedProjectExternalAssetDependencies(flexProjectData);
uint24 assetCount = flexProjectData.externalAssetDependencyCount;
require(_index < assetCount, "Asset index out of range");
// EFFECTS
// overwrite the relevant fields of the previous asset
IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependency
storage currentDependency = flexProjectData
.externalAssetDependencies[_index];
currentDependency.cid = "";
currentDependency.dependencyType = IGenArt721CoreContractV3_Engine_Flex
.ExternalAssetDependencyType
.ONCHAIN;
currentDependency.bytecodeAddress = _assetAddress;
// emit the event
emit ExternalAssetDependencyUpdated({
_projectId: _projectId,
_index: _index,
_cid: "",
_dependencyType: IGenArt721CoreContractV3_Engine_Flex
.ExternalAssetDependencyType
.ONCHAIN,
_externalAssetDependencyCount: assetCount
});
}
/**
* @notice Removes external asset dependency for project `_projectId` at index `_index`.
* Removal is done by swapping the element to be removed with the last element in the array, then deleting this last element.
* Assets with indices higher than `_index` can have their indices adjusted as a result of this operation.
* @param _projectId Project to be updated.
* @param _index Asset index
*/
function removeProjectExternalAssetDependency(
uint256 _projectId,
uint256 _index
) external {
FlexProjectData storage flexProjectData = getFlexProjectData(
_projectId
);
_onlyUnlockedProjectExternalAssetDependencies(flexProjectData);
// ensure the index is within the range of the asset count
uint24 assetCount = flexProjectData.externalAssetDependencyCount;
require(_index < assetCount, "Asset index out of range");
// @dev solidity underflow will revert on the following statement if assetCount is 0
uint24 lastElementIndex = assetCount - 1;
// for UX purposes, only allow removal of the last lastElementIndex
require(_index == lastElementIndex, "Only removal of last asset");
// @dev simply delete last element; no need to copy last to deleted index due to require statement above
delete flexProjectData.externalAssetDependencies[lastElementIndex];
flexProjectData.externalAssetDependencyCount = lastElementIndex;
emit ExternalAssetDependencyRemoved({
_projectId: _projectId,
_index: _index
});
}
/**
* @notice Adds external asset dependency for project `_projectId`.
* @dev Making this an external function adds roughly 1% to the gas cost of adding an asset, but
* significantly reduces the bytecode of contracts using this library.
* @param _projectId Project to be updated.
* @param _cidOrData Field that contains the CID of the dependency if IPFS or ARWEAVE,
* empty string of ONCHAIN, or a string representation of the Art Blocks Dependency
* Registry's `dependencyNameAndVersion` if ART_BLOCKS_DEPENDENCY_REGISTRY.
* @param _dependencyType Asset dependency type.
* 0 - IPFS
* 1 - ARWEAVE
* 2 - ONCHAIN
* 3 - ART_BLOCKS_DEPENDENCY_REGISTRY
*/
function addProjectExternalAssetDependency(
uint256 _projectId,
string memory _cidOrData,
IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependencyType _dependencyType
) external {
FlexProjectData storage flexProjectData = getFlexProjectData(
_projectId
);
_onlyUnlockedProjectExternalAssetDependencies(flexProjectData);
// @dev dependencyNameAndVersion are not validated against the dependency registry
// due to limitations of L1 reads on L2 networks at this time
uint24 assetCount = flexProjectData.externalAssetDependencyCount;
address _bytecodeAddress = address(0);
// if the incoming dependency type is onchain, we need to write the data to bytecode
if (
_dependencyType ==
IGenArt721CoreContractV3_Engine_Flex
.ExternalAssetDependencyType
.ONCHAIN
) {
_bytecodeAddress = _cidOrData.writeToBytecode();
// we don't want to assign or emit data, so we emit the cid as an empty string
_cidOrData = "";
}
// append the new asset to the end of the project's asset storage array
flexProjectData.externalAssetDependencies[
assetCount
] = IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependency({
cid: _cidOrData,
dependencyType: _dependencyType,
bytecodeAddress: _bytecodeAddress
});
// increment the project's asset count
flexProjectData.externalAssetDependencyCount = assetCount + 1;
// emit event indicating the asset has been added
emit ExternalAssetDependencyUpdated({
_projectId: _projectId,
_index: assetCount,
_cid: _cidOrData,
_dependencyType: _dependencyType,
_externalAssetDependencyCount: assetCount + 1
});
}
/**
* @notice Adds external asset dependency for project `_projectId` of type
* ONCHAIN using on-chain compression. This function stores the string
* in a compressed format on-chain. For reads, the compressed script is
* decompressed on-chain, ensuring the original text is reconstructed without
* external dependencies.
* @param _projectId Project to be updated.
* @param _compressedString Pre-compressed string asset to be added.
*/
function addProjectExternalAssetDependencyOnChainCompressed(
uint256 _projectId,
bytes memory _compressedString
) external {
FlexProjectData storage flexProjectData = getFlexProjectData(
_projectId
);
_onlyUnlockedProjectExternalAssetDependencies(flexProjectData);
// append the new asset to the end of the project's asset storage array
uint24 assetCount = flexProjectData.externalAssetDependencyCount;
flexProjectData.externalAssetDependencies[
assetCount
] = IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependency({
cid: "",
dependencyType: IGenArt721CoreContractV3_Engine_Flex
.ExternalAssetDependencyType
.ONCHAIN,
bytecodeAddress: _compressedString.writeToBytecodeCompressed()
});
// increment the asset count
flexProjectData.externalAssetDependencyCount = assetCount + 1;
// emit event indicating the asset has been added
emit ExternalAssetDependencyUpdated({
_projectId: _projectId,
_index: assetCount,
_cid: "",
_dependencyType: IGenArt721CoreContractV3_Engine_Flex
.ExternalAssetDependencyType
.ONCHAIN,
_externalAssetDependencyCount: assetCount + 1
});
}
/**
* @notice Adds an on-chain external asset dependency for project
* `_projectId`, with data at BytecodeStorage-compatible address
* `_assetAddress`.
* @dev This function may point to any BytecodeStorageReader-compatible
* on-chain asset, including a web3call contract.
* @param _projectId Project to be updated.
* @param _assetAddress Address of the BytecodeStorageReader-compatible on-chain asset.
*/
function addProjectAssetDependencyOnChainAtAddress(
uint256 _projectId,
address _assetAddress
) external {
FlexProjectData storage flexProjectData = getFlexProjectData(
_projectId
);
_onlyUnlockedProjectExternalAssetDependencies(flexProjectData);
// append the new asset to the end of the project's asset storage array
uint24 assetCount = flexProjectData.externalAssetDependencyCount;
flexProjectData.externalAssetDependencies[
assetCount
] = IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependency({
cid: "",
dependencyType: IGenArt721CoreContractV3_Engine_Flex
.ExternalAssetDependencyType
.ONCHAIN,
bytecodeAddress: _assetAddress
});
// increment the asset count
flexProjectData.externalAssetDependencyCount = assetCount + 1;
// emit event indicating the asset has been added
emit ExternalAssetDependencyUpdated({
_projectId: _projectId,
_index: assetCount,
_cid: "",
_dependencyType: IGenArt721CoreContractV3_Engine_Flex
.ExternalAssetDependencyType
.ONCHAIN,
_externalAssetDependencyCount: assetCount + 1
});
}
/**
* @notice Returns external asset dependency count for project `_projectId` at index `_index`.
*/
function projectExternalAssetDependencyCount(
uint256 _projectId
) external view returns (uint256) {
FlexProjectData storage flexProjectData = getFlexProjectData(
_projectId
);
return uint256(flexProjectData.externalAssetDependencyCount);
}
/**
* @notice Returns external asset dependency for project `_projectId` at index `_index`.
* If the dependencyType is ONCHAIN, the `data` field will contain the read BytecodeStorage data and `cid`
* will be an empty string. Conversly, for any other dependencyType, the `data` field will be an empty string
* and the `bytecodeAddress` will point to the zero address.
* @dev If an ONCHAIN dependency is a web3call, the `data` field will be returned per the BytecodeStorageReader
* contract's active spec, which at the time of this writing is the special string "#web3call_contract#".
* @param _projectId Project to get external asset dependency for.
* @param _index Index of the external asset dependency.
* @param _bytecodeStorageReaderContract The bytecode storage reader contract to use for reading on-chain
* dependencies; only used if the dependency is of type ONCHAIN.
*/
function projectExternalAssetDependencyByIndex(
uint256 _projectId,
uint256 _index,
IBytecodeStorageReader_Base _bytecodeStorageReaderContract
)
external
view
returns (
IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependencyWithData
memory
)
{
FlexProjectData storage flexProjectData = getFlexProjectData(
_projectId
);
IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependency
storage _dependency = flexProjectData.externalAssetDependencies[
_index
];
address _bytecodeAddress = _dependency.bytecodeAddress;
return
IGenArt721CoreContractV3_Engine_Flex
.ExternalAssetDependencyWithData({
dependencyType: _dependency.dependencyType,
cid: _dependency.cid,
bytecodeAddress: _bytecodeAddress,
data: (_dependency.dependencyType ==
IGenArt721CoreContractV3_Engine_Flex
.ExternalAssetDependencyType
.ONCHAIN)
? _bytecodeStorageReaderContract.readFromBytecode(
_bytecodeAddress
)
: ""
});
}
/**
* @notice Returns the preferred IPFS gateway.
*/
function preferredIPFSGateway() external view returns (string memory) {
return s().preferredIPFSGateway;
}
/**
* @notice Returns the preferred Arweave gateway.
*/
function preferredArweaveGateway() external view returns (string memory) {
return s().preferredArweaveGateway;
}
/**
* @notice Loads the FlexProjectData for a given project.
* @param projectId Project Id to get FlexProjectData for
*/
function getFlexProjectData(
uint256 projectId
) internal view returns (FlexProjectData storage) {
return s().flexProjectsData[projectId];
}
/**
* @notice Return the storage struct for reading and writing. This library
* uses a diamond storage pattern when managing storage.
* @return storageStruct The V3FlexLibStorage struct.
*/
function s()
internal
pure
returns (V3FlexLibStorage storage storageStruct)
{
bytes32 position = V3_FLEX_LIB_STORAGE_POSITION;
assembly ("memory-safe") {
storageStruct.slot := position
}
}
function _onlyUnlockedProjectExternalAssetDependencies(
FlexProjectData storage flexProjectData
) private view {
require(
!flexProjectData.externalAssetDependenciesLocked,
"External dependencies locked"
);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for compressing and decompressing bytes.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibZip.sol)
/// @author Calldata compression by clabby (https://github.com/clabby/op-kompressor)
/// @author FastLZ by ariya (https://github.com/ariya/FastLZ)
///
/// @dev Note:
/// The accompanying solady.js library includes implementations of
/// FastLZ and calldata operations for convenience.
library LibZip {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* FAST LZ OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// LZ77 implementation based on FastLZ.
// Equivalent to level 1 compression and decompression at the following commit:
// https://github.com/ariya/FastLZ/commit/344eb4025f9ae866ebf7a2ec48850f7113a97a42
// Decompression is backwards compatible.
/// @dev Returns the compressed `data`.
function flzCompress(bytes memory data) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
function ms8(d_, v_) -> _d {
mstore8(d_, v_)
_d := add(d_, 1)
}
function u24(p_) -> _u {
_u := mload(p_)
_u := or(shl(16, byte(2, _u)), or(shl(8, byte(1, _u)), byte(0, _u)))
}
function cmp(p_, q_, e_) -> _l {
for { e_ := sub(e_, q_) } lt(_l, e_) { _l := add(_l, 1) } {
e_ := mul(iszero(byte(0, xor(mload(add(p_, _l)), mload(add(q_, _l))))), e_)
}
}
function literals(runs_, src_, dest_) -> _o {
for { _o := dest_ } iszero(lt(runs_, 0x20)) { runs_ := sub(runs_, 0x20) } {
mstore(ms8(_o, 31), mload(src_))
_o := add(_o, 0x21)
src_ := add(src_, 0x20)
}
if iszero(runs_) { leave }
mstore(ms8(_o, sub(runs_, 1)), mload(src_))
_o := add(1, add(_o, runs_))
}
function mt(l_, d_, o_) -> _o {
for { d_ := sub(d_, 1) } iszero(lt(l_, 263)) { l_ := sub(l_, 262) } {
o_ := ms8(ms8(ms8(o_, add(224, shr(8, d_))), 253), and(0xff, d_))
}
if iszero(lt(l_, 7)) {
_o := ms8(ms8(ms8(o_, add(224, shr(8, d_))), sub(l_, 7)), and(0xff, d_))
leave
}
_o := ms8(ms8(o_, add(shl(5, l_), shr(8, d_))), and(0xff, d_))
}
function setHash(i_, v_) {
let p_ := add(mload(0x40), shl(2, i_))
mstore(p_, xor(mload(p_), shl(224, xor(shr(224, mload(p_)), v_))))
}
function getHash(i_) -> _h {
_h := shr(224, mload(add(mload(0x40), shl(2, i_))))
}
function hash(v_) -> _r {
_r := and(shr(19, mul(2654435769, v_)), 0x1fff)
}
function setNextHash(ip_, ipStart_) -> _ip {
setHash(hash(u24(ip_)), sub(ip_, ipStart_))
_ip := add(ip_, 1)
}
result := mload(0x40)
codecopy(result, codesize(), 0x8000) // Zeroize the hashmap.
let op := add(result, 0x8000)
let a := add(data, 0x20)
let ipStart := a
let ipLimit := sub(add(ipStart, mload(data)), 13)
for { let ip := add(2, a) } lt(ip, ipLimit) {} {
let r := 0
let d := 0
for {} 1 {} {
let s := u24(ip)
let h := hash(s)
r := add(ipStart, getHash(h))
setHash(h, sub(ip, ipStart))
d := sub(ip, r)
if iszero(lt(ip, ipLimit)) { break }
ip := add(ip, 1)
if iszero(gt(d, 0x1fff)) { if eq(s, u24(r)) { break } }
}
if iszero(lt(ip, ipLimit)) { break }
ip := sub(ip, 1)
if gt(ip, a) { op := literals(sub(ip, a), a, op) }
let l := cmp(add(r, 3), add(ip, 3), add(ipLimit, 9))
op := mt(l, d, op)
ip := setNextHash(setNextHash(add(ip, l), ipStart), ipStart)
a := ip
}
// Copy the result to compact the memory, overwriting the hashmap.
let end := sub(literals(sub(add(ipStart, mload(data)), a), a, op), 0x7fe0)
let o := add(result, 0x20)
mstore(result, sub(end, o)) // Store the length.
for {} iszero(gt(o, end)) { o := add(o, 0x20) } { mstore(o, mload(add(o, 0x7fe0))) }
mstore(end, 0) // Zeroize the slot after the string.
mstore(0x40, add(end, 0x20)) // Allocate the memory.
}
}
/// @dev Returns the decompressed `data`.
function flzDecompress(bytes memory data) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let op := add(result, 0x20)
let end := add(add(data, 0x20), mload(data))
for { data := add(data, 0x20) } lt(data, end) {} {
let w := mload(data)
let c := byte(0, w)
let t := shr(5, c)
if iszero(t) {
mstore(op, mload(add(data, 1)))
data := add(data, add(2, c))
op := add(op, add(1, c))
continue
}
for {
let g := eq(t, 7)
let l := add(2, xor(t, mul(g, xor(t, add(7, byte(1, w)))))) // M
let s := add(add(shl(8, and(0x1f, c)), byte(add(1, g), w)), 1) // R
let r := sub(op, s)
let f := xor(s, mul(gt(s, 0x20), xor(s, 0x20)))
let j := 0
} 1 {} {
mstore(add(op, j), mload(add(r, j)))
j := add(j, f)
if lt(j, l) { continue }
data := add(data, add(2, g))
op := add(op, l)
break
}
}
mstore(result, sub(op, add(result, 0x20))) // Store the length.
mstore(op, 0) // Zeroize the slot after the string.
mstore(0x40, add(op, 0x20)) // Allocate the memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CALLDATA OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Calldata compression and decompression using selective run length encoding:
// - Sequences of 0x00 (up to 128 consecutive).
// - Sequences of 0xff (up to 32 consecutive).
//
// A run length encoded block consists of two bytes:
// (0) 0x00
// (1) A control byte with the following bit layout:
// - [7] `0: 0x00, 1: 0xff`.
// - [0..6] `runLength - 1`.
//
// The first 4 bytes are bitwise negated so that the compressed calldata
// can be dispatched into the `fallback` and `receive` functions.
/// @dev Returns the compressed `data`.
function cdCompress(bytes memory data) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
function rle(v_, o_, d_) -> _o, _d {
mstore(o_, shl(240, or(and(0xff, add(d_, 0xff)), and(0x80, v_))))
_o := add(o_, 2)
}
result := mload(0x40)
let o := add(result, 0x20)
let z := 0 // Number of consecutive 0x00.
let y := 0 // Number of consecutive 0xff.
for { let end := add(data, mload(data)) } iszero(eq(data, end)) {} {
data := add(data, 1)
let c := byte(31, mload(data))
if iszero(c) {
if y { o, y := rle(0xff, o, y) }
z := add(z, 1)
if eq(z, 0x80) { o, z := rle(0x00, o, 0x80) }
continue
}
if eq(c, 0xff) {
if z { o, z := rle(0x00, o, z) }
y := add(y, 1)
if eq(y, 0x20) { o, y := rle(0xff, o, 0x20) }
continue
}
if y { o, y := rle(0xff, o, y) }
if z { o, z := rle(0x00, o, z) }
mstore8(o, c)
o := add(o, 1)
}
if y { o, y := rle(0xff, o, y) }
if z { o, z := rle(0x00, o, z) }
// Bitwise negate the first 4 bytes.
mstore(add(result, 4), not(mload(add(result, 4))))
mstore(result, sub(o, add(result, 0x20))) // Store the length.
mstore(o, 0) // Zeroize the slot after the string.
mstore(0x40, add(o, 0x20)) // Allocate the memory.
}
}
/// @dev Returns the decompressed `data`.
function cdDecompress(bytes memory data) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
if mload(data) {
result := mload(0x40)
let o := add(result, 0x20)
let s := add(data, 4)
let v := mload(s)
let end := add(data, mload(data))
mstore(s, not(v)) // Bitwise negate the first 4 bytes.
for {} lt(data, end) {} {
data := add(data, 1)
let c := byte(31, mload(data))
if iszero(c) {
data := add(data, 1)
let d := byte(31, mload(data))
// Fill with either 0xff or 0x00.
mstore(o, not(0))
if iszero(gt(d, 0x7f)) { codecopy(o, codesize(), add(d, 1)) }
o := add(o, add(and(d, 0x7f), 1))
continue
}
mstore8(o, c)
o := add(o, 1)
}
mstore(s, v) // Restore the first 4 bytes.
mstore(result, sub(o, add(result, 0x20))) // Store the length.
mstore(o, 0) // Zeroize the slot after the string.
mstore(0x40, add(o, 0x20)) // Allocate the memory.
}
}
}
/// @dev To be called in the `fallback` function.
/// ```
/// fallback() external payable { LibZip.cdFallback(); }
/// receive() external payable {} // Silence compiler warning to add a `receive` function.
/// ```
/// For efficiency, this function will directly return the results, terminating the context.
/// If called internally, it must be called at the end of the function.
function cdFallback() internal {
assembly {
if iszero(calldatasize()) { return(calldatasize(), calldatasize()) }
let o := 0
let f := not(3) // For negating the first 4 bytes.
for { let i := 0 } lt(i, calldatasize()) {} {
let c := byte(0, xor(add(i, f), calldataload(i)))
i := add(i, 1)
if iszero(c) {
let d := byte(0, xor(add(i, f), calldataload(i)))
i := add(i, 1)
// Fill with either 0xff or 0x00.
mstore(o, not(0))
if iszero(gt(d, 0x7f)) { codecopy(o, codesize(), add(d, 1)) }
o := add(o, add(and(d, 0x7f), 1))
continue
}
mstore8(o, c)
o := add(o, 1)
}
let success := delegatecall(gas(), address(), 0x00, o, codesize(), 0x00)
returndatacopy(0x00, 0x00, returndatasize())
if iszero(success) { revert(0x00, returndatasize()) }
return(0x00, returndatasize())
}
}
}{
"optimizer": {
"enabled": true,
"runs": 10
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {
"contracts/GenArt721CoreV3_Curated_Flex.sol": {
"BytecodeStorageReader": "0x000000000016a5a5ff2fa7799c4bee89ba59b74e",
"V3FlexLib": "0x00000000db6f2ebe627260e411e6c973b7c48a62"
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"components":[{"internalType":"string","name":"tokenName","type":"string"},{"internalType":"string","name":"tokenSymbol","type":"string"},{"internalType":"address","name":"renderProviderAddress","type":"address"},{"internalType":"address","name":"platformProviderAddress","type":"address"},{"internalType":"address","name":"newSuperAdminAddress","type":"address"},{"internalType":"address","name":"randomizerContract","type":"address"},{"internalType":"address","name":"splitProviderAddress","type":"address"},{"internalType":"address","name":"minterFilterAddress","type":"address"},{"internalType":"uint248","name":"startingProjectId","type":"uint248"},{"internalType":"bool","name":"autoApproveArtistSplitProposals","type":"bool"},{"internalType":"bool","name":"nullPlatformProvider","type":"bool"},{"internalType":"bool","name":"allowArtistProjectActivation","type":"bool"}],"internalType":"struct EngineConfiguration","name":"engineConfiguration","type":"tuple"},{"internalType":"address","name":"adminACLContract_","type":"address"},{"internalType":"string","name":"defaultBaseURIHost","type":"string"},{"internalType":"address","name":"bytecodeStorageReaderContract_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721IncorrectOwner","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721InsufficientApproval","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC721InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"ERC721InvalidOperator","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721InvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC721InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC721InvalidSender","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721NonexistentToken","type":"error"},{"inputs":[{"internalType":"enum IGenArt721CoreContractV3_Base.ErrorCodes","name":"_errorCode","type":"uint8"}],"name":"GenArt721Error","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"length","type":"uint256"}],"name":"StringsInsufficientHexLength","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"AcceptedArtistAddressesAndSplits","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"artblocksCurationRegistryAddress","type":"address"}],"name":"ArtBlocksCurationRegistryContractUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"_index","type":"uint256"}],"name":"ExternalAssetDependencyRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"_index","type":"uint256"},{"indexed":false,"internalType":"string","name":"_cid","type":"string"},{"indexed":false,"internalType":"enum IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependencyType","name":"_dependencyType","type":"uint8"},{"indexed":false,"internalType":"uint24","name":"_externalAssetDependencyCount","type":"uint24"}],"name":"ExternalAssetDependencyUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"enum IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependencyType","name":"_dependencyType","type":"uint8"},{"indexed":false,"internalType":"string","name":"_gatewayAddress","type":"string"}],"name":"GatewayUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":true,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_currentMinter","type":"address"}],"name":"MinterUpdated","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":"bytes32","name":"_field","type":"bytes32"}],"name":"PlatformUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"ProjectExternalAssetDependenciesLocked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":true,"internalType":"address","name":"royaltySplitter","type":"address"}],"name":"ProjectRoyaltySplitterUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"},{"indexed":true,"internalType":"bytes32","name":"_update","type":"bytes32"}],"name":"ProjectUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"},{"indexed":false,"internalType":"address","name":"_artistAddress","type":"address"},{"indexed":false,"internalType":"address","name":"_additionalPayeePrimarySales","type":"address"},{"indexed":false,"internalType":"uint256","name":"_additionalPayeePrimarySalesPercentage","type":"uint256"},{"indexed":false,"internalType":"address","name":"_additionalPayeeSecondarySales","type":"address"},{"indexed":false,"internalType":"uint256","name":"_additionalPayeeSecondarySalesPercentage","type":"uint256"}],"name":"ProposedArtistAddressesAndSplits","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"IS_FLAGSHIP","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"PREVIOUS_ART_BLOCKS_CONTRACTS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_projectName","type":"string"},{"internalType":"address payable","name":"_artistAddress","type":"address"}],"name":"addProject","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"address","name":"_assetAddress","type":"address"}],"name":"addProjectAssetDependencyOnChainAtAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"string","name":"_cidOrData","type":"string"},{"internalType":"enum IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependencyType","name":"_dependencyType","type":"uint8"}],"name":"addProjectExternalAssetDependency","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"bytes","name":"_compressedString","type":"bytes"}],"name":"addProjectExternalAssetDependencyOnChainCompressed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"string","name":"_script","type":"string"}],"name":"addProjectScript","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"bytes","name":"_compressedScript","type":"bytes"}],"name":"addProjectScriptCompressed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"address","name":"_contract","type":"address"},{"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"adminACLAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"adminACLContract","outputs":[{"internalType":"contract IAdminACLV0","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"address payable","name":"_artistAddress","type":"address"},{"internalType":"address payable","name":"_additionalPayeePrimarySales","type":"address"},{"internalType":"uint256","name":"_additionalPayeePrimarySalesPercentage","type":"uint256"},{"internalType":"address payable","name":"_additionalPayeeSecondarySales","type":"address"},{"internalType":"uint256","name":"_additionalPayeeSecondarySalesPercentage","type":"uint256"}],"name":"adminAcceptArtistAddressesAndSplits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"allowArtistProjectActivation","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"artblocksCurationRegistryAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"artblocksDependencyRegistryAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"artblocksOnChainGeneratorAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"autoApproveArtistSplitProposals","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bytecodeStorageReaderContract","outputs":[{"internalType":"contract IBytecodeStorageReader_Base","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"coreType","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"coreVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"defaultBaseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultPlatformProviderSecondarySalesAddress","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultPlatformProviderSecondarySalesBPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultRenderProviderSecondarySalesAddress","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultRenderProviderSecondarySalesBPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"forbidNewProjects","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_script","type":"string"}],"name":"getCompressed","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"getHistoricalRandomizerAt","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"getPrimaryRevenueSplits","outputs":[{"internalType":"uint256","name":"renderProviderRevenue_","type":"uint256"},{"internalType":"address payable","name":"renderProviderAddress_","type":"address"},{"internalType":"uint256","name":"platformProviderRevenue_","type":"uint256"},{"internalType":"address payable","name":"platformProviderAddress_","type":"address"},{"internalType":"uint256","name":"artistRevenue_","type":"uint256"},{"internalType":"address payable","name":"artistAddress_","type":"address"},{"internalType":"uint256","name":"additionalPayeePrimaryRevenue_","type":"uint256"},{"internalType":"address payable","name":"additionalPayeePrimaryAddress_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"tokenName","type":"string"},{"internalType":"string","name":"tokenSymbol","type":"string"},{"internalType":"address","name":"renderProviderAddress","type":"address"},{"internalType":"address","name":"platformProviderAddress","type":"address"},{"internalType":"address","name":"newSuperAdminAddress","type":"address"},{"internalType":"address","name":"randomizerContract","type":"address"},{"internalType":"address","name":"splitProviderAddress","type":"address"},{"internalType":"address","name":"minterFilterAddress","type":"address"},{"internalType":"uint248","name":"startingProjectId","type":"uint248"},{"internalType":"bool","name":"autoApproveArtistSplitProposals","type":"bool"},{"internalType":"bool","name":"nullPlatformProvider","type":"bool"},{"internalType":"bool","name":"allowArtistProjectActivation","type":"bool"}],"internalType":"struct EngineConfiguration","name":"","type":"tuple"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_minter","type":"address"}],"name":"isMintWhitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"lockProjectExternalAssetDependencies","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"address","name":"_by","type":"address"}],"name":"mint_Ecf","outputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"minterContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"newProjectsForbidden","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextCoreContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextProjectId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nullPlatformProvider","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numHistoricalRandomizers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"platformProviderPrimarySalesAddress","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"platformProviderPrimarySalesPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"preferredArweaveGateway","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"preferredIPFSGateway","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"projectDetails","outputs":[{"internalType":"string","name":"projectName","type":"string"},{"internalType":"string","name":"artist","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"string","name":"website","type":"string"},{"internalType":"string","name":"license","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"projectExternalAssetDependencyByIndex","outputs":[{"components":[{"internalType":"string","name":"cid","type":"string"},{"internalType":"enum IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependencyType","name":"dependencyType","type":"uint8"},{"internalType":"address","name":"bytecodeAddress","type":"address"},{"internalType":"string","name":"data","type":"string"}],"internalType":"struct IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependencyWithData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"projectExternalAssetDependencyCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"projectIdToArtistAddress","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"projectIdToFinancials","outputs":[{"components":[{"internalType":"address payable","name":"additionalPayeePrimarySales","type":"address"},{"internalType":"uint8","name":"secondaryMarketRoyaltyPercentage","type":"uint8"},{"internalType":"address payable","name":"additionalPayeeSecondarySales","type":"address"},{"internalType":"uint8","name":"additionalPayeeSecondarySalesPercentage","type":"uint8"},{"internalType":"address payable","name":"artistAddress","type":"address"},{"internalType":"uint8","name":"additionalPayeePrimarySalesPercentage","type":"uint8"},{"internalType":"address","name":"platformProviderSecondarySalesAddress","type":"address"},{"internalType":"uint16","name":"platformProviderSecondarySalesBPS","type":"uint16"},{"internalType":"address","name":"renderProviderSecondarySalesAddress","type":"address"},{"internalType":"uint16","name":"renderProviderSecondarySalesBPS","type":"uint16"},{"internalType":"address","name":"royaltySplitter","type":"address"}],"internalType":"struct IGenArt721CoreContractV3_ProjectFinance.ProjectFinance","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"projectIdToSecondaryMarketRoyaltyPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"projectScriptByIndex","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"projectScriptBytecodeAddressByIndex","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"projectScriptDetails","outputs":[{"internalType":"string","name":"scriptTypeAndVersion","type":"string"},{"internalType":"string","name":"aspectRatio","type":"string"},{"internalType":"uint256","name":"scriptCount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"projectStateData","outputs":[{"internalType":"uint256","name":"invocations","type":"uint256"},{"internalType":"uint256","name":"maxInvocations","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"bool","name":"paused","type":"bool"},{"internalType":"uint256","name":"completedTimestamp","type":"uint256"},{"internalType":"bool","name":"locked","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"projectURIInfo","outputs":[{"internalType":"string","name":"projectBaseURI","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"address payable","name":"_artistAddress","type":"address"},{"internalType":"address payable","name":"_additionalPayeePrimarySales","type":"address"},{"internalType":"uint256","name":"_additionalPayeePrimarySalesPercentage","type":"uint256"},{"internalType":"address payable","name":"_additionalPayeeSecondarySales","type":"address"},{"internalType":"uint256","name":"_additionalPayeeSecondarySalesPercentage","type":"uint256"}],"name":"proposeArtistPaymentAddressesAndSplits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"proposedArtistAddressesAndSplitsHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"randomizerContract","outputs":[{"internalType":"contract IRandomizer_V3CoreBase","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"removeProjectExternalAssetDependency","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"removeProjectLastScript","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renderProviderPrimarySalesAddress","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renderProviderPrimarySalesPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"royaltyAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"bytes32","name":"_hashSeed","type":"bytes32"}],"name":"setTokenHash_8PT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"splitProvider","outputs":[{"internalType":"contract ISplitProviderV0","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startingProjectId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"syncProviderSecondaryForProjectToDefaults","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"toggleProjectIsActive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"toggleProjectIsPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenIdToHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenIdToHashSeed","outputs":[{"internalType":"bytes12","name":"","type":"bytes12"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenIdToProjectId","outputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_artblocksCurationRegistryAddress","type":"address"}],"name":"updateArtblocksCurationRegistryAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_artblocksDependencyRegistryAddress","type":"address"}],"name":"updateArtblocksDependencyRegistryAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_artblocksOnChainGeneratorAddress","type":"address"}],"name":"updateArtblocksOnChainGeneratorAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_gateway","type":"string"}],"name":"updateArweaveGateway","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_bytecodeStorageReaderContract","type":"address"}],"name":"updateBytecodeStorageReaderContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_defaultBaseURI","type":"string"}],"name":"updateDefaultBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_gateway","type":"string"}],"name":"updateIPFSGateway","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"updateMinterContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_nextCoreContract","type":"address"}],"name":"updateNextCoreContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"address payable","name":"_artistAddress","type":"address"}],"name":"updateProjectArtistAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"string","name":"_projectArtistName","type":"string"}],"name":"updateProjectArtistName","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"string","name":"_aspectRatio","type":"string"}],"name":"updateProjectAspectRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint256","name":"_index","type":"uint256"},{"internalType":"address","name":"_assetAddress","type":"address"}],"name":"updateProjectAssetDependencyOnChainAtAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"string","name":"_newBaseURI","type":"string"}],"name":"updateProjectBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"string","name":"_projectDescription","type":"string"}],"name":"updateProjectDescription","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint256","name":"_index","type":"uint256"},{"internalType":"string","name":"_cidOrData","type":"string"},{"internalType":"enum IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependencyType","name":"_dependencyType","type":"uint8"}],"name":"updateProjectExternalAssetDependency","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint256","name":"_index","type":"uint256"},{"internalType":"bytes","name":"_compressedString","type":"bytes"}],"name":"updateProjectExternalAssetDependencyOnChainCompressed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"string","name":"_projectLicense","type":"string"}],"name":"updateProjectLicense","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint24","name":"_maxInvocations","type":"uint24"}],"name":"updateProjectMaxInvocations","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"string","name":"_projectName","type":"string"}],"name":"updateProjectName","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint256","name":"_scriptId","type":"uint256"},{"internalType":"string","name":"_script","type":"string"}],"name":"updateProjectScript","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint256","name":"_scriptId","type":"uint256"},{"internalType":"bytes","name":"_compressedScript","type":"bytes"}],"name":"updateProjectScriptCompressed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"bytes32","name":"_scriptTypeAndVersion","type":"bytes32"}],"name":"updateProjectScriptType","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint256","name":"_secondaryMarketRoyalty","type":"uint256"}],"name":"updateProjectSecondaryMarketRoyaltyPercentage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"string","name":"_projectWebsite","type":"string"}],"name":"updateProjectWebsite","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_defaultRenderProviderSecondarySalesBPS","type":"uint256"},{"internalType":"uint256","name":"_defaultPlatformProviderSecondarySalesBPS","type":"uint256"}],"name":"updateProviderDefaultSecondarySalesBPS","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"renderProviderPrimarySalesPercentage_","type":"uint256"},{"internalType":"uint256","name":"platformProviderPrimarySalesPercentage_","type":"uint256"}],"name":"updateProviderPrimarySalesPercentages","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"_renderProviderPrimarySalesAddress","type":"address"},{"internalType":"address payable","name":"_defaultRenderProviderSecondarySalesAddress","type":"address"},{"internalType":"address payable","name":"_platformProviderPrimarySalesAddress","type":"address"},{"internalType":"address payable","name":"_defaultPlatformProviderSecondarySalesAddress","type":"address"}],"name":"updateProviderSalesAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_randomizerAddress","type":"address"}],"name":"updateRandomizerAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_splitProviderAddress","type":"address"}],"name":"updateSplitProvider","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
61010060405273059edd72cd353df5106d2b9cc5ab83a52287ac3a608090815273a7d8d9ef8d8ce8992df33d8b8cf4aebabd5bd27060a0527399a9b7c1116f9ceeb1652de04d5969cce509b06960c05273ab0000000000aa06f89b268d604a9c1c41524ac660e0526200007790601e90600462000ac2565b503480156200008557600080fd5b5060405162006eb438038062006eb4833981016040819052620000a89162000c82565b61dead620000ba565b60405180910390fd5b620000c581620003cd565b506040840151620000d690620003fa565b60a0840151620000e690620003fa565b620000f183620003fa565b620000fc81620003fa565b6000825111620001805760405162461bcd60e51b815260206004820152604260248201527f47656e417274373231436f726556335f437572617465645f466c65783a20646560448201527f6661756c7442617365555249486f7374206d757374206265206e6f6e2d656d70606482015261747960f01b608482015260a401620000b1565b836101200151156200020f5760405162461bcd60e51b815260206004820152604b60248201527f47656e417274373231436f726556335f437572617465645f466c65783a20617560448201527f746f417070726f766541727469737453706c697450726f706f73616c73206d7560648201526a73742062652066616c736560a81b608482015260a401620000b1565b836101400151620002895760405162461bcd60e51b815260206004820152603f60248201527f47656e417274373231436f726556335f437572617465645f466c65783a206e7560448201527f6c6c506c6174666f726d50726f7669646572206d7573742062652074727565006064820152608401620000b1565b83610160015115620003155760405162461bcd60e51b815260206004820152604860248201527f47656e417274373231436f726556335f437572617465645f466c65783a20616c60448201527f6c6f7741727469737450726f6a65637441637469766174696f6e206d7573742060648201526762652066616c736560c01b608482015260a401620000b1565b60008461010001516001600160f81b031611620003aa5760405162461bcd60e51b815260206004820152604660248201527f47656e417274373231436f726556335f437572617465645f466c65783a20737460448201527f617274696e6750726f6a6563744964206d75737420626520677265617465722060648201526507468616e20360d41b608482015260a401620000b1565b620003b8848484846200042a565b620003c38262000643565b5050505062001068565b620003d88162000670565b600d80546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0381166200042757600060405163d327ad1b60e01b8152600401620000b1919062000e2d565b50565b600a54600160a01b900460ff16156200045b57602560405163d327ad1b60e01b8152600401620000b1919062000e2d565b600a8054600160a01b60ff60a01b199182161790915560118054909116600560a11b17905560fa6014556101408401516200049857600a6200049b565b60005b601260146101000a81548160ff021916908360ff160217905550836101400151620004c85760fa620004cb565b60005b60ff1660165583516020850151620004e49190620006c2565b60e08401516001600160a01b031615620005085760e0840151620005089062000746565b60c0840151620005189062000790565b6200052381620007be565b610120840151601a805461014087015161016088015161ffff1990921693151561ff0019169390931761010093151584021762ff0000191662010000911515919091021790558401516001600160f81b031660185560408401516060850151620005919190819080620007e1565b60a0840151620005a190620008a4565b620005ac83620003cd565b620005e582620005bc3062000907565b604051602001620005cf92919062000e56565b60408051601f1981840301815291905262000643565b610100840151601980547fff00000000000000000000000000000000000000000000000000000000000000166001600160f81b0390921691909117905560005b60405160008051602062006e9483398151915290600090a250505050565b601b62000651828262000f26565b5060025b60405160008051602062006e9483398151915290600090a250565b600780546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60025460ff1615620007175760405162461bcd60e51b815260206004820152601360248201527f416c726561647920696e697469616c697a6564000000000000000000000000006044820152606401620000b1565b600062000725838262000f26565b50600162000734828262000f26565b50506002805460ff1916600117905550565b601780546001600160a01b0319166001600160a01b0383169081179091556040517fad0f299ec81a386c98df0ac27dae11dd020ed1b56963c53a7292e7a3a314539a90600090a250565b6200079b81620003fa565b601c80546001600160a01b0319166001600160a01b038316179055600a62000655565b601d80546001600160a01b0319166001600160a01b038316179055600b62000655565b601a54610100900460ff16156200083c576001600160a01b0382161515806200081257506001600160a01b03811615155b156200083657602460405163d327ad1b60e01b8152600401620000b1919062000e2d565b62000852565b6200084782620003fa565b6200085281620003fa565b601280546001600160a01b038085166001600160a01b03199283161790925560158054848416908316179055601180548784169083161790556013805492861692909116919091179055600762000625565b600b80546001600160a01b0383166001600160a01b03199182168117909255600c80546001810182556000919091527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c70180549091169091179055600362000655565b60606200091f6001600160a01b038316601462000925565b92915050565b60608260006200093784600262001008565b6200094490600262001022565b6001600160401b038111156200095e576200095e62000b43565b6040519080825280601f01601f19166020018201604052801562000989576020820181803683370190505b509050600360fc1b81600081518110620009a757620009a762001038565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110620009d957620009d962001038565b60200101906001600160f81b031916908160001a9053506000620009ff85600262001008565b62000a0c90600162001022565b90505b600181111562000a8e576f181899199a1a9b1b9c1cb0b131b232b360811b83600f166010811062000a445762000a4462001038565b1a60f81b82828151811062000a5d5762000a5d62001038565b60200101906001600160f81b031916908160001a90535060049290921c9162000a86816200104e565b905062000a0f565b50811562000aba5760405163e22e27eb60e01b81526004810186905260248101859052604401620000b1565b949350505050565b82805482825590600052602060002090810192821562000b1a579160200282015b8281111562000b1a57825182546001600160a01b0319166001600160a01b0390911617825560209092019160019091019062000ae3565b5062000b2892915062000b2c565b5090565b5b8082111562000b28576000815560010162000b2d565b634e487b7160e01b600052604160045260246000fd5b60405161018081016001600160401b038111828210171562000b7f5762000b7f62000b43565b60405290565b60005b8381101562000ba257818101518382015260200162000b88565b50506000910152565b600082601f83011262000bbd57600080fd5b81516001600160401b038082111562000bda5762000bda62000b43565b604051601f8301601f19908116603f0116810190828211818310171562000c055762000c0562000b43565b8160405283815286602085880101111562000c1f57600080fd5b62000c3284602083016020890162000b85565b9695505050505050565b80516001600160a01b038116811462000c5457600080fd5b919050565b80516001600160f81b038116811462000c5457600080fd5b8051801515811462000c5457600080fd5b6000806000806080858703121562000c9957600080fd5b84516001600160401b038082111562000cb157600080fd5b90860190610180828903121562000cc757600080fd5b62000cd162000b59565b82518281111562000ce157600080fd5b62000cef8a82860162000bab565b82525060208301518281111562000d0557600080fd5b62000d138a82860162000bab565b60208301525062000d276040840162000c3c565b604082015262000d3a6060840162000c3c565b606082015262000d4d6080840162000c3c565b608082015262000d6060a0840162000c3c565b60a082015262000d7360c0840162000c3c565b60c082015262000d8660e0840162000c3c565b60e082015261010062000d9b81850162000c59565b9082015261012062000daf84820162000c71565b9082015261014062000dc384820162000c71565b9082015261016062000dd784820162000c71565b90820152955062000deb6020880162000c3c565b9450604087015191508082111562000e0257600080fd5b5062000e118782880162000bab565b92505062000e226060860162000c3c565b905092959194509250565b602081016026831062000e5057634e487b7160e01b600052602160045260246000fd5b91905290565b6000835162000e6a81846020880162000b85565b83519083019062000e8081836020880162000b85565b602f60f81b9101908152600101949350505050565b600181811c9082168062000eaa57607f821691505b60208210810362000ecb57634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111562000f21576000816000526020600020601f850160051c8101602086101562000efc5750805b601f850160051c820191505b8181101562000f1d5782815560010162000f08565b5050505b505050565b81516001600160401b0381111562000f425762000f4262000b43565b62000f5a8162000f53845462000e95565b8462000ed1565b602080601f83116001811462000f92576000841562000f795750858301515b600019600386901b1c1916600185901b17855562000f1d565b600085815260208120601f198616915b8281101562000fc35788860151825594840194600190910190840162000fa2565b508582101562000fe25787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176200091f576200091f62000ff2565b808201808211156200091f576200091f62000ff2565b634e487b7160e01b600052603260045260246000fd5b60008162001060576200106062000ff2565b506000190190565b615e1c80620010786000396000f3fe608060405234801561001057600080fd5b50600436106105335760003560e01c8063801aa941116102b8578063801aa94114610b125780638639415b14610b255780638997618f14610b805780638c3c9cdd14610b935780638da5cb5b14610ba65780638dd91a5614610bae57806392f0023314610bd25780639424702b14610be55780639523751714610bf85780639549179514610c0b57806395d89b4114610c1e578063993c0cbf14610c265780639a02e4fa14610c395780639d97f4a014610c70578063a0bee56414610c83578063a11ec70a14610c96578063a22cb46514610ca9578063a3b2cca614610cbc578063a47d29cb14610ccf578063abcbb7b414610cfb578063ac11fa1c14610d03578063acad012414610d23578063acd4c66f14610d36578063ad0305ce14610d48578063ad28329014610d6a578063ad576c4514610d7d578063ae45ad9814610d90578063b1656ba314610d98578063b168762214610dab578063b202b56514610dbe578063b75395e014610dd1578063b7b04fae14610de4578063b7ba527d14610df7578063b88d4fde14610dff578063b971136814610e12578063ba3c234514610e1a578063bb7dbb6c14610e2d578063bba4448a14610e40578063bd3d10e714610e53578063c34a03b514610e66578063c87b56dd14610e79578063cc90e72514610e8c578063ce90652014610e9f578063d03c390c14610eb2578063d50f513814610ec5578063db2ff86114610ed8578063e32551e714610eeb578063e6032df214610efe578063e935b7b114610f0b578063e985e9c514610f1c578063eb9cd5d414610f2f578063ed8abfda14610f51578063eef719a414610f7b578063f23f702114610f84578063f2fde38b14610f97578063f6cd39e314610faa578063f851a44014610fb2578063f893c07b14610fba578063ffd43f6514610fc357600080fd5b80611e3c146105385780615de51461054d5780630132c6971461057357806301856fd41461058657806301ffc9a71461059957806304143a5c146105bc57806306fdde03146105c4578063081812fc146105d9578063095ea7b3146105f95780630a1df77a1461060c5780630d170673146107375780630e79c9281461074a5780630ea5613f1461075d57806310a9ef18146107a757806317df5366146107ba5780631ab6014c146107cd5780631b689c0b146107e05780631c05cad7146107f35780631e9bef46146108065780632302cbda14610819578063230448b11461082c57806323b872dd1461083f57806325b75d68146108525780632642c6b61461086557806327df6c1a146108855780632a55205a146108985780632b274166146108b95780632b65e67d146108cc5780632d9c0205146108df57806330ef4c5f146108f2578063329dab731461090457806336c7c12c14610917578063378599631461092a57806337fbc9651461093d57806339077c6e14610950578063398a2895146109635780633e48e8481461097657806342842e0e14610989578063483372821461099c5780634b976697146109af5780634e1d64af146109c25780635119d04b146109ca5780635464c309146109d3578063546a26f6146109e65780635508fd52146109f957806357a8e57414610a0d57806358b9a5a914610a20578063621a1f7414610a585780636352211e14610a6b5780636580a1ed14610a7e57806366e4623714610a8657806369d14faf14610a995780636c907b7f14610aac5780636ddba41114610abf5780636ee96f2a14610ad157806370a0823114610ae4578063715018a614610af757806376ee6fab14610aff575b600080fd5b61054b610546366004614995565b610fd6565b005b61056061055b3660046149dc565b61109e565b6040519081526020015b60405180910390f35b61054b610581366004614a1e565b61129e565b61054b610594366004614995565b611365565b6105ac6105a7366004614a6c565b6113e4565b604051901515815260200161056a565b61054b61140f565b6105cc611457565b60405161056a9190614ad9565b6105ec6105e7366004614aec565b6114e9565b60405161056a9190614b12565b61054b610607366004614b26565b6114fe565b61072a61061a366004614aec565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101829052610140810191909152506000908152600f602090815260409182902082516101608101845281546001600160a01b03808216835260ff600160a01b9283900481169584019590955260018401548082169684019690965294819004841660608301526002830154808616608084015281900490931660a0820152600382015480851660c083015261ffff90849004811660e08301526004830154808616610100840152939093049092166101208301526005015490911661014082015290565b60405161056a9190614b52565b61054b610745366004614d41565b61150d565b61054b610758366004614d41565b61156c565b61077061076b366004614aec565b6116c5565b60408051968752602087019590955292151593850193909352151560608401526080830191909152151560a082015260c00161056a565b61054b6107b5366004614d94565b611728565b6009546105ec906001600160a01b031681565b6012546105ec906001600160a01b031681565b6105606107ee366004614aec565b611765565b61054b610801366004614df6565b611774565b600d546105ec906001600160a01b031681565b61054b610827366004614e47565b6117a2565b6105ac61083a366004614e7b565b6117c7565b61054b61084d366004614ebb565b611873565b61054b610860366004614d41565b6118f8565b610878610873366004614995565b61193e565b60405161056a9190614f26565b61054b610893366004614995565b6119f5565b6108ab6108a6366004614995565b611a93565b60405161056a929190614f90565b61054b6108c7366004614fa9565b611b5f565b61054b6108da366004614fc6565b611bb0565b6105cc6108ed366004614aec565b611e1d565b601254600160a01b900460ff16610560565b61054b61091236600461502d565b611ec2565b600b546105ec906001600160a01b031681565b61054b610938366004614d41565b611f7b565b601c546105ec906001600160a01b031681565b61054b61095e36600461507c565b611fa7565b61054b610971366004614aec565b612028565b61054b610984366004614d41565b6120df565b61054b610997366004614ebb565b612114565b61054b6109aa366004614fa9565b612134565b601d546105ec906001600160a01b031681565b6105cc612156565b61056060165481565b61054b6109e13660046150aa565b61216f565b6105ec6109f4366004614aec565b6121ec565b6019546105ac90600160f81b900460ff1681565b6008546105ec906001600160a01b031681565b6105ec610a2e366004614995565b6000918252600e60209081526040808420928452600990920190529020546001600160a01b031690565b610560610a66366004614aec565b612216565b6105ec610a79366004614aec565b612278565b6105ac600181565b61054b610a9436600461511b565b612283565b61054b610aa736600461516a565b6122bf565b61054b610aba366004614fa9565b61231c565b601a546105ac90610100900460ff1681565b61054b610adf36600461516a565b61233e565b610560610af2366004614fa9565b612396565b61054b6123de565b61054b610b0d366004614fc6565b6123f6565b6015546105ec906001600160a01b031681565b610b38610b33366004614995565b61258a565b604080519889526001600160a01b0397881660208a015288019590955292851660608701526080860191909152831660a085015260c08401521660e08201526101000161056a565b600a546105ec906001600160a01b031681565b6105cc610ba1366004614995565b612693565b6105ec6126f2565b610bc1610bbc366004614aec565b612706565b60405161056a95949392919061518f565b6017546105ec906001600160a01b031681565b61054b610bf3366004614d41565b6129a3565b61054b610c06366004614fa9565b6129ed565b61054b610c1936600461522c565b612a28565b6105cc612aa0565b61054b610c34366004614fa9565b612aaf565b610c63610c47366004614aec565b600090815260036020526040902054600160a01b900460a01b90565b60405161056a91906153b1565b61054b610c7e366004614fa9565b612ad1565b61054b610c91366004614fa9565b612aea565b61054b610ca4366004614aec565b612b44565b61054b610cb73660046153c6565b612b97565b61054b610cca366004614d41565b612ba2565b6105ec610cdd366004614aec565b6000908152600f60205260409020600201546001600160a01b031690565b6105cc612c63565b610560610d11366004614aec565b60106020526000908152604090205481565b61054b610d31366004614d41565b612cf1565b601154600160a01b900460ff16610560565b6105ac610d56366004614fa9565b6017546001600160a01b0390811691161490565b610560610d78366004614aec565b612da2565b61054b610d8b366004614995565b612e19565b6105cc612e69565b61054b610da636600461502d565b612e92565b6105ec610db9366004614aec565b612efc565b61054b610dcc366004614d41565b612f52565b6011546105ec906001600160a01b031681565b61054b610df2366004614d41565b612f8c565b6105cc61303a565b61054b610e0d3660046153f4565b6130ad565b600c54610560565b61054b610e28366004614aec565b6130c4565b61054b610e3b36600461502d565b61313b565b601f546105ec906001600160a01b031681565b61054b610e61366004614995565b613187565b61054b610e74366004614995565b613230565b6105cc610e87366004614aec565b613292565b61054b610e9a36600461545f565b61337a565b601a546105ac9062010000900460ff1681565b61054b610ec0366004614aec565b613523565b61054b610ed33660046150aa565b613595565b61054b610ee6366004614aec565b6135de565b6105cc610ef9366004614e47565b6136a9565b601a546105ac9060ff1681565b6019546001600160f81b0316610560565b6105ac610f2a3660046154a5565b613730565b610f42610f3d366004614aec565b61375e565b60405161056a939291906154d3565b610560610f5f366004614aec565b6000908152600f6020526040902054600160a01b900460ff1690565b61056060145481565b61054b610f92366004614fa9565b61382a565b61054b610fa5366004614fa9565b61385c565b6105cc613897565b6105ec6138e2565b61056060185481565b6013546105ec906001600160a01b031681565b610fdf826138ec565b6000828152600360205260409020600b546001600160a01b0316331461102457600e60405163d327ad1b60e01b815260040161101b9190615509565b60405180910390fd5b8054600160a01b900460a01b6001600160a01b0319161561105b57600f60405163d327ad1b60e01b815260040161101b9190615509565b8161107c57601060405163d327ad1b60e01b815260040161101b9190615509565b805460a09290921c600160a01b026001600160a01b0390921691909117905550565b6017546000906001600160a01b031633146110cf57600a60405163d327ad1b60e01b815260040161101b9190615509565b6000838152600e60205260409020805462ffffff80821691600183019163010000009091041680831061111857600b60405163d327ad1b60e01b815260040161101b9190615509565b8354600160881b900460ff168061114b57506000878152600f60205260409020600201546001600160a01b038781169116145b61116b57600c60405163d327ad1b60e01b815260040161101b9190615509565b8354600160901b900460ff1680156111a057506000878152600f60205260409020600201546001600160a01b03878116911614155b156111c157600d60405163d327ad1b60e01b815260040161101b9190615509565b835462ffffff191662ffffff8381169182178655848116620f42408a02019190831690036111f2576111f288613921565b6111fc8982613956565b600b54604051635b140b8d60e11b8152600481018390526001600160a01b039091169063b628171a90602401600060405180830381600087803b15801561124257600080fd5b505af1158015611256573d6000803e3d6000fd5b50506040518392506001600160a01b038c1691507f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d412139688590600090a39450505050505b9392505050565b6112a7826139bb565b6000828152600e60205260409020805462ffffff80821691630100000090048116908416106112ec57601c60405163d327ad1b60e01b815260040161101b9190615509565b808362ffffff16101561131557601d60405163d327ad1b60e01b815260040161101b9190615509565b815465ffffff0000001916630100000062ffffff851602178255600b6040518590600080516020615dc783398151915290600090a3808362ffffff160361135f5761135f84613921565b50505050565b61136e826139f8565b61137e8262615bf560e21b613a21565b6000828152600e6020526040908190209061139c9083906001613a6f565b6113bc57602060405163d327ad1b60e01b815260040161101b9190615509565b60078101829055600d5b6040518490600080516020615dc783398151915290600090a3505050565b60006001600160e01b0319821663152a902d60e11b1480611409575061140982613ae3565b92915050565b61141f6301050e9760e21b613b33565b601954600160f81b900460ff161561144d57601960405163d327ad1b60e01b815260040161101b9190615509565b611455613b5e565b565b60606000805461146690615523565b80601f016020809104026020016040519081016040528092919081815260200182805461149290615523565b80156114df5780601f106114b4576101008083540402835291602001916114df565b820191906000526020600020905b8154815290600101906020018083116114c257829003601f168201915b5050505050905090565b60006114f482613b9f565b5061140982613bd7565b611509828233613bf2565b5050565b611516826139f8565b61152782630d17067360e01b613a21565b61153081613bff565b6000828152600e6020526040902060010161154b82826155b4565b5060055b6040518390600080516020615dc783398151915290600090a35050565b611575826139f8565b611586826301cf392560e31b613a21565b61158f81613bff565b80518190600b8111156115b857602160405163d327ad1b60e01b815260040161101b9190615509565b60008060005b838110156116645760008582815181106115da576115da61566d565b01602001516001600160f81b0319169050600360fc1b811080159061160d5750603960f81b6001600160f81b0319821611155b1561161c57600192505061165c565b6001600160f81b03198116601760f91b03611640578361164057600193505061165c565b602360405163d327ad1b60e01b815260040161101b9190615509565b6001016115be565b508061168657602260405163d327ad1b60e01b815260040161101b9190615509565b6000868152600e602052604090206008016116a186826155b4565b50600e6040518790600080516020615dc783398151915290600090a3505050505050565b6000818152600e60205260408120805462ffffff808216936301000000830490911692600160881b830460ff90811693600160901b810490911692600160481b9091046001600160401b03169161171b88613c24565b1591505091939550919395565b600281600381111561173c5761173c614efc565b146117595760405162461bcd60e51b815260040161101b90615683565b61135f84848484613c6e565b6000611409620f424083615704565b611784631c05cad760e01b613b33565b61178d84613ce8565b61179683613ce8565b61135f84848484613d12565b6117b263118165ed60e11b613b33565b6117bb81613bff565b6117c481613de2565b50565b6000806117d26126f2565b6001600160a01b03161415801561186b5750600d546040516217798b60e61b81526001600160a01b03868116600483015285811660248301526001600160e01b031985166044830152909116906305de62c0906064016020604051808303816000875af1158015611847573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061186b9190615726565b949350505050565b6001600160a01b03821661189d576000604051633250574960e11b815260040161101b9190614b12565b60006118aa838333613df6565b9050836001600160a01b0316816001600160a01b03161461135f576040516364283d7b60e01b81526001600160a01b038086166004830152602482018490528216604482015260640161101b565b611901826139f8565b611912826304b6ebad60e31b613a21565b61191b81613bff565b6000828152600e6020526040902060050161193682826155b4565b50600a61154f565b61196a604080516080810190915260608152602081016000815260006020820152606060409091015290565b601d546040516315d3a7a360e01b81527300000000db6f2ebe627260e411e6c973b7c48a62916315d3a7a3916119b091879187916001600160a01b031690600401615743565b600060405180830381865af41580156119cd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261129791908101906157b2565b611a056313efb60d60e11b613b33565b601a54610100900460ff168015611a1b57508015155b15611a3c57602460405163d327ad1b60e01b815260040161101b9190615509565b612710611a498284615868565b1115611a6b57601360405163d327ad1b60e01b815260040161101b9190615509565b6014829055601681905560095b604051600080516020615d6783398151915290600090a25050565b600080611a9f846138ec565b6000611aaa85611765565b6000818152600f6020526040812060058101546004820154600383015483546001600160a01b039093169850949550919361ffff600160a01b93849004811693918290041691611b009160ff910416606461587b565b611b0a9190615868565b611b149190615868565b9050612710811115611b3c57601360405163d327ad1b60e01b815260040161101b9190615509565b612710611b49828861587b565b611b539190615704565b93505050509250929050565b611b6f631593a0b360e11b613b33565b611b7881613ce8565b600980546001600160a01b0319166001600160a01b03831617905560055b604051600080516020615d6783398151915290600090a250565b611bb986613eea565b611bc2866139bb565b611bcb85613ce8565b6000868152600f602052604090206064841180611be85750606482115b15611c0957601460405163d327ad1b60e01b815260040161101b9190615509565b600084118015611c2057506001600160a01b038516155b15611c4157601560405163d327ad1b60e01b815260040161101b9190615509565b600082118015611c5857506001600160a01b038316155b15611c7957601660405163d327ad1b60e01b815260040161101b9190615509565b867f6ff7d102bb3657a26dcbbcd299d821a066718a7cf76ae7cd98279f18b74da8ac8787878787604051611cb1959493929190615892565b60405180910390a2601a5460ff1680611d3c57600282015482546001600160a01b03918216898316149160009181169089161480611cf657506001600160a01b038816155b60018501549091506000906001600160a01b0388811691161480611d2157506001600160a01b038716155b9050828015611d2d5750815b8015611d365750805b93505050505b8015611dd45760008881526010602052604081205560028201805483546001600160a01b038981166001600160a01b03199290921691909117855560ff808916600160a01b9081026001600160a81b0319948516848e161717909455600186018054918816909402921690871617179055611db688613f26565b6040518890600080516020615d8783398151915290600090a2611e13565b8686868686604051602001611ded959493929190615892565b60408051601f19818403018152918152815160209283012060008b815260109093529120555b5050505050505050565b6000818152600e60205260409020600601805460609190611e3d90615523565b80601f0160208091040260200160405190810160405280929190818152602001828054611e6990615523565b8015611eb65780601f10611e8b57610100808354040283529160200191611eb6565b820191906000526020600020905b815481529060010190602001808311611e9957829003601f168201915b50505050509050919050565b611ecb836139f8565b611edc8363329dab7360e01b613a21565b611ee581614072565b6000838152600e602052604090208054600160301b900462ffffff168310611f2357601e60405163d327ad1b60e01b815260040161101b9190615509565b611f2c82614097565b6000848152600983016020526040902080546001600160a01b0319166001600160a01b0392909216919091179055600c6040518590600080516020615dc783398151915290600090a350505050565b611f84826139bb565b6000828152600e60205260409020600401611f9f82826155b4565b50600961154f565b611fb883631c83be3760e11b613a21565b604051631c83be3760e11b81527300000000db6f2ebe627260e411e6c973b7c48a62906339077c6e90611ff390869086908690600401615743565b60006040518083038186803b15801561200b57600080fd5b505af415801561201f573d6000803e3d6000fd5b50505050505050565b61203863398a289560e01b613b33565b61204181613eea565b6000818152600f602052604090206015546003820180546001600160a01b039283166001600160a01b0319808316821784556016546001600160b01b0319938416909217600160a01b61ffff9384168102919091179094556013546004870180549190961691811682178655601454931617911690910217905560106040518390600080516020615dc783398151915290600090a361150982613f26565b6120e8826139bb565b6120f181613bff565b6000828152600e6020526040902060060161210c82826155b4565b50600f61154f565b61212f838383604051806020016040528060008152506130ad565b505050565b612144632419b94160e11b613b33565b61214d81613ce8565b6117c481614135565b606061216a6576332e322e3760d01b61417f565b905090565b61217f635464c30960e01b613b33565b604051635464c30960e01b81527300000000db6f2ebe627260e411e6c973b7c48a6290635464c309906121b890859085906004016158c5565b60006040518083038186803b1580156121d057600080fd5b505af41580156121e4573d6000803e3d6000fd5b505050505050565b601e81815481106121fc57600080fd5b6000918252602090912001546001600160a01b0316905081565b600081815260036020526040812054600160a01b900460a01b6001600160a01b0319811682036122495750600092915050565b8060405160200161225a91906153b1565b60405160208183030381529060405280519060200120915050919050565b600061140982613b9f565b600281600381111561229757612297614efc565b146122b45760405162461bcd60e51b815260040161101b90615683565b61212f8383836141ea565b6122c882613eea565b6122d9826369d14faf60e01b614236565b6122e281613ce8565b6000828152600f6020526040902060020180546001600160a01b0319166001600160a01b03831617905561231582613f26565b600261154f565b61232c636c907b7f60e01b613b33565b61233581613ce8565b6117c4816142a8565b61234f82633774b79560e11b613a21565b604051633774b79560e11b8152600481018390526001600160a01b03821660248201527300000000db6f2ebe627260e411e6c973b7c48a6290636ee96f2a906044016121b8565b60006001600160a01b0382166123c25760006040516322718ad960e21b815260040161101b9190614b12565b506001600160a01b031660009081526004602052604090205490565b6123e661430a565b6123ee613b5e565b61145561433c565b6123ff86613eea565b612410866376ee6fab60e01b614236565b61241985613ce8565b8484848484604051602001612432959493929190615892565b60408051601f198184030181529181528151602092830120600089815260109093529120541461247857601760405163d327ad1b60e01b815260040161101b9190615509565b6000600f60008881526020019081526020016000209050858160020160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550848160000160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550838160020160146101000a81548160ff021916908360ff160217905550828160010160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550818160010160146101000a81548160ff021916908360ff1602179055506000801b601060008981526020019081526020016000208190555061256887613f26565b6040518790600080516020615d8783398151915290600090a250505050505050565b6000828152600f602052604081206011548291829182918291829182918291908a906064906125c390600160a01b900460ff168361587b565b6125cd9190615704565b99506125d98a826158f4565b6012549091506064906125f690600160a01b900460ff168d61587b565b6126009190615704565b975061260c88826158f4565b600283015490915060649061262b90600160a01b900460ff168361587b565b6126359190615704565b935061264184826158f4565b6011546012546001600160a01b039182169b50169750955085156126705760028201546001600160a01b031694505b83156126845781546001600160a01b031692505b50509295985092959890939650565b6000828152600e60205260409020805460609190600160301b900462ffffff1683106126cf575050604080516020810190915260008152611409565b600083815260098201602052604090205461186b906001600160a01b031661434e565b600061216a6007546001600160a01b031690565b60608060608060606000600e6000888152602001908152602001600020905080600101805461273490615523565b80601f016020809104026020016040519081016040528092919081815260200182805461276090615523565b80156127ad5780601f10612782576101008083540402835291602001916127ad565b820191906000526020600020905b81548152906001019060200180831161279057829003601f168201915b505050505095508060020180546127c390615523565b80601f01602080910402602001604051908101604052809291908181526020018280546127ef90615523565b801561283c5780601f106128115761010080835404028352916020019161283c565b820191906000526020600020905b81548152906001019060200180831161281f57829003601f168201915b505050506003830154919650506001600160a01b03168061286e5760405180602001604052806000815250945061287a565b6128778161434e565b94505b81600401805461288990615523565b80601f01602080910402602001604051908101604052809291908181526020018280546128b590615523565b80156129025780601f106128d757610100808354040283529160200191612902565b820191906000526020600020905b8154815290600101906020018083116128e557829003601f168201915b5050505050935081600501805461291890615523565b80601f016020809104026020016040519081016040528092919081815260200182805461294490615523565b80156129915780601f1061296657610100808354040283529160200191612991565b820191906000526020600020905b81548152906001019060200180831161297457829003601f168201915b50505050509250505091939590929450565b6129b482639424702b60e01b613a21565b604051639424702b60e01b81527300000000db6f2ebe627260e411e6c973b7c48a6290639424702b906121b89085908590600401615907565b6129fd639523751760e01b613b33565b612a0681613ce8565b600a80546001600160a01b0319166001600160a01b0383161790556006611b96565b60405162461bcd60e51b815260206004820152604160248201527f47656e417274373231436f726556335f437572617465645f466c65783a20636f60448201527f6e747261637420696e697469616c697a656420696e20636f6e7374727563746f6064820152603960f91b608482015260a40161101b565b60606001805461146690615523565b612abf63993c0cbf60e01b613b33565b612ac881613ce8565b6117c4816143c4565b612ae16304ecbfa560e51b613b33565b6117c4816143e6565b612afa63282fb95960e21b613b33565b601f80546001600160a01b0319166001600160a01b0383169081179091556040517fdd6101118dad49a63dd2ba96813aa513ada53dfb2bb3632a650894405e320af490600090a250565b612b4d816139bb565b6000818152600e60205260409020805460ff60901b198116600160901b9182900460ff161590910217905560035b6040518290600080516020615dc783398151915290600090a350565b611509338383614411565b612bab82613c24565b15612bf2576000828152600f60205260409020600201546001600160a01b03163314612bed57601a60405163d327ad1b60e01b815260040161101b9190615509565b612c24565b612c0433306351d9665360e11b6117c7565b612c2457601a60405163d327ad1b60e01b815260040161101b9190615509565b612c2d816144a7565b6000838152600e6020526040902060030180546001600160a01b0319166001600160a01b0392909216919091179055600861154f565b601b8054612c7090615523565b80601f0160208091040260200160405190810160405280929190818152602001828054612c9c90615523565b8015612ce95780601f10612cbe57610100808354040283529160200191612ce9565b820191906000526020600020905b815481529060010190602001808311612ccc57829003601f168201915b505050505081565b612cfa826139f8565b612d0b82632b2b404960e21b613a21565b612d1481613bff565b6000828152600e60205260409020612d2b826144a7565b8154600160301b9081900462ffffff9081166000908152600985016020526040902080546001600160a01b0319166001600160a01b0394909416939093179092558254612d7d92919004166001615920565b815462ffffff91909116600160301b0262ffffff60301b19909116178155600c6113c6565b604051630ad2832960e41b8152600481018290526000907300000000db6f2ebe627260e411e6c973b7c48a629063ad28329090602401602060405180830381865af4158015612df5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114099190615943565b612e2a8263ad576c4560e01b613a21565b60405163ad576c4560e01b815260048101839052602481018290527300000000db6f2ebe627260e411e6c973b7c48a629063ad576c45906044016121b8565b606061216a7a08ecadc82e4e86e646286dee4caac66be8adcced2dccabe8cd8caf602b1b61417f565b612e9b836139f8565b612eac8363b1656ba360e01b613a21565b612eb581613bff565b6000838152600e602052604090208054600160301b900462ffffff168310612ef357601e60405163d327ad1b60e01b815260040161101b9190615509565b611f2c826144a7565b600c546000908210612f2457601260405163d327ad1b60e01b815260040161101b9190615509565b600c8281548110612f3757612f3761566d565b6000918252602090912001546001600160a01b031692915050565b612f5b826139f8565b612f6c8263b202b56560e01b613a21565b612f7581614072565b6000828152600e60205260409020612d2b82614097565b612f9582613c24565b15612fdc576000828152600f60205260409020600201546001600160a01b03163314612fd757601a60405163d327ad1b60e01b815260040161101b9190615509565b61300e565b612fee3330635bd827d760e11b6117c7565b61300e57601a60405163d327ad1b60e01b815260040161101b9190615509565b61301781613bff565b6000828152600e6020526040902060020161303282826155b4565b50600661154f565b60607300000000db6f2ebe627260e411e6c973b7c48a6263b7ba527d6040518163ffffffff1660e01b8152600401600060405180830381865af4158015613085573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261216a919081019061595c565b6130b8848484611873565b61135f848484846144cd565b6130d58163ba3c234560e01b613a21565b60405163ba3c234560e01b8152600481018290527300000000db6f2ebe627260e411e6c973b7c48a629063ba3c23459060240160006040518083038186803b15801561312057600080fd5b505af4158015613134573d6000803e3d6000fd5b5050505050565b61314c83632edf6edb60e21b613a21565b604051632edf6edb60e21b81527300000000db6f2ebe627260e411e6c973b7c48a629063bb7dbb6c90611ff390869086908690600401615990565b61319763bd3d10e760e01b613b33565b601a54610100900460ff1680156131ad57508015155b156131ce57602460405163d327ad1b60e01b815260040161101b9190615509565b60646131da8284615868565b11156131fc57601160405163d327ad1b60e01b815260040161101b9190615509565b6011805460ff808516600160a01b90810260ff60a01b19938416179093556012805491851690930291161790556008611a78565b613239826139bb565b605f81111561325e57601b60405163d327ad1b60e01b815260040161101b9190615509565b6000828152600f60205260409020805460ff60a01b1916600160a01b60ff84160217905561328b82613f26565b600761154f565b606061329d826138ec565b6000600e60006132ac85611765565b815260200190815260200160002060060180546132c890615523565b80601f01602080910402602001604051908101604052809291908181526020018280546132f490615523565b80156133415780601f1061331657610100808354040283529160200191613341565b820191906000526020600020905b81548152906001019060200180831161332457829003601f168201915b5050505050905080613352846145dd565b6040516020016133639291906159af565b604051602081830303815290604052915050919050565b61338a63cc90e72560e01b613b33565b61339382613bff565b61339c81613ce8565b601954600160f81b900460ff16156133ca57601860405163d327ad1b60e01b815260040161101b9190615509565b6019546001600160f81b03166000818152600f602090815260408083206002810180546001600160a01b0319166001600160a01b038816179055600e90925290912060010161341985826155b4565b506000828152600e60205260409020805465ffffff00000060ff60901b011916613d09601e1b600160901b01178155600601613456601b826159de565b50805460ff60a01b1916600560a01b1781556015546003820180546001600160a01b039283166001600160a01b0319808316821784556016546001600160b01b0319938416909217600160a01b61ffff938416810291909117909455601354600487018054919096169181168217865560145493161791169091021790556134df826001615aaa565b601980546001600160f81b0319166001600160f81b039290921691909117905560046040518390600080516020615dc783398151915290600090a361135f82613f26565b601a5462010000900460ff161561354a576135458163340f0e4360e21b613a21565b61355a565b61355a63340f0e4360e21b613b33565b61356381613eea565b6000818152600e60205260409020805460ff60881b198116600160881b9182900460ff16159091021790556001612b7b565b6135a5631aa1ea2760e31b613b33565b604051631aa1ea2760e31b81527300000000db6f2ebe627260e411e6c973b7c48a629063d50f5138906121b890859085906004016158c5565b6135e7816139f8565b6135f88163db2ff86160e01b613a21565b6000818152600e6020526040812080549091600160301b90910462ffffff16900361363957601f60405163d327ad1b60e01b815260040161101b9190615509565b8054600982019060009061365b90600190600160301b900462ffffff16615aca565b62ffffff9081168252602082019290925260400160002080546001600160a01b0319169055815462ffffff60301b198116600160301b9182900483166000190190921602178155600c61154f565b60606136b482613bff565b60405163e32551e760e01b815273000000000016a5a5ff2fa7799c4bee89ba59b74e9063e32551e7906136eb908590600401614ad9565b600060405180830381865af4158015613708573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526114099190810190615ae6565b6001600160a01b03918216600090815260066020908152604080832093909416825291909152205460ff1690565b6000818152600e602052604081206007810154606092839290916137819061417f565b935080600801805461379290615523565b80601f01602080910402602001604051908101604052809291908181526020018280546137be90615523565b801561380b5780601f106137e05761010080835404028352916020019161380b565b820191906000526020600020905b8154815290600101906020018083116137ee57829003601f168201915b5050935496989297505050600160301b90940462ffffff169392505050565b61383a63f23f702160e01b613b33565b600880546001600160a01b0319166001600160a01b0383161790556004611b96565b61386461430a565b6001600160a01b03811661388e576000604051631e4fbdf760e01b815260040161101b9190614b12565b6117c48161466f565b60607300000000db6f2ebe627260e411e6c973b7c48a6263f6cd39e36040518163ffffffff1660e01b8152600401600060405180830381865af4158015613085573d6000803e3d6000fd5b600061216a6126f2565b60006138f78261469a565b6001600160a01b0316036117c457600360405163d327ad1b60e01b815260040161101b9190615509565b6000818152600e602052604081208054600160481b600160881b031916600160481b426001600160401b031602179055612b7b565b6001600160a01b038216613980576000604051633250574960e11b815260040161101b9190614b12565b600061398e83836000613df6565b90506001600160a01b0381161561212f5760006040516339e3563760e11b815260040161101b9190614b12565b6000818152600f60205260409020600201546001600160a01b031633146117c457600760405163d327ad1b60e01b815260040161101b9190615509565b613a0181613c24565b6117c457600560405163d327ad1b60e01b815260040161101b9190615509565b6000828152600f60205260409020600201546001600160a01b0316331480613a4f5750613a4f3330836117c7565b61150957600860405163d327ad1b60e01b815260040161101b9190615509565b60008060005b60208160ff161015613acf576000868260ff1660208110613a9857613a9861566d565b1a90508015801590613aad575060808160ff16105b15613ac6578560ff168160ff1603613ac6578260010192505b50600101613a75565b8360ff168260ff1614925050509392505050565b60006001600160e01b031982166380ac58cd60e01b1480613b1457506001600160e01b03198216635b5e139f60e01b145b8061140957506301ffc9a760e01b6001600160e01b0319831614611409565b613b3e3330836117c7565b6117c457600660405163d327ad1b60e01b815260040161101b9190615509565b601954600160f81b900460ff1661145557601980546001600160f81b0316600160f81b1790556001604051600080516020615d6783398151915290600090a2565b600080613bab8361469a565b90506001600160a01b03811661140957604051637e27328960e01b81526004810184905260240161101b565b6000908152600560205260409020546001600160a01b031690565b61212f83838360016146b5565b80516000036117c457600160405163d327ad1b60e01b815260040161101b9190615509565b6000613c2f82613eea565b6000828152600e6020526040902054600160481b90046001600160401b03168015808061186b57506224ea00613c6583426158f4565b10949350505050565b613c7f846302153de360e31b613a21565b6040516374a4e96960e11b81527300000000db6f2ebe627260e411e6c973b7c48a629063e949d2d290613cbc908790879087908790600401615b2e565b60006040518083038186803b158015613cd457600080fd5b505af4158015611e13573d6000803e3d6000fd5b6001600160a01b0381166117c457600060405163d327ad1b60e01b815260040161101b9190615509565b601a54610100900460ff1615613d67576001600160a01b038216151580613d4157506001600160a01b03811615155b15613d6257602460405163d327ad1b60e01b815260040161101b9190615509565b613d79565b613d7082613ce8565b613d7981613ce8565b601280546001600160a01b038085166001600160a01b031992831617909255601580548484169083161790556011805487841690831617905560138054928616929091169190911790556007604051600080516020615d6783398151915290600090a250505050565b601b613dee82826155b4565b506002611b96565b600080613e028461469a565b90506001600160a01b03831615613e1e57613e1e8184866147b2565b6001600160a01b03811615613e5c57613e3b6000856000806146b5565b6001600160a01b038116600090815260046020526040902080546000190190555b6001600160a01b03851615613e8b576001600160a01b0385166000908152600460205260409020805460010190555b60008481526003602052604080822080546001600160a01b0319166001600160a01b0389811691821790925591518793918516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4949350505050565b601854811080613f0557506019546001600160f81b03168110155b156117c457600460405163d327ad1b60e01b815260040161101b9190615509565b6000818152600f60209081526040808320601c5482516101008101845260038301546001600160a01b03818116835261ffff600160a01b92839004811697840197909752600480860154808316858901528390049097166060840152845460ff90839004811660808501526002860154821660a0850152600186015480831660c08601529290920490911660e08301529351631718245360e21b81529295949390911692635c60914c92613fdb929101615b5c565b6020604051808303816000875af1158015613ffa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061401e9190615be4565b6005830180546001600160a01b0319166001600160a01b0383169081179091556040519192509084907f301670c9279b1d4319606681ed95e193d888fddba44d836b9e8d5585d8cc47b590600090a3505050565b80516000036117c457600260405163d327ad1b60e01b815260040161101b9190615509565b600080600080516020615da783398151915230846040516020016140bd93929190615c01565b60405160208183030381529060405290508051602082016000f091506001600160a01b03821661412f5760405162461bcd60e51b815260206004820152601e60248201527f436f6e7472616374417353746f726167653a205772697465204572726f720000604482015260640161101b565b50919050565b601780546001600160a01b0319166001600160a01b0383169081179091556040517fad0f299ec81a386c98df0ac27dae11dd020ed1b56963c53a7292e7a3a314539a90600090a250565b606060005b828160ff16602081106141995761419961566d565b1a60f81b6001600160f81b031916158015906141b8575060208160ff16105b156141cf57806141c781615c6c565b915050614184565b60405191506040820160405280825282602083015250919050565b6141fb836366e4623760e01b613a21565b604051638f65b1f360e01b81527300000000db6f2ebe627260e411e6c973b7c48a6290638f65b1f390611ff390869086908690600401615c8b565b6142413330836117c7565b1561424a575050565b60006142546126f2565b6001600160a01b031614801561428357506000828152600f60205260409020600201546001600160a01b031633145b1561428c575050565b600960405163d327ad1b60e01b815260040161101b9190615509565b600b80546001600160a01b0383166001600160a01b03199182168117909255600c80546001810182556000919091527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c701805490911690911790556003611b96565b336143136126f2565b6001600160a01b031614611455573360405163118cdaa760e01b815260040161101b9190614b12565b61434461430a565b611455600061466f565b601d54604051630eacc5e760e31b81526060916001600160a01b0316906375662f389061437f908590600401614b12565b600060405180830381865afa15801561439c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611409919081019061595c565b601d80546001600160a01b0319166001600160a01b038316179055600b611b96565b6143ef81613ce8565b601c80546001600160a01b0319166001600160a01b038316179055600a611b96565b6001600160a01b03821661443a5781604051630b61174360e31b815260040161101b9190614b12565b6001600160a01b03838116600081815260066020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b600080600080516020615da783398151915230846040516020016140bd93929190615cb3565b6001600160a01b0383163b1561135f57604051630a85bd0160e11b81526001600160a01b0384169063150b7a029061450f903390889087908790600401615d0c565b6020604051808303816000875af192505050801561454a575060408051601f3d908101601f1916820190925261454791810190615d49565b60015b6145aa573d808015614578576040519150601f19603f3d011682016040523d82523d6000602084013e61457d565b606091505b5080516000036145a25783604051633250574960e11b815260040161101b9190614b12565b805181602001fd5b6001600160e01b03198116630a85bd0160e11b146131345783604051633250574960e11b815260040161101b9190614b12565b606060006145ea83614808565b60010190506000816001600160401b0381111561460957614609614c33565b6040519080825280601f01601f191660200182016040528015614633576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a850494508461463d57509392505050565b614678816148de565b600d80546001600160a01b0319166001600160a01b0392909216919091179055565b6000908152600360205260409020546001600160a01b031690565b80806146c957506001600160a01b03821615155b156147825760006146d984613b9f565b90506001600160a01b038316158015906147055750826001600160a01b0316816001600160a01b031614155b801561471857506147168184613730565b155b15614738578260405163a9fbf51f60e01b815260040161101b9190614b12565b81156147805783856001600160a01b0316826001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b5050600090815260056020526040902080546001600160a01b0319166001600160a01b0392909216919091179055565b6147bd838383614930565b61212f576001600160a01b0383166147eb57604051637e27328960e01b81526004810182905260240161101b565b818160405163177e802f60e01b815260040161101b929190614f90565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b83106148475772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6904ee2d6d415b85acef8160201b8310614871576904ee2d6d415b85acef8160201b830492506020015b662386f26fc10000831061488f57662386f26fc10000830492506010015b6305f5e10083106148a7576305f5e100830492506008015b61271083106148bb57612710830492506004015b606483106148cd576064830492506002015b600a83106114095760010192915050565b600780546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60006001600160a01b0383161580159061186b5750826001600160a01b0316846001600160a01b0316148061496a575061496a8484613730565b8061186b5750826001600160a01b031661498383613bd7565b6001600160a01b031614949350505050565b600080604083850312156149a857600080fd5b50508035926020909101359150565b6001600160a01b03811681146117c457600080fd5b80356149d7816149b7565b919050565b6000806000606084860312156149f157600080fd5b83356149fc816149b7565b9250602084013591506040840135614a13816149b7565b809150509250925092565b60008060408385031215614a3157600080fd5b82359150602083013562ffffff81168114614a4b57600080fd5b809150509250929050565b6001600160e01b0319811681146117c457600080fd5b600060208284031215614a7e57600080fd5b813561129781614a56565b60005b83811015614aa4578181015183820152602001614a8c565b50506000910152565b60008151808452614ac5816020860160208601614a89565b601f01601f19169290920160200192915050565b6020815260006112976020830184614aad565b600060208284031215614afe57600080fd5b5035919050565b6001600160a01b03169052565b6001600160a01b0391909116815260200190565b60008060408385031215614b3957600080fd5b8235614b44816149b7565b946020939093013593505050565b600061016082019050614b66828451614b05565b6020830151614b7a602084018260ff169052565b506040830151614b8d6040840182614b05565b506060830151614ba2606084018260ff169052565b506080830151614bb56080840182614b05565b5060a0830151614bca60a084018260ff169052565b5060c0830151614bdd60c0840182614b05565b5060e0830151614bf360e084018261ffff169052565b5061010080840151614c0782850182614b05565b50506101208381015161ffff169083015261014080840151614c2b82850182614b05565b505092915050565b634e487b7160e01b600052604160045260246000fd5b60405161018081016001600160401b0381118282101715614c6c57614c6c614c33565b60405290565b604051608081016001600160401b0381118282101715614c6c57614c6c614c33565b604051601f8201601f191681016001600160401b0381118282101715614cbc57614cbc614c33565b604052919050565b60006001600160401b03821115614cdd57614cdd614c33565b50601f01601f191660200190565b600082601f830112614cfc57600080fd5b8135614d0f614d0a82614cc4565b614c94565b818152846020838601011115614d2457600080fd5b816020850160208301376000918101602001919091529392505050565b60008060408385031215614d5457600080fd5b8235915060208301356001600160401b03811115614d7157600080fd5b614d7d85828601614ceb565b9150509250929050565b600481106117c457600080fd5b60008060008060808587031215614daa57600080fd5b843593506020850135925060408501356001600160401b03811115614dce57600080fd5b614dda87828801614ceb565b9250506060850135614deb81614d87565b939692955090935050565b60008060008060808587031215614e0c57600080fd5b8435614e17816149b7565b93506020850135614e27816149b7565b92506040850135614e37816149b7565b91506060850135614deb816149b7565b600060208284031215614e5957600080fd5b81356001600160401b03811115614e6f57600080fd5b61186b84828501614ceb565b600080600060608486031215614e9057600080fd5b8335614e9b816149b7565b92506020840135614eab816149b7565b91506040840135614a1381614a56565b600080600060608486031215614ed057600080fd5b8335614edb816149b7565b92506020840135614eeb816149b7565b929592945050506040919091013590565b634e487b7160e01b600052602160045260246000fd5b60048110614f2257614f22614efc565b9052565b602081526000825160806020840152614f4260a0840182614aad565b90506020840151614f566040850182614f12565b5060408401516001600160a01b0316606084810191909152840151838203601f19016080850152614f878282614aad565b95945050505050565b6001600160a01b03929092168252602082015260400190565b600060208284031215614fbb57600080fd5b8135611297816149b7565b60008060008060008060c08789031215614fdf57600080fd5b863595506020870135614ff1816149b7565b94506040870135615001816149b7565b9350606087013592506080870135615018816149b7565b8092505060a087013590509295509295509295565b60008060006060848603121561504257600080fd5b833592506020840135915060408401356001600160401b0381111561506657600080fd5b61507286828701614ceb565b9150509250925092565b60008060006060848603121561509157600080fd5b83359250602084013591506040840135614a13816149b7565b600080602083850312156150bd57600080fd5b82356001600160401b03808211156150d457600080fd5b818501915085601f8301126150e857600080fd5b8135818111156150f757600080fd5b86602082850101111561510957600080fd5b60209290920196919550909350505050565b60008060006060848603121561513057600080fd5b8335925060208401356001600160401b0381111561514d57600080fd5b61515986828701614ceb565b9250506040840135614a1381614d87565b6000806040838503121561517d57600080fd5b823591506020830135614a4b816149b7565b60a0815260006151a260a0830188614aad565b82810360208401526151b48188614aad565b905082810360408401526151c88187614aad565b905082810360608401526151dc8186614aad565b905082810360808401526151f08185614aad565b98975050505050505050565b80356001600160f81b03811681146149d757600080fd5b80151581146117c457600080fd5b80356149d781615213565b6000806000806080858703121561524257600080fd5b84356001600160401b038082111561525957600080fd5b90860190610180828903121561526e57600080fd5b615276614c49565b82358281111561528557600080fd5b6152918a828601614ceb565b8252506020830135828111156152a657600080fd5b6152b28a828601614ceb565b6020830152506152c4604084016149cc565b60408201526152d5606084016149cc565b60608201526152e6608084016149cc565b60808201526152f760a084016149cc565b60a082015261530860c084016149cc565b60c082015261531960e084016149cc565b60e082015261010061532c8185016151fc565b9082015261012061533e848201615221565b90820152610140615350848201615221565b90820152610160615362848201615221565b908201529550615374602088016149cc565b9450604087013591508082111561538a57600080fd5b5061539787828801614ceb565b9250506153a6606086016149cc565b905092959194509250565b6001600160a01b031991909116815260200190565b600080604083850312156153d957600080fd5b82356153e4816149b7565b91506020830135614a4b81615213565b6000806000806080858703121561540a57600080fd5b8435615415816149b7565b93506020850135615425816149b7565b92506040850135915060608501356001600160401b0381111561544757600080fd5b61545387828801614ceb565b91505092959194509250565b6000806040838503121561547257600080fd5b82356001600160401b0381111561548857600080fd5b61549485828601614ceb565b9250506020830135614a4b816149b7565b600080604083850312156154b857600080fd5b82356154c3816149b7565b91506020830135614a4b816149b7565b6060815260006154e66060830186614aad565b82810360208401526154f88186614aad565b915050826040830152949350505050565b602081016026831061551d5761551d614efc565b91905290565b600181811c9082168061553757607f821691505b60208210810361412f57634e487b7160e01b600052602260045260246000fd5b601f82111561212f576000816000526020600020601f850160051c810160208610156155805750805b601f850160051c820191505b818110156121e45782815560010161558c565b600019600383901b1c191660019190911b1790565b81516001600160401b038111156155cd576155cd614c33565b6155e1816155db8454615523565b84615557565b602080601f83116001811461561057600084156155fe5750858301515b615608858261559f565b8655506121e4565b600085815260208120601f198616915b8281101561563f57888601518255948401946001909101908401615620565b508582101561565d5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b634e487b7160e01b600052603260045260246000fd5b60208082526045908201527f47656e417274373231436f726556335f437572617465645f466c65783a20437560408201527f726174656420646570656e64656e63792074797065206d757374206265204f4e60608201526421a420a4a760d91b608082015260a00190565b634e487b7160e01b600052601160045260246000fd5b60008261572157634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561573857600080fd5b815161129781615213565b92835260208301919091526001600160a01b0316604082015260600190565b6000615770614d0a84614cc4565b905082815283838301111561578457600080fd5b611297836020830184614a89565b600082601f8301126157a357600080fd5b61129783835160208501615762565b6000602082840312156157c457600080fd5b81516001600160401b03808211156157db57600080fd5b90830190608082860312156157ef57600080fd5b6157f7614c72565b82518281111561580657600080fd5b61581287828601615792565b825250602083015161582381614d87565b60208201526040830151615836816149b7565b604082015260608301518281111561584d57600080fd5b61585987828601615792565b60608301525095945050505050565b80820180821115611409576114096156ee565b8082028115828204841417611409576114096156ee565b6001600160a01b039586168152938516602085015260408401929092529092166060820152608081019190915260a00190565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b81810381811115611409576114096156ee565b82815260406020820152600061186b6040830184614aad565b62ffffff81811683821601908082111561593c5761593c6156ee565b5092915050565b60006020828403121561595557600080fd5b5051919050565b60006020828403121561596e57600080fd5b81516001600160401b0381111561598457600080fd5b61186b84828501615792565b838152826020820152606060408201526000614f876060830184614aad565b600083516159c1818460208801614a89565b8351908301906159d5818360208801614a89565b01949350505050565b8181036159e9575050565b6159f38254615523565b6001600160401b03811115615a0a57615a0a614c33565b615a18816155db8454615523565b6000601f821160018114615a465760008315615a345750848201545b615a3e848261559f565b855550613134565b600085815260209020601f19841690600086815260209020845b83811015615a805782860154825560019586019590910190602001615a60565b508583101561565d5793015460001960f8600387901b161c19169092555050600190811b01905550565b6001600160f81b0381811683821601908082111561593c5761593c6156ee565b62ffffff82811682821603908082111561593c5761593c6156ee565b600060208284031215615af857600080fd5b81516001600160401b03811115615b0e57600080fd5b8201601f81018413615b1f57600080fd5b61186b84825160208401615762565b848152836020820152608060408201526000615b4d6080830185614aad565b9050614f876060830184614f12565b60006101008201905060018060a01b03808451168352602084015161ffff808216602086015282604087015116604086015280606087015116606086015250505060ff608084015116608083015260a0830151615bbc60a0840182614b05565b5060c0830151615bcf60c0840182614b05565b5060e083015161593c60e084018260ff169052565b600060208284031215615bf657600080fd5b8151611297816149b7565b6a600b5981380380925939f360a81b8152607f60f91b600b820152600c81018490526000602c8201819052606084901b6001600160601b0319166038830152600160f81b604c8301528251615c5d81604d850160208701614a89565b91909101604d01949350505050565b600060ff821660ff8103615c8257615c826156ee565b60010192915050565b838152606060208201526000615ca46060830185614aad565b905061186b6040830184614f12565b6a600b5981380380925939f360a81b8152607f60f91b600b820152600c81018490526000602c8201819052606084901b6001600160601b0319166038830152604c82018190528251615c5d81604d850160208701614a89565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090615d3f90830184614aad565b9695505050505050565b600060208284031215615d5b57600080fd5b815161129781614a5656fe8b810f233ce7ee6e962ab4d98bf0277751de1f5589de3dcc812ac2047994d009c582d05e1da854143bd3271ef4529d79cf5a69fc6057ae320f357acfd291b73842797465636f646553746f726167655f56322e302e305f5f5f5f5f5f5f5f5f20b96a30340e86d03ce4be42f94ac02d7b27b4a4cdae942beb69026718dfe66afca26469706673582212201e9d38eabd3d6e63cfa25300a549cc409eafe74b8758e4d291ae27478132413664736f6c634300081600338b810f233ce7ee6e962ab4d98bf0277751de1f5589de3dcc812ac2047994d0090000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000abb7a99780820c87c850af7fd1bc5e67880000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000a791abed33872c44a3d215a3743b000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000021a89ef8c577ebacfe8198644222b49dfd9284f90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013178a7a8a1a9460dbe39f7eccebd91b31752b9100000000000000000000000000000000ce5eebab4b5c2d6cc5e73eaafa634db3000000000000000000000000a2ccfe293bc2cdd78d8166a82d1e18cd2148122b00000000000000000000000000000000000000000000000000000000000001f9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a41727420426c6f636b73000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006424c4f434b530000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b68747470733a2f2f746f6b656e2e617274626c6f636b732e696f2f0000000000
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106105335760003560e01c8063801aa941116102b8578063801aa94114610b125780638639415b14610b255780638997618f14610b805780638c3c9cdd14610b935780638da5cb5b14610ba65780638dd91a5614610bae57806392f0023314610bd25780639424702b14610be55780639523751714610bf85780639549179514610c0b57806395d89b4114610c1e578063993c0cbf14610c265780639a02e4fa14610c395780639d97f4a014610c70578063a0bee56414610c83578063a11ec70a14610c96578063a22cb46514610ca9578063a3b2cca614610cbc578063a47d29cb14610ccf578063abcbb7b414610cfb578063ac11fa1c14610d03578063acad012414610d23578063acd4c66f14610d36578063ad0305ce14610d48578063ad28329014610d6a578063ad576c4514610d7d578063ae45ad9814610d90578063b1656ba314610d98578063b168762214610dab578063b202b56514610dbe578063b75395e014610dd1578063b7b04fae14610de4578063b7ba527d14610df7578063b88d4fde14610dff578063b971136814610e12578063ba3c234514610e1a578063bb7dbb6c14610e2d578063bba4448a14610e40578063bd3d10e714610e53578063c34a03b514610e66578063c87b56dd14610e79578063cc90e72514610e8c578063ce90652014610e9f578063d03c390c14610eb2578063d50f513814610ec5578063db2ff86114610ed8578063e32551e714610eeb578063e6032df214610efe578063e935b7b114610f0b578063e985e9c514610f1c578063eb9cd5d414610f2f578063ed8abfda14610f51578063eef719a414610f7b578063f23f702114610f84578063f2fde38b14610f97578063f6cd39e314610faa578063f851a44014610fb2578063f893c07b14610fba578063ffd43f6514610fc357600080fd5b80611e3c146105385780615de51461054d5780630132c6971461057357806301856fd41461058657806301ffc9a71461059957806304143a5c146105bc57806306fdde03146105c4578063081812fc146105d9578063095ea7b3146105f95780630a1df77a1461060c5780630d170673146107375780630e79c9281461074a5780630ea5613f1461075d57806310a9ef18146107a757806317df5366146107ba5780631ab6014c146107cd5780631b689c0b146107e05780631c05cad7146107f35780631e9bef46146108065780632302cbda14610819578063230448b11461082c57806323b872dd1461083f57806325b75d68146108525780632642c6b61461086557806327df6c1a146108855780632a55205a146108985780632b274166146108b95780632b65e67d146108cc5780632d9c0205146108df57806330ef4c5f146108f2578063329dab731461090457806336c7c12c14610917578063378599631461092a57806337fbc9651461093d57806339077c6e14610950578063398a2895146109635780633e48e8481461097657806342842e0e14610989578063483372821461099c5780634b976697146109af5780634e1d64af146109c25780635119d04b146109ca5780635464c309146109d3578063546a26f6146109e65780635508fd52146109f957806357a8e57414610a0d57806358b9a5a914610a20578063621a1f7414610a585780636352211e14610a6b5780636580a1ed14610a7e57806366e4623714610a8657806369d14faf14610a995780636c907b7f14610aac5780636ddba41114610abf5780636ee96f2a14610ad157806370a0823114610ae4578063715018a614610af757806376ee6fab14610aff575b600080fd5b61054b610546366004614995565b610fd6565b005b61056061055b3660046149dc565b61109e565b6040519081526020015b60405180910390f35b61054b610581366004614a1e565b61129e565b61054b610594366004614995565b611365565b6105ac6105a7366004614a6c565b6113e4565b604051901515815260200161056a565b61054b61140f565b6105cc611457565b60405161056a9190614ad9565b6105ec6105e7366004614aec565b6114e9565b60405161056a9190614b12565b61054b610607366004614b26565b6114fe565b61072a61061a366004614aec565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101829052610140810191909152506000908152600f602090815260409182902082516101608101845281546001600160a01b03808216835260ff600160a01b9283900481169584019590955260018401548082169684019690965294819004841660608301526002830154808616608084015281900490931660a0820152600382015480851660c083015261ffff90849004811660e08301526004830154808616610100840152939093049092166101208301526005015490911661014082015290565b60405161056a9190614b52565b61054b610745366004614d41565b61150d565b61054b610758366004614d41565b61156c565b61077061076b366004614aec565b6116c5565b60408051968752602087019590955292151593850193909352151560608401526080830191909152151560a082015260c00161056a565b61054b6107b5366004614d94565b611728565b6009546105ec906001600160a01b031681565b6012546105ec906001600160a01b031681565b6105606107ee366004614aec565b611765565b61054b610801366004614df6565b611774565b600d546105ec906001600160a01b031681565b61054b610827366004614e47565b6117a2565b6105ac61083a366004614e7b565b6117c7565b61054b61084d366004614ebb565b611873565b61054b610860366004614d41565b6118f8565b610878610873366004614995565b61193e565b60405161056a9190614f26565b61054b610893366004614995565b6119f5565b6108ab6108a6366004614995565b611a93565b60405161056a929190614f90565b61054b6108c7366004614fa9565b611b5f565b61054b6108da366004614fc6565b611bb0565b6105cc6108ed366004614aec565b611e1d565b601254600160a01b900460ff16610560565b61054b61091236600461502d565b611ec2565b600b546105ec906001600160a01b031681565b61054b610938366004614d41565b611f7b565b601c546105ec906001600160a01b031681565b61054b61095e36600461507c565b611fa7565b61054b610971366004614aec565b612028565b61054b610984366004614d41565b6120df565b61054b610997366004614ebb565b612114565b61054b6109aa366004614fa9565b612134565b601d546105ec906001600160a01b031681565b6105cc612156565b61056060165481565b61054b6109e13660046150aa565b61216f565b6105ec6109f4366004614aec565b6121ec565b6019546105ac90600160f81b900460ff1681565b6008546105ec906001600160a01b031681565b6105ec610a2e366004614995565b6000918252600e60209081526040808420928452600990920190529020546001600160a01b031690565b610560610a66366004614aec565b612216565b6105ec610a79366004614aec565b612278565b6105ac600181565b61054b610a9436600461511b565b612283565b61054b610aa736600461516a565b6122bf565b61054b610aba366004614fa9565b61231c565b601a546105ac90610100900460ff1681565b61054b610adf36600461516a565b61233e565b610560610af2366004614fa9565b612396565b61054b6123de565b61054b610b0d366004614fc6565b6123f6565b6015546105ec906001600160a01b031681565b610b38610b33366004614995565b61258a565b604080519889526001600160a01b0397881660208a015288019590955292851660608701526080860191909152831660a085015260c08401521660e08201526101000161056a565b600a546105ec906001600160a01b031681565b6105cc610ba1366004614995565b612693565b6105ec6126f2565b610bc1610bbc366004614aec565b612706565b60405161056a95949392919061518f565b6017546105ec906001600160a01b031681565b61054b610bf3366004614d41565b6129a3565b61054b610c06366004614fa9565b6129ed565b61054b610c1936600461522c565b612a28565b6105cc612aa0565b61054b610c34366004614fa9565b612aaf565b610c63610c47366004614aec565b600090815260036020526040902054600160a01b900460a01b90565b60405161056a91906153b1565b61054b610c7e366004614fa9565b612ad1565b61054b610c91366004614fa9565b612aea565b61054b610ca4366004614aec565b612b44565b61054b610cb73660046153c6565b612b97565b61054b610cca366004614d41565b612ba2565b6105ec610cdd366004614aec565b6000908152600f60205260409020600201546001600160a01b031690565b6105cc612c63565b610560610d11366004614aec565b60106020526000908152604090205481565b61054b610d31366004614d41565b612cf1565b601154600160a01b900460ff16610560565b6105ac610d56366004614fa9565b6017546001600160a01b0390811691161490565b610560610d78366004614aec565b612da2565b61054b610d8b366004614995565b612e19565b6105cc612e69565b61054b610da636600461502d565b612e92565b6105ec610db9366004614aec565b612efc565b61054b610dcc366004614d41565b612f52565b6011546105ec906001600160a01b031681565b61054b610df2366004614d41565b612f8c565b6105cc61303a565b61054b610e0d3660046153f4565b6130ad565b600c54610560565b61054b610e28366004614aec565b6130c4565b61054b610e3b36600461502d565b61313b565b601f546105ec906001600160a01b031681565b61054b610e61366004614995565b613187565b61054b610e74366004614995565b613230565b6105cc610e87366004614aec565b613292565b61054b610e9a36600461545f565b61337a565b601a546105ac9062010000900460ff1681565b61054b610ec0366004614aec565b613523565b61054b610ed33660046150aa565b613595565b61054b610ee6366004614aec565b6135de565b6105cc610ef9366004614e47565b6136a9565b601a546105ac9060ff1681565b6019546001600160f81b0316610560565b6105ac610f2a3660046154a5565b613730565b610f42610f3d366004614aec565b61375e565b60405161056a939291906154d3565b610560610f5f366004614aec565b6000908152600f6020526040902054600160a01b900460ff1690565b61056060145481565b61054b610f92366004614fa9565b61382a565b61054b610fa5366004614fa9565b61385c565b6105cc613897565b6105ec6138e2565b61056060185481565b6013546105ec906001600160a01b031681565b610fdf826138ec565b6000828152600360205260409020600b546001600160a01b0316331461102457600e60405163d327ad1b60e01b815260040161101b9190615509565b60405180910390fd5b8054600160a01b900460a01b6001600160a01b0319161561105b57600f60405163d327ad1b60e01b815260040161101b9190615509565b8161107c57601060405163d327ad1b60e01b815260040161101b9190615509565b805460a09290921c600160a01b026001600160a01b0390921691909117905550565b6017546000906001600160a01b031633146110cf57600a60405163d327ad1b60e01b815260040161101b9190615509565b6000838152600e60205260409020805462ffffff80821691600183019163010000009091041680831061111857600b60405163d327ad1b60e01b815260040161101b9190615509565b8354600160881b900460ff168061114b57506000878152600f60205260409020600201546001600160a01b038781169116145b61116b57600c60405163d327ad1b60e01b815260040161101b9190615509565b8354600160901b900460ff1680156111a057506000878152600f60205260409020600201546001600160a01b03878116911614155b156111c157600d60405163d327ad1b60e01b815260040161101b9190615509565b835462ffffff191662ffffff8381169182178655848116620f42408a02019190831690036111f2576111f288613921565b6111fc8982613956565b600b54604051635b140b8d60e11b8152600481018390526001600160a01b039091169063b628171a90602401600060405180830381600087803b15801561124257600080fd5b505af1158015611256573d6000803e3d6000fd5b50506040518392506001600160a01b038c1691507f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d412139688590600090a39450505050505b9392505050565b6112a7826139bb565b6000828152600e60205260409020805462ffffff80821691630100000090048116908416106112ec57601c60405163d327ad1b60e01b815260040161101b9190615509565b808362ffffff16101561131557601d60405163d327ad1b60e01b815260040161101b9190615509565b815465ffffff0000001916630100000062ffffff851602178255600b6040518590600080516020615dc783398151915290600090a3808362ffffff160361135f5761135f84613921565b50505050565b61136e826139f8565b61137e8262615bf560e21b613a21565b6000828152600e6020526040908190209061139c9083906001613a6f565b6113bc57602060405163d327ad1b60e01b815260040161101b9190615509565b60078101829055600d5b6040518490600080516020615dc783398151915290600090a3505050565b60006001600160e01b0319821663152a902d60e11b1480611409575061140982613ae3565b92915050565b61141f6301050e9760e21b613b33565b601954600160f81b900460ff161561144d57601960405163d327ad1b60e01b815260040161101b9190615509565b611455613b5e565b565b60606000805461146690615523565b80601f016020809104026020016040519081016040528092919081815260200182805461149290615523565b80156114df5780601f106114b4576101008083540402835291602001916114df565b820191906000526020600020905b8154815290600101906020018083116114c257829003601f168201915b5050505050905090565b60006114f482613b9f565b5061140982613bd7565b611509828233613bf2565b5050565b611516826139f8565b61152782630d17067360e01b613a21565b61153081613bff565b6000828152600e6020526040902060010161154b82826155b4565b5060055b6040518390600080516020615dc783398151915290600090a35050565b611575826139f8565b611586826301cf392560e31b613a21565b61158f81613bff565b80518190600b8111156115b857602160405163d327ad1b60e01b815260040161101b9190615509565b60008060005b838110156116645760008582815181106115da576115da61566d565b01602001516001600160f81b0319169050600360fc1b811080159061160d5750603960f81b6001600160f81b0319821611155b1561161c57600192505061165c565b6001600160f81b03198116601760f91b03611640578361164057600193505061165c565b602360405163d327ad1b60e01b815260040161101b9190615509565b6001016115be565b508061168657602260405163d327ad1b60e01b815260040161101b9190615509565b6000868152600e602052604090206008016116a186826155b4565b50600e6040518790600080516020615dc783398151915290600090a3505050505050565b6000818152600e60205260408120805462ffffff808216936301000000830490911692600160881b830460ff90811693600160901b810490911692600160481b9091046001600160401b03169161171b88613c24565b1591505091939550919395565b600281600381111561173c5761173c614efc565b146117595760405162461bcd60e51b815260040161101b90615683565b61135f84848484613c6e565b6000611409620f424083615704565b611784631c05cad760e01b613b33565b61178d84613ce8565b61179683613ce8565b61135f84848484613d12565b6117b263118165ed60e11b613b33565b6117bb81613bff565b6117c481613de2565b50565b6000806117d26126f2565b6001600160a01b03161415801561186b5750600d546040516217798b60e61b81526001600160a01b03868116600483015285811660248301526001600160e01b031985166044830152909116906305de62c0906064016020604051808303816000875af1158015611847573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061186b9190615726565b949350505050565b6001600160a01b03821661189d576000604051633250574960e11b815260040161101b9190614b12565b60006118aa838333613df6565b9050836001600160a01b0316816001600160a01b03161461135f576040516364283d7b60e01b81526001600160a01b038086166004830152602482018490528216604482015260640161101b565b611901826139f8565b611912826304b6ebad60e31b613a21565b61191b81613bff565b6000828152600e6020526040902060050161193682826155b4565b50600a61154f565b61196a604080516080810190915260608152602081016000815260006020820152606060409091015290565b601d546040516315d3a7a360e01b81527300000000db6f2ebe627260e411e6c973b7c48a62916315d3a7a3916119b091879187916001600160a01b031690600401615743565b600060405180830381865af41580156119cd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261129791908101906157b2565b611a056313efb60d60e11b613b33565b601a54610100900460ff168015611a1b57508015155b15611a3c57602460405163d327ad1b60e01b815260040161101b9190615509565b612710611a498284615868565b1115611a6b57601360405163d327ad1b60e01b815260040161101b9190615509565b6014829055601681905560095b604051600080516020615d6783398151915290600090a25050565b600080611a9f846138ec565b6000611aaa85611765565b6000818152600f6020526040812060058101546004820154600383015483546001600160a01b039093169850949550919361ffff600160a01b93849004811693918290041691611b009160ff910416606461587b565b611b0a9190615868565b611b149190615868565b9050612710811115611b3c57601360405163d327ad1b60e01b815260040161101b9190615509565b612710611b49828861587b565b611b539190615704565b93505050509250929050565b611b6f631593a0b360e11b613b33565b611b7881613ce8565b600980546001600160a01b0319166001600160a01b03831617905560055b604051600080516020615d6783398151915290600090a250565b611bb986613eea565b611bc2866139bb565b611bcb85613ce8565b6000868152600f602052604090206064841180611be85750606482115b15611c0957601460405163d327ad1b60e01b815260040161101b9190615509565b600084118015611c2057506001600160a01b038516155b15611c4157601560405163d327ad1b60e01b815260040161101b9190615509565b600082118015611c5857506001600160a01b038316155b15611c7957601660405163d327ad1b60e01b815260040161101b9190615509565b867f6ff7d102bb3657a26dcbbcd299d821a066718a7cf76ae7cd98279f18b74da8ac8787878787604051611cb1959493929190615892565b60405180910390a2601a5460ff1680611d3c57600282015482546001600160a01b03918216898316149160009181169089161480611cf657506001600160a01b038816155b60018501549091506000906001600160a01b0388811691161480611d2157506001600160a01b038716155b9050828015611d2d5750815b8015611d365750805b93505050505b8015611dd45760008881526010602052604081205560028201805483546001600160a01b038981166001600160a01b03199290921691909117855560ff808916600160a01b9081026001600160a81b0319948516848e161717909455600186018054918816909402921690871617179055611db688613f26565b6040518890600080516020615d8783398151915290600090a2611e13565b8686868686604051602001611ded959493929190615892565b60408051601f19818403018152918152815160209283012060008b815260109093529120555b5050505050505050565b6000818152600e60205260409020600601805460609190611e3d90615523565b80601f0160208091040260200160405190810160405280929190818152602001828054611e6990615523565b8015611eb65780601f10611e8b57610100808354040283529160200191611eb6565b820191906000526020600020905b815481529060010190602001808311611e9957829003601f168201915b50505050509050919050565b611ecb836139f8565b611edc8363329dab7360e01b613a21565b611ee581614072565b6000838152600e602052604090208054600160301b900462ffffff168310611f2357601e60405163d327ad1b60e01b815260040161101b9190615509565b611f2c82614097565b6000848152600983016020526040902080546001600160a01b0319166001600160a01b0392909216919091179055600c6040518590600080516020615dc783398151915290600090a350505050565b611f84826139bb565b6000828152600e60205260409020600401611f9f82826155b4565b50600961154f565b611fb883631c83be3760e11b613a21565b604051631c83be3760e11b81527300000000db6f2ebe627260e411e6c973b7c48a62906339077c6e90611ff390869086908690600401615743565b60006040518083038186803b15801561200b57600080fd5b505af415801561201f573d6000803e3d6000fd5b50505050505050565b61203863398a289560e01b613b33565b61204181613eea565b6000818152600f602052604090206015546003820180546001600160a01b039283166001600160a01b0319808316821784556016546001600160b01b0319938416909217600160a01b61ffff9384168102919091179094556013546004870180549190961691811682178655601454931617911690910217905560106040518390600080516020615dc783398151915290600090a361150982613f26565b6120e8826139bb565b6120f181613bff565b6000828152600e6020526040902060060161210c82826155b4565b50600f61154f565b61212f838383604051806020016040528060008152506130ad565b505050565b612144632419b94160e11b613b33565b61214d81613ce8565b6117c481614135565b606061216a6576332e322e3760d01b61417f565b905090565b61217f635464c30960e01b613b33565b604051635464c30960e01b81527300000000db6f2ebe627260e411e6c973b7c48a6290635464c309906121b890859085906004016158c5565b60006040518083038186803b1580156121d057600080fd5b505af41580156121e4573d6000803e3d6000fd5b505050505050565b601e81815481106121fc57600080fd5b6000918252602090912001546001600160a01b0316905081565b600081815260036020526040812054600160a01b900460a01b6001600160a01b0319811682036122495750600092915050565b8060405160200161225a91906153b1565b60405160208183030381529060405280519060200120915050919050565b600061140982613b9f565b600281600381111561229757612297614efc565b146122b45760405162461bcd60e51b815260040161101b90615683565b61212f8383836141ea565b6122c882613eea565b6122d9826369d14faf60e01b614236565b6122e281613ce8565b6000828152600f6020526040902060020180546001600160a01b0319166001600160a01b03831617905561231582613f26565b600261154f565b61232c636c907b7f60e01b613b33565b61233581613ce8565b6117c4816142a8565b61234f82633774b79560e11b613a21565b604051633774b79560e11b8152600481018390526001600160a01b03821660248201527300000000db6f2ebe627260e411e6c973b7c48a6290636ee96f2a906044016121b8565b60006001600160a01b0382166123c25760006040516322718ad960e21b815260040161101b9190614b12565b506001600160a01b031660009081526004602052604090205490565b6123e661430a565b6123ee613b5e565b61145561433c565b6123ff86613eea565b612410866376ee6fab60e01b614236565b61241985613ce8565b8484848484604051602001612432959493929190615892565b60408051601f198184030181529181528151602092830120600089815260109093529120541461247857601760405163d327ad1b60e01b815260040161101b9190615509565b6000600f60008881526020019081526020016000209050858160020160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550848160000160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550838160020160146101000a81548160ff021916908360ff160217905550828160010160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550818160010160146101000a81548160ff021916908360ff1602179055506000801b601060008981526020019081526020016000208190555061256887613f26565b6040518790600080516020615d8783398151915290600090a250505050505050565b6000828152600f602052604081206011548291829182918291829182918291908a906064906125c390600160a01b900460ff168361587b565b6125cd9190615704565b99506125d98a826158f4565b6012549091506064906125f690600160a01b900460ff168d61587b565b6126009190615704565b975061260c88826158f4565b600283015490915060649061262b90600160a01b900460ff168361587b565b6126359190615704565b935061264184826158f4565b6011546012546001600160a01b039182169b50169750955085156126705760028201546001600160a01b031694505b83156126845781546001600160a01b031692505b50509295985092959890939650565b6000828152600e60205260409020805460609190600160301b900462ffffff1683106126cf575050604080516020810190915260008152611409565b600083815260098201602052604090205461186b906001600160a01b031661434e565b600061216a6007546001600160a01b031690565b60608060608060606000600e6000888152602001908152602001600020905080600101805461273490615523565b80601f016020809104026020016040519081016040528092919081815260200182805461276090615523565b80156127ad5780601f10612782576101008083540402835291602001916127ad565b820191906000526020600020905b81548152906001019060200180831161279057829003601f168201915b505050505095508060020180546127c390615523565b80601f01602080910402602001604051908101604052809291908181526020018280546127ef90615523565b801561283c5780601f106128115761010080835404028352916020019161283c565b820191906000526020600020905b81548152906001019060200180831161281f57829003601f168201915b505050506003830154919650506001600160a01b03168061286e5760405180602001604052806000815250945061287a565b6128778161434e565b94505b81600401805461288990615523565b80601f01602080910402602001604051908101604052809291908181526020018280546128b590615523565b80156129025780601f106128d757610100808354040283529160200191612902565b820191906000526020600020905b8154815290600101906020018083116128e557829003601f168201915b5050505050935081600501805461291890615523565b80601f016020809104026020016040519081016040528092919081815260200182805461294490615523565b80156129915780601f1061296657610100808354040283529160200191612991565b820191906000526020600020905b81548152906001019060200180831161297457829003601f168201915b50505050509250505091939590929450565b6129b482639424702b60e01b613a21565b604051639424702b60e01b81527300000000db6f2ebe627260e411e6c973b7c48a6290639424702b906121b89085908590600401615907565b6129fd639523751760e01b613b33565b612a0681613ce8565b600a80546001600160a01b0319166001600160a01b0383161790556006611b96565b60405162461bcd60e51b815260206004820152604160248201527f47656e417274373231436f726556335f437572617465645f466c65783a20636f60448201527f6e747261637420696e697469616c697a656420696e20636f6e7374727563746f6064820152603960f91b608482015260a40161101b565b60606001805461146690615523565b612abf63993c0cbf60e01b613b33565b612ac881613ce8565b6117c4816143c4565b612ae16304ecbfa560e51b613b33565b6117c4816143e6565b612afa63282fb95960e21b613b33565b601f80546001600160a01b0319166001600160a01b0383169081179091556040517fdd6101118dad49a63dd2ba96813aa513ada53dfb2bb3632a650894405e320af490600090a250565b612b4d816139bb565b6000818152600e60205260409020805460ff60901b198116600160901b9182900460ff161590910217905560035b6040518290600080516020615dc783398151915290600090a350565b611509338383614411565b612bab82613c24565b15612bf2576000828152600f60205260409020600201546001600160a01b03163314612bed57601a60405163d327ad1b60e01b815260040161101b9190615509565b612c24565b612c0433306351d9665360e11b6117c7565b612c2457601a60405163d327ad1b60e01b815260040161101b9190615509565b612c2d816144a7565b6000838152600e6020526040902060030180546001600160a01b0319166001600160a01b0392909216919091179055600861154f565b601b8054612c7090615523565b80601f0160208091040260200160405190810160405280929190818152602001828054612c9c90615523565b8015612ce95780601f10612cbe57610100808354040283529160200191612ce9565b820191906000526020600020905b815481529060010190602001808311612ccc57829003601f168201915b505050505081565b612cfa826139f8565b612d0b82632b2b404960e21b613a21565b612d1481613bff565b6000828152600e60205260409020612d2b826144a7565b8154600160301b9081900462ffffff9081166000908152600985016020526040902080546001600160a01b0319166001600160a01b0394909416939093179092558254612d7d92919004166001615920565b815462ffffff91909116600160301b0262ffffff60301b19909116178155600c6113c6565b604051630ad2832960e41b8152600481018290526000907300000000db6f2ebe627260e411e6c973b7c48a629063ad28329090602401602060405180830381865af4158015612df5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114099190615943565b612e2a8263ad576c4560e01b613a21565b60405163ad576c4560e01b815260048101839052602481018290527300000000db6f2ebe627260e411e6c973b7c48a629063ad576c45906044016121b8565b606061216a7a08ecadc82e4e86e646286dee4caac66be8adcced2dccabe8cd8caf602b1b61417f565b612e9b836139f8565b612eac8363b1656ba360e01b613a21565b612eb581613bff565b6000838152600e602052604090208054600160301b900462ffffff168310612ef357601e60405163d327ad1b60e01b815260040161101b9190615509565b611f2c826144a7565b600c546000908210612f2457601260405163d327ad1b60e01b815260040161101b9190615509565b600c8281548110612f3757612f3761566d565b6000918252602090912001546001600160a01b031692915050565b612f5b826139f8565b612f6c8263b202b56560e01b613a21565b612f7581614072565b6000828152600e60205260409020612d2b82614097565b612f9582613c24565b15612fdc576000828152600f60205260409020600201546001600160a01b03163314612fd757601a60405163d327ad1b60e01b815260040161101b9190615509565b61300e565b612fee3330635bd827d760e11b6117c7565b61300e57601a60405163d327ad1b60e01b815260040161101b9190615509565b61301781613bff565b6000828152600e6020526040902060020161303282826155b4565b50600661154f565b60607300000000db6f2ebe627260e411e6c973b7c48a6263b7ba527d6040518163ffffffff1660e01b8152600401600060405180830381865af4158015613085573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261216a919081019061595c565b6130b8848484611873565b61135f848484846144cd565b6130d58163ba3c234560e01b613a21565b60405163ba3c234560e01b8152600481018290527300000000db6f2ebe627260e411e6c973b7c48a629063ba3c23459060240160006040518083038186803b15801561312057600080fd5b505af4158015613134573d6000803e3d6000fd5b5050505050565b61314c83632edf6edb60e21b613a21565b604051632edf6edb60e21b81527300000000db6f2ebe627260e411e6c973b7c48a629063bb7dbb6c90611ff390869086908690600401615990565b61319763bd3d10e760e01b613b33565b601a54610100900460ff1680156131ad57508015155b156131ce57602460405163d327ad1b60e01b815260040161101b9190615509565b60646131da8284615868565b11156131fc57601160405163d327ad1b60e01b815260040161101b9190615509565b6011805460ff808516600160a01b90810260ff60a01b19938416179093556012805491851690930291161790556008611a78565b613239826139bb565b605f81111561325e57601b60405163d327ad1b60e01b815260040161101b9190615509565b6000828152600f60205260409020805460ff60a01b1916600160a01b60ff84160217905561328b82613f26565b600761154f565b606061329d826138ec565b6000600e60006132ac85611765565b815260200190815260200160002060060180546132c890615523565b80601f01602080910402602001604051908101604052809291908181526020018280546132f490615523565b80156133415780601f1061331657610100808354040283529160200191613341565b820191906000526020600020905b81548152906001019060200180831161332457829003601f168201915b5050505050905080613352846145dd565b6040516020016133639291906159af565b604051602081830303815290604052915050919050565b61338a63cc90e72560e01b613b33565b61339382613bff565b61339c81613ce8565b601954600160f81b900460ff16156133ca57601860405163d327ad1b60e01b815260040161101b9190615509565b6019546001600160f81b03166000818152600f602090815260408083206002810180546001600160a01b0319166001600160a01b038816179055600e90925290912060010161341985826155b4565b506000828152600e60205260409020805465ffffff00000060ff60901b011916613d09601e1b600160901b01178155600601613456601b826159de565b50805460ff60a01b1916600560a01b1781556015546003820180546001600160a01b039283166001600160a01b0319808316821784556016546001600160b01b0319938416909217600160a01b61ffff938416810291909117909455601354600487018054919096169181168217865560145493161791169091021790556134df826001615aaa565b601980546001600160f81b0319166001600160f81b039290921691909117905560046040518390600080516020615dc783398151915290600090a361135f82613f26565b601a5462010000900460ff161561354a576135458163340f0e4360e21b613a21565b61355a565b61355a63340f0e4360e21b613b33565b61356381613eea565b6000818152600e60205260409020805460ff60881b198116600160881b9182900460ff16159091021790556001612b7b565b6135a5631aa1ea2760e31b613b33565b604051631aa1ea2760e31b81527300000000db6f2ebe627260e411e6c973b7c48a629063d50f5138906121b890859085906004016158c5565b6135e7816139f8565b6135f88163db2ff86160e01b613a21565b6000818152600e6020526040812080549091600160301b90910462ffffff16900361363957601f60405163d327ad1b60e01b815260040161101b9190615509565b8054600982019060009061365b90600190600160301b900462ffffff16615aca565b62ffffff9081168252602082019290925260400160002080546001600160a01b0319169055815462ffffff60301b198116600160301b9182900483166000190190921602178155600c61154f565b60606136b482613bff565b60405163e32551e760e01b815273000000000016a5a5ff2fa7799c4bee89ba59b74e9063e32551e7906136eb908590600401614ad9565b600060405180830381865af4158015613708573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526114099190810190615ae6565b6001600160a01b03918216600090815260066020908152604080832093909416825291909152205460ff1690565b6000818152600e602052604081206007810154606092839290916137819061417f565b935080600801805461379290615523565b80601f01602080910402602001604051908101604052809291908181526020018280546137be90615523565b801561380b5780601f106137e05761010080835404028352916020019161380b565b820191906000526020600020905b8154815290600101906020018083116137ee57829003601f168201915b5050935496989297505050600160301b90940462ffffff169392505050565b61383a63f23f702160e01b613b33565b600880546001600160a01b0319166001600160a01b0383161790556004611b96565b61386461430a565b6001600160a01b03811661388e576000604051631e4fbdf760e01b815260040161101b9190614b12565b6117c48161466f565b60607300000000db6f2ebe627260e411e6c973b7c48a6263f6cd39e36040518163ffffffff1660e01b8152600401600060405180830381865af4158015613085573d6000803e3d6000fd5b600061216a6126f2565b60006138f78261469a565b6001600160a01b0316036117c457600360405163d327ad1b60e01b815260040161101b9190615509565b6000818152600e602052604081208054600160481b600160881b031916600160481b426001600160401b031602179055612b7b565b6001600160a01b038216613980576000604051633250574960e11b815260040161101b9190614b12565b600061398e83836000613df6565b90506001600160a01b0381161561212f5760006040516339e3563760e11b815260040161101b9190614b12565b6000818152600f60205260409020600201546001600160a01b031633146117c457600760405163d327ad1b60e01b815260040161101b9190615509565b613a0181613c24565b6117c457600560405163d327ad1b60e01b815260040161101b9190615509565b6000828152600f60205260409020600201546001600160a01b0316331480613a4f5750613a4f3330836117c7565b61150957600860405163d327ad1b60e01b815260040161101b9190615509565b60008060005b60208160ff161015613acf576000868260ff1660208110613a9857613a9861566d565b1a90508015801590613aad575060808160ff16105b15613ac6578560ff168160ff1603613ac6578260010192505b50600101613a75565b8360ff168260ff1614925050509392505050565b60006001600160e01b031982166380ac58cd60e01b1480613b1457506001600160e01b03198216635b5e139f60e01b145b8061140957506301ffc9a760e01b6001600160e01b0319831614611409565b613b3e3330836117c7565b6117c457600660405163d327ad1b60e01b815260040161101b9190615509565b601954600160f81b900460ff1661145557601980546001600160f81b0316600160f81b1790556001604051600080516020615d6783398151915290600090a2565b600080613bab8361469a565b90506001600160a01b03811661140957604051637e27328960e01b81526004810184905260240161101b565b6000908152600560205260409020546001600160a01b031690565b61212f83838360016146b5565b80516000036117c457600160405163d327ad1b60e01b815260040161101b9190615509565b6000613c2f82613eea565b6000828152600e6020526040902054600160481b90046001600160401b03168015808061186b57506224ea00613c6583426158f4565b10949350505050565b613c7f846302153de360e31b613a21565b6040516374a4e96960e11b81527300000000db6f2ebe627260e411e6c973b7c48a629063e949d2d290613cbc908790879087908790600401615b2e565b60006040518083038186803b158015613cd457600080fd5b505af4158015611e13573d6000803e3d6000fd5b6001600160a01b0381166117c457600060405163d327ad1b60e01b815260040161101b9190615509565b601a54610100900460ff1615613d67576001600160a01b038216151580613d4157506001600160a01b03811615155b15613d6257602460405163d327ad1b60e01b815260040161101b9190615509565b613d79565b613d7082613ce8565b613d7981613ce8565b601280546001600160a01b038085166001600160a01b031992831617909255601580548484169083161790556011805487841690831617905560138054928616929091169190911790556007604051600080516020615d6783398151915290600090a250505050565b601b613dee82826155b4565b506002611b96565b600080613e028461469a565b90506001600160a01b03831615613e1e57613e1e8184866147b2565b6001600160a01b03811615613e5c57613e3b6000856000806146b5565b6001600160a01b038116600090815260046020526040902080546000190190555b6001600160a01b03851615613e8b576001600160a01b0385166000908152600460205260409020805460010190555b60008481526003602052604080822080546001600160a01b0319166001600160a01b0389811691821790925591518793918516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4949350505050565b601854811080613f0557506019546001600160f81b03168110155b156117c457600460405163d327ad1b60e01b815260040161101b9190615509565b6000818152600f60209081526040808320601c5482516101008101845260038301546001600160a01b03818116835261ffff600160a01b92839004811697840197909752600480860154808316858901528390049097166060840152845460ff90839004811660808501526002860154821660a0850152600186015480831660c08601529290920490911660e08301529351631718245360e21b81529295949390911692635c60914c92613fdb929101615b5c565b6020604051808303816000875af1158015613ffa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061401e9190615be4565b6005830180546001600160a01b0319166001600160a01b0383169081179091556040519192509084907f301670c9279b1d4319606681ed95e193d888fddba44d836b9e8d5585d8cc47b590600090a3505050565b80516000036117c457600260405163d327ad1b60e01b815260040161101b9190615509565b600080600080516020615da783398151915230846040516020016140bd93929190615c01565b60405160208183030381529060405290508051602082016000f091506001600160a01b03821661412f5760405162461bcd60e51b815260206004820152601e60248201527f436f6e7472616374417353746f726167653a205772697465204572726f720000604482015260640161101b565b50919050565b601780546001600160a01b0319166001600160a01b0383169081179091556040517fad0f299ec81a386c98df0ac27dae11dd020ed1b56963c53a7292e7a3a314539a90600090a250565b606060005b828160ff16602081106141995761419961566d565b1a60f81b6001600160f81b031916158015906141b8575060208160ff16105b156141cf57806141c781615c6c565b915050614184565b60405191506040820160405280825282602083015250919050565b6141fb836366e4623760e01b613a21565b604051638f65b1f360e01b81527300000000db6f2ebe627260e411e6c973b7c48a6290638f65b1f390611ff390869086908690600401615c8b565b6142413330836117c7565b1561424a575050565b60006142546126f2565b6001600160a01b031614801561428357506000828152600f60205260409020600201546001600160a01b031633145b1561428c575050565b600960405163d327ad1b60e01b815260040161101b9190615509565b600b80546001600160a01b0383166001600160a01b03199182168117909255600c80546001810182556000919091527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c701805490911690911790556003611b96565b336143136126f2565b6001600160a01b031614611455573360405163118cdaa760e01b815260040161101b9190614b12565b61434461430a565b611455600061466f565b601d54604051630eacc5e760e31b81526060916001600160a01b0316906375662f389061437f908590600401614b12565b600060405180830381865afa15801561439c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611409919081019061595c565b601d80546001600160a01b0319166001600160a01b038316179055600b611b96565b6143ef81613ce8565b601c80546001600160a01b0319166001600160a01b038316179055600a611b96565b6001600160a01b03821661443a5781604051630b61174360e31b815260040161101b9190614b12565b6001600160a01b03838116600081815260066020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b600080600080516020615da783398151915230846040516020016140bd93929190615cb3565b6001600160a01b0383163b1561135f57604051630a85bd0160e11b81526001600160a01b0384169063150b7a029061450f903390889087908790600401615d0c565b6020604051808303816000875af192505050801561454a575060408051601f3d908101601f1916820190925261454791810190615d49565b60015b6145aa573d808015614578576040519150601f19603f3d011682016040523d82523d6000602084013e61457d565b606091505b5080516000036145a25783604051633250574960e11b815260040161101b9190614b12565b805181602001fd5b6001600160e01b03198116630a85bd0160e11b146131345783604051633250574960e11b815260040161101b9190614b12565b606060006145ea83614808565b60010190506000816001600160401b0381111561460957614609614c33565b6040519080825280601f01601f191660200182016040528015614633576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a850494508461463d57509392505050565b614678816148de565b600d80546001600160a01b0319166001600160a01b0392909216919091179055565b6000908152600360205260409020546001600160a01b031690565b80806146c957506001600160a01b03821615155b156147825760006146d984613b9f565b90506001600160a01b038316158015906147055750826001600160a01b0316816001600160a01b031614155b801561471857506147168184613730565b155b15614738578260405163a9fbf51f60e01b815260040161101b9190614b12565b81156147805783856001600160a01b0316826001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b5050600090815260056020526040902080546001600160a01b0319166001600160a01b0392909216919091179055565b6147bd838383614930565b61212f576001600160a01b0383166147eb57604051637e27328960e01b81526004810182905260240161101b565b818160405163177e802f60e01b815260040161101b929190614f90565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b83106148475772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6904ee2d6d415b85acef8160201b8310614871576904ee2d6d415b85acef8160201b830492506020015b662386f26fc10000831061488f57662386f26fc10000830492506010015b6305f5e10083106148a7576305f5e100830492506008015b61271083106148bb57612710830492506004015b606483106148cd576064830492506002015b600a83106114095760010192915050565b600780546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60006001600160a01b0383161580159061186b5750826001600160a01b0316846001600160a01b0316148061496a575061496a8484613730565b8061186b5750826001600160a01b031661498383613bd7565b6001600160a01b031614949350505050565b600080604083850312156149a857600080fd5b50508035926020909101359150565b6001600160a01b03811681146117c457600080fd5b80356149d7816149b7565b919050565b6000806000606084860312156149f157600080fd5b83356149fc816149b7565b9250602084013591506040840135614a13816149b7565b809150509250925092565b60008060408385031215614a3157600080fd5b82359150602083013562ffffff81168114614a4b57600080fd5b809150509250929050565b6001600160e01b0319811681146117c457600080fd5b600060208284031215614a7e57600080fd5b813561129781614a56565b60005b83811015614aa4578181015183820152602001614a8c565b50506000910152565b60008151808452614ac5816020860160208601614a89565b601f01601f19169290920160200192915050565b6020815260006112976020830184614aad565b600060208284031215614afe57600080fd5b5035919050565b6001600160a01b03169052565b6001600160a01b0391909116815260200190565b60008060408385031215614b3957600080fd5b8235614b44816149b7565b946020939093013593505050565b600061016082019050614b66828451614b05565b6020830151614b7a602084018260ff169052565b506040830151614b8d6040840182614b05565b506060830151614ba2606084018260ff169052565b506080830151614bb56080840182614b05565b5060a0830151614bca60a084018260ff169052565b5060c0830151614bdd60c0840182614b05565b5060e0830151614bf360e084018261ffff169052565b5061010080840151614c0782850182614b05565b50506101208381015161ffff169083015261014080840151614c2b82850182614b05565b505092915050565b634e487b7160e01b600052604160045260246000fd5b60405161018081016001600160401b0381118282101715614c6c57614c6c614c33565b60405290565b604051608081016001600160401b0381118282101715614c6c57614c6c614c33565b604051601f8201601f191681016001600160401b0381118282101715614cbc57614cbc614c33565b604052919050565b60006001600160401b03821115614cdd57614cdd614c33565b50601f01601f191660200190565b600082601f830112614cfc57600080fd5b8135614d0f614d0a82614cc4565b614c94565b818152846020838601011115614d2457600080fd5b816020850160208301376000918101602001919091529392505050565b60008060408385031215614d5457600080fd5b8235915060208301356001600160401b03811115614d7157600080fd5b614d7d85828601614ceb565b9150509250929050565b600481106117c457600080fd5b60008060008060808587031215614daa57600080fd5b843593506020850135925060408501356001600160401b03811115614dce57600080fd5b614dda87828801614ceb565b9250506060850135614deb81614d87565b939692955090935050565b60008060008060808587031215614e0c57600080fd5b8435614e17816149b7565b93506020850135614e27816149b7565b92506040850135614e37816149b7565b91506060850135614deb816149b7565b600060208284031215614e5957600080fd5b81356001600160401b03811115614e6f57600080fd5b61186b84828501614ceb565b600080600060608486031215614e9057600080fd5b8335614e9b816149b7565b92506020840135614eab816149b7565b91506040840135614a1381614a56565b600080600060608486031215614ed057600080fd5b8335614edb816149b7565b92506020840135614eeb816149b7565b929592945050506040919091013590565b634e487b7160e01b600052602160045260246000fd5b60048110614f2257614f22614efc565b9052565b602081526000825160806020840152614f4260a0840182614aad565b90506020840151614f566040850182614f12565b5060408401516001600160a01b0316606084810191909152840151838203601f19016080850152614f878282614aad565b95945050505050565b6001600160a01b03929092168252602082015260400190565b600060208284031215614fbb57600080fd5b8135611297816149b7565b60008060008060008060c08789031215614fdf57600080fd5b863595506020870135614ff1816149b7565b94506040870135615001816149b7565b9350606087013592506080870135615018816149b7565b8092505060a087013590509295509295509295565b60008060006060848603121561504257600080fd5b833592506020840135915060408401356001600160401b0381111561506657600080fd5b61507286828701614ceb565b9150509250925092565b60008060006060848603121561509157600080fd5b83359250602084013591506040840135614a13816149b7565b600080602083850312156150bd57600080fd5b82356001600160401b03808211156150d457600080fd5b818501915085601f8301126150e857600080fd5b8135818111156150f757600080fd5b86602082850101111561510957600080fd5b60209290920196919550909350505050565b60008060006060848603121561513057600080fd5b8335925060208401356001600160401b0381111561514d57600080fd5b61515986828701614ceb565b9250506040840135614a1381614d87565b6000806040838503121561517d57600080fd5b823591506020830135614a4b816149b7565b60a0815260006151a260a0830188614aad565b82810360208401526151b48188614aad565b905082810360408401526151c88187614aad565b905082810360608401526151dc8186614aad565b905082810360808401526151f08185614aad565b98975050505050505050565b80356001600160f81b03811681146149d757600080fd5b80151581146117c457600080fd5b80356149d781615213565b6000806000806080858703121561524257600080fd5b84356001600160401b038082111561525957600080fd5b90860190610180828903121561526e57600080fd5b615276614c49565b82358281111561528557600080fd5b6152918a828601614ceb565b8252506020830135828111156152a657600080fd5b6152b28a828601614ceb565b6020830152506152c4604084016149cc565b60408201526152d5606084016149cc565b60608201526152e6608084016149cc565b60808201526152f760a084016149cc565b60a082015261530860c084016149cc565b60c082015261531960e084016149cc565b60e082015261010061532c8185016151fc565b9082015261012061533e848201615221565b90820152610140615350848201615221565b90820152610160615362848201615221565b908201529550615374602088016149cc565b9450604087013591508082111561538a57600080fd5b5061539787828801614ceb565b9250506153a6606086016149cc565b905092959194509250565b6001600160a01b031991909116815260200190565b600080604083850312156153d957600080fd5b82356153e4816149b7565b91506020830135614a4b81615213565b6000806000806080858703121561540a57600080fd5b8435615415816149b7565b93506020850135615425816149b7565b92506040850135915060608501356001600160401b0381111561544757600080fd5b61545387828801614ceb565b91505092959194509250565b6000806040838503121561547257600080fd5b82356001600160401b0381111561548857600080fd5b61549485828601614ceb565b9250506020830135614a4b816149b7565b600080604083850312156154b857600080fd5b82356154c3816149b7565b91506020830135614a4b816149b7565b6060815260006154e66060830186614aad565b82810360208401526154f88186614aad565b915050826040830152949350505050565b602081016026831061551d5761551d614efc565b91905290565b600181811c9082168061553757607f821691505b60208210810361412f57634e487b7160e01b600052602260045260246000fd5b601f82111561212f576000816000526020600020601f850160051c810160208610156155805750805b601f850160051c820191505b818110156121e45782815560010161558c565b600019600383901b1c191660019190911b1790565b81516001600160401b038111156155cd576155cd614c33565b6155e1816155db8454615523565b84615557565b602080601f83116001811461561057600084156155fe5750858301515b615608858261559f565b8655506121e4565b600085815260208120601f198616915b8281101561563f57888601518255948401946001909101908401615620565b508582101561565d5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b634e487b7160e01b600052603260045260246000fd5b60208082526045908201527f47656e417274373231436f726556335f437572617465645f466c65783a20437560408201527f726174656420646570656e64656e63792074797065206d757374206265204f4e60608201526421a420a4a760d91b608082015260a00190565b634e487b7160e01b600052601160045260246000fd5b60008261572157634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561573857600080fd5b815161129781615213565b92835260208301919091526001600160a01b0316604082015260600190565b6000615770614d0a84614cc4565b905082815283838301111561578457600080fd5b611297836020830184614a89565b600082601f8301126157a357600080fd5b61129783835160208501615762565b6000602082840312156157c457600080fd5b81516001600160401b03808211156157db57600080fd5b90830190608082860312156157ef57600080fd5b6157f7614c72565b82518281111561580657600080fd5b61581287828601615792565b825250602083015161582381614d87565b60208201526040830151615836816149b7565b604082015260608301518281111561584d57600080fd5b61585987828601615792565b60608301525095945050505050565b80820180821115611409576114096156ee565b8082028115828204841417611409576114096156ee565b6001600160a01b039586168152938516602085015260408401929092529092166060820152608081019190915260a00190565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b81810381811115611409576114096156ee565b82815260406020820152600061186b6040830184614aad565b62ffffff81811683821601908082111561593c5761593c6156ee565b5092915050565b60006020828403121561595557600080fd5b5051919050565b60006020828403121561596e57600080fd5b81516001600160401b0381111561598457600080fd5b61186b84828501615792565b838152826020820152606060408201526000614f876060830184614aad565b600083516159c1818460208801614a89565b8351908301906159d5818360208801614a89565b01949350505050565b8181036159e9575050565b6159f38254615523565b6001600160401b03811115615a0a57615a0a614c33565b615a18816155db8454615523565b6000601f821160018114615a465760008315615a345750848201545b615a3e848261559f565b855550613134565b600085815260209020601f19841690600086815260209020845b83811015615a805782860154825560019586019590910190602001615a60565b508583101561565d5793015460001960f8600387901b161c19169092555050600190811b01905550565b6001600160f81b0381811683821601908082111561593c5761593c6156ee565b62ffffff82811682821603908082111561593c5761593c6156ee565b600060208284031215615af857600080fd5b81516001600160401b03811115615b0e57600080fd5b8201601f81018413615b1f57600080fd5b61186b84825160208401615762565b848152836020820152608060408201526000615b4d6080830185614aad565b9050614f876060830184614f12565b60006101008201905060018060a01b03808451168352602084015161ffff808216602086015282604087015116604086015280606087015116606086015250505060ff608084015116608083015260a0830151615bbc60a0840182614b05565b5060c0830151615bcf60c0840182614b05565b5060e083015161593c60e084018260ff169052565b600060208284031215615bf657600080fd5b8151611297816149b7565b6a600b5981380380925939f360a81b8152607f60f91b600b820152600c81018490526000602c8201819052606084901b6001600160601b0319166038830152600160f81b604c8301528251615c5d81604d850160208701614a89565b91909101604d01949350505050565b600060ff821660ff8103615c8257615c826156ee565b60010192915050565b838152606060208201526000615ca46060830185614aad565b905061186b6040830184614f12565b6a600b5981380380925939f360a81b8152607f60f91b600b820152600c81018490526000602c8201819052606084901b6001600160601b0319166038830152604c82018190528251615c5d81604d850160208701614a89565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090615d3f90830184614aad565b9695505050505050565b600060208284031215615d5b57600080fd5b815161129781614a5656fe8b810f233ce7ee6e962ab4d98bf0277751de1f5589de3dcc812ac2047994d009c582d05e1da854143bd3271ef4529d79cf5a69fc6057ae320f357acfd291b73842797465636f646553746f726167655f56322e302e305f5f5f5f5f5f5f5f5f20b96a30340e86d03ce4be42f94ac02d7b27b4a4cdae942beb69026718dfe66afca26469706673582212201e9d38eabd3d6e63cfa25300a549cc409eafe74b8758e4d291ae27478132413664736f6c63430008160033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000abb7a99780820c87c850af7fd1bc5e67880000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000a791abed33872c44a3d215a3743b000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000021a89ef8c577ebacfe8198644222b49dfd9284f90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013178a7a8a1a9460dbe39f7eccebd91b31752b9100000000000000000000000000000000ce5eebab4b5c2d6cc5e73eaafa634db3000000000000000000000000a2ccfe293bc2cdd78d8166a82d1e18cd2148122b00000000000000000000000000000000000000000000000000000000000001f9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a41727420426c6f636b73000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006424c4f434b530000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b68747470733a2f2f746f6b656e2e617274626c6f636b732e696f2f0000000000
-----Decoded View---------------
Arg [0] : engineConfiguration (tuple):
Arg [1] : tokenName (string): Art Blocks
Arg [2] : tokenSymbol (string): BLOCKS
Arg [3] : renderProviderAddress (address): 0x21A89ef8c577ebaCfe8198644222B49DFD9284F9
Arg [4] : platformProviderAddress (address): 0x0000000000000000000000000000000000000000
Arg [5] : newSuperAdminAddress (address): 0x0000000000000000000000000000000000000000
Arg [6] : randomizerContract (address): 0x13178A7a8A1A9460dBE39f7eCcEbD91B31752b91
Arg [7] : splitProviderAddress (address): 0x00000000CE5EEBAB4B5C2d6Cc5E73eaafA634DB3
Arg [8] : minterFilterAddress (address): 0xa2ccfE293bc2CDD78D8166a82D1e18cD2148122b
Arg [9] : startingProjectId (uint248): 505
Arg [10] : autoApproveArtistSplitProposals (bool): False
Arg [11] : nullPlatformProvider (bool): True
Arg [12] : allowArtistProjectActivation (bool): False
Arg [1] : adminACLContract_ (address): 0x000000abB7A99780820c87c850Af7fD1Bc5e6788
Arg [2] : defaultBaseURIHost (string): https://token.artblocks.io/
Arg [3] : bytecodeStorageReaderContract_ (address): 0x000000000000A791ABed33872C44a3D215a3743B
-----Encoded View---------------
22 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [1] : 000000000000000000000000000000abb7a99780820c87c850af7fd1bc5e6788
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000280
Arg [3] : 000000000000000000000000000000000000a791abed33872c44a3d215a3743b
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000180
Arg [5] : 00000000000000000000000000000000000000000000000000000000000001c0
Arg [6] : 00000000000000000000000021a89ef8c577ebacfe8198644222b49dfd9284f9
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [9] : 00000000000000000000000013178a7a8a1a9460dbe39f7eccebd91b31752b91
Arg [10] : 00000000000000000000000000000000ce5eebab4b5c2d6cc5e73eaafa634db3
Arg [11] : 000000000000000000000000a2ccfe293bc2cdd78d8166a82d1e18cd2148122b
Arg [12] : 00000000000000000000000000000000000000000000000000000000000001f9
Arg [13] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [14] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [15] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [16] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [17] : 41727420426c6f636b7300000000000000000000000000000000000000000000
Arg [18] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [19] : 424c4f434b530000000000000000000000000000000000000000000000000000
Arg [20] : 000000000000000000000000000000000000000000000000000000000000001b
Arg [21] : 68747470733a2f2f746f6b656e2e617274626c6f636b732e696f2f0000000000
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.