Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 25 from a total of 391 transactions
| Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Configure Token ... | 23677238 | 17 hrs ago | IN | 0 ETH | 0.00026672 | ||||
| Configure Token ... | 23663383 | 2 days ago | IN | 0 ETH | 0.00002066 | ||||
| Configure Token ... | 23661763 | 2 days ago | IN | 0 ETH | 0.00001556 | ||||
| Configure Token ... | 23655840 | 3 days ago | IN | 0 ETH | 0.0002032 | ||||
| Configure Token ... | 23650696 | 4 days ago | IN | 0 ETH | 0.00001182 | ||||
| Configure Token ... | 23641508 | 5 days ago | IN | 0 ETH | 0.00003427 | ||||
| Configure Token ... | 23640488 | 5 days ago | IN | 0 ETH | 0.00001673 | ||||
| Configure Token ... | 23633456 | 6 days ago | IN | 0 ETH | 0.00002054 | ||||
| Configure Token ... | 23630352 | 7 days ago | IN | 0 ETH | 0.00009766 | ||||
| Configure Token ... | 23621310 | 8 days ago | IN | 0 ETH | 0.00001202 | ||||
| Configure Token ... | 23621280 | 8 days ago | IN | 0 ETH | 0.00001236 | ||||
| Configure Token ... | 23614336 | 9 days ago | IN | 0 ETH | 0.00015767 | ||||
| Configure Token ... | 23600543 | 11 days ago | IN | 0 ETH | 0.00009758 | ||||
| Configure Token ... | 23592863 | 12 days ago | IN | 0 ETH | 0.0000274 | ||||
| Configure Token ... | 23586278 | 13 days ago | IN | 0 ETH | 0.00017472 | ||||
| Configure Token ... | 23586176 | 13 days ago | IN | 0 ETH | 0.00010205 | ||||
| Configure Token ... | 23585262 | 13 days ago | IN | 0 ETH | 0.00010799 | ||||
| Configure Token ... | 23584186 | 13 days ago | IN | 0 ETH | 0.00035878 | ||||
| Configure Token ... | 23583845 | 13 days ago | IN | 0 ETH | 0.00022571 | ||||
| Configure Token ... | 23581654 | 14 days ago | IN | 0 ETH | 0.00010369 | ||||
| Configure Token ... | 23581636 | 14 days ago | IN | 0 ETH | 0.00010294 | ||||
| Configure Token ... | 23581331 | 14 days ago | IN | 0 ETH | 0.00015825 | ||||
| Configure Token ... | 23580890 | 14 days ago | IN | 0 ETH | 0.00009884 | ||||
| Configure Token ... | 23579842 | 14 days ago | IN | 0 ETH | 0.00015889 | ||||
| Configure Token ... | 23579814 | 14 days ago | IN | 0 ETH | 0.00015874 |
Latest 9 internal transactions
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
To
|
|||
|---|---|---|---|---|---|---|---|
| 0x600b5981 | 23515642 | 23 days ago | Contract Creation | 0 ETH | |||
| 0x600b5981 | 23370509 | 43 days ago | Contract Creation | 0 ETH | |||
| 0x600b5981 | 23125373 | 77 days ago | Contract Creation | 0 ETH | |||
| 0x600b5981 | 22782064 | 125 days ago | Contract Creation | 0 ETH | |||
| 0x600b5981 | 22782064 | 125 days ago | Contract Creation | 0 ETH | |||
| 0x600b5981 | 22755586 | 129 days ago | Contract Creation | 0 ETH | |||
| 0x600b5981 | 22755586 | 129 days ago | Contract Creation | 0 ETH | |||
| 0x600b5981 | 22755586 | 129 days ago | Contract Creation | 0 ETH | |||
| 0x60a06040 | 22420273 | 176 days ago | Contract Creation | 0 ETH |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
PMPV0
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
// Created By: Art Blocks Inc.
pragma solidity 0.8.22;
import {IWeb3Call} from "../interfaces/v0.8.x/IWeb3Call.sol";
import {IPMPV0} from "../interfaces/v0.8.x/IPMPV0.sol";
import {IPMPConfigureHook} from "../interfaces/v0.8.x/IPMPConfigureHook.sol";
import {IPMPAugmentHook} from "../interfaces/v0.8.x/IPMPAugmentHook.sol";
import {IGenArt721CoreContractV3_Base} from "../interfaces/v0.8.x/IGenArt721CoreContractV3_Base.sol";
import {IERC721} from "@openzeppelin-5.0/contracts/token/ERC721/IERC721.sol";
import {IDelegateRegistry} from "../interfaces/v0.8.x/IDelegateRegistry.sol";
import {ERC165Checker} from "@openzeppelin-5.0/contracts/utils/introspection/ERC165Checker.sol";
import {Strings} from "@openzeppelin-5.0/contracts/utils/Strings.sol";
import {ReentrancyGuard} from "@openzeppelin-5.0/contracts/utils/ReentrancyGuard.sol";
import {Web3Call} from "./Web3Call.sol";
import {ImmutableStringArray} from "../libs/v0.8.x/ImmutableStringArray.sol";
import {ABHelpers} from "../libs/v0.8.x/ABHelpers.sol";
/**
* @title Project Metadata Parameters (PMP) contract, V0
* @author Art Blocks Inc.
* @notice This contract enables Artists to define and configure project parameters that token
* owners can set within constraints. This provides a standardized way for projects to expose
* configurable parameters that can be used by renderers and other contracts.
* WARNING: This contract implements an open protocol for parameter configuration, and does not
* restrict usage to Art Blocks or its affiliates. Use with caution. The contract does assume
* that the core contract conforms to the IGenArt721CoreContractV3_Base interface for authentication,
* and that the core contract is using the ERC721 standard for token management. These assumptions
* may not hold for all core contracts, especially those outside of the Art Blocks ecosystem.
* @notice Artists may configure arbitrary external hooks for post-token-configuration and
* read-augmentation. These hooks are executed at the end of the token configuration process
* and when reading token PMPs, respectively. These hooks are validated for ERC165 interface
* compatibility, but have the ability to execute arbitrary code at the discretion of the artist.
* The contract implements reentrancy guards to protect against reentrancy attacks during hook calls,
* resulting in the subcall having a similar level as a minting contract sending funds to an artist's
* additional payee address during a token sale. Use appropriate discretion.
* WARNING: Hook calls are not validated, and may cause unexpected behavior. These include:
* - Hooks may revert the transaction, resulting in denial of service
* - Hooks may augment the token's returned parameters with unintended data, resulting in
* unexpected behavior
* The artist is solely responsible for configuring hooks and validating their behavior.
* @dev This contract implements the IWeb3Call and IPMPV0 interfaces, providing functionality
* for parameter configuration and retrieval. It includes support for various parameter types,
* authorization options, and hooks for extending functionality.
*/
contract PMPV0 is IPMPV0, Web3Call, ReentrancyGuard {
using Strings for string;
using Strings for uint256;
using Strings for int256;
using ImmutableStringArray for ImmutableStringArray.StringArray;
IDelegateRegistry public immutable delegateRegistry;
bytes32 public constant DELEGATION_REGISTRY_TOKEN_OWNER_RIGHTS =
bytes32("postmintparameters");
bytes32 private constant _TYPE = "PMPV0";
bytes16 private constant _HEX_DIGITS = "0123456789abcdef";
uint256 private constant _DECIMAL_PRECISION_DIGITS = 10;
uint256 private constant _DECIMAL_PRECISION =
10 ** _DECIMAL_PRECISION_DIGITS;
// @dev min hex color assumed to be 0x000000
// @dev intentionally not including alpha channel, can be separate PMP if desired
uint256 private constant _HEX_COLOR_MAX = 0xFFFFFF;
uint256 private constant _TIMESTAMP_MIN = 0; // @dev unix timestamp, 0 = 1970-01-01
uint256 private constant _TIMESTAMP_MAX = type(uint64).max; // max guardrail, ~10 billion years
/**
* @notice Storage structure for parameter configuration.
* @dev Includes additional field highestConfigNonce compared to PMPConfig.
*/
struct PMPConfigStorage {
// @dev highest config nonce for which this PMPConfig is valid (relative to projectConfig.configNonce)
uint8 highestConfigNonce; // slot 0: 1 byte
AuthOption authOption; // slot 0: 1 byte
ParamType paramType; // slot 0: 1 byte
uint48 pmpLockedAfterTimestamp; // slot 0: 6 bytes // @dev uint48 is sufficient to store ~2^48 seconds, ~8,900 years
address authAddress; // slot 0: 20 bytes
// @dev store array length as uint8 in slot 0 for SLOAD efficiency during token configuration
uint8 selectOptionsLength; // slot 0: 1 byte
// @dev use immutable string array for storage efficiency during project configuration
ImmutableStringArray.StringArray selectOptions; // slot 1: 32 bytes
bytes32 minRange; // slot 2: 32 bytes
bytes32 maxRange; // slot 3: 32 bytes
}
// @dev core contract address and projectId are implicit based on mapping pointing to ProjectConfig struct
struct ProjectConfig {
// @dev array of pmpKeys for efficient enumeration, uses efficient SSTORE2 storage
ImmutableStringArray.StringArray pmpKeys; // slot 0: 32 bytes
// @dev mapping of pmpKeys to PMPConfigStorage for O(1) access, and cheap updates when no changes
mapping(bytes32 pmpKeyHash => PMPConfigStorage pmpConfigStorage) pmpConfigsStorage; // slot 1: 32 bytes
// config nonce that is incremented during each configureProject call
uint8 configNonce; // slot 2: 1 byte
// post-configuration hook to be called after a token's PMP is configured
IPMPConfigureHook tokenPMPPostConfigHook; // slot 2: 20 bytes
// token pmp read augmentation hook to be called when reading a token's PMPs
IPMPAugmentHook tokenPMPReadAugmentationHook; // slot 3: 20 bytes
}
// mapping of ProjectConfig structs for each project
mapping(address coreContract => mapping(uint256 projectId => ProjectConfig projectConfig)) projectConfigs;
// mapping of PMP structs for each token
mapping(address coreContract => mapping(uint256 tokenId => mapping(bytes32 pmpKeyHash => PMPStorage pmp))) tokenPMPs;
/**
* @notice Constructor for PMPV0 contract.
* @param delegateRegistry_ The address of the delegate registry contract. Intended to be
* the delegate.xyz v2 contract.
*/
constructor(IDelegateRegistry delegateRegistry_) {
delegateRegistry = delegateRegistry_;
emit DelegationRegistryUpdated(address(delegateRegistry_));
}
/**
* @notice Configure project hooks for post-configuration and read augmentation.
* WARNING: Hook calls may revert the transaction, and may cause unexpected behavior.
* The artist is solely responsible for configuring hooks and validating their behavior.
* Improper configuration or hooks with unexpected behavior may result in denial of service,
* unexpected behavior, or other issues.
* @param coreContract The address of the core contract.
* @param projectId The project ID to configure hooks for.
* @param tokenPMPPostConfigHook The hook to call after a token's PMP is configured.
* @param tokenPMPReadAugmentationHook The hook to call when reading a token's PMPs.
* @dev Only the project artist can configure project hooks.
* @dev Both hooks are validated for ERC165 interface compatibility.
* @dev Uses nonReentrant modifier to prevent reentrancy attacks during hook calls or auth checks.
*/
function configureProjectHooks(
address coreContract,
uint256 projectId,
IPMPConfigureHook tokenPMPPostConfigHook,
IPMPAugmentHook tokenPMPReadAugmentationHook
) external nonReentrant {
// only artists may configure project hooks
_onlyArtist({
coreContract: coreContract,
projectId: projectId,
sender: msg.sender
});
// validation - check for ERC165 implementation for non-null hooks
if (address(tokenPMPPostConfigHook) != address(0)) {
// use ERC165 checker to validate implementation
require(
ERC165Checker.supportsInterface({
account: address(tokenPMPPostConfigHook),
interfaceId: type(IPMPConfigureHook).interfaceId
}),
"PMP: tokenPMPPostConfigHook does not implement IPMPConfigureHook"
);
}
if (address(tokenPMPReadAugmentationHook) != address(0)) {
require(
ERC165Checker.supportsInterface({
account: address(tokenPMPReadAugmentationHook),
interfaceId: type(IPMPAugmentHook).interfaceId
}),
"PMP: tokenPMPReadAugmentationHook does not implement IPMPAugmentHook"
);
}
// update projectConfig
ProjectConfig storage projectConfig = projectConfigs[coreContract][
projectId
];
projectConfig.tokenPMPPostConfigHook = tokenPMPPostConfigHook;
projectConfig
.tokenPMPReadAugmentationHook = tokenPMPReadAugmentationHook;
// emit event
emit ProjectHooksConfigured({
coreContract: coreContract,
projectId: projectId,
tokenPMPPostConfigHook: tokenPMPPostConfigHook,
tokenPMPReadAugmentationHook: tokenPMPReadAugmentationHook
});
}
/**
* @notice Configure the available parameters for a project and their constraints.
* @param coreContract The address of the core contract.
* @param projectId The project ID to configure parameters for.
* @param pmpInputConfigs Array of parameter configurations defining the available parameters.
* @dev Only the project artist can configure project parameters.
* @dev Each configuration is validated for proper parameter type and constraints.
* @dev The project's configuration nonce is incremented with each call.
* @dev Only <= 256 configs are supported.
* @dev Only <= 255 bytes are supported for pmpKeys.
* @dev nonReentrant due to auth check that requires interaction.
*/
function configureProject(
address coreContract,
uint256 projectId,
PMPInputConfig[] calldata pmpInputConfigs
) external nonReentrant {
// only artists may configure projects
_onlyArtist({
coreContract: coreContract,
projectId: projectId,
sender: msg.sender
});
// validate pmpInputConfigs
uint256 pmpInputConfigsLength = pmpInputConfigs.length;
// @dev no coverage on else branch due to test complexity
require(pmpInputConfigsLength <= 256, "PMP: Only <= 256 configs");
for (uint256 i = 0; i < pmpInputConfigsLength; i++) {
uint256 keyLengthBytes = bytes(pmpInputConfigs[i].key).length;
// @dev max key length constraint is a reasonable gas guardrail
require(
keyLengthBytes > 0 && keyLengthBytes < 256,
"PMP: pmpKey cannot be empty or exceed 255 bytes"
);
_validatePMPConfig(pmpInputConfigs[i].pmpConfig);
}
// store pmpInputConfigs data in ProjectConfig struct
// @dev load projectConfig storage pointer
ProjectConfig storage projectConfig = projectConfigs[coreContract][
projectId
];
// increment config nonce
// @dev solidity ^0.8 reverts on overflow (greater than 255 edits not supported)
uint8 newConfigNonce = projectConfig.configNonce + 1;
projectConfig.configNonce = newConfigNonce;
// efficiently sync pmp keys to ProjectConfig
// copy input pmp keys to memory array for efficient passing to _syncPMPKeys
string[] memory pmpKeys = new string[](pmpInputConfigsLength);
for (uint256 i = 0; i < pmpInputConfigsLength; i++) {
pmpKeys[i] = pmpInputConfigs[i].key;
}
_syncPMPKeys({inputKeys: pmpKeys, projectConfig: projectConfig});
// store pmp configs in ProjectConfig struct's mapping
for (uint256 i = 0; i < pmpInputConfigsLength; i++) {
// store pmpConfigStorage in ProjectConfig struct's mapping
PMPConfigStorage storage pmpConfigStorage = projectConfig
.pmpConfigsStorage[_getStringHash(pmpKeys[i])];
{
// validate that any current pmp at this key is not locked
uint256 currentPPMLockedAfterTimestamp = pmpConfigStorage
.pmpLockedAfterTimestamp;
require(
currentPPMLockedAfterTimestamp == 0 ||
currentPPMLockedAfterTimestamp > block.timestamp,
"PMP: pmp is locked and cannot be updated"
);
}
// update highestConfigNonce
pmpConfigStorage.highestConfigNonce = newConfigNonce;
// copy function input pmpConfig data to pmpConfigStorage
PMPConfig memory inputPMPConfig = pmpInputConfigs[i].pmpConfig;
pmpConfigStorage.authOption = inputPMPConfig.authOption;
pmpConfigStorage.paramType = inputPMPConfig.paramType;
pmpConfigStorage.pmpLockedAfterTimestamp = inputPMPConfig
.pmpLockedAfterTimestamp;
pmpConfigStorage.authAddress = inputPMPConfig.authAddress;
// @dev length already validated <= 255 in _validatePMPConfig, safe to cast unchecked
pmpConfigStorage.selectOptionsLength = uint8(
inputPMPConfig.selectOptions.length
);
// @dev ImmutableStringArray is optimized for the case where the array is empty.
ImmutableStringArray.store(
pmpConfigStorage.selectOptions,
inputPMPConfig.selectOptions
);
pmpConfigStorage.minRange = inputPMPConfig.minRange;
pmpConfigStorage.maxRange = inputPMPConfig.maxRange;
}
// emit event
emit ProjectConfigured({
coreContract: coreContract,
projectId: projectId,
pmpInputConfigs: pmpInputConfigs,
projectConfigNonce: newConfigNonce
});
}
/**
* @notice Configure the parameters for a specific token according to project constraints.
* WARNING: This contract represents an open protocol for parameter configuration, and does not
* restrict usage to Art Blocks or its affiliates. Use with caution. The contract does assume
* that the core contract conforms to the IGenArt721CoreContractV3_Base interface for authentication,
* and that the core contract is using the ERC721 standard for token management. These assumptions
* may not hold for all core contracts, especially those outside of the Art Blocks ecosystem.
* WARNING: Hook calls may revert the transaction, and may cause unexpected behavior.
* The artist is solely responsible for configuring hooks and validating their behavior.
* Improper configuration or hooks with unexpected behavior may result in denial of service,
* unexpected behavior, or other issues.
* ERC-721 Token-level wallet delegation for the TokenOwner role is supported via delegate.xyz v2.
* For opt-in granular control of rights specific to these operations, vault owners may define subdelegations
* with bytes32 rights "postmintparameters". Per the delegate.xyz v2 specification, delegations made with the
* empty string "" will be interpreted as a full delegation of all rights.
* @param coreContract The address of the core contract.
* @param tokenId The tokenId of the token to configure.
* @param pmpInputs The parameter inputs to configure for the token.
* @dev Validates each parameter input against the project's configuration.
* @dev Stores the configured parameters for the token.
* @dev Calls the post-configuration hook if one is configured for the project.
* @dev Uses nonReentrant modifier to prevent reentrancy attacks during hook calls or auth checks.
*/
function configureTokenParams(
address coreContract,
uint256 tokenId,
PMPInput[] calldata pmpInputs
) external nonReentrant {
ProjectConfig storage projectConfig = projectConfigs[coreContract][
ABHelpers.tokenIdToProjectId(tokenId)
];
// preallocate memory for auth addresses of each pmpInput
address[] memory authAddresses = new address[](pmpInputs.length);
// assign each pmpInput to the token
// @dev pmpInputs processed sequentially in order of input
for (uint256 i = 0; i < pmpInputs.length; i++) {
bytes32 pmpKeyHash = _getStringHash(pmpInputs[i].key);
PMPInput memory pmpInput = pmpInputs[i];
PMPStorage storage tokenPMP = tokenPMPs[coreContract][tokenId][
pmpKeyHash
];
PMPConfigStorage storage pmpConfigStorage = projectConfig
.pmpConfigsStorage[pmpKeyHash];
// validate pmpInput + record the authenticated address used to configure the pmpInput
authAddresses[i] = _validatePMPInputAndAuth({
tokenId: tokenId,
coreContract: coreContract,
pmpInput: pmpInput,
pmpConfigStorage: pmpConfigStorage,
projectConfigNonce: projectConfig.configNonce
});
// store pmpInput data in PMPStorage struct
tokenPMP.configuredParamType = pmpConfigStorage.paramType;
// only assign the value that affects param type (gas savings)
if (pmpConfigStorage.paramType == ParamType.String) {
if (pmpInput.configuringArtistString) {
tokenPMP.artistConfiguredValueString = pmpInput
.configuredValueString;
} else {
tokenPMP.nonArtistConfiguredValueString = pmpInput
.configuredValueString;
}
} else {
tokenPMP.configuredValue = pmpInput.configuredValue;
}
// call post-config hook if configured for the project
// @dev this function is nonreentrant for additional reentrancy protection
// @dev intentionally revert entire transaction if hook reverts to prevent latent failure
if (address(projectConfig.tokenPMPPostConfigHook) != address(0)) {
projectConfig.tokenPMPPostConfigHook.onTokenPMPConfigure({
coreContract: coreContract,
tokenId: tokenId,
pmpInput: pmpInput
});
}
}
// emit event
emit TokenParamsConfigured({
coreContract: coreContract,
tokenId: tokenId,
pmpInputs: pmpInputs,
authAddresses: authAddresses
});
}
/**
* @notice Get the token parameters for a given token.
* If none are configured, the tokenParams should be empty.
* WARNING: Hook calls may revert the transaction, and may cause unexpected behavior.
* The artist is solely responsible for configuring hooks and validating their behavior.
* Improper configuration or hooks with unexpected behavior may result in denial of service,
* unexpected behavior, or other issues.
* @param coreContract The address of the core contract to call.
* @param tokenId The tokenId of the token to get data for.
* @return tokenParams An array of token parameters for the queried token.
*/
function getTokenParams(
address coreContract,
uint256 tokenId
)
external
view
override
returns (IWeb3Call.TokenParam[] memory tokenParams)
{
uint256 projectId = ABHelpers.tokenIdToProjectId(tokenId);
ProjectConfig storage projectConfig = projectConfigs[coreContract][
projectId
];
string[] memory pmpKeys = projectConfig.pmpKeys.getAll();
uint256 pmpKeysLength = pmpKeys.length;
// @dev initialize tokenParams array with maximum possible length
tokenParams = new IWeb3Call.TokenParam[](pmpKeysLength);
uint256 populatedParamsIndex = 0; // @dev index of next populated tokenParam
for (uint256 i = 0; i < pmpKeysLength; i++) {
// load pmp value
bytes32 pmpKeyHash = _getStringHash(pmpKeys[i]);
(bool isConfigured, string memory value) = _getPMPValue({
pmpConfigStorage: projectConfig.pmpConfigsStorage[pmpKeyHash],
pmp: tokenPMPs[coreContract][tokenId][pmpKeyHash]
});
// continue when param is unconfigured
if (!isConfigured) {
// continue without incrementing populatedParamsIndex
continue;
}
// append configured to tokenParams array
tokenParams[populatedParamsIndex] = IWeb3Call.TokenParam({
key: pmpKeys[i],
value: value
});
populatedParamsIndex++;
}
// truncate tokenParams array to populatedParamsIndex via assembly
assembly {
// directly modify the memory array length in memory
mstore(tokenParams, populatedParamsIndex)
}
// call augmentation hook if configured for the project
if (address(projectConfig.tokenPMPReadAugmentationHook) != address(0)) {
// assign return value to the augmented tokenParams
// @dev executed in read-only context, artist-configured hook contract (not entirely arbitrary)
tokenParams = projectConfig
.tokenPMPReadAugmentationHook
.onTokenPMPReadAugmentation({
coreContract: coreContract,
tokenId: tokenId,
tokenParams: tokenParams
});
}
// @dev implicitly returns tokenParams
}
// ---- introspection view functions ----
/**
* @notice Get the project config for a given project.
* @param coreContract The address of the core contract to call.
* @param projectId The projectId of the project to get data for.
* @return pmpKeys The configured pmpKeys for the project.
* @return configNonce The config nonce for the project.
* @return tokenPMPPostConfigHook The tokenPMPPostConfigHook for the project.
* @return tokenPMPReadAugmentationHook The tokenPMPReadAugmentationHook for the project.
*/
function getProjectConfig(
address coreContract,
uint256 projectId
)
external
view
returns (
string[] memory pmpKeys,
uint8 configNonce,
IPMPConfigureHook tokenPMPPostConfigHook,
IPMPAugmentHook tokenPMPReadAugmentationHook
)
{
ProjectConfig storage projectConfig = projectConfigs[coreContract][
projectId
];
// @dev edge case - uninitialized pmpKeys - empty array returned by getAll()
pmpKeys = projectConfig.pmpKeys.getAll();
configNonce = projectConfig.configNonce;
tokenPMPPostConfigHook = projectConfig.tokenPMPPostConfigHook;
tokenPMPReadAugmentationHook = projectConfig
.tokenPMPReadAugmentationHook;
}
/**
* @notice Checks if the given wallet has the owner role for the given token.
* It returns true if the wallet is the owner of the token or if the wallet
* is a delegate of the token owner; otherwise it returns false.
* Reverts if an invalid coreContract or tokenId is provided.
* Provided for convenience, as the same check is performed in the
* configureTokenParams function.
* @param wallet The wallet address to check.
* @param coreContract The address of the core contract to call.
* @param tokenId The tokenId of the token to check.
* @return isTokenOwnerOrDelegate_ True if the wallet is the owner or a delegate of the token,
* false otherwise.
*/
function isTokenOwnerOrDelegate(
address wallet,
address coreContract,
uint256 tokenId
) external view returns (bool isTokenOwnerOrDelegate_) {
(isTokenOwnerOrDelegate_, ) = _isTokenOwnerOrDelegate({
tokenId: tokenId,
coreContract: coreContract,
sender: wallet
});
}
/**
* @notice Get the PMP config from storage for a given project and pmpKey.
* @dev Returns the storage values, even if unconfigured or not part of the
* active project config. Check latestConfigNonce to verify if the pmpKey
* is part of the active project config.
* @dev any populated select options are loaded and returned as a string array
* @param coreContract The address of the core contract to call.
* @param projectId The projectId of the project to get data for.
* @param pmpKey The pmpKey of the pmp to get data for.
* @return pmpConfigView The PMP config for the given project and pmpKey.
*/
function getProjectPMPConfig(
address coreContract,
uint256 projectId,
string memory pmpKey
) external view returns (PMPConfigView memory pmpConfigView) {
PMPConfigStorage storage pmpConfigStorage = projectConfigs[
coreContract
][projectId].pmpConfigsStorage[_getStringHash(pmpKey)];
// load values from storage
pmpConfigView.highestConfigNonce = pmpConfigStorage.highestConfigNonce;
pmpConfigView.authOption = pmpConfigStorage.authOption;
pmpConfigView.paramType = pmpConfigStorage.paramType;
pmpConfigView.pmpLockedAfterTimestamp = pmpConfigStorage
.pmpLockedAfterTimestamp;
pmpConfigView.authAddress = pmpConfigStorage.authAddress;
pmpConfigView.selectOptionsLength = pmpConfigStorage
.selectOptionsLength;
pmpConfigView.selectOptions = pmpConfigStorage.selectOptions.getAll();
pmpConfigView.minRange = pmpConfigStorage.minRange;
pmpConfigView.maxRange = pmpConfigStorage.maxRange;
}
/**
* @notice Get the PMP storage for a given token and pmpKey.
* Returns the PMP storage struct for the queried token and pmpKey. The storage
* may be stale if the token has been reconfigured, or empty if never configured.
* @param coreContract The address of the core contract to call.
* @param tokenId The tokenId of the token to get data for.
* @param pmpKey The pmpKey of the pmp to get data for.
* @return pmp The PMP storage for the given token and pmpKey.
*/
function getTokenPMPStorage(
address coreContract,
uint256 tokenId,
string memory pmpKey
) external view returns (PMPStorage memory pmp) {
pmp = tokenPMPs[coreContract][tokenId][_getStringHash(pmpKey)];
}
/**
* @notice Synchronizes the project's parameter keys with the provided input keys.
* @dev Only updates the storage if the keys have changed to optimize gas usage.
* @param inputKeys The new parameter keys to store.
* @param projectConfig The project configuration storage reference.
*/
function _syncPMPKeys(
string[] memory inputKeys,
ProjectConfig storage projectConfig
) internal {
string[] memory currentKeys = projectConfig.pmpKeys.getAll();
// determine if inputKeys are different from currentKeys to avoid expensive operation
// @dev in general, we don't expect many changes to pmpKeys after initial configuration
bool keysChanged = false;
if (inputKeys.length != currentKeys.length) {
keysChanged = true;
} else {
for (uint256 i = 0; i < inputKeys.length; i++) {
if (!inputKeys[i].equal(currentKeys[i])) {
keysChanged = true;
break;
}
}
}
// if keys changed, update projectConfig's pmpKeys (expensive operation)
if (keysChanged) {
ImmutableStringArray.store(projectConfig.pmpKeys, inputKeys);
}
}
/**
* @notice Validates a PMP configuration. Ensures arbitrary input is valid and intentional.
* @dev Verifies parameter types, authorization options, and constraints.
* @param pmpConfig The PMP configuration to validate.
*/
function _validatePMPConfig(PMPConfig calldata pmpConfig) internal view {
// memoize paramType and authOption
ParamType paramType = pmpConfig.paramType;
AuthOption authOption = pmpConfig.authOption;
// require type is not unconfigured
require(
paramType != ParamType.Unconfigured,
"PMP: paramType is unconfigured"
);
// validate locked after timestamp is in the future, or is zero (unlimited)
require(
pmpConfig.pmpLockedAfterTimestamp == 0 ||
pmpConfig.pmpLockedAfterTimestamp > block.timestamp,
"PMP: pmpLockedAfterTimestamp is in the past and not unlimited (zero)"
);
// validate enums are within bounds
// @dev no coverage else branch, this is redundant as used due to solidity compiler checks on function enum inputs
require(
authOption <= AuthOption.ArtistAndTokenOwnerAndAddress,
"PMP: Invalid authOption"
);
// @dev no coverage else branch, this is redundant as used due to solidity compiler checks on function enum inputs
require(paramType <= ParamType.String, "PMP: Invalid paramType");
// only artist+ authentication types are supported for string params
if (paramType == ParamType.String) {
require(
authOption == AuthOption.Artist ||
authOption == AuthOption.ArtistAndTokenOwner ||
authOption == AuthOption.ArtistAndTokenOwnerAndAddress ||
authOption == AuthOption.ArtistAndAddress,
"PMP: String params must have artist+ authentication"
);
}
// validate auth with address has non-zero auth address
if (
authOption == AuthOption.Address ||
authOption == AuthOption.TokenOwnerAndAddress ||
authOption == AuthOption.ArtistAndAddress ||
authOption == AuthOption.ArtistAndTokenOwnerAndAddress
) {
require(
pmpConfig.authAddress != address(0),
"PMP: authAddress is zero"
);
} else {
// auth address must be zero for any non-address auth option
require(
pmpConfig.authAddress == address(0),
"PMP: authAddress is not zero"
);
}
// validate appropriate fields are empty
if (
paramType == ParamType.Bool ||
paramType == ParamType.String ||
paramType == ParamType.HexColor
) {
// @dev should have all fields empty
require(
pmpConfig.selectOptions.length == 0,
"PMP: selectOptions is not empty"
);
// @dev min/max range for hex color checked during assignment, should be empty in config
require(pmpConfig.minRange == 0, "PMP: minRange is not empty");
require(pmpConfig.maxRange == 0, "PMP: maxRange is not empty");
} else if (paramType == ParamType.Select) {
// @dev select should have selectOptions and empty min/max range values
require(
pmpConfig.selectOptions.length > 0,
"PMP: selectOptions is empty"
);
require(
pmpConfig.selectOptions.length < 256,
"PMP: selectOptions length > 255"
);
// @dev do not check if options are unique on-chain
// require min/max range values are empty
require(pmpConfig.minRange == 0, "PMP: minRange is not empty");
require(pmpConfig.maxRange == 0, "PMP: maxRange is not empty");
} else if (
paramType == ParamType.Uint256Range ||
paramType == ParamType.Int256Range ||
paramType == ParamType.DecimalRange ||
paramType == ParamType.Timestamp
) {
// @dev range params should have empty selectOptions
require(
pmpConfig.selectOptions.length == 0,
"PMP: selectOptions is not empty"
);
// require minRange is less than maxRange
if (paramType == ParamType.Int256Range) {
// cast minRange and maxRange to int256
int256 minRange = int256(uint256(pmpConfig.minRange));
int256 maxRange = int256(uint256(pmpConfig.maxRange));
require(minRange < maxRange, "PMP: minRange >= maxRange");
} else {
// cast minRange and maxRange to uint256
// @dev lt works for uint256, decimal, or timestamp types cast as uint256
uint256 minRange = uint256(pmpConfig.minRange);
uint256 maxRange = uint256(pmpConfig.maxRange);
require(minRange < maxRange, "PMP: minRange >= maxRange");
// additional guardrails on timestamp range
if (paramType == ParamType.Timestamp) {
// require maxRange is not gt _TIMESTAMP_MAX
require(
maxRange <= _TIMESTAMP_MAX,
"PMP: maxRange > _TIMESTAMP_MAX"
);
}
}
} else {
// @dev should never reach, no coverage
revert("PMP: Invalid paramType");
}
}
/**
* @notice Validates a PMP input against the project's configuration.
* Includes auth checks based on the pmpKey's current authOption.
* @dev Checks authorization, parameter type consistency, and value constraints.
* @dev Checks that pmp param is included in most recently configured PMP config for token's project.
* @param tokenId The token ID for which the parameter is being configured.
* @param coreContract The address of the core contract.
* @param pmpInput The parameter input to validate.
* @param pmpConfigStorage The project's configuration storage for this parameter.
* @param projectConfigNonce The project's current configuration nonce.
* @return permissionedAddress the address used to make the update, accounting for delegation.
*/
function _validatePMPInputAndAuth(
uint256 tokenId,
address coreContract,
PMPInput memory pmpInput,
PMPConfigStorage storage pmpConfigStorage,
uint8 projectConfigNonce
) internal view returns (address permissionedAddress) {
// check that the param is part of the project's most recently configured PMP params
// @dev use config nonce to check if param is part of most recently configured PMP params
require(
pmpConfigStorage.highestConfigNonce == projectConfigNonce,
"PMP: param not part of most recently configured PMP params"
);
// check that the param type matches and is not unconfigured
require(
pmpInput.configuredParamType == pmpConfigStorage.paramType,
"PMP: paramType mismatch"
);
// @dev checking value in memory for lower gas cost
require(
pmpInput.configuredParamType != ParamType.Unconfigured,
"PMP: input paramType is unconfigured"
);
// ensure caller has appropriate auth
bool isAuthenticated;
{
AuthOption authOption = pmpConfigStorage.authOption;
// if artist may configure, check and authenticate if not already authenticated
if (
authOption == AuthOption.Artist ||
authOption == AuthOption.ArtistAndAddress ||
authOption == AuthOption.ArtistAndTokenOwner ||
authOption == AuthOption.ArtistAndTokenOwnerAndAddress
) {
bool isArtist;
(isArtist, permissionedAddress) = _isArtist({
tokenId: tokenId,
coreContract: coreContract,
sender: msg.sender
});
isAuthenticated = isArtist;
}
// if address may configure, check and authenticate if not already authenticated
if (
!isAuthenticated &&
(authOption == AuthOption.Address ||
authOption == AuthOption.TokenOwnerAndAddress ||
authOption == AuthOption.ArtistAndAddress ||
authOption == AuthOption.ArtistAndTokenOwnerAndAddress)
) {
permissionedAddress = pmpConfigStorage.authAddress;
isAuthenticated = permissionedAddress == msg.sender;
}
// if token owner or delegate may configure, check and authenticate
// @dev check token ownerlast to enable pre-mint PMP configuration by non-token-owner
if (
!isAuthenticated &&
(authOption == AuthOption.TokenOwner ||
authOption == AuthOption.ArtistAndTokenOwner ||
authOption == AuthOption.TokenOwnerAndAddress ||
authOption == AuthOption.ArtistAndTokenOwnerAndAddress)
) {
bool isTokenOwnerOrDelegate_;
(
isTokenOwnerOrDelegate_,
permissionedAddress
) = _isTokenOwnerOrDelegate({
tokenId: tokenId,
coreContract: coreContract,
sender: msg.sender
});
isAuthenticated = isTokenOwnerOrDelegate_;
}
}
// if not authenticated, revert with appropriate auth error
if (!isAuthenticated) {
AuthOption authOption = pmpConfigStorage.authOption;
if (authOption == AuthOption.Artist) {
revert("PMP: artist auth required");
} else if (authOption == AuthOption.TokenOwner) {
revert("PMP: token owner auth required");
} else if (authOption == AuthOption.ArtistAndTokenOwner) {
revert("PMP: artist and token owner auth required");
} else if (authOption == AuthOption.Address) {
revert("PMP: address auth required");
} else if (authOption == AuthOption.ArtistAndTokenOwnerAndAddress) {
revert("PMP: artist and token owner and address auth required");
} else if (authOption == AuthOption.ArtistAndAddress) {
revert("PMP: artist and address auth required");
} else if (authOption == AuthOption.TokenOwnerAndAddress) {
revert("PMP: token owner and address auth required");
} else {
// @dev no coverage, this should never be reached
revert("PMP: invalid authOption");
}
}
// @dev auth check is complete
// ensure properly configured value
ParamType paramType = pmpConfigStorage.paramType;
// range checks for non-string params
if (paramType != ParamType.String) {
if (paramType == ParamType.Select) {
require(
uint256(pmpInput.configuredValue) <
pmpConfigStorage.selectOptionsLength,
"PMP: selectOptions index out of bounds"
);
} else if (paramType == ParamType.Bool) {
require(
pmpInput.configuredValue == bytes32(0) ||
uint256(pmpInput.configuredValue) == 1,
"PMP: bool param value must be 0 or 1"
);
} else if (
paramType == ParamType.Uint256Range ||
paramType == ParamType.DecimalRange
) {
require(
pmpInput.configuredValue >= pmpConfigStorage.minRange &&
pmpInput.configuredValue <= pmpConfigStorage.maxRange,
"PMP: param value out of bounds"
);
} else if (paramType == ParamType.Int256Range) {
require(
int256(uint256(pmpInput.configuredValue)) >= // @dev ensure this converts as expected
int256(uint256(pmpConfigStorage.minRange)) &&
int256(uint256(pmpInput.configuredValue)) <=
int256(uint256(pmpConfigStorage.maxRange)),
"PMP: param value out of bounds"
);
} else if (paramType == ParamType.Timestamp) {
require(
uint256(pmpInput.configuredValue) <= _TIMESTAMP_MAX &&
uint256(pmpInput.configuredValue) >= _TIMESTAMP_MIN, // @dev no coverage, this is redundant due to being assigned zero
"PMP: param value out of bounds"
);
} else if (paramType == ParamType.HexColor) {
require(
uint256(pmpInput.configuredValue) <= _HEX_COLOR_MAX, // @dev minimum hex color of zero implicitly passed by using uint256
"PMP: invalid hex color"
);
} else {
// @dev no coverage, this should never be reached
revert("PMP: invalid paramType");
}
}
// string and non-string checks
if (pmpConfigStorage.paramType == ParamType.String) {
require(
pmpInput.configuredValue == bytes32(0),
"PMP: value must be empty for string params"
);
// require artist is caller if configuring artist string
if (pmpInput.configuringArtistString) {
// require artist is caller
(bool isArtist, ) = _isArtist({
tokenId: tokenId,
coreContract: coreContract,
sender: msg.sender
});
require(
isArtist,
"PMP: artist auth required to configure artist string"
);
}
} else {
// non-string - ensure configured string is empty
require(
bytes(pmpInput.configuredValueString).length == 0,
"PMP: non-string param must have empty string value"
);
// non-string - ensure configuring artist string is false, because it is not relevant for non-string params
require(
!pmpInput.configuringArtistString,
"PMP: artist string cannot be configured for non-string params"
);
}
}
/**
* @notice Gets the value of a PMP as a formatted string.
* @dev Used internally to retrieve a PMP value in string format for external consumption.
* @param pmpConfigStorage The config storage for the PMP.
* @param pmp The PMP storage instance.
* @return isConfigured Whether the PMP has been configured for the token.
* @return value The string representation of the PMP value, or empty if not configured.
*/
function _getPMPValue(
PMPConfigStorage storage pmpConfigStorage,
PMPStorage storage pmp
) internal view returns (bool isConfigured, string memory value) {
ParamType configuredParamType = pmp.configuredParamType;
// unconfigured param for token
if (
configuredParamType == ParamType.Unconfigured || // unconfigured for token
configuredParamType != pmpConfigStorage.paramType // stale - token configured param type is different from project config
) {
return (false, "");
}
// if string, return value
if (pmpConfigStorage.paramType == ParamType.String) {
// return artist configured value if present
if (bytes(pmp.artistConfiguredValueString).length > 0) {
return (true, pmp.artistConfiguredValueString);
}
// return non-artist configured value if present
if (bytes(pmp.nonArtistConfiguredValueString).length > 0) {
return (true, pmp.nonArtistConfiguredValueString);
}
// empty string is considered not configured
return (false, "");
}
if (configuredParamType == ParamType.Select) {
// return unconfigured if index is out of bounds (obviously stale)
if (
uint256(pmp.configuredValue) >=
pmpConfigStorage.selectOptionsLength
) {
return (false, "");
}
return (
true,
pmpConfigStorage.selectOptions.get(uint256(pmp.configuredValue))
);
}
if (configuredParamType == ParamType.Bool) {
return (true, pmp.configuredValue == bytes32(0) ? "false" : "true");
}
if (
configuredParamType == ParamType.Uint256Range ||
configuredParamType == ParamType.HexColor ||
configuredParamType == ParamType.Timestamp ||
configuredParamType == ParamType.DecimalRange
) {
// verify configured value is within bounds (obviously stale if not)
uint256 configuredValue = uint256(pmp.configuredValue);
uint256 maxRange = configuredParamType == ParamType.HexColor
? _HEX_COLOR_MAX
: uint256(pmpConfigStorage.maxRange);
uint256 minRange = configuredParamType == ParamType.HexColor
? 0
: uint256(pmpConfigStorage.minRange);
if (
configuredValue < uint256(minRange) ||
configuredValue > uint256(maxRange)
) {
return (false, "");
}
// handle decimal case
if (configuredParamType == ParamType.DecimalRange) {
return (true, _uintToDecimalString(configuredValue));
}
// handle hex color case
if (configuredParamType == ParamType.HexColor) {
return (true, _uintToHexColorString(configuredValue));
}
// handle other cases - uint256 and timestamp
return (true, configuredValue.toString());
}
if (configuredParamType == ParamType.Int256Range) {
// verify configured value is within bounds (obviously stale if not)
int256 configuredValue = int256(uint256(pmp.configuredValue));
if (
configuredValue < int256(uint256(pmpConfigStorage.minRange)) ||
configuredValue > int256(uint256(pmpConfigStorage.maxRange))
) {
return (false, "");
}
return (true, configuredValue.toStringSigned());
}
// @dev should never reach
// @dev no coverage, unreachable
revert("PMP: Unhandled ParamType");
}
/**
* @notice Enforces that the caller is the artist of the specified project.
* @dev Reverts if the caller is not the artist.
* @dev Assumes Art Blocks V3 Core Contract interface.
* @param coreContract The address of the core contract.
* @param projectId The project ID to check artist permissions for.
* @param sender The address to check if it's the artist.
*/
function _onlyArtist(
address coreContract,
uint256 projectId,
address sender
) internal view {
require(
IGenArt721CoreContractV3_Base(coreContract)
.projectIdToArtistAddress(projectId) == sender,
"PMP: only artist"
);
}
/**
* @notice Checks if an address is the artist of the project associated with a token.
* @dev Assumes Art Blocks V3 Core Contract interface.
* @param tokenId The token ID to get the project ID from.
* @param coreContract The address of the core contract.
* @param sender The address to check if it's the artist.
* @return isArtist true if the sender is the artist, false otherwise.
* @return artistAddress the address of the artist.
*/
function _isArtist(
uint256 tokenId,
address coreContract,
address sender
) internal view returns (bool isArtist, address artistAddress) {
uint256 projectId = ABHelpers.tokenIdToProjectId(tokenId);
artistAddress = IGenArt721CoreContractV3_Base(coreContract)
.projectIdToArtistAddress(projectId);
isArtist = artistAddress == sender;
}
/**
* @notice Checks if an address is the owner of a token.
* Supports ERC-721 Token-level wallet delegation for the TokenOwner role via delegate.xyz v2,
* using the delegate.xyz v2 "postmintparameters" subdelegation rights.
* @dev Assumes Art Blocks V3 Core Contract interface.
* @param tokenId The token ID to check ownership for.
* @param coreContract The address of the core contract.
* @param sender The address to check if it's the token owner.
* @return isTokenOwnerOrDelegate_ true if the sender is the token owner or delegate of the token owner, false otherwise.
* @return tokenOwner the address of the token owner.
* @dev Always execute within a nonReentrant context.
*/
function _isTokenOwnerOrDelegate(
uint256 tokenId,
address coreContract,
address sender
) internal view returns (bool isTokenOwnerOrDelegate_, address tokenOwner) {
// @dev leading interaction - only execute within a nonReentrant context
tokenOwner = IERC721(coreContract).ownerOf(tokenId);
isTokenOwnerOrDelegate_ =
(tokenOwner == sender) ||
delegateRegistry.checkDelegateForERC721({
to: sender, // hot wallet
from: tokenOwner, // vault
contract_: coreContract, // ERC-721 contract
tokenId: tokenId, // tokenId
rights: bytes32(DELEGATION_REGISTRY_TOKEN_OWNER_RIGHTS) // opt-in granular control of rights
});
}
/**
* @notice Computes the keccak256 hash of a string.
* @dev Used to create mapping keys from string values.
* @param str The string to hash.
* @return The bytes32 hash of the input string.
*/
function _getStringHash(string memory str) internal pure returns (bytes32) {
return keccak256(abi.encode(str));
}
/**
* @notice Converts a uint256 to a decimal string representation.
* @dev Converts integer and fractional parts separately and combines them.
* @param number The uint256 value to convert to decimal string.
* @return A string representation of the decimal number.
*/
function _uintToDecimalString(
uint256 number
) private pure returns (string memory) {
uint256 integerPart = number / _DECIMAL_PRECISION; // Integer part
uint256 fractionalPart = number % _DECIMAL_PRECISION; // Fractional part
// Convert integer and fractional parts to strings
string memory intStr = integerPart.toString();
string memory fracStr = fractionalPart.toString();
// Pad fractional part with zeros if necessary
while (bytes(fracStr).length < _DECIMAL_PRECISION_DIGITS) {
fracStr = string(abi.encodePacked("0", fracStr));
}
// Combine integer and fractional parts with a decimal point
return string(abi.encodePacked(intStr, ".", fracStr));
}
/**
* @notice Converts a uint256 to a hex color string.
* @dev forked from OpenZeppelin's Strings library to use # prefix
* @dev Assumes the value is a valid hex color.
* @param value The uint256 value to convert to hex color string.
* @return A string representation of the hex color.
*/
function _uintToHexColorString(
uint256 value
) private pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * 3 + 1);
buffer[0] = "#";
for (uint256 i = 2 * 3; i > 0; --i) {
buffer[i] = _HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
// @dev no coverage, this is redundant due to use of max hex color value
revert("PMP: invalid hex color");
}
return string(buffer);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: MIT
// 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) (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/ERC165Checker.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Library used to query support of an interface declared via {IERC165}.
*
* Note that these functions return the actual result of the query: they do not
* `revert` if an interface is not supported. It is up to the caller to decide
* what to do in these cases.
*/
library ERC165Checker {
// As per the EIP-165 spec, no interface should ever match 0xffffffff
bytes4 private constant INTERFACE_ID_INVALID = 0xffffffff;
/**
* @dev Returns true if `account` supports the {IERC165} interface.
*/
function supportsERC165(address account) internal view returns (bool) {
// Any contract that implements ERC165 must explicitly indicate support of
// InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
return
supportsERC165InterfaceUnchecked(account, type(IERC165).interfaceId) &&
!supportsERC165InterfaceUnchecked(account, INTERFACE_ID_INVALID);
}
/**
* @dev Returns true if `account` supports the interface defined by
* `interfaceId`. Support for {IERC165} itself is queried automatically.
*
* See {IERC165-supportsInterface}.
*/
function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
// query support of both ERC165 as per the spec and support of _interfaceId
return supportsERC165(account) && supportsERC165InterfaceUnchecked(account, interfaceId);
}
/**
* @dev Returns a boolean array where each value corresponds to the
* interfaces passed in and whether they're supported or not. This allows
* you to batch check interfaces for a contract where your expectation
* is that some interfaces may not be supported.
*
* See {IERC165-supportsInterface}.
*/
function getSupportedInterfaces(
address account,
bytes4[] memory interfaceIds
) internal view returns (bool[] memory) {
// an array of booleans corresponding to interfaceIds and whether they're supported or not
bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);
// query support of ERC165 itself
if (supportsERC165(account)) {
// query support of each interface in interfaceIds
for (uint256 i = 0; i < interfaceIds.length; i++) {
interfaceIdsSupported[i] = supportsERC165InterfaceUnchecked(account, interfaceIds[i]);
}
}
return interfaceIdsSupported;
}
/**
* @dev Returns true if `account` supports all the interfaces defined in
* `interfaceIds`. Support for {IERC165} itself is queried automatically.
*
* Batch-querying can lead to gas savings by skipping repeated checks for
* {IERC165} support.
*
* See {IERC165-supportsInterface}.
*/
function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
// query support of ERC165 itself
if (!supportsERC165(account)) {
return false;
}
// query support of each interface in interfaceIds
for (uint256 i = 0; i < interfaceIds.length; i++) {
if (!supportsERC165InterfaceUnchecked(account, interfaceIds[i])) {
return false;
}
}
// all interfaces supported
return true;
}
/**
* @notice Query if a contract implements an interface, does not check ERC165 support
* @param account The address of the contract to query for support of an interface
* @param interfaceId The interface identifier, as specified in ERC-165
* @return true if the contract at account indicates support of the interface with
* identifier interfaceId, false otherwise
* @dev Assumes that account contains a contract that supports ERC165, otherwise
* the behavior of this method is undefined. This precondition can be checked
* with {supportsERC165}.
*
* Some precompiled contracts will falsely indicate support for a given interface, so caution
* should be exercised when using this function.
*
* Interface identification is specified in ERC-165.
*/
function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal view returns (bool) {
// prepare call
bytes memory encodedParams = abi.encodeCall(IERC165.supportsInterface, (interfaceId));
// perform static call
bool success;
uint256 returnSize;
uint256 returnValue;
assembly {
success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20)
returnSize := returndatasize()
returnValue := mload(0x00)
}
return success && returnSize >= 0x20 && returnValue > 0;
}
}// 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/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.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
// 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: CC0-1.0
pragma solidity >=0.8.13;
/**
* @title IDelegateRegistry
* @custom:version 2.0
* @custom:author foobar (0xfoobar)
* @notice A standalone immutable registry storing delegated permissions from one address to another
*/
interface IDelegateRegistry {
/// @notice Delegation type, NONE is used when a delegation does not exist or is revoked
enum DelegationType {
NONE,
ALL,
CONTRACT,
ERC721,
ERC20,
ERC1155
}
/// @notice Struct for returning delegations
struct Delegation {
DelegationType type_;
address to;
address from;
bytes32 rights;
address contract_;
uint256 tokenId;
uint256 amount;
}
/// @notice Emitted when an address delegates or revokes rights for their entire wallet
event DelegateAll(
address indexed from,
address indexed to,
bytes32 rights,
bool enable
);
/// @notice Emitted when an address delegates or revokes rights for a contract address
event DelegateContract(
address indexed from,
address indexed to,
address indexed contract_,
bytes32 rights,
bool enable
);
/// @notice Emitted when an address delegates or revokes rights for an ERC721 tokenId
event DelegateERC721(
address indexed from,
address indexed to,
address indexed contract_,
uint256 tokenId,
bytes32 rights,
bool enable
);
/// @notice Emitted when an address delegates or revokes rights for an amount of ERC20 tokens
event DelegateERC20(
address indexed from,
address indexed to,
address indexed contract_,
bytes32 rights,
uint256 amount
);
/// @notice Emitted when an address delegates or revokes rights for an amount of an ERC1155 tokenId
event DelegateERC1155(
address indexed from,
address indexed to,
address indexed contract_,
uint256 tokenId,
bytes32 rights,
uint256 amount
);
/// @notice Thrown if multicall calldata is malformed
error MulticallFailed();
/**
* ----------- WRITE -----------
*/
/**
* @notice Call multiple functions in the current contract and return the data from all of them if they all succeed
* @param data The encoded function data for each of the calls to make to this contract
* @return results The results from each of the calls passed in via data
*/
function multicall(
bytes[] calldata data
) external payable returns (bytes[] memory results);
/**
* @notice Allow the delegate to act on behalf of `msg.sender` for all contracts
* @param to The address to act as delegate
* @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
* @param enable Whether to enable or disable this delegation, true delegates and false revokes
* @return delegationHash The unique identifier of the delegation
*/
function delegateAll(
address to,
bytes32 rights,
bool enable
) external payable returns (bytes32 delegationHash);
/**
* @notice Allow the delegate to act on behalf of `msg.sender` for a specific contract
* @param to The address to act as delegate
* @param contract_ The contract whose rights are being delegated
* @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
* @param enable Whether to enable or disable this delegation, true delegates and false revokes
* @return delegationHash The unique identifier of the delegation
*/
function delegateContract(
address to,
address contract_,
bytes32 rights,
bool enable
) external payable returns (bytes32 delegationHash);
/**
* @notice Allow the delegate to act on behalf of `msg.sender` for a specific ERC721 token
* @param to The address to act as delegate
* @param contract_ The contract whose rights are being delegated
* @param tokenId The token id to delegate
* @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
* @param enable Whether to enable or disable this delegation, true delegates and false revokes
* @return delegationHash The unique identifier of the delegation
*/
function delegateERC721(
address to,
address contract_,
uint256 tokenId,
bytes32 rights,
bool enable
) external payable returns (bytes32 delegationHash);
/**
* @notice Allow the delegate to act on behalf of `msg.sender` for a specific amount of ERC20 tokens
* @dev The actual amount is not encoded in the hash, just the existence of a amount (since it is an upper bound)
* @param to The address to act as delegate
* @param contract_ The address for the fungible token contract
* @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
* @param amount The amount to delegate, > 0 delegates and 0 revokes
* @return delegationHash The unique identifier of the delegation
*/
function delegateERC20(
address to,
address contract_,
bytes32 rights,
uint256 amount
) external payable returns (bytes32 delegationHash);
/**
* @notice Allow the delegate to act on behalf of `msg.sender` for a specific amount of ERC1155 tokens
* @dev The actual amount is not encoded in the hash, just the existence of a amount (since it is an upper bound)
* @param to The address to act as delegate
* @param contract_ The address of the contract that holds the token
* @param tokenId The token id to delegate
* @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
* @param amount The amount of that token id to delegate, > 0 delegates and 0 revokes
* @return delegationHash The unique identifier of the delegation
*/
function delegateERC1155(
address to,
address contract_,
uint256 tokenId,
bytes32 rights,
uint256 amount
) external payable returns (bytes32 delegationHash);
/**
* ----------- CHECKS -----------
*/
/**
* @notice Check if `to` is a delegate of `from` for the entire wallet
* @param to The potential delegate address
* @param from The potential address who delegated rights
* @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
* @return valid Whether delegate is granted to act on the from's behalf
*/
function checkDelegateForAll(
address to,
address from,
bytes32 rights
) external view returns (bool);
/**
* @notice Check if `to` is a delegate of `from` for the specified `contract_` or the entire wallet
* @param to The delegated address to check
* @param contract_ The specific contract address being checked
* @param from The cold wallet who issued the delegation
* @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
* @return valid Whether delegate is granted to act on from's behalf for entire wallet or that specific contract
*/
function checkDelegateForContract(
address to,
address from,
address contract_,
bytes32 rights
) external view returns (bool);
/**
* @notice Check if `to` is a delegate of `from` for the specific `contract` and `tokenId`, the entire `contract_`, or the entire wallet
* @param to The delegated address to check
* @param contract_ The specific contract address being checked
* @param tokenId The token id for the token to delegating
* @param from The wallet that issued the delegation
* @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
* @return valid Whether delegate is granted to act on from's behalf for entire wallet, that contract, or that specific tokenId
*/
function checkDelegateForERC721(
address to,
address from,
address contract_,
uint256 tokenId,
bytes32 rights
) external view returns (bool);
/**
* @notice Returns the amount of ERC20 tokens the delegate is granted rights to act on the behalf of
* @param to The delegated address to check
* @param contract_ The address of the token contract
* @param from The cold wallet who issued the delegation
* @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
* @return balance The delegated balance, which will be 0 if the delegation does not exist
*/
function checkDelegateForERC20(
address to,
address from,
address contract_,
bytes32 rights
) external view returns (uint256);
/**
* @notice Returns the amount of a ERC1155 tokens the delegate is granted rights to act on the behalf of
* @param to The delegated address to check
* @param contract_ The address of the token contract
* @param tokenId The token id to check the delegated amount of
* @param from The cold wallet who issued the delegation
* @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
* @return balance The delegated balance, which will be 0 if the delegation does not exist
*/
function checkDelegateForERC1155(
address to,
address from,
address contract_,
uint256 tokenId,
bytes32 rights
) external view returns (uint256);
/**
* ----------- ENUMERATIONS -----------
*/
/**
* @notice Returns all enabled delegations a given delegate has received
* @param to The address to retrieve delegations for
* @return delegations Array of Delegation structs
*/
function getIncomingDelegations(
address to
) external view returns (Delegation[] memory delegations);
/**
* @notice Returns all enabled delegations an address has given out
* @param from The address to retrieve delegations for
* @return delegations Array of Delegation structs
*/
function getOutgoingDelegations(
address from
) external view returns (Delegation[] memory delegations);
/**
* @notice Returns all hashes associated with enabled delegations an address has received
* @param to The address to retrieve incoming delegation hashes for
* @return delegationHashes Array of delegation hashes
*/
function getIncomingDelegationHashes(
address to
) external view returns (bytes32[] memory delegationHashes);
/**
* @notice Returns all hashes associated with enabled delegations an address has given out
* @param from The address to retrieve outgoing delegation hashes for
* @return delegationHashes Array of delegation hashes
*/
function getOutgoingDelegationHashes(
address from
) external view returns (bytes32[] memory delegationHashes);
/**
* @notice Returns the delegations for a given array of delegation hashes
* @param delegationHashes is an array of hashes that correspond to delegations
* @return delegations Array of Delegation structs, return empty structs for nonexistent or revoked delegations
*/
function getDelegationsFromHashes(
bytes32[] calldata delegationHashes
) external view returns (Delegation[] memory delegations);
/**
* ----------- STORAGE ACCESS -----------
*/
/**
* @notice Allows external contracts to read arbitrary storage slots
*/
function readSlot(bytes32 location) external view returns (bytes32);
/**
* @notice Allows external contracts to read an arbitrary array of storage slots
*/
function readSlots(
bytes32[] calldata locations
) external view returns (bytes32[] memory);
}// 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
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
// Creatd By: Art Blocks Inc.
pragma solidity ^0.8.0;
import {IPMPV0} from "./IPMPV0.sol";
import {IWeb3Call} from "./IWeb3Call.sol";
import {IERC165} from "@openzeppelin-5.0/contracts/interfaces/IERC165.sol";
interface IPMPAugmentHook is IERC165 {
/**
* @notice Augment the token parameters for a given token.
* @dev This hook is called when a token's PMPs are read.
* @dev This must return all desired tokenParams, not just additional data.
* @param coreContract The address of the core contract to call.
* @param tokenId The tokenId of the token to get data for.
* @param tokenParams The token parameters for the queried token.
* @return augmentedTokenParams The augmented token parameters.
*/
function onTokenPMPReadAugmentation(
address coreContract,
uint256 tokenId,
IWeb3Call.TokenParam[] calldata tokenParams
)
external
view
returns (IWeb3Call.TokenParam[] memory augmentedTokenParams);
// @dev ERC156 function supportsInterface must be implemented and broadcast true for this interfaceId
}// SPDX-License-Identifier: LGPL-3.0-only
// Creatd By: Art Blocks Inc.
pragma solidity ^0.8.0;
import {IPMPV0} from "./IPMPV0.sol";
import {IERC165} from "@openzeppelin-5.0/contracts/interfaces/IERC165.sol";
interface IPMPConfigureHook is IERC165 {
/**
* @notice Execution logic to be executed when a token's PMP is configured.
* @dev This hook is executed after the PMP is configured.
* @param coreContract The address of the core contract that was configured.
* @param tokenId The tokenId of the token that was configured.
* @param pmpInput The PMP input that was used to successfully configure the token.
*/
function onTokenPMPConfigure(
address coreContract,
uint256 tokenId,
IPMPV0.PMPInput calldata pmpInput
) external;
// @dev ERC156 function supportsInterface must be implemented and broadcast true for this interfaceId
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
import {IPMPConfigureHook} from "./IPMPConfigureHook.sol";
import {IPMPAugmentHook} from "./IPMPAugmentHook.sol";
import {IWeb3Call} from "./IWeb3Call.sol";
import {ImmutableStringArray} from "../../libs/v0.8.x/ImmutableStringArray.sol";
/**
* @title Project Metadata Parameters (PMP) Interface, V0
* @author Art Blocks Inc.
* @notice Interface for the Project Metadata Parameters (PMP) contract that defines
* how projects can expose configurable parameters for tokens. This interface establishes
* the standard for parameter configuration, validation, and retrieval.
* @dev This interface extends the IWeb3Call interface to provide compatibility with
* existing Web3 infrastructure.
*/
interface IPMPV0 is IWeb3Call {
/**
* @notice Emitted when project hooks are configured.
* @param coreContract The address of the core contract.
* @param projectId The project ID for which hooks were configured.
* @param tokenPMPPostConfigHook The hook to call after a token's PMP is configured.
* @param tokenPMPReadAugmentationHook The hook to call when reading a token's PMPs.
*/
event ProjectHooksConfigured(
address coreContract,
uint256 projectId,
IPMPConfigureHook tokenPMPPostConfigHook,
IPMPAugmentHook tokenPMPReadAugmentationHook
);
/**
* @notice Emitted when a project's available parameters are configured.
* @param coreContract The address of the core contract.
* @param projectId The project ID for which parameters were configured.
* @param pmpInputConfigs Array of parameter configurations defining the available parameters.
* @param projectConfigNonce The nonce of the project configuration.
*/
event ProjectConfigured(
address coreContract,
uint256 projectId,
PMPInputConfig[] pmpInputConfigs,
uint8 projectConfigNonce
);
/**
* @notice Emitted when a token's parameters are configured.
* @param coreContract The address of the core contract.
* @param tokenId The token ID for which parameters were configured.
* @param pmpInputs Array of parameter inputs that were configured.
* @param authAddresses Array of addresses that authenticated the parameters. Aligned by index with pmpInputs.
*/
event TokenParamsConfigured(
address coreContract,
uint256 tokenId,
PMPInput[] pmpInputs,
address[] authAddresses
);
/**
* @notice Emitted when the delegation registry is updated.
* @dev At the time of writing, the delegate.xyz v2 contract is the only
* delegate registry contract that is supported.
* @param delegationRegistry The address of the new delegation registry.
*/
event DelegationRegistryUpdated(address delegationRegistry);
/**
* @notice Defines who can configure a parameter for a token.
* @dev Enum ordering is relied on (ArtistAndTokenOwnerAndAddress being last).
*/
enum AuthOption {
Artist,
TokenOwner,
Address,
ArtistAndTokenOwner,
ArtistAndAddress,
TokenOwnerAndAddress,
ArtistAndTokenOwnerAndAddress
}
/**
* @notice Defines the type of parameter that can be configured.
* @dev Enum ordering is relied on (String being last).
*/
enum ParamType {
Unconfigured, // @dev default value, used to check if PMP is configured
Select,
Bool,
Uint256Range,
Int256Range,
DecimalRange,
HexColor,
Timestamp,
String // utilizes string in PMP struct, all other param types utilize the generic bytes32 param
}
/**
* @notice Structure for a parameter configuration input.
* @dev Used when configuring a project's available parameters.
*/
struct PMPInputConfig {
string key; // slot 0: 32 bytes
PMPConfig pmpConfig; // slot 1: 32 bytes
}
/**
* @notice Structure for a parameter configuration.
* @dev Defines the constraints and options for a parameter.
*/
struct PMPConfig {
AuthOption authOption; // slot 0: 1 byte
ParamType paramType; // slot 0: 1 byte
uint48 pmpLockedAfterTimestamp; // slot 0: 6 bytes
address authAddress; // slot 0: 20 bytes
string[] selectOptions; // slot 1: 32 bytes
// @dev use bytes32 for all range types for SSTORE efficiency
// @dev minRange and maxRange cast to defined numeric type when verifying assigned PMP values
bytes32 minRange; // slot 2: 32 bytes
bytes32 maxRange; // slot 3: 32 bytes
}
/**
* @notice View structure for a parameter configuration.
* @dev Used when reading a project's available parameters.
* @dev any populated select options are loaded and returned as a string array
* instead of remaining as a pointer to a data contract
*/
struct PMPConfigView {
uint8 highestConfigNonce;
AuthOption authOption;
ParamType paramType;
uint48 pmpLockedAfterTimestamp;
address authAddress;
uint8 selectOptionsLength;
string[] selectOptions;
bytes32 minRange;
bytes32 maxRange;
}
/**
* @notice Structure for a parameter input.
* @dev Used when configuring a token's parameters.
*/
struct PMPInput {
string key; // slot 0: 32 bytes
ParamType configuredParamType;
// @dev store values as bytes32 for efficiency, cast appropriately when reading
bytes32 configuredValue;
bool configuringArtistString;
string configuredValueString;
}
/**
* @notice Storage structure for a configured parameter.
* @dev Includes both artist and non-artist configured string values.
*/
struct PMPStorage {
ParamType configuredParamType; // slot 0: 1 byte
// @dev store values as bytes32 for efficiency, cast appropriately when reading
bytes32 configuredValue; // slot 1: 32 bytes
string artistConfiguredValueString; // slot 2: 32 bytes
string nonArtistConfiguredValueString; // slot 3: 32 bytes
}
/**
* @notice Configure the hooks for a project.
* @param coreContract The address of the core contract.
* @param projectId The project ID to configure hooks for.
* @param tokenPMPPostConfigHook The hook to call after a token's PMP is configured.
* @param tokenPMPReadAugmentationHook The hook to call when reading a token's PMPs.
*/
function configureProjectHooks(
address coreContract,
uint256 projectId,
IPMPConfigureHook tokenPMPPostConfigHook,
IPMPAugmentHook tokenPMPReadAugmentationHook
) external;
/**
* @notice Configure the available parameters for a project.
* @param coreContract The address of the core contract.
* @param projectId The project ID to configure parameters for.
* @param pmpInputConfigs Array of parameter configurations defining the available parameters.
*/
function configureProject(
address coreContract,
uint256 projectId,
PMPInputConfig[] calldata pmpInputConfigs
) external;
/**
* @notice Configure the parameters for a specific token.
* @param coreContract The address of the core contract.
* @param tokenId The token ID to configure parameters for.
* @param pmpInputs Array of parameter inputs to configure.
*/
function configureTokenParams(
address coreContract,
uint256 tokenId,
PMPInput[] calldata pmpInputs
) external;
/**
* @notice Checks if the given wallet has the owner role for the given token.
* It returns true if the wallet is the owner of the token or if the wallet
* is a delegate of the token owner; otherwise it returns false.
* Reverts if an invalid coreContract or tokenId is provided.
* Provided for convenience, as the same check is performed in the
* configureTokenParams function.
* @param wallet The wallet address to check.
* @param coreContract The address of the core contract to call.
* @param tokenId The tokenId of the token to check.
* @return isTokenOwnerOrDelegate_ True if the wallet is the owner or a delegate of the token,
* false otherwise.
*/
function isTokenOwnerOrDelegate(
address wallet,
address coreContract,
uint256 tokenId
) external view returns (bool isTokenOwnerOrDelegate_);
}// SPDX-License-Identifier: LGPL-3.0-only
// Creatd By: Art Blocks Inc.
pragma solidity ^0.8.0;
interface IWeb3Call {
/**
* @notice TokenParam struct defines the parameters for a given token.
* @param key The key of the token parameter.
* @param value The value of the token parameter.
*/
struct TokenParam {
string key;
string value;
}
/**
* @notice Get the token parameters for a given token.
* If none are configured, the tokenParams should be empty.
* @param coreContract The address of the core contract to call.
* @param tokenId The tokenId of the token to get data for.
* @return tokenParams An array of token parameters for the queried token.
*/
function getTokenParams(
address coreContract,
uint256 tokenId
) external view returns (TokenParam[] memory tokenParams);
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
/**
* @title Art Blocks Helpers Library
* @notice This library contains helper functions for common operations in the
* Art Blocks ecosystem of smart contracts.
* @author Art Blocks Inc.
*/
library ABHelpers {
uint256 constant ONE_MILLION = 1_000_000;
/**
* @notice Function to convert token id to project id.
* @param tokenId The id of the token.
*/
function tokenIdToProjectId(
uint256 tokenId
) internal pure returns (uint256) {
// int division properly rounds down
// @dev no way to disable division by zero check in solidity v0.8.24, so not unchecked
return tokenId / ONE_MILLION;
}
/**
* @notice Function to convert token id to token number.
* @param tokenId The id of the token.
*/
function tokenIdToTokenNumber(
uint256 tokenId
) internal pure returns (uint256) {
// mod returns remainder, which is the token number
// @dev no way to disable mod zero check in solidity, so not unchecked
return tokenId % ONE_MILLION;
}
/**
* @notice Function to convert token id to token invocation.
* @dev token invocation is the token number plus one, because token #0 is
* invocation 1.
* @param tokenId The id of the token.
*/
function tokenIdToTokenInvocation(
uint256 tokenId
) internal pure returns (uint256) {
unchecked {
// mod returns remainder, which is the token number
// @dev no way to disable mod zero check in solidity, unchecked to optimize gas for addition
return (tokenId % ONE_MILLION) + 1;
}
}
/**
* @notice Function to convert project id and token number to token id.
* @param projectId The id of the project.
* @param tokenNumber The token number.
*/
function tokenIdFromProjectIdAndTokenNumber(
uint256 projectId,
uint256 tokenNumber
) internal pure returns (uint256) {
// @dev intentionally not unchecked to ensure overflow detection, which
// would likley only occur in a malicious call
return (projectId * ONE_MILLION) + tokenNumber;
}
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
import {SSTORE2} from "./SSTORE2.sol"; // Import SSTORE2 library
/**
* @title ImmutableStringArray
* @author Art Blocks Inc.
* @notice This library is optimized to store immutable arrays of strings much more efficiently than using native Solidity storage arrays.
* It uses a single SSTORE2 contract to store the length + packed offsets + packed string data.
* Allows overwriting the pointer to point to a new immutable array.
*/
library ImmutableStringArray {
/**
* @notice Struct to store the SSTORE2 pointer to the packed string array.
* @dev dataPointer is the SSTORE2 pointer to the packed string array. Assigned to address(0) if the array is empty.
*/
struct StringArray {
address dataPointer; // SSTORE2 pointer storing length + packed offsets + packed string data
}
/**
* @notice Stores a packed immutable string array using a single SSTORE2 contract.
* Allows overwriting the pointer to point to a new immutable array.
* @dev Optimized for the case where the array is empty by assigning the dataPointer to address(0).
* @param storageArray The storage reference to store the packed array.
* @param strings The array of strings to pack.
*/
function store(
StringArray storage storageArray,
string[] memory strings
) internal {
uint256 arrayLength = strings.length;
// edge case - empty array
if (arrayLength == 0) {
storageArray.dataPointer = address(0);
return;
}
uint64[] memory offsets = new uint64[](arrayLength);
// compute total bytes length and offsets (ensuring no overflow)
uint256 totalStringBytesLength;
for (uint256 i = 0; i < arrayLength; i++) {
offsets[i] = uint64(totalStringBytesLength);
totalStringBytesLength += bytes(strings[i]).length;
}
// realistically will not overflow uint64, but check for security guarantees
// @dev no coverage on else (difficulty of triggering)
require(
totalStringBytesLength <= type(uint64).max,
"Offset exceeds uint64 limit"
);
// prepare the combined storage structure (length + packed offsets + packed strings)
// @dev 8 bytes for length, 8 bytes per offset, totalStringBytesLength bytes for strings
// over-allocate 32 bytes for memory safety
// note: shorten length by 32 bytes at end of function before returning
uint256 packedDataLength = 8 +
(arrayLength * 8) +
totalStringBytesLength;
bytes memory packedData = new bytes(packedDataLength + 0x20);
uint256 ptr;
// store the length of the strings array in the first 8 bytes of the packedData
assembly ("memory-safe") {
ptr := add(packedData, 0x20) // pointer to first byte of packedData
mstore(ptr, shl(192, arrayLength)) // left-align only 8 bytes in the 32-byte slot
ptr := add(ptr, 8) // move pointer forward by 8 bytes
}
// store offsets in packed `uint64` format
for (uint256 i = 0; i < arrayLength; i++) {
uint256 offset = offsets[i];
assembly ("memory-safe") {
mstore(ptr, shl(192, offset))
// move pointer ahead 8 bytes for uint64
ptr := add(ptr, 8)
}
}
// pack the strings efficiently, using assembly to copy 32 bytes at a time
for (uint i = 0; i < arrayLength; i++) {
bytes memory currentString = bytes(strings[i]);
uint currentLength = currentString.length;
uint currentPtr;
assembly ("memory-safe") {
currentPtr := add(currentString, 0x20) // start of current string's data
}
// copy the full 32-byte chunks
uint chunks = currentLength / 32;
uint remainder = currentLength % 32;
// store the full 32 byte chunks
for (uint j = 0; j < chunks; j++) {
assembly ("memory-safe") {
let chunk := mload(currentPtr) // load 32 bytes of the current string
mstore(ptr, chunk) // store the 32 bytes into the result
ptr := add(ptr, 0x20) // move the result pointer forward by 32 bytes
currentPtr := add(currentPtr, 0x20) // move the current string pointer forward by 32 bytes
}
}
// store any partial chunks
if (remainder > 0) {
bytes32 chunk;
assembly ("memory-safe") {
chunk := mload(currentPtr) // load full 32-byte word
// safe to write past end of final array - 32 bytes of buffer at end of array
mstore(ptr, chunk) // store the final chunk's remaining bytes
ptr := add(ptr, remainder) // move the result pointer forward by the remainder length for next iteration
}
}
}
// remove the buffer from the packedData bytes array length
assembly ("memory-safe") {
ptr := packedData
mstore(ptr, packedDataLength)
}
// Store all packed data in a SSTORE2 contract, store result in storage
storageArray.dataPointer = SSTORE2.write(packedData);
}
/**
* @notice Retrieves the total number of stored strings from SSTORE2.
* @param storageArray The storage reference containing packed strings.
* @return count The count of stored strings.
*/
function length(
StringArray storage storageArray
) internal view returns (uint256 count) {
// edge case - unassigned dataPointer or empty array
if (storageArray.dataPointer == address(0)) {
return 0;
}
// @dev this could be more efficient by only reading the first 8 bytes of the SSTORE2 contract
// but prefer to keep simple
bytes memory allData = SSTORE2.read(storageArray.dataPointer);
assembly ("memory-safe") {
count := shr(192, mload(add(allData, 32)))
}
}
/**
* @notice Retrieves a single string from storage by index.
* @param storageArray The storage reference containing packed strings.
* @param index The index of the string to retrieve.
* @return result The retrieved string.
*/
function get(
StringArray storage storageArray,
uint256 index
) internal view returns (string memory result) {
// edge case - unassigned dataPointer or empty array
if (storageArray.dataPointer == address(0)) {
revert("Index out of bounds");
}
bytes memory allData = SSTORE2.read(storageArray.dataPointer);
uint256 offsetsStart;
uint64 start;
uint64 end;
// load the total length of the strings array from the first 8 bytes of the SSTORE2 contract
uint256 arrayLength;
assembly ("memory-safe") {
arrayLength := shr(192, mload(add(allData, 32)))
}
require(index < arrayLength, "Index out of bounds");
if (index + 1 == arrayLength) {
uint256 allDataLength = allData.length;
assembly ("memory-safe") {
offsetsStart := add(allData, 40) // skipping first 8 bytes (length)
start := shr(192, mload(add(offsetsStart, mul(index, 8)))) // load offsets[index] as uint64
end := sub(
allDataLength,
add(8, mul(arrayLength, 8)) // subtract 8 bytes for overall array length, and 8*length for the offsets section
)
}
} else {
assembly ("memory-safe") {
offsetsStart := add(allData, 40) // skipping first 8 bytes (length)
start := shr(192, mload(add(offsetsStart, mul(index, 8)))) // load offsets[index] as uint64
end := shr(192, mload(add(offsetsStart, mul(add(index, 1), 8)))) // load offsets[index + 1]
}
}
uint256 strLength = end - start;
// over-allocate 32 bytes for memory safety
bytes memory strBytes = new bytes(strLength + 32);
uint256 ptr;
assembly ("memory-safe") {
ptr := add(strBytes, 0x20) // pointer to first byte of strBytes
}
// efficiently copy 32 bytes at a time from allData to strBytes
for (uint256 i = 0; i < strLength; i += 32) {
// offset = 32 bytes for length + 8 bytes for overall array length + 8 bytes per offset * length of array + start of string + i
uint256 offset = 32 + 8 + (arrayLength * 8) + start + i;
assembly ("memory-safe") {
let chunk := mload(add(allData, offset)) // load 32 bytes of the current string
mstore(ptr, chunk) // store the 32 bytes into the result (buffer keeps this memory safe)
ptr := add(ptr, 0x20) // move the result pointer forward by 32 bytes
}
}
// remove the buffer length from the strBytes bytes array length
assembly ("memory-safe") {
ptr := strBytes
mstore(ptr, strLength)
}
result = string(strBytes);
}
/**
* @notice Retrieves all stored strings in a single batch call.
* @param storageArray The storage reference containing packed strings.
* @return results An array of all stored strings.
*/
function getAll(
StringArray storage storageArray
) internal view returns (string[] memory results) {
// edge case - unassigned dataPointer or empty array
if (storageArray.dataPointer == address(0)) {
return new string[](0);
}
// load the packed data from the SSTORE2 contract
bytes memory allData = SSTORE2.read(storageArray.dataPointer);
// load the total length of the strings array from the first 8 bytes of the SSTORE2 contract
uint256 arrayLength;
assembly ("memory-safe") {
arrayLength := shr(192, mload(add(allData, 32)))
}
// for each string in the array, populate results
results = new string[](arrayLength);
for (uint256 index = 0; index < arrayLength; index++) {
// @dev start and end are calculated for each index for code simplicity/code re-use from get() function
uint256 offsetsStart;
uint64 start;
uint64 end;
if (index + 1 == arrayLength) {
uint256 allDataLength = allData.length;
assembly ("memory-safe") {
offsetsStart := add(allData, 40) // Skipping first 8 bytes (length)
start := shr(192, mload(add(offsetsStart, mul(index, 8)))) // Load offsets[index] as uint64
end := sub(
allDataLength,
add(8, mul(arrayLength, 8)) // subtract 8 bytes for overall array length, and 8*length for the offsets section
)
}
} else {
assembly ("memory-safe") {
offsetsStart := add(allData, 40) // Skipping first 8 bytes (length)
start := shr(192, mload(add(offsetsStart, mul(index, 8)))) // Load offsets[index] as uint64
end := shr(
192,
mload(add(offsetsStart, mul(add(index, 1), 8)))
) // Load offsets[index + 1]
}
}
uint256 strLength = end - start;
// over-allocate 32 bytes for memory safety
bytes memory strBytes = new bytes(strLength + 32);
uint256 ptr;
assembly ("memory-safe") {
ptr := add(strBytes, 0x20) // pointer to first byte of strBytes
}
// efficiently copy 32 bytes at a time from allData to strBytes
for (uint256 i = 0; i < strLength; i += 32) {
// offset = 32 bytes for length + 8 bytes for overall array length + 8 bytes per offset * length of array + start of string + i
uint256 offset = 32 + 8 + (arrayLength * 8) + start + i;
assembly ("memory-safe") {
let chunk := mload(add(allData, offset)) // Load 32 bytes of the current string
mstore(ptr, chunk) // Store the 32 bytes into the result (buffer keeps this memory safe)
ptr := add(ptr, 0x20) // Move the result pointer forward by 32 bytes
}
}
// remove the buffer length from the strBytes bytes array length
assembly ("memory-safe") {
ptr := strBytes
mstore(ptr, strLength)
}
// store the string in the results array
results[index] = string(strBytes);
}
// results is returned by reference, so no need for explicit return
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Read and write to persistent storage at a fraction of the cost.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol)
/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol)
library SSTORE2 {
uint256 internal constant DATA_OFFSET = 1; // We skip the first byte as it's a STOP opcode to ensure the contract can't be called.
/*//////////////////////////////////////////////////////////////
WRITE LOGIC
//////////////////////////////////////////////////////////////*/
function write(bytes memory data) internal returns (address pointer) {
// Prefix the bytecode with a STOP opcode to ensure it cannot be called.
bytes memory runtimeCode = abi.encodePacked(hex"00", data);
bytes memory creationCode = abi.encodePacked(
//---------------------------------------------------------------------------------------------------------------//
// Opcode | Opcode + Arguments | Description | Stack View //
//---------------------------------------------------------------------------------------------------------------//
// 0x60 | 0x600B | 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 | //
//---------------------------------------------------------------------------------------------------------------//
hex"60_0B_59_81_38_03_80_92_59_39_F3", // Returns all code in the contract except for the first 11 (0B in hex) bytes.
runtimeCode // The bytecode we want the contract to have after deployment. Capped at 1 byte less than the code size limit.
);
/// @solidity memory-safe-assembly
assembly {
// Deploy a new contract with the generated creation code.
// We start 32 bytes into the code to avoid copying the byte length.
pointer := create(0, add(creationCode, 32), mload(creationCode))
}
require(pointer != address(0), "DEPLOYMENT_FAILED");
}
/*//////////////////////////////////////////////////////////////
READ LOGIC
//////////////////////////////////////////////////////////////*/
function read(address pointer) internal view returns (bytes memory) {
return
readBytecode(
pointer,
DATA_OFFSET,
pointer.code.length - DATA_OFFSET
);
}
function read(
address pointer,
uint256 start
) internal view returns (bytes memory) {
start += DATA_OFFSET;
return readBytecode(pointer, start, pointer.code.length - start);
}
function read(
address pointer,
uint256 start,
uint256 end
) internal view returns (bytes memory) {
start += DATA_OFFSET;
end += DATA_OFFSET;
require(pointer.code.length >= end, "OUT_OF_BOUNDS");
return readBytecode(pointer, start, end - start);
}
/*//////////////////////////////////////////////////////////////
INTERNAL HELPER LOGIC
//////////////////////////////////////////////////////////////*/
function readBytecode(
address pointer,
uint256 start,
uint256 size
) private view returns (bytes memory data) {
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
data := mload(0x40)
// Update the free memory pointer to prevent overriding our data.
// We use and(x, not(31)) as a cheaper equivalent to sub(x, mod(x, 32)).
// Adding 31 to size and running the result through the logic above ensures
// the memory pointer remains word-aligned, following the Solidity convention.
mstore(0x40, add(data, and(add(add(size, 32), 31), not(31))))
// Store the size of the data in the first 32 byte chunk of free memory.
mstore(data, size)
// Copy the code into memory right after the 32 bytes we used to store the size.
extcodecopy(pointer, add(data, 32), start, size)
}
}
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
import {IWeb3Call} from "../interfaces/v0.8.x/IWeb3Call.sol";
import {ERC165} from "@openzeppelin-5.0/contracts/utils/introspection/ERC165.sol";
/**
* @title Abstract Web3Call contract
* @author Art Blocks Inc.
* @notice This abstract can be inherited by any contract that wants to implement the Web3Call interface.
* It indicates support for the IWeb3Call interface via ERC165 interface detection, and ensures that all
* child contracts implement the required IWeb3Call functions.
*/
abstract contract Web3Call is IWeb3Call, ERC165 {
function supportsInterface(
bytes4 interfaceId
) public view override returns (bool) {
return
interfaceId == type(IWeb3Call).interfaceId ||
super.supportsInterface(interfaceId);
}
}{
"optimizer": {
"enabled": true,
"runs": 10
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"contract IDelegateRegistry","name":"delegateRegistry_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"delegationRegistry","type":"address"}],"name":"DelegationRegistryUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"coreContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"projectId","type":"uint256"},{"components":[{"internalType":"string","name":"key","type":"string"},{"components":[{"internalType":"enum IPMPV0.AuthOption","name":"authOption","type":"uint8"},{"internalType":"enum IPMPV0.ParamType","name":"paramType","type":"uint8"},{"internalType":"uint48","name":"pmpLockedAfterTimestamp","type":"uint48"},{"internalType":"address","name":"authAddress","type":"address"},{"internalType":"string[]","name":"selectOptions","type":"string[]"},{"internalType":"bytes32","name":"minRange","type":"bytes32"},{"internalType":"bytes32","name":"maxRange","type":"bytes32"}],"internalType":"struct IPMPV0.PMPConfig","name":"pmpConfig","type":"tuple"}],"indexed":false,"internalType":"struct IPMPV0.PMPInputConfig[]","name":"pmpInputConfigs","type":"tuple[]"},{"indexed":false,"internalType":"uint8","name":"projectConfigNonce","type":"uint8"}],"name":"ProjectConfigured","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"coreContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":false,"internalType":"contract IPMPConfigureHook","name":"tokenPMPPostConfigHook","type":"address"},{"indexed":false,"internalType":"contract IPMPAugmentHook","name":"tokenPMPReadAugmentationHook","type":"address"}],"name":"ProjectHooksConfigured","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"coreContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"components":[{"internalType":"string","name":"key","type":"string"},{"internalType":"enum IPMPV0.ParamType","name":"configuredParamType","type":"uint8"},{"internalType":"bytes32","name":"configuredValue","type":"bytes32"},{"internalType":"bool","name":"configuringArtistString","type":"bool"},{"internalType":"string","name":"configuredValueString","type":"string"}],"indexed":false,"internalType":"struct IPMPV0.PMPInput[]","name":"pmpInputs","type":"tuple[]"},{"indexed":false,"internalType":"address[]","name":"authAddresses","type":"address[]"}],"name":"TokenParamsConfigured","type":"event"},{"inputs":[],"name":"DELEGATION_REGISTRY_TOKEN_OWNER_RIGHTS","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint256","name":"projectId","type":"uint256"},{"components":[{"internalType":"string","name":"key","type":"string"},{"components":[{"internalType":"enum IPMPV0.AuthOption","name":"authOption","type":"uint8"},{"internalType":"enum IPMPV0.ParamType","name":"paramType","type":"uint8"},{"internalType":"uint48","name":"pmpLockedAfterTimestamp","type":"uint48"},{"internalType":"address","name":"authAddress","type":"address"},{"internalType":"string[]","name":"selectOptions","type":"string[]"},{"internalType":"bytes32","name":"minRange","type":"bytes32"},{"internalType":"bytes32","name":"maxRange","type":"bytes32"}],"internalType":"struct IPMPV0.PMPConfig","name":"pmpConfig","type":"tuple"}],"internalType":"struct IPMPV0.PMPInputConfig[]","name":"pmpInputConfigs","type":"tuple[]"}],"name":"configureProject","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"contract IPMPConfigureHook","name":"tokenPMPPostConfigHook","type":"address"},{"internalType":"contract IPMPAugmentHook","name":"tokenPMPReadAugmentationHook","type":"address"}],"name":"configureProjectHooks","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"components":[{"internalType":"string","name":"key","type":"string"},{"internalType":"enum IPMPV0.ParamType","name":"configuredParamType","type":"uint8"},{"internalType":"bytes32","name":"configuredValue","type":"bytes32"},{"internalType":"bool","name":"configuringArtistString","type":"bool"},{"internalType":"string","name":"configuredValueString","type":"string"}],"internalType":"struct IPMPV0.PMPInput[]","name":"pmpInputs","type":"tuple[]"}],"name":"configureTokenParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"delegateRegistry","outputs":[{"internalType":"contract IDelegateRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint256","name":"projectId","type":"uint256"}],"name":"getProjectConfig","outputs":[{"internalType":"string[]","name":"pmpKeys","type":"string[]"},{"internalType":"uint8","name":"configNonce","type":"uint8"},{"internalType":"contract IPMPConfigureHook","name":"tokenPMPPostConfigHook","type":"address"},{"internalType":"contract IPMPAugmentHook","name":"tokenPMPReadAugmentationHook","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"string","name":"pmpKey","type":"string"}],"name":"getProjectPMPConfig","outputs":[{"components":[{"internalType":"uint8","name":"highestConfigNonce","type":"uint8"},{"internalType":"enum IPMPV0.AuthOption","name":"authOption","type":"uint8"},{"internalType":"enum IPMPV0.ParamType","name":"paramType","type":"uint8"},{"internalType":"uint48","name":"pmpLockedAfterTimestamp","type":"uint48"},{"internalType":"address","name":"authAddress","type":"address"},{"internalType":"uint8","name":"selectOptionsLength","type":"uint8"},{"internalType":"string[]","name":"selectOptions","type":"string[]"},{"internalType":"bytes32","name":"minRange","type":"bytes32"},{"internalType":"bytes32","name":"maxRange","type":"bytes32"}],"internalType":"struct IPMPV0.PMPConfigView","name":"pmpConfigView","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"string","name":"pmpKey","type":"string"}],"name":"getTokenPMPStorage","outputs":[{"components":[{"internalType":"enum IPMPV0.ParamType","name":"configuredParamType","type":"uint8"},{"internalType":"bytes32","name":"configuredValue","type":"bytes32"},{"internalType":"string","name":"artistConfiguredValueString","type":"string"},{"internalType":"string","name":"nonArtistConfiguredValueString","type":"string"}],"internalType":"struct IPMPV0.PMPStorage","name":"pmp","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getTokenParams","outputs":[{"components":[{"internalType":"string","name":"key","type":"string"},{"internalType":"string","name":"value","type":"string"}],"internalType":"struct IWeb3Call.TokenParam[]","name":"tokenParams","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"isTokenOwnerOrDelegate","outputs":[{"internalType":"bool","name":"isTokenOwnerOrDelegate_","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60a06040523480156200001157600080fd5b5060405162004ea438038062004ea4833981016040819052620000349162000082565b60016000556001600160a01b03811660808190526040519081527f836360d1b094a7de3c3eab3d1185f3a5939467c23d4a12709dbdbf8c8d7e2f3b9060200160405180910390a150620000b4565b6000602082840312156200009557600080fd5b81516001600160a01b0381168114620000ad57600080fd5b9392505050565b608051614dce620000d66000396000818160cb0152612c060152614dce6000f3fe608060405234801561001057600080fd5b50600436106100995760003560e01c806301ffc9a71461009e57806313bfffac146100c65780633c303afb1461010557806354ca22371461011a5780636f8637a01461013d57806398a6e11814610150578063af544a611461017a578063b69c984d1461018d578063ba8dba48146101a0578063c2b4f411146101c0578063dc809b1a146101e0575b600080fd5b6100b16100ac3660046138a9565b610200565b60405190151581526020015b60405180910390f35b6100ed7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100bd565b61011861011336600461393f565b610237565b005b61012d61012836600461399a565b610719565b6040516100bd9493929190613a16565b61011861014b36600461393f565b61077c565b61016c71706f73746d696e74706172616d657465727360701b81565b6040519081526020016100bd565b6100b1610188366004613a9d565b610a9a565b61011861019b366004613ade565b610ab0565b6101b36101ae366004613c3e565b610ca4565b6040516100bd9190613d2a565b6101d36101ce36600461399a565b610e0c565b6040516100bd9190613e4b565b6101f36101ee366004613c3e565b61102c565b6040516100bd9190613e5e565b60006001600160e01b0319821663c2b4f41160e01b148061023157506301ffc9a760e01b6001600160e01b03198316145b92915050565b61023f611202565b61024a84843361122c565b8061010081111561029d5760405162461bcd60e51b8152602060048201526018602482015277504d503a204f6e6c79203c3d2032353620636f6e6669677360401b60448201526064015b60405180910390fd5b60005b818110156103925760008484838181106102bc576102bc613ebb565b90506020028101906102ce9190613ed1565b6102d89080613ef1565b91505080158015906102eb575061010081105b61034f5760405162461bcd60e51b815260206004820152602f60248201527f504d503a20706d704b65792063616e6e6f7420626520656d707479206f72206560448201526e78636565642032353520627974657360881b6064820152608401610294565b61038985858481811061036457610364613ebb565b90506020028101906103769190613ed1565b610384906020810190613f37565b6112e8565b506001016102a0565b506001600160a01b0385166000908152600160208181526040808420888552909152822060028101549092916103cb9160ff1690613f63565b60028301805460ff191660ff831617905590506000836001600160401b038111156103f8576103f8613b31565b60405190808252806020026020018201604052801561042b57816020015b60608152602001906001900390816104165790505b50905060005b848110156104c25786868281811061044b5761044b613ebb565b905060200281019061045d9190613ed1565b6104679080613ef1565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050845185925084915081106104af576104af613ebb565b6020908102919091010152600101610431565b506104cd8184611a61565b60005b848110156106c55760008460010160006105028585815181106104f5576104f5613ebb565b6020026020010151611af9565b8152602081019190915260400160002080549091506301000000900465ffffffffffff1680158061053257504281115b61058f5760405162461bcd60e51b815260206004820152602860248201527f504d503a20706d70206973206c6f636b656420616e642063616e6e6f74206265604482015267081d5c19185d195960c21b6064820152608401610294565b50805460ff191660ff851617815560008888848181106105b1576105b1613ebb565b90506020028101906105c39190613ed1565b6105d1906020810190613f37565b6105da9061405d565b8051835491925090839061ff0019166101008360068111156105fe576105fe613c96565b021790555060208101518254839062ff000019166201000083600881111561062857610628613c96565b02179055506040810151825460608301516080840151805160ff16600160e81b0260ff60e81b196001600160a01b03909316600160481b02600160481b600160e81b031965ffffffffffff909616630100000002959095166301000000600160e81b03199094169390931793909317161783556106a9906001840190611b29565b60a0810151600283015560c001516003909101556001016104d0565b507f935e8b8dc0c19500cc0d4c29698f1af13be297f62b71eda8dd0e1a0bc6927fca88888888866040516106fd9594939291906141bf565b60405180910390a1505050506107136001600055565b50505050565b6001600160a01b03821660009081526001602090815260408083208484529091528120606091908190819061074d81611deb565b6002820154600390920154909860ff831698506001600160a01b03610100909304831697509116945092505050565b610784611202565b6001600160a01b0384166000908152600160205260408120816107a686612015565b815260200190815260200160002090506000838390506001600160401b038111156107d3576107d3613b31565b6040519080825280602002602001820160405280156107fc578160200160208202803683370190505b50905060005b83811015610a4e57600061087786868481811061082157610821613ebb565b9050602002810190610833919061435e565b61083d9080613ef1565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611af992505050565b9050600086868481811061088d5761088d613ebb565b905060200281019061089f919061435e565b6108a890614382565b6001600160a01b038a1660009081526002602081815260408084208d85528252808420878552825280842060018b019092529092209088015492935090916108fa908b908d908690859060ff16612024565b86868151811061090c5761090c613ebb565b6001600160a01b039290921660209283029190910190910152805482546201000090910460ff1690839060ff1916600183600881111561094e5761094e613c96565b02179055506008815462010000900460ff16600881111561097157610971613c96565b036109ae5782606001511561099a576080830151600283019061099490826144c2565b506109b9565b6080830151600383019061099490826144c2565b604083015160018301555b600287015461010090046001600160a01b031615610a3e57600287015460405163677e713560e01b81526101009091046001600160a01b03169063677e713590610a0b908e908e908890600401614581565b600060405180830381600087803b158015610a2557600080fd5b505af1158015610a39573d6000803e3d6000fd5b505050505b5050600190920191506108029050565b507f3dfcd927ac9e2552184b368328e6cca04f875ef453930277d7dfa53f40dbeac48686868685604051610a8695949392919061463d565b60405180910390a150506107136001600055565b6000610aa7828486612b2a565b50949350505050565b610ab8611202565b610ac384843361122c565b6001600160a01b03821615610b5757610ae38263677e713560e01b612c7b565b610b57576040805162461bcd60e51b81526020600482015260248101919091527f504d503a20746f6b656e504d50506f7374436f6e666967486f6f6b20646f657360448201527f206e6f7420696d706c656d656e742049504d50436f6e666967757265486f6f6b6064820152608401610294565b6001600160a01b03811615610bf757610b77816358f8699f60e01b612c7b565b610bf75760405162461bcd60e51b8152602060048201526044602482018190527f504d503a20746f6b656e504d50526561644175676d656e746174696f6e486f6f908201527f6b20646f6573206e6f7420696d706c656d656e742049504d504175676d656e74606482015263486f6f6b60e01b608482015260a401610294565b6001600160a01b038481166000818152600160209081526040808320888452825291829020600281018054610100600160a81b031916610100898816908102919091179091556003820180546001600160a01b031916968816968717905583519485529184018890529183015260608201929092527fd7a93580965b5dbb5c8a723af5a48167de5f41dc65e456be819bf739da05ce389060800160405180910390a1506107136001600055565b610cf360408051610120810190915260008082526020820190815260200160008152600060208201819052604082018190526060808301829052608083015260a0820181905260c09091015290565b6001600160a01b038416600090815260016020818152604080842087855290915282200181610d2185611af9565b8152602080820192909252604001600020805460ff8082168652919350918401916101009004166006811115610d5957610d59613c96565b90816006811115610d6c57610d6c613c96565b9052508054604083019062010000900460ff166008811115610d9057610d90613c96565b90816008811115610da357610da3613c96565b90525080546301000000810465ffffffffffff166060840152600160481b81046001600160a01b03166080840152600160e81b900460ff1660a0830152610dec60018201611deb565b60c0830152600281015460e0830152600301546101008201529392505050565b60606000610e1983612015565b6001600160a01b03851660009081526001602090815260408083208484529091528120919250610e4882611deb565b8051909150806001600160401b03811115610e6557610e65613b31565b604051908082528060200260200182016040528015610eaa57816020015b6040805180820190915260608082526020820152815260200190600190039081610e835790505b5094506000805b82811015610f8c576000610ed08583815181106104f5576104f5613ebb565b600081815260018801602090815260408083206001600160a01b038f168452600283528184208e85528352818420858552909252822092935090918291610f1691612c9e565b9150915081610f2757505050610f84565b6040518060400160405280888681518110610f4457610f44613ebb565b60200260200101518152602001828152508a8681518110610f6757610f67613ebb565b60200260200101819052508480610f7d9061474b565b9550505050505b600101610eb1565b5080865260038401546001600160a01b0316156110215760038401546040516358f8699f60e01b81526001600160a01b03909116906358f8699f90610fd9908b908b908b90600401614764565b600060405180830381865afa158015610ff6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261101e91908101906147d8565b95505b505050505092915050565b61105660408051608081019091528060008152600060208201526060604082018190529081015290565b6001600160a01b038416600090815260026020908152604080832086845290915281209061108384611af9565b8152602081019190915260409081016000208151608081019092528054829060ff1660088111156110b6576110b6613c96565b60088111156110c7576110c7613c96565b8152602001600182015481526020016002820180546110e590614438565b80601f016020809104026020016040519081016040528092919081815260200182805461111190614438565b801561115e5780601f106111335761010080835404028352916020019161115e565b820191906000526020600020905b81548152906001019060200180831161114157829003601f168201915b5050505050815260200160038201805461117790614438565b80601f01602080910402602001604051908101604052809291908181526020018280546111a390614438565b80156111f05780601f106111c5576101008083540402835291602001916111f0565b820191906000526020600020905b8154815290600101906020018083116111d357829003601f168201915b50505050508152505090509392505050565b60026000540361122557604051633ee5aeb560e01b815260040160405180910390fd5b6002600055565b60405163a47d29cb60e01b8152600481018390526001600160a01b03808316919085169063a47d29cb90602401602060405180830381865afa158015611276573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061129a91906148e3565b6001600160a01b0316146112e35760405162461bcd60e51b815260206004820152601060248201526f1413540e881bdb9b1e48185c9d1a5cdd60821b6044820152606401610294565b505050565b60006112fa6040830160208401614900565b9050600061130b602084018461491b565b9050600082600881111561132157611321613c96565b0361136e5760405162461bcd60e51b815260206004820152601e60248201527f504d503a20706172616d5479706520697320756e636f6e6669677572656400006044820152606401610294565b61137e6060840160408501614936565b65ffffffffffff1615806113a857504261139e6060850160408601614936565b65ffffffffffff16115b6114285760405162461bcd60e51b8152602060048201526044602482018190527f504d503a20706d704c6f636b6564416674657254696d657374616d7020697320908201527f696e20746865207061737420616e64206e6f7420756e6c696d6974656420287a60648201526365726f2960e01b608482015260a401610294565b600681600681111561143c5761143c613c96565b11156114845760405162461bcd60e51b81526020600482015260176024820152762826a81d1024b73b30b634b21030baba3427b83a34b7b760491b6044820152606401610294565b600882600881111561149857611498613c96565b11156114b65760405162461bcd60e51b815260040161029490614951565b60088260088111156114ca576114ca613c96565b036115a05760008160068111156114e3576114e3613c96565b1480611500575060038160068111156114fe576114fe613c96565b145b8061151c5750600681600681111561151a5761151a613c96565b145b806115385750600481600681111561153657611536613c96565b145b6115a05760405162461bcd60e51b815260206004820152603360248201527f504d503a20537472696e6720706172616d73206d75737420686176652061727460448201527234b9ba159030baba3432b73a34b1b0ba34b7b760691b6064820152608401610294565b60028160068111156115b4576115b4613c96565b14806115d1575060058160068111156115cf576115cf613c96565b145b806115ed575060048160068111156115eb576115eb613c96565b145b806116095750600681600681111561160757611607613c96565b145b156116765760006116206080850160608601614981565b6001600160a01b0316036116715760405162461bcd60e51b8152602060048201526018602482015277504d503a206175746841646472657373206973207a65726f60401b6044820152606401610294565b6116dd565b60006116886080850160608601614981565b6001600160a01b0316146116dd5760405162461bcd60e51b815260206004820152601c60248201527b504d503a206175746841646472657373206973206e6f74207a65726f60201b6044820152606401610294565b60028260088111156116f1576116f1613c96565b148061170e5750600882600881111561170c5761170c613c96565b145b8061172a5750600682600881111561172857611728613c96565b145b156117f95761173c608084018461499e565b15905061175b5760405162461bcd60e51b8152600401610294906149e7565b60a0830135156117aa5760405162461bcd60e51b815260206004820152601a602482015279504d503a206d696e52616e6765206973206e6f7420656d70747960301b6044820152606401610294565b60c0830135156112e35760405162461bcd60e51b815260206004820152601a602482015279504d503a206d617852616e6765206973206e6f7420656d70747960301b6044820152606401610294565b600182600881111561180d5761180d613c96565b036118cd576000611821608085018561499e565b90501161186e5760405162461bcd60e51b815260206004820152601b60248201527a504d503a2073656c6563744f7074696f6e7320697320656d70747960281b6044820152606401610294565b61010061187e608085018561499e565b90501061175b5760405162461bcd60e51b815260206004820152601f60248201527f504d503a2073656c6563744f7074696f6e73206c656e677468203e20323535006044820152606401610294565b60038260088111156118e1576118e1613c96565b14806118fe575060048260088111156118fc576118fc613c96565b145b8061191a5750600582600881111561191857611918613c96565b145b806119365750600782600881111561193457611934613c96565b145b15611a4957611948608084018461499e565b1590506119675760405162461bcd60e51b8152600401610294906149e7565b600482600881111561197b5761197b613c96565b036119b05760a083013560c08401358082126119a95760405162461bcd60e51b815260040161029490614a1e565b5050505050565b60a083013560c08401358082106119d95760405162461bcd60e51b815260040161029490614a1e565b60078460088111156119ed576119ed613c96565b036119a9576001600160401b038111156119a95760405162461bcd60e51b815260206004820152601e60248201527f504d503a206d617852616e6765203e205f54494d455354414d505f4d415800006044820152606401610294565b60405162461bcd60e51b815260040161029490614951565b6000611a6c82611deb565b905060008151845114611a8157506001611ae9565b60005b8451811015611ae757611ad2838281518110611aa257611aa2613ebb565b6020026020010151868381518110611abc57611abc613ebb565b602002602001015161311190919063ffffffff16565b611adf5760019150611ae7565b600101611a84565b505b8015610713576107138385611b29565b600081604051602001611b0c9190614a51565b604051602081830303815290604052805190602001209050919050565b80516000819003611b4657505080546001600160a01b0319169055565b6000816001600160401b03811115611b6057611b60613b31565b604051908082528060200260200182016040528015611b89578160200160208202803683370190505b5090506000805b83811015611bfb5781838281518110611bab57611bab613ebb565b60200260200101906001600160401b031690816001600160401b031681525050848181518110611bdd57611bdd613ebb565b60200260200101515182611bf19190614a64565b9150600101611b90565b506001600160401b03811115611c515760405162461bcd60e51b815260206004820152601b60248201527a13d9999cd95d08195e18d959591cc81d5a5b9d0d8d081b1a5b5a5d602a1b6044820152606401610294565b600081611c5f856008614a77565b611c6a906008614a64565b611c749190614a64565b90506000611c83826020614a64565b6001600160401b03811115611c9a57611c9a613b31565b6040519080825280601f01601f191660200182016040528015611cc4576020820181803683370190505b5060c086901b602082015290506028810160005b86811015611d1d576000868281518110611cf457611cf4613ebb565b602090810291909101015160c01b6001600160c01b031916835250600890910190600101611cd8565b5060005b86811015611db7576000888281518110611d3d57611d3d613ebb565b6020026020010151905060008151905060006020830190506000602083611d649190614aa4565b90506000611d73602085614ab8565b905060005b82811015611d96578351885260209788019790930192600101611d78565b508015611da65782518752958601955b505060019093019250611d21915050565b505081815280611dc681613136565b88546001600160a01b0319166001600160a01b03919091161790975550505050505050565b80546060906001600160a01b0316611e2f576040805160008082526020820190925290611e28565b6060815260200190600190039081611e135790505b5092915050565b8154600090611e46906001600160a01b03166131db565b602081015190915060c01c806001600160401b03811115611e6957611e69613b31565b604051908082528060200260200182016040528015611e9c57816020015b6060815260200190600190039081611e875790505b50925060005b8181101561200d576000808084611eba856001614a64565b03611ee8576000865190506028870193506008850284015160c01c9250600886026008018103915050611f0a565b6028860192506008840283015160c01c91506008600185010283015160c01c90505b6000611f168383614acc565b6001600160401b031690506000611f2e826020614a64565b6001600160401b03811115611f4557611f45613b31565b6040519080825280601f01601f191660200182016040528015611f6f576020820181803683370190505b5090506020810160005b83811015611fd5576000816001600160401b038816611f998c6008614a77565b611fa4906028614a64565b611fae9190614a64565b611fb89190614a64565b8b0151835250602091820191611fce9082614a64565b9050611f79565b50819050828152818a8881518110611fef57611fef613ebb565b60200260200101819052505050505050508080600101915050611ea2565b505050919050565b6000610231620f424083614aa4565b815460009060ff8381169116146120a05760405162461bcd60e51b815260206004820152603a60248201527f504d503a20706172616d206e6f742070617274206f66206d6f737420726563656044820152796e746c7920636f6e6669677572656420504d5020706172616d7360301b6064820152608401610294565b825462010000900460ff1660088111156120bc576120bc613c96565b846020015160088111156120d2576120d2613c96565b146121195760405162461bcd60e51b81526020600482015260176024820152760a09aa07440e0c2e4c2daa8f2e0ca40dad2e6dac2e8c6d604b1b6044820152606401610294565b60008460200151600881111561213157612131613c96565b0361218a5760405162461bcd60e51b8152602060048201526024808201527f504d503a20696e70757420706172616d5479706520697320756e636f6e6669676044820152631d5c995960e21b6064820152608401610294565b8254600090610100900460ff16818160068111156121aa576121aa613c96565b14806121c7575060048160068111156121c5576121c5613c96565b145b806121e3575060038160068111156121e1576121e1613c96565b145b806121ff575060068160068111156121fd576121fd613c96565b145b156122175760006122118989336131fc565b94509250505b811580156122895750600281600681111561223457612234613c96565b14806122515750600581600681111561224f5761224f613c96565b145b8061226d5750600481600681111561226b5761226b613c96565b145b806122895750600681600681111561228757612287613c96565b145b156122a8578454600160481b90046001600160a01b0316925033831491505b8115801561231a575060018160068111156122c5576122c5613c96565b14806122e2575060038160068111156122e0576122e0613c96565b145b806122fe575060058160068111156122fc576122fc613c96565b145b8061231a5750600681600681111561231857612318613c96565b145b1561233257600061232c898933612b2a565b94509250505b5080612675578354610100900460ff16600081600681111561235657612356613c96565b0361239f5760405162461bcd60e51b81526020600482015260196024820152781413540e88185c9d1a5cdd08185d5d1a081c995c5d5a5c9959603a1b6044820152606401610294565b60018160068111156123b3576123b3613c96565b036124005760405162461bcd60e51b815260206004820152601e60248201527f504d503a20746f6b656e206f776e6572206175746820726571756972656400006044820152606401610294565b600381600681111561241457612414613c96565b036124735760405162461bcd60e51b815260206004820152602960248201527f504d503a2061727469737420616e6420746f6b656e206f776e65722061757468604482015268081c995c5d5a5c995960ba1b6064820152608401610294565b600281600681111561248757612487613c96565b036124d15760405162461bcd60e51b815260206004820152601a6024820152791413540e881859191c995cdcc8185d5d1a081c995c5d5a5c995960321b6044820152606401610294565b60068160068111156124e5576124e5613c96565b036125505760405162461bcd60e51b815260206004820152603560248201527f504d503a2061727469737420616e6420746f6b656e206f776e657220616e64206044820152741859191c995cdcc8185d5d1a081c995c5d5a5c9959605a1b6064820152608401610294565b600481600681111561256457612564613c96565b036125bf5760405162461bcd60e51b815260206004820152602560248201527f504d503a2061727469737420616e6420616464726573732061757468207265716044820152641d5a5c995960da1b6064820152608401610294565b60058160068111156125d3576125d3613c96565b036126335760405162461bcd60e51b815260206004820152602a60248201527f504d503a20746f6b656e206f776e657220616e642061646472657373206175746044820152691a081c995c5d5a5c995960b21b6064820152608401610294565b60405162461bcd60e51b81526020600482015260176024820152762826a81d1034b73b30b634b21030baba3427b83a34b7b760491b6044820152606401610294565b835462010000900460ff16600881600881111561269457612694613c96565b146129265760018160088111156126ad576126ad613c96565b036127255784546040870151600160e81b90910460ff16116127205760405162461bcd60e51b815260206004820152602660248201527f504d503a2073656c6563744f7074696f6e7320696e646578206f7574206f6620604482015265626f756e647360d01b6064820152608401610294565b612926565b600281600881111561273957612739613c96565b036127ab5760408601511580612753575060408601516001145b6127205760405162461bcd60e51b8152602060048201526024808201527f504d503a20626f6f6c20706172616d2076616c7565206d7573742062652030206044820152636f72203160e01b6064820152608401610294565b60038160088111156127bf576127bf613c96565b14806127dc575060058160088111156127da576127da613c96565b145b1561281d57846002015486604001511015801561280157508460030154866040015111155b6127205760405162461bcd60e51b815260040161029490614aec565b600481600881111561283157612831613c96565b0361287157600285015460408701511280159061280157506003850154604087015113156127205760405162461bcd60e51b815260040161029490614aec565b600781600881111561288557612885613c96565b036128a55760408601516001600160401b03108015906128015750612926565b60068160088111156128b9576128b9613c96565b036128e557604086015162ffffff10156127205760405162461bcd60e51b815260040161029490614b23565b60405162461bcd60e51b8152602060048201526016602482015275504d503a20696e76616c696420706172616d5479706560501b6044820152606401610294565b6008855462010000900460ff16600881111561294457612944613c96565b03612a39576040860151156129ae5760405162461bcd60e51b815260206004820152602a60248201527f504d503a2076616c7565206d75737420626520656d70747920666f7220737472604482015269696e6720706172616d7360b01b6064820152608401610294565b856060015115612a345760006129c58989336131fc565b50905080612a325760405162461bcd60e51b815260206004820152603460248201527f504d503a20617274697374206175746820726571756972656420746f20636f6e6044820152736669677572652061727469737420737472696e6760601b6064820152608401610294565b505b612b1f565b60808601515115612aa75760405162461bcd60e51b815260206004820152603260248201527f504d503a206e6f6e2d737472696e6720706172616d206d757374206861766520604482015271656d70747920737472696e672076616c756560701b6064820152608401610294565b856060015115612b1f5760405162461bcd60e51b815260206004820152603d60248201527f504d503a2061727469737420737472696e672063616e6e6f7420626520636f6e60448201527f6669677572656420666f72206e6f6e2d737472696e6720706172616d730000006064820152608401610294565b505095945050505050565b6040516331a9108f60e11b81526004810184905260009081906001600160a01b03851690636352211e90602401602060405180830381865afa158015612b74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b9891906148e3565b9050826001600160a01b0316816001600160a01b03161480612c715750604051632e7cda1d60e21b81526001600160a01b038481166004830152828116602483015285811660448301526064820187905271706f73746d696e74706172616d657465727360701b60848301527f0000000000000000000000000000000000000000000000000000000000000000169063b9f368749060a401602060405180830381865afa158015612c4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c719190614b53565b9150935093915050565b6000612c8683613298565b8015612c975750612c9783836132cb565b9392505050565b805460009060609060ff1682816008811115612cbc57612cbc613c96565b1480612cf45750845462010000900460ff166008811115612cdf57612cdf613c96565b816008811115612cf157612cf1613c96565b14155b15612d1257505060408051602081019091526000808252915061310a565b6008855462010000900460ff166008811115612d3057612d30613c96565b03612e2e576000846002018054612d4690614438565b90501115612dea57600184600201808054612d6090614438565b80601f0160208091040260200160405190810160405280929190818152602001828054612d8c90614438565b8015612dd95780601f10612dae57610100808354040283529160200191612dd9565b820191906000526020600020905b815481529060010190602001808311612dbc57829003601f168201915b50505050509050925092505061310a565b6000846003018054612dfb90614438565b90501115612e1557600184600301808054612d6090614438565b505060408051602081019091526000808252915061310a565b6001816008811115612e4257612e42613c96565b03612e945784546001850154600160e81b90910460ff1611612e7757505060408051602081019091526000808252915061310a565b600184810154612e8a9087830190613355565b925092505061310a565b6002816008811115612ea857612ea8613c96565b03612f025760018481015415612eda57604051806040016040528060048152602001637472756560e01b815250612e8a565b6040518060400160405280600581526020016466616c736560d81b815250925092505061310a565b6003816008811115612f1657612f16613c96565b1480612f3357506006816008811115612f3157612f31613c96565b145b80612f4f57506007816008811115612f4d57612f4d613c96565b145b80612f6b57506005816008811115612f6957612f69613c96565b145b1561305c57600184015460006006836008811115612f8b57612f8b613c96565b14612f9a576003870154612f9f565b62ffffff5b905060006006846008811115612fb757612fb7613c96565b14612fc6576002880154612fc9565b60005b905080831080612fd857508183115b15612ffc57600060405180602001604052806000815250955095505050505061310a565b600584600881111561301057613010613c96565b0361302d576001613020846134ff565b955095505050505061310a565b600684600881111561304157613041613c96565b03613051576001613020846135ac565b600161302084613695565b600481600881111561307057613070613c96565b036130c7576001840154600286015481128061308f5750600386015481135b156130b15760006040518060200160405280600081525093509350505061310a565b60016130bc82613727565b93509350505061310a565b60405162461bcd60e51b8152602060048201526018602482015277504d503a20556e68616e646c656420506172616d5479706560401b6044820152606401610294565b9250929050565b600081518351148015612c975750508051602091820120825192909101919091201490565b6000808260405160200161314a9190614b70565b604051602081830303815290604052905060008160405160200161316e9190614b96565b60405160208183030381529060405290508051602082016000f092506001600160a01b0383166131d45760405162461bcd60e51b81526020600482015260116024820152701111541313d65351539517d19052531151607a1b6044820152606401610294565b5050919050565b60606102318260016131f7816001600160a01b0384163b614bca565b613799565b600080600061320a86612015565b60405163a47d29cb60e01b8152600481018290529091506001600160a01b0386169063a47d29cb90602401602060405180830381865afa158015613252573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061327691906148e3565b9150836001600160a01b0316826001600160a01b031614925050935093915050565b60006132ab826301ffc9a760e01b6132cb565b801561023157506132c4826001600160e01b03196132cb565b1592915050565b6040516001600160e01b031982166024820152600090819060440160408051601f19818403018152919052602080820180516001600160e01b03166301ffc9a760e01b178152825192935060009283928392909183918a617530fa92503d9150600051905082801561333e575060208210155b801561334a5750600081115b979650505050505050565b81546060906001600160a01b031661337f5760405162461bcd60e51b815260040161029490614bdd565b8254600090613396906001600160a01b03166131db565b9050600080600080602085015160c01c90508087106133c75760405162461bcd60e51b815260040161029490614bdd565b806133d3886001614a64565b03613401576000855190506028860194506008880285015160c01c9350600882026008018103925050613423565b6028850193506008870284015160c01c92506008600188010284015160c01c91505b600061342f8484614acc565b6001600160401b031690506000613447826020614a64565b6001600160401b0381111561345e5761345e613b31565b6040519080825280601f01601f191660200182016040528015613488576020820181803683370190505b5090506020810160005b838110156134ee576000816001600160401b0389166134b2886008614a77565b6134bd906028614a64565b6134c79190614a64565b6134d19190614a64565b8a01518352506020918201916134e79082614a64565b9050613492565b505090815298975050505050505050565b6060600061350e600a80614cee565b6135189084614aa4565b90506000613527600a80614cee565b6135319085614ab8565b9050600061353e83613695565b9050600061354b83613695565b90505b600a8151101561357f57806040516020016135699190614cfa565b604051602081830303815290604052905061354e565b8181604051602001613592929190614d16565b604051602081830303815290604052945050505050919050565b6040805160078082528183019092526060918391600091602082018180368337019050509050602360f81b816000815181106135ea576135ea613ebb565b60200101906001600160f81b031916908160001a90535060065b8015613676576f181899199a1a9b1b9c1cb0b131b232b360811b83600f166010811061363257613632613ebb565b1a60f81b82828151811061364857613648613ebb565b60200101906001600160f81b031916908160001a90535060049290921c9161366f81614d52565b9050613604565b508115612c975760405162461bcd60e51b815260040161029490614b23565b606060006136a2836137bc565b60010190506000816001600160401b038111156136c1576136c1613b31565b6040519080825280601f01601f1916602001820160405280156136eb576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a85049450846136f557509392505050565b6060600082126137465760405180602001604052806000815250613761565b604051806040016040528060018152602001602d60f81b8152505b61377261376d84613892565b613695565b604051602001613783929190614d69565b6040516020818303038152906040529050919050565b60408051603f8301601f19168101909152818152818360208301863c9392505050565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b83106137fb5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6904ee2d6d415b85acef8160201b8310613825576904ee2d6d415b85acef8160201b830492506020015b662386f26fc10000831061384357662386f26fc10000830492506010015b6305f5e100831061385b576305f5e100830492506008015b612710831061386f57612710830492506004015b60648310613881576064830492506002015b600a83106102315760010192915050565b6000808212156138a55781600003610231565b5090565b6000602082840312156138bb57600080fd5b81356001600160e01b031981168114612c9757600080fd5b6001600160a01b03811681146138e857600080fd5b50565b80356138f6816138d3565b919050565b60008083601f84011261390d57600080fd5b5081356001600160401b0381111561392457600080fd5b6020830191508360208260051b850101111561310a57600080fd5b6000806000806060858703121561395557600080fd5b8435613960816138d3565b93506020850135925060408501356001600160401b0381111561398257600080fd5b61398e878288016138fb565b95989497509550505050565b600080604083850312156139ad57600080fd5b82356139b8816138d3565b946020939093013593505050565b60005b838110156139e15781810151838201526020016139c9565b50506000910152565b60008151808452613a028160208601602086016139c6565b601f01601f19169290920160200192915050565b6000608082016080835280875180835260a08501915060a08160051b86010192506020808a0160005b83811015613a6d57609f19888703018552613a5b8683516139ea565b95509382019390820190600101613a3f565b5050505060ff9690961660208401526001600160a01b039485166040840152929093166060909101529392505050565b600080600060608486031215613ab257600080fd5b8335613abd816138d3565b92506020840135613acd816138d3565b929592945050506040919091013590565b60008060008060808587031215613af457600080fd5b8435613aff816138d3565b9350602085013592506040850135613b16816138d3565b91506060850135613b26816138d3565b939692955090935050565b634e487b7160e01b600052604160045260246000fd5b60405160e081016001600160401b0381118282101715613b6957613b69613b31565b60405290565b604080519081016001600160401b0381118282101715613b6957613b69613b31565b604051601f8201601f191681016001600160401b0381118282101715613bb957613bb9613b31565b604052919050565b60006001600160401b03821115613bda57613bda613b31565b50601f01601f191660200190565b600082601f830112613bf957600080fd5b8135613c0c613c0782613bc1565b613b91565b818152846020838601011115613c2157600080fd5b816020850160208301376000918101602001919091529392505050565b600080600060608486031215613c5357600080fd5b8335613c5e816138d3565b92506020840135915060408401356001600160401b03811115613c8057600080fd5b613c8c86828701613be8565b9150509250925092565b634e487b7160e01b600052602160045260246000fd5b60078110613cbc57613cbc613c96565b9052565b60098110613cbc57613cbc613c96565b60008282518085526020808601955060208260051b8401016020860160005b84811015613d1d57601f19868403018952613d0b8383516139ea565b98840198925090830190600101613cef565b5090979650505050505050565b60208152613d3e60208201835160ff169052565b60006020830151613d526040840182613cac565b506040830151613d656060840182613cc0565b50606083015165ffffffffffff811660808401525060808301516001600160a01b03811660a08401525060a083015160ff811660c08401525060c08301516101208060e0850152613dba610140850183613cd0565b60e086015161010086810191909152909501519301929092525090919050565b600082825180855260208086019550808260051b84010181860160005b84811015613d1d57601f19868403018952815160408151818652613e1d828701826139ea565b91505085820151915084810386860152613e3781836139ea565b9a86019a9450505090830190600101613df7565b602081526000612c976020830184613dda565b60208152613e70602082018351613cc0565b602082015160408201526000604083015160806060840152613e9560a08401826139ea565b90506060840151601f19848303016080850152613eb282826139ea565b95945050505050565b634e487b7160e01b600052603260045260246000fd5b60008235603e19833603018112613ee757600080fd5b9190910192915050565b6000808335601e19843603018112613f0857600080fd5b8301803591506001600160401b03821115613f2257600080fd5b60200191503681900382131561310a57600080fd5b6000823560de19833603018112613ee757600080fd5b634e487b7160e01b600052601160045260246000fd5b60ff818116838216019081111561023157610231613f4d565b8035600781106138f657600080fd5b8035600981106138f657600080fd5b803565ffffffffffff811681146138f657600080fd5b60006001600160401b03821115613fc957613fc9613b31565b5060051b60200190565b600082601f830112613fe457600080fd5b81356020613ff4613c0783613fb0565b82815260059290921b8401810191818101908684111561401357600080fd5b8286015b848110156140525780356001600160401b038111156140365760008081fd5b6140448986838b0101613be8565b845250918301918301614017565b509695505050505050565b600060e0823603121561406f57600080fd5b614077613b47565b61408083613f7c565b815261408e60208401613f8b565b602082015261409f60408401613f9a565b60408201526140b0606084016138eb565b606082015260808301356001600160401b038111156140ce57600080fd5b6140da36828601613fd3565b60808301525060a083013560a082015260c083013560c082015280915050919050565b6000808335601e1984360301811261411457600080fd5b83016020810192503590506001600160401b0381111561413357600080fd5b80360382131561310a57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6000838385526020808601955060208560051b8301018460005b87811015613d1d57848303601f190189526141a082886140fd565b6141ab858284614142565b9a86019a9450505090830190600101614185565b6001600160a01b038681168252602080830187905260806040808501829052848201879052600093919260a0929183870160058a901b880185018b885b8c81101561433b578a8303609f190184528135368f9003603e1901811261422257600080fd5b8e0161422e81806140fd565b87865261423e8887018284614142565b9150508782013560de1983360301811261425757600080fd5b858203898701529091019060e06142768261427185613f7c565b613cac565b614281898401613f8b565b61428d8a840182613cc0565b5065ffffffffffff6142a0898501613f9a565b16888301526060808401356142b4816138d3565b8b1690830152828c013536849003601e190181126142d157600080fd5b830189810190356001600160401b038111156142ec57600080fd5b8060051b36038213156142fe57600080fd5b828e850152614310838501828461416b565b858e0135858f015260c0958601359590940194909452505094870194935050908501906001016141fc565b505060ff8a1660608a0152965061435495505050505050565b9695505050505050565b60008235609e19833603018112613ee757600080fd5b80151581146138e857600080fd5b600060a0823603121561439457600080fd5b60405160a081016001600160401b0380821183831017156143b7576143b7613b31565b8160405284359150808211156143cc57600080fd5b6143d836838701613be8565b83526143e660208601613f8b565b6020840152604085013560408401526060850135915061440582614374565b816060840152608085013591508082111561441f57600080fd5b5061442c36828601613be8565b60808301525092915050565b600181811c9082168061444c57607f821691505b60208210810361446c57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156112e3576000816000526020600020601f850160051c8101602086101561449b5750805b601f850160051c820191505b818110156144ba578281556001016144a7565b505050505050565b81516001600160401b038111156144db576144db613b31565b6144ef816144e98454614438565b84614472565b602080601f831160018114614524576000841561450c5750858301515b600019600386901b1c1916600185901b1785556144ba565b600085815260208120601f198616915b8281101561455357888601518255948401946001909101908401614534565b50858210156145715787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60018060a01b0384168152826020820152606060408201526000825160a060608401526145b26101008401826139ea565b905060208401516145c66080850182613cc0565b50604084015160a08401526060840151151560c08401526080840151838203605f190160e085015261334a82826139ea565b60008151808452602080850194506020840160005b838110156146325781516001600160a01b03168752958201959082019060010161460d565b509495945050505050565b6001600160a01b038616815260208082018690526080604080840182905283820186905260009260a091828601600589901b870184018a87805b8c811015614725578a8403609f190185528235368f9003609e1901811261469c578283fd5b8e016146a881806140fd565b8a87526146b88b88018284614142565b9150506146c6898301613f8b565b6146d28a880182613cc0565b5087820135888701526060808301356146ea81614374565b1515908701526146fc828c01836140fd565b92508682038c880152614710828483614142565b978a0197965050509287019250600101614677565b505050878103606089015261473a818a6145f8565b9d9c50505050505050505050505050565b60006001820161475d5761475d613f4d565b5060010190565b60018060a01b0384168152826020820152606060408201526000613eb26060830184613dda565b600082601f83011261479c57600080fd5b81516147aa613c0782613bc1565b8181528460208386010111156147bf57600080fd5b6147d08260208301602087016139c6565b949350505050565b600060208083850312156147eb57600080fd5b82516001600160401b038082111561480257600080fd5b818501915085601f83011261481657600080fd5b8151614824613c0782613fb0565b81815260059190911b8301840190848101908883111561484357600080fd5b8585015b838110156148d65780518581111561485f5760008081fd5b86016040818c03601f19018113156148775760008081fd5b61487f613b6f565b89830151888111156148915760008081fd5b61489f8e8c8387010161478b565b8252509082015190878211156148b55760008081fd5b6148c38d8b8486010161478b565b818b015285525050918601918601614847565b5098975050505050505050565b6000602082840312156148f557600080fd5b8151612c97816138d3565b60006020828403121561491257600080fd5b612c9782613f8b565b60006020828403121561492d57600080fd5b612c9782613f7c565b60006020828403121561494857600080fd5b612c9782613f9a565b602080825260169082015275504d503a20496e76616c696420706172616d5479706560501b604082015260600190565b60006020828403121561499357600080fd5b8135612c97816138d3565b6000808335601e198436030181126149b557600080fd5b8301803591506001600160401b038211156149cf57600080fd5b6020019150600581901b360382131561310a57600080fd5b6020808252601f908201527f504d503a2073656c6563744f7074696f6e73206973206e6f7420656d70747900604082015260600190565b602080825260199082015278504d503a206d696e52616e6765203e3d206d617852616e676560381b604082015260600190565b602081526000612c9760208301846139ea565b8082018082111561023157610231613f4d565b808202811582820484141761023157610231613f4d565b634e487b7160e01b600052601260045260246000fd5b600082614ab357614ab3614a8e565b500490565b600082614ac757614ac7614a8e565b500690565b6001600160401b03828116828216039080821115611e2857611e28613f4d565b6020808252601e908201527f504d503a20706172616d2076616c7565206f7574206f6620626f756e64730000604082015260600190565b6020808252601690820152752826a81d1034b73b30b634b2103432bc1031b7b637b960511b604082015260600190565b600060208284031215614b6557600080fd5b8151612c9781614374565b6000815260008251614b898160018501602087016139c6565b9190910160010192915050565b6a600b5981380380925939f360a81b81528151600090614bbd81600b8501602087016139c6565b91909101600b0192915050565b8181038181111561023157610231613f4d565b602080825260139082015272496e646578206f7574206f6620626f756e647360681b604082015260600190565b600181815b80851115614c45578160001904821115614c2b57614c2b613f4d565b80851615614c3857918102915b93841c9390800290614c0f565b509250929050565b600082614c5c57506001610231565b81614c6957506000610231565b8160018114614c7f5760028114614c8957614ca5565b6001915050610231565b60ff841115614c9a57614c9a613f4d565b50506001821b610231565b5060208310610133831016604e8410600b8410161715614cc8575081810a610231565b614cd28383614c0a565b8060001904821115614ce657614ce6613f4d565b029392505050565b6000612c978383614c4d565b600360fc1b815260008251614b898160018501602087016139c6565b60008351614d288184602088016139c6565b601760f91b9083019081528351614d468160018401602088016139c6565b01600101949350505050565b600081614d6157614d61613f4d565b506000190190565b60008351614d7b8184602088016139c6565b835190830190614d8f8183602088016139c6565b0194935050505056fea26469706673582212200bede6c2fd14d9777b1f2a93c2b7e9c38cd1e28475e132481a6b6b474e0a5f9064736f6c6343000816003300000000000000000000000000000000000000447e69651d841bd8d104bed493
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106100995760003560e01c806301ffc9a71461009e57806313bfffac146100c65780633c303afb1461010557806354ca22371461011a5780636f8637a01461013d57806398a6e11814610150578063af544a611461017a578063b69c984d1461018d578063ba8dba48146101a0578063c2b4f411146101c0578063dc809b1a146101e0575b600080fd5b6100b16100ac3660046138a9565b610200565b60405190151581526020015b60405180910390f35b6100ed7f00000000000000000000000000000000000000447e69651d841bd8d104bed49381565b6040516001600160a01b0390911681526020016100bd565b61011861011336600461393f565b610237565b005b61012d61012836600461399a565b610719565b6040516100bd9493929190613a16565b61011861014b36600461393f565b61077c565b61016c71706f73746d696e74706172616d657465727360701b81565b6040519081526020016100bd565b6100b1610188366004613a9d565b610a9a565b61011861019b366004613ade565b610ab0565b6101b36101ae366004613c3e565b610ca4565b6040516100bd9190613d2a565b6101d36101ce36600461399a565b610e0c565b6040516100bd9190613e4b565b6101f36101ee366004613c3e565b61102c565b6040516100bd9190613e5e565b60006001600160e01b0319821663c2b4f41160e01b148061023157506301ffc9a760e01b6001600160e01b03198316145b92915050565b61023f611202565b61024a84843361122c565b8061010081111561029d5760405162461bcd60e51b8152602060048201526018602482015277504d503a204f6e6c79203c3d2032353620636f6e6669677360401b60448201526064015b60405180910390fd5b60005b818110156103925760008484838181106102bc576102bc613ebb565b90506020028101906102ce9190613ed1565b6102d89080613ef1565b91505080158015906102eb575061010081105b61034f5760405162461bcd60e51b815260206004820152602f60248201527f504d503a20706d704b65792063616e6e6f7420626520656d707479206f72206560448201526e78636565642032353520627974657360881b6064820152608401610294565b61038985858481811061036457610364613ebb565b90506020028101906103769190613ed1565b610384906020810190613f37565b6112e8565b506001016102a0565b506001600160a01b0385166000908152600160208181526040808420888552909152822060028101549092916103cb9160ff1690613f63565b60028301805460ff191660ff831617905590506000836001600160401b038111156103f8576103f8613b31565b60405190808252806020026020018201604052801561042b57816020015b60608152602001906001900390816104165790505b50905060005b848110156104c25786868281811061044b5761044b613ebb565b905060200281019061045d9190613ed1565b6104679080613ef1565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050845185925084915081106104af576104af613ebb565b6020908102919091010152600101610431565b506104cd8184611a61565b60005b848110156106c55760008460010160006105028585815181106104f5576104f5613ebb565b6020026020010151611af9565b8152602081019190915260400160002080549091506301000000900465ffffffffffff1680158061053257504281115b61058f5760405162461bcd60e51b815260206004820152602860248201527f504d503a20706d70206973206c6f636b656420616e642063616e6e6f74206265604482015267081d5c19185d195960c21b6064820152608401610294565b50805460ff191660ff851617815560008888848181106105b1576105b1613ebb565b90506020028101906105c39190613ed1565b6105d1906020810190613f37565b6105da9061405d565b8051835491925090839061ff0019166101008360068111156105fe576105fe613c96565b021790555060208101518254839062ff000019166201000083600881111561062857610628613c96565b02179055506040810151825460608301516080840151805160ff16600160e81b0260ff60e81b196001600160a01b03909316600160481b02600160481b600160e81b031965ffffffffffff909616630100000002959095166301000000600160e81b03199094169390931793909317161783556106a9906001840190611b29565b60a0810151600283015560c001516003909101556001016104d0565b507f935e8b8dc0c19500cc0d4c29698f1af13be297f62b71eda8dd0e1a0bc6927fca88888888866040516106fd9594939291906141bf565b60405180910390a1505050506107136001600055565b50505050565b6001600160a01b03821660009081526001602090815260408083208484529091528120606091908190819061074d81611deb565b6002820154600390920154909860ff831698506001600160a01b03610100909304831697509116945092505050565b610784611202565b6001600160a01b0384166000908152600160205260408120816107a686612015565b815260200190815260200160002090506000838390506001600160401b038111156107d3576107d3613b31565b6040519080825280602002602001820160405280156107fc578160200160208202803683370190505b50905060005b83811015610a4e57600061087786868481811061082157610821613ebb565b9050602002810190610833919061435e565b61083d9080613ef1565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611af992505050565b9050600086868481811061088d5761088d613ebb565b905060200281019061089f919061435e565b6108a890614382565b6001600160a01b038a1660009081526002602081815260408084208d85528252808420878552825280842060018b019092529092209088015492935090916108fa908b908d908690859060ff16612024565b86868151811061090c5761090c613ebb565b6001600160a01b039290921660209283029190910190910152805482546201000090910460ff1690839060ff1916600183600881111561094e5761094e613c96565b02179055506008815462010000900460ff16600881111561097157610971613c96565b036109ae5782606001511561099a576080830151600283019061099490826144c2565b506109b9565b6080830151600383019061099490826144c2565b604083015160018301555b600287015461010090046001600160a01b031615610a3e57600287015460405163677e713560e01b81526101009091046001600160a01b03169063677e713590610a0b908e908e908890600401614581565b600060405180830381600087803b158015610a2557600080fd5b505af1158015610a39573d6000803e3d6000fd5b505050505b5050600190920191506108029050565b507f3dfcd927ac9e2552184b368328e6cca04f875ef453930277d7dfa53f40dbeac48686868685604051610a8695949392919061463d565b60405180910390a150506107136001600055565b6000610aa7828486612b2a565b50949350505050565b610ab8611202565b610ac384843361122c565b6001600160a01b03821615610b5757610ae38263677e713560e01b612c7b565b610b57576040805162461bcd60e51b81526020600482015260248101919091527f504d503a20746f6b656e504d50506f7374436f6e666967486f6f6b20646f657360448201527f206e6f7420696d706c656d656e742049504d50436f6e666967757265486f6f6b6064820152608401610294565b6001600160a01b03811615610bf757610b77816358f8699f60e01b612c7b565b610bf75760405162461bcd60e51b8152602060048201526044602482018190527f504d503a20746f6b656e504d50526561644175676d656e746174696f6e486f6f908201527f6b20646f6573206e6f7420696d706c656d656e742049504d504175676d656e74606482015263486f6f6b60e01b608482015260a401610294565b6001600160a01b038481166000818152600160209081526040808320888452825291829020600281018054610100600160a81b031916610100898816908102919091179091556003820180546001600160a01b031916968816968717905583519485529184018890529183015260608201929092527fd7a93580965b5dbb5c8a723af5a48167de5f41dc65e456be819bf739da05ce389060800160405180910390a1506107136001600055565b610cf360408051610120810190915260008082526020820190815260200160008152600060208201819052604082018190526060808301829052608083015260a0820181905260c09091015290565b6001600160a01b038416600090815260016020818152604080842087855290915282200181610d2185611af9565b8152602080820192909252604001600020805460ff8082168652919350918401916101009004166006811115610d5957610d59613c96565b90816006811115610d6c57610d6c613c96565b9052508054604083019062010000900460ff166008811115610d9057610d90613c96565b90816008811115610da357610da3613c96565b90525080546301000000810465ffffffffffff166060840152600160481b81046001600160a01b03166080840152600160e81b900460ff1660a0830152610dec60018201611deb565b60c0830152600281015460e0830152600301546101008201529392505050565b60606000610e1983612015565b6001600160a01b03851660009081526001602090815260408083208484529091528120919250610e4882611deb565b8051909150806001600160401b03811115610e6557610e65613b31565b604051908082528060200260200182016040528015610eaa57816020015b6040805180820190915260608082526020820152815260200190600190039081610e835790505b5094506000805b82811015610f8c576000610ed08583815181106104f5576104f5613ebb565b600081815260018801602090815260408083206001600160a01b038f168452600283528184208e85528352818420858552909252822092935090918291610f1691612c9e565b9150915081610f2757505050610f84565b6040518060400160405280888681518110610f4457610f44613ebb565b60200260200101518152602001828152508a8681518110610f6757610f67613ebb565b60200260200101819052508480610f7d9061474b565b9550505050505b600101610eb1565b5080865260038401546001600160a01b0316156110215760038401546040516358f8699f60e01b81526001600160a01b03909116906358f8699f90610fd9908b908b908b90600401614764565b600060405180830381865afa158015610ff6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261101e91908101906147d8565b95505b505050505092915050565b61105660408051608081019091528060008152600060208201526060604082018190529081015290565b6001600160a01b038416600090815260026020908152604080832086845290915281209061108384611af9565b8152602081019190915260409081016000208151608081019092528054829060ff1660088111156110b6576110b6613c96565b60088111156110c7576110c7613c96565b8152602001600182015481526020016002820180546110e590614438565b80601f016020809104026020016040519081016040528092919081815260200182805461111190614438565b801561115e5780601f106111335761010080835404028352916020019161115e565b820191906000526020600020905b81548152906001019060200180831161114157829003601f168201915b5050505050815260200160038201805461117790614438565b80601f01602080910402602001604051908101604052809291908181526020018280546111a390614438565b80156111f05780601f106111c5576101008083540402835291602001916111f0565b820191906000526020600020905b8154815290600101906020018083116111d357829003601f168201915b50505050508152505090509392505050565b60026000540361122557604051633ee5aeb560e01b815260040160405180910390fd5b6002600055565b60405163a47d29cb60e01b8152600481018390526001600160a01b03808316919085169063a47d29cb90602401602060405180830381865afa158015611276573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061129a91906148e3565b6001600160a01b0316146112e35760405162461bcd60e51b815260206004820152601060248201526f1413540e881bdb9b1e48185c9d1a5cdd60821b6044820152606401610294565b505050565b60006112fa6040830160208401614900565b9050600061130b602084018461491b565b9050600082600881111561132157611321613c96565b0361136e5760405162461bcd60e51b815260206004820152601e60248201527f504d503a20706172616d5479706520697320756e636f6e6669677572656400006044820152606401610294565b61137e6060840160408501614936565b65ffffffffffff1615806113a857504261139e6060850160408601614936565b65ffffffffffff16115b6114285760405162461bcd60e51b8152602060048201526044602482018190527f504d503a20706d704c6f636b6564416674657254696d657374616d7020697320908201527f696e20746865207061737420616e64206e6f7420756e6c696d6974656420287a60648201526365726f2960e01b608482015260a401610294565b600681600681111561143c5761143c613c96565b11156114845760405162461bcd60e51b81526020600482015260176024820152762826a81d1024b73b30b634b21030baba3427b83a34b7b760491b6044820152606401610294565b600882600881111561149857611498613c96565b11156114b65760405162461bcd60e51b815260040161029490614951565b60088260088111156114ca576114ca613c96565b036115a05760008160068111156114e3576114e3613c96565b1480611500575060038160068111156114fe576114fe613c96565b145b8061151c5750600681600681111561151a5761151a613c96565b145b806115385750600481600681111561153657611536613c96565b145b6115a05760405162461bcd60e51b815260206004820152603360248201527f504d503a20537472696e6720706172616d73206d75737420686176652061727460448201527234b9ba159030baba3432b73a34b1b0ba34b7b760691b6064820152608401610294565b60028160068111156115b4576115b4613c96565b14806115d1575060058160068111156115cf576115cf613c96565b145b806115ed575060048160068111156115eb576115eb613c96565b145b806116095750600681600681111561160757611607613c96565b145b156116765760006116206080850160608601614981565b6001600160a01b0316036116715760405162461bcd60e51b8152602060048201526018602482015277504d503a206175746841646472657373206973207a65726f60401b6044820152606401610294565b6116dd565b60006116886080850160608601614981565b6001600160a01b0316146116dd5760405162461bcd60e51b815260206004820152601c60248201527b504d503a206175746841646472657373206973206e6f74207a65726f60201b6044820152606401610294565b60028260088111156116f1576116f1613c96565b148061170e5750600882600881111561170c5761170c613c96565b145b8061172a5750600682600881111561172857611728613c96565b145b156117f95761173c608084018461499e565b15905061175b5760405162461bcd60e51b8152600401610294906149e7565b60a0830135156117aa5760405162461bcd60e51b815260206004820152601a602482015279504d503a206d696e52616e6765206973206e6f7420656d70747960301b6044820152606401610294565b60c0830135156112e35760405162461bcd60e51b815260206004820152601a602482015279504d503a206d617852616e6765206973206e6f7420656d70747960301b6044820152606401610294565b600182600881111561180d5761180d613c96565b036118cd576000611821608085018561499e565b90501161186e5760405162461bcd60e51b815260206004820152601b60248201527a504d503a2073656c6563744f7074696f6e7320697320656d70747960281b6044820152606401610294565b61010061187e608085018561499e565b90501061175b5760405162461bcd60e51b815260206004820152601f60248201527f504d503a2073656c6563744f7074696f6e73206c656e677468203e20323535006044820152606401610294565b60038260088111156118e1576118e1613c96565b14806118fe575060048260088111156118fc576118fc613c96565b145b8061191a5750600582600881111561191857611918613c96565b145b806119365750600782600881111561193457611934613c96565b145b15611a4957611948608084018461499e565b1590506119675760405162461bcd60e51b8152600401610294906149e7565b600482600881111561197b5761197b613c96565b036119b05760a083013560c08401358082126119a95760405162461bcd60e51b815260040161029490614a1e565b5050505050565b60a083013560c08401358082106119d95760405162461bcd60e51b815260040161029490614a1e565b60078460088111156119ed576119ed613c96565b036119a9576001600160401b038111156119a95760405162461bcd60e51b815260206004820152601e60248201527f504d503a206d617852616e6765203e205f54494d455354414d505f4d415800006044820152606401610294565b60405162461bcd60e51b815260040161029490614951565b6000611a6c82611deb565b905060008151845114611a8157506001611ae9565b60005b8451811015611ae757611ad2838281518110611aa257611aa2613ebb565b6020026020010151868381518110611abc57611abc613ebb565b602002602001015161311190919063ffffffff16565b611adf5760019150611ae7565b600101611a84565b505b8015610713576107138385611b29565b600081604051602001611b0c9190614a51565b604051602081830303815290604052805190602001209050919050565b80516000819003611b4657505080546001600160a01b0319169055565b6000816001600160401b03811115611b6057611b60613b31565b604051908082528060200260200182016040528015611b89578160200160208202803683370190505b5090506000805b83811015611bfb5781838281518110611bab57611bab613ebb565b60200260200101906001600160401b031690816001600160401b031681525050848181518110611bdd57611bdd613ebb565b60200260200101515182611bf19190614a64565b9150600101611b90565b506001600160401b03811115611c515760405162461bcd60e51b815260206004820152601b60248201527a13d9999cd95d08195e18d959591cc81d5a5b9d0d8d081b1a5b5a5d602a1b6044820152606401610294565b600081611c5f856008614a77565b611c6a906008614a64565b611c749190614a64565b90506000611c83826020614a64565b6001600160401b03811115611c9a57611c9a613b31565b6040519080825280601f01601f191660200182016040528015611cc4576020820181803683370190505b5060c086901b602082015290506028810160005b86811015611d1d576000868281518110611cf457611cf4613ebb565b602090810291909101015160c01b6001600160c01b031916835250600890910190600101611cd8565b5060005b86811015611db7576000888281518110611d3d57611d3d613ebb565b6020026020010151905060008151905060006020830190506000602083611d649190614aa4565b90506000611d73602085614ab8565b905060005b82811015611d96578351885260209788019790930192600101611d78565b508015611da65782518752958601955b505060019093019250611d21915050565b505081815280611dc681613136565b88546001600160a01b0319166001600160a01b03919091161790975550505050505050565b80546060906001600160a01b0316611e2f576040805160008082526020820190925290611e28565b6060815260200190600190039081611e135790505b5092915050565b8154600090611e46906001600160a01b03166131db565b602081015190915060c01c806001600160401b03811115611e6957611e69613b31565b604051908082528060200260200182016040528015611e9c57816020015b6060815260200190600190039081611e875790505b50925060005b8181101561200d576000808084611eba856001614a64565b03611ee8576000865190506028870193506008850284015160c01c9250600886026008018103915050611f0a565b6028860192506008840283015160c01c91506008600185010283015160c01c90505b6000611f168383614acc565b6001600160401b031690506000611f2e826020614a64565b6001600160401b03811115611f4557611f45613b31565b6040519080825280601f01601f191660200182016040528015611f6f576020820181803683370190505b5090506020810160005b83811015611fd5576000816001600160401b038816611f998c6008614a77565b611fa4906028614a64565b611fae9190614a64565b611fb89190614a64565b8b0151835250602091820191611fce9082614a64565b9050611f79565b50819050828152818a8881518110611fef57611fef613ebb565b60200260200101819052505050505050508080600101915050611ea2565b505050919050565b6000610231620f424083614aa4565b815460009060ff8381169116146120a05760405162461bcd60e51b815260206004820152603a60248201527f504d503a20706172616d206e6f742070617274206f66206d6f737420726563656044820152796e746c7920636f6e6669677572656420504d5020706172616d7360301b6064820152608401610294565b825462010000900460ff1660088111156120bc576120bc613c96565b846020015160088111156120d2576120d2613c96565b146121195760405162461bcd60e51b81526020600482015260176024820152760a09aa07440e0c2e4c2daa8f2e0ca40dad2e6dac2e8c6d604b1b6044820152606401610294565b60008460200151600881111561213157612131613c96565b0361218a5760405162461bcd60e51b8152602060048201526024808201527f504d503a20696e70757420706172616d5479706520697320756e636f6e6669676044820152631d5c995960e21b6064820152608401610294565b8254600090610100900460ff16818160068111156121aa576121aa613c96565b14806121c7575060048160068111156121c5576121c5613c96565b145b806121e3575060038160068111156121e1576121e1613c96565b145b806121ff575060068160068111156121fd576121fd613c96565b145b156122175760006122118989336131fc565b94509250505b811580156122895750600281600681111561223457612234613c96565b14806122515750600581600681111561224f5761224f613c96565b145b8061226d5750600481600681111561226b5761226b613c96565b145b806122895750600681600681111561228757612287613c96565b145b156122a8578454600160481b90046001600160a01b0316925033831491505b8115801561231a575060018160068111156122c5576122c5613c96565b14806122e2575060038160068111156122e0576122e0613c96565b145b806122fe575060058160068111156122fc576122fc613c96565b145b8061231a5750600681600681111561231857612318613c96565b145b1561233257600061232c898933612b2a565b94509250505b5080612675578354610100900460ff16600081600681111561235657612356613c96565b0361239f5760405162461bcd60e51b81526020600482015260196024820152781413540e88185c9d1a5cdd08185d5d1a081c995c5d5a5c9959603a1b6044820152606401610294565b60018160068111156123b3576123b3613c96565b036124005760405162461bcd60e51b815260206004820152601e60248201527f504d503a20746f6b656e206f776e6572206175746820726571756972656400006044820152606401610294565b600381600681111561241457612414613c96565b036124735760405162461bcd60e51b815260206004820152602960248201527f504d503a2061727469737420616e6420746f6b656e206f776e65722061757468604482015268081c995c5d5a5c995960ba1b6064820152608401610294565b600281600681111561248757612487613c96565b036124d15760405162461bcd60e51b815260206004820152601a6024820152791413540e881859191c995cdcc8185d5d1a081c995c5d5a5c995960321b6044820152606401610294565b60068160068111156124e5576124e5613c96565b036125505760405162461bcd60e51b815260206004820152603560248201527f504d503a2061727469737420616e6420746f6b656e206f776e657220616e64206044820152741859191c995cdcc8185d5d1a081c995c5d5a5c9959605a1b6064820152608401610294565b600481600681111561256457612564613c96565b036125bf5760405162461bcd60e51b815260206004820152602560248201527f504d503a2061727469737420616e6420616464726573732061757468207265716044820152641d5a5c995960da1b6064820152608401610294565b60058160068111156125d3576125d3613c96565b036126335760405162461bcd60e51b815260206004820152602a60248201527f504d503a20746f6b656e206f776e657220616e642061646472657373206175746044820152691a081c995c5d5a5c995960b21b6064820152608401610294565b60405162461bcd60e51b81526020600482015260176024820152762826a81d1034b73b30b634b21030baba3427b83a34b7b760491b6044820152606401610294565b835462010000900460ff16600881600881111561269457612694613c96565b146129265760018160088111156126ad576126ad613c96565b036127255784546040870151600160e81b90910460ff16116127205760405162461bcd60e51b815260206004820152602660248201527f504d503a2073656c6563744f7074696f6e7320696e646578206f7574206f6620604482015265626f756e647360d01b6064820152608401610294565b612926565b600281600881111561273957612739613c96565b036127ab5760408601511580612753575060408601516001145b6127205760405162461bcd60e51b8152602060048201526024808201527f504d503a20626f6f6c20706172616d2076616c7565206d7573742062652030206044820152636f72203160e01b6064820152608401610294565b60038160088111156127bf576127bf613c96565b14806127dc575060058160088111156127da576127da613c96565b145b1561281d57846002015486604001511015801561280157508460030154866040015111155b6127205760405162461bcd60e51b815260040161029490614aec565b600481600881111561283157612831613c96565b0361287157600285015460408701511280159061280157506003850154604087015113156127205760405162461bcd60e51b815260040161029490614aec565b600781600881111561288557612885613c96565b036128a55760408601516001600160401b03108015906128015750612926565b60068160088111156128b9576128b9613c96565b036128e557604086015162ffffff10156127205760405162461bcd60e51b815260040161029490614b23565b60405162461bcd60e51b8152602060048201526016602482015275504d503a20696e76616c696420706172616d5479706560501b6044820152606401610294565b6008855462010000900460ff16600881111561294457612944613c96565b03612a39576040860151156129ae5760405162461bcd60e51b815260206004820152602a60248201527f504d503a2076616c7565206d75737420626520656d70747920666f7220737472604482015269696e6720706172616d7360b01b6064820152608401610294565b856060015115612a345760006129c58989336131fc565b50905080612a325760405162461bcd60e51b815260206004820152603460248201527f504d503a20617274697374206175746820726571756972656420746f20636f6e6044820152736669677572652061727469737420737472696e6760601b6064820152608401610294565b505b612b1f565b60808601515115612aa75760405162461bcd60e51b815260206004820152603260248201527f504d503a206e6f6e2d737472696e6720706172616d206d757374206861766520604482015271656d70747920737472696e672076616c756560701b6064820152608401610294565b856060015115612b1f5760405162461bcd60e51b815260206004820152603d60248201527f504d503a2061727469737420737472696e672063616e6e6f7420626520636f6e60448201527f6669677572656420666f72206e6f6e2d737472696e6720706172616d730000006064820152608401610294565b505095945050505050565b6040516331a9108f60e11b81526004810184905260009081906001600160a01b03851690636352211e90602401602060405180830381865afa158015612b74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b9891906148e3565b9050826001600160a01b0316816001600160a01b03161480612c715750604051632e7cda1d60e21b81526001600160a01b038481166004830152828116602483015285811660448301526064820187905271706f73746d696e74706172616d657465727360701b60848301527f00000000000000000000000000000000000000447e69651d841bd8d104bed493169063b9f368749060a401602060405180830381865afa158015612c4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c719190614b53565b9150935093915050565b6000612c8683613298565b8015612c975750612c9783836132cb565b9392505050565b805460009060609060ff1682816008811115612cbc57612cbc613c96565b1480612cf45750845462010000900460ff166008811115612cdf57612cdf613c96565b816008811115612cf157612cf1613c96565b14155b15612d1257505060408051602081019091526000808252915061310a565b6008855462010000900460ff166008811115612d3057612d30613c96565b03612e2e576000846002018054612d4690614438565b90501115612dea57600184600201808054612d6090614438565b80601f0160208091040260200160405190810160405280929190818152602001828054612d8c90614438565b8015612dd95780601f10612dae57610100808354040283529160200191612dd9565b820191906000526020600020905b815481529060010190602001808311612dbc57829003601f168201915b50505050509050925092505061310a565b6000846003018054612dfb90614438565b90501115612e1557600184600301808054612d6090614438565b505060408051602081019091526000808252915061310a565b6001816008811115612e4257612e42613c96565b03612e945784546001850154600160e81b90910460ff1611612e7757505060408051602081019091526000808252915061310a565b600184810154612e8a9087830190613355565b925092505061310a565b6002816008811115612ea857612ea8613c96565b03612f025760018481015415612eda57604051806040016040528060048152602001637472756560e01b815250612e8a565b6040518060400160405280600581526020016466616c736560d81b815250925092505061310a565b6003816008811115612f1657612f16613c96565b1480612f3357506006816008811115612f3157612f31613c96565b145b80612f4f57506007816008811115612f4d57612f4d613c96565b145b80612f6b57506005816008811115612f6957612f69613c96565b145b1561305c57600184015460006006836008811115612f8b57612f8b613c96565b14612f9a576003870154612f9f565b62ffffff5b905060006006846008811115612fb757612fb7613c96565b14612fc6576002880154612fc9565b60005b905080831080612fd857508183115b15612ffc57600060405180602001604052806000815250955095505050505061310a565b600584600881111561301057613010613c96565b0361302d576001613020846134ff565b955095505050505061310a565b600684600881111561304157613041613c96565b03613051576001613020846135ac565b600161302084613695565b600481600881111561307057613070613c96565b036130c7576001840154600286015481128061308f5750600386015481135b156130b15760006040518060200160405280600081525093509350505061310a565b60016130bc82613727565b93509350505061310a565b60405162461bcd60e51b8152602060048201526018602482015277504d503a20556e68616e646c656420506172616d5479706560401b6044820152606401610294565b9250929050565b600081518351148015612c975750508051602091820120825192909101919091201490565b6000808260405160200161314a9190614b70565b604051602081830303815290604052905060008160405160200161316e9190614b96565b60405160208183030381529060405290508051602082016000f092506001600160a01b0383166131d45760405162461bcd60e51b81526020600482015260116024820152701111541313d65351539517d19052531151607a1b6044820152606401610294565b5050919050565b60606102318260016131f7816001600160a01b0384163b614bca565b613799565b600080600061320a86612015565b60405163a47d29cb60e01b8152600481018290529091506001600160a01b0386169063a47d29cb90602401602060405180830381865afa158015613252573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061327691906148e3565b9150836001600160a01b0316826001600160a01b031614925050935093915050565b60006132ab826301ffc9a760e01b6132cb565b801561023157506132c4826001600160e01b03196132cb565b1592915050565b6040516001600160e01b031982166024820152600090819060440160408051601f19818403018152919052602080820180516001600160e01b03166301ffc9a760e01b178152825192935060009283928392909183918a617530fa92503d9150600051905082801561333e575060208210155b801561334a5750600081115b979650505050505050565b81546060906001600160a01b031661337f5760405162461bcd60e51b815260040161029490614bdd565b8254600090613396906001600160a01b03166131db565b9050600080600080602085015160c01c90508087106133c75760405162461bcd60e51b815260040161029490614bdd565b806133d3886001614a64565b03613401576000855190506028860194506008880285015160c01c9350600882026008018103925050613423565b6028850193506008870284015160c01c92506008600188010284015160c01c91505b600061342f8484614acc565b6001600160401b031690506000613447826020614a64565b6001600160401b0381111561345e5761345e613b31565b6040519080825280601f01601f191660200182016040528015613488576020820181803683370190505b5090506020810160005b838110156134ee576000816001600160401b0389166134b2886008614a77565b6134bd906028614a64565b6134c79190614a64565b6134d19190614a64565b8a01518352506020918201916134e79082614a64565b9050613492565b505090815298975050505050505050565b6060600061350e600a80614cee565b6135189084614aa4565b90506000613527600a80614cee565b6135319085614ab8565b9050600061353e83613695565b9050600061354b83613695565b90505b600a8151101561357f57806040516020016135699190614cfa565b604051602081830303815290604052905061354e565b8181604051602001613592929190614d16565b604051602081830303815290604052945050505050919050565b6040805160078082528183019092526060918391600091602082018180368337019050509050602360f81b816000815181106135ea576135ea613ebb565b60200101906001600160f81b031916908160001a90535060065b8015613676576f181899199a1a9b1b9c1cb0b131b232b360811b83600f166010811061363257613632613ebb565b1a60f81b82828151811061364857613648613ebb565b60200101906001600160f81b031916908160001a90535060049290921c9161366f81614d52565b9050613604565b508115612c975760405162461bcd60e51b815260040161029490614b23565b606060006136a2836137bc565b60010190506000816001600160401b038111156136c1576136c1613b31565b6040519080825280601f01601f1916602001820160405280156136eb576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a85049450846136f557509392505050565b6060600082126137465760405180602001604052806000815250613761565b604051806040016040528060018152602001602d60f81b8152505b61377261376d84613892565b613695565b604051602001613783929190614d69565b6040516020818303038152906040529050919050565b60408051603f8301601f19168101909152818152818360208301863c9392505050565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b83106137fb5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6904ee2d6d415b85acef8160201b8310613825576904ee2d6d415b85acef8160201b830492506020015b662386f26fc10000831061384357662386f26fc10000830492506010015b6305f5e100831061385b576305f5e100830492506008015b612710831061386f57612710830492506004015b60648310613881576064830492506002015b600a83106102315760010192915050565b6000808212156138a55781600003610231565b5090565b6000602082840312156138bb57600080fd5b81356001600160e01b031981168114612c9757600080fd5b6001600160a01b03811681146138e857600080fd5b50565b80356138f6816138d3565b919050565b60008083601f84011261390d57600080fd5b5081356001600160401b0381111561392457600080fd5b6020830191508360208260051b850101111561310a57600080fd5b6000806000806060858703121561395557600080fd5b8435613960816138d3565b93506020850135925060408501356001600160401b0381111561398257600080fd5b61398e878288016138fb565b95989497509550505050565b600080604083850312156139ad57600080fd5b82356139b8816138d3565b946020939093013593505050565b60005b838110156139e15781810151838201526020016139c9565b50506000910152565b60008151808452613a028160208601602086016139c6565b601f01601f19169290920160200192915050565b6000608082016080835280875180835260a08501915060a08160051b86010192506020808a0160005b83811015613a6d57609f19888703018552613a5b8683516139ea565b95509382019390820190600101613a3f565b5050505060ff9690961660208401526001600160a01b039485166040840152929093166060909101529392505050565b600080600060608486031215613ab257600080fd5b8335613abd816138d3565b92506020840135613acd816138d3565b929592945050506040919091013590565b60008060008060808587031215613af457600080fd5b8435613aff816138d3565b9350602085013592506040850135613b16816138d3565b91506060850135613b26816138d3565b939692955090935050565b634e487b7160e01b600052604160045260246000fd5b60405160e081016001600160401b0381118282101715613b6957613b69613b31565b60405290565b604080519081016001600160401b0381118282101715613b6957613b69613b31565b604051601f8201601f191681016001600160401b0381118282101715613bb957613bb9613b31565b604052919050565b60006001600160401b03821115613bda57613bda613b31565b50601f01601f191660200190565b600082601f830112613bf957600080fd5b8135613c0c613c0782613bc1565b613b91565b818152846020838601011115613c2157600080fd5b816020850160208301376000918101602001919091529392505050565b600080600060608486031215613c5357600080fd5b8335613c5e816138d3565b92506020840135915060408401356001600160401b03811115613c8057600080fd5b613c8c86828701613be8565b9150509250925092565b634e487b7160e01b600052602160045260246000fd5b60078110613cbc57613cbc613c96565b9052565b60098110613cbc57613cbc613c96565b60008282518085526020808601955060208260051b8401016020860160005b84811015613d1d57601f19868403018952613d0b8383516139ea565b98840198925090830190600101613cef565b5090979650505050505050565b60208152613d3e60208201835160ff169052565b60006020830151613d526040840182613cac565b506040830151613d656060840182613cc0565b50606083015165ffffffffffff811660808401525060808301516001600160a01b03811660a08401525060a083015160ff811660c08401525060c08301516101208060e0850152613dba610140850183613cd0565b60e086015161010086810191909152909501519301929092525090919050565b600082825180855260208086019550808260051b84010181860160005b84811015613d1d57601f19868403018952815160408151818652613e1d828701826139ea565b91505085820151915084810386860152613e3781836139ea565b9a86019a9450505090830190600101613df7565b602081526000612c976020830184613dda565b60208152613e70602082018351613cc0565b602082015160408201526000604083015160806060840152613e9560a08401826139ea565b90506060840151601f19848303016080850152613eb282826139ea565b95945050505050565b634e487b7160e01b600052603260045260246000fd5b60008235603e19833603018112613ee757600080fd5b9190910192915050565b6000808335601e19843603018112613f0857600080fd5b8301803591506001600160401b03821115613f2257600080fd5b60200191503681900382131561310a57600080fd5b6000823560de19833603018112613ee757600080fd5b634e487b7160e01b600052601160045260246000fd5b60ff818116838216019081111561023157610231613f4d565b8035600781106138f657600080fd5b8035600981106138f657600080fd5b803565ffffffffffff811681146138f657600080fd5b60006001600160401b03821115613fc957613fc9613b31565b5060051b60200190565b600082601f830112613fe457600080fd5b81356020613ff4613c0783613fb0565b82815260059290921b8401810191818101908684111561401357600080fd5b8286015b848110156140525780356001600160401b038111156140365760008081fd5b6140448986838b0101613be8565b845250918301918301614017565b509695505050505050565b600060e0823603121561406f57600080fd5b614077613b47565b61408083613f7c565b815261408e60208401613f8b565b602082015261409f60408401613f9a565b60408201526140b0606084016138eb565b606082015260808301356001600160401b038111156140ce57600080fd5b6140da36828601613fd3565b60808301525060a083013560a082015260c083013560c082015280915050919050565b6000808335601e1984360301811261411457600080fd5b83016020810192503590506001600160401b0381111561413357600080fd5b80360382131561310a57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6000838385526020808601955060208560051b8301018460005b87811015613d1d57848303601f190189526141a082886140fd565b6141ab858284614142565b9a86019a9450505090830190600101614185565b6001600160a01b038681168252602080830187905260806040808501829052848201879052600093919260a0929183870160058a901b880185018b885b8c81101561433b578a8303609f190184528135368f9003603e1901811261422257600080fd5b8e0161422e81806140fd565b87865261423e8887018284614142565b9150508782013560de1983360301811261425757600080fd5b858203898701529091019060e06142768261427185613f7c565b613cac565b614281898401613f8b565b61428d8a840182613cc0565b5065ffffffffffff6142a0898501613f9a565b16888301526060808401356142b4816138d3565b8b1690830152828c013536849003601e190181126142d157600080fd5b830189810190356001600160401b038111156142ec57600080fd5b8060051b36038213156142fe57600080fd5b828e850152614310838501828461416b565b858e0135858f015260c0958601359590940194909452505094870194935050908501906001016141fc565b505060ff8a1660608a0152965061435495505050505050565b9695505050505050565b60008235609e19833603018112613ee757600080fd5b80151581146138e857600080fd5b600060a0823603121561439457600080fd5b60405160a081016001600160401b0380821183831017156143b7576143b7613b31565b8160405284359150808211156143cc57600080fd5b6143d836838701613be8565b83526143e660208601613f8b565b6020840152604085013560408401526060850135915061440582614374565b816060840152608085013591508082111561441f57600080fd5b5061442c36828601613be8565b60808301525092915050565b600181811c9082168061444c57607f821691505b60208210810361446c57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156112e3576000816000526020600020601f850160051c8101602086101561449b5750805b601f850160051c820191505b818110156144ba578281556001016144a7565b505050505050565b81516001600160401b038111156144db576144db613b31565b6144ef816144e98454614438565b84614472565b602080601f831160018114614524576000841561450c5750858301515b600019600386901b1c1916600185901b1785556144ba565b600085815260208120601f198616915b8281101561455357888601518255948401946001909101908401614534565b50858210156145715787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60018060a01b0384168152826020820152606060408201526000825160a060608401526145b26101008401826139ea565b905060208401516145c66080850182613cc0565b50604084015160a08401526060840151151560c08401526080840151838203605f190160e085015261334a82826139ea565b60008151808452602080850194506020840160005b838110156146325781516001600160a01b03168752958201959082019060010161460d565b509495945050505050565b6001600160a01b038616815260208082018690526080604080840182905283820186905260009260a091828601600589901b870184018a87805b8c811015614725578a8403609f190185528235368f9003609e1901811261469c578283fd5b8e016146a881806140fd565b8a87526146b88b88018284614142565b9150506146c6898301613f8b565b6146d28a880182613cc0565b5087820135888701526060808301356146ea81614374565b1515908701526146fc828c01836140fd565b92508682038c880152614710828483614142565b978a0197965050509287019250600101614677565b505050878103606089015261473a818a6145f8565b9d9c50505050505050505050505050565b60006001820161475d5761475d613f4d565b5060010190565b60018060a01b0384168152826020820152606060408201526000613eb26060830184613dda565b600082601f83011261479c57600080fd5b81516147aa613c0782613bc1565b8181528460208386010111156147bf57600080fd5b6147d08260208301602087016139c6565b949350505050565b600060208083850312156147eb57600080fd5b82516001600160401b038082111561480257600080fd5b818501915085601f83011261481657600080fd5b8151614824613c0782613fb0565b81815260059190911b8301840190848101908883111561484357600080fd5b8585015b838110156148d65780518581111561485f5760008081fd5b86016040818c03601f19018113156148775760008081fd5b61487f613b6f565b89830151888111156148915760008081fd5b61489f8e8c8387010161478b565b8252509082015190878211156148b55760008081fd5b6148c38d8b8486010161478b565b818b015285525050918601918601614847565b5098975050505050505050565b6000602082840312156148f557600080fd5b8151612c97816138d3565b60006020828403121561491257600080fd5b612c9782613f8b565b60006020828403121561492d57600080fd5b612c9782613f7c565b60006020828403121561494857600080fd5b612c9782613f9a565b602080825260169082015275504d503a20496e76616c696420706172616d5479706560501b604082015260600190565b60006020828403121561499357600080fd5b8135612c97816138d3565b6000808335601e198436030181126149b557600080fd5b8301803591506001600160401b038211156149cf57600080fd5b6020019150600581901b360382131561310a57600080fd5b6020808252601f908201527f504d503a2073656c6563744f7074696f6e73206973206e6f7420656d70747900604082015260600190565b602080825260199082015278504d503a206d696e52616e6765203e3d206d617852616e676560381b604082015260600190565b602081526000612c9760208301846139ea565b8082018082111561023157610231613f4d565b808202811582820484141761023157610231613f4d565b634e487b7160e01b600052601260045260246000fd5b600082614ab357614ab3614a8e565b500490565b600082614ac757614ac7614a8e565b500690565b6001600160401b03828116828216039080821115611e2857611e28613f4d565b6020808252601e908201527f504d503a20706172616d2076616c7565206f7574206f6620626f756e64730000604082015260600190565b6020808252601690820152752826a81d1034b73b30b634b2103432bc1031b7b637b960511b604082015260600190565b600060208284031215614b6557600080fd5b8151612c9781614374565b6000815260008251614b898160018501602087016139c6565b9190910160010192915050565b6a600b5981380380925939f360a81b81528151600090614bbd81600b8501602087016139c6565b91909101600b0192915050565b8181038181111561023157610231613f4d565b602080825260139082015272496e646578206f7574206f6620626f756e647360681b604082015260600190565b600181815b80851115614c45578160001904821115614c2b57614c2b613f4d565b80851615614c3857918102915b93841c9390800290614c0f565b509250929050565b600082614c5c57506001610231565b81614c6957506000610231565b8160018114614c7f5760028114614c8957614ca5565b6001915050610231565b60ff841115614c9a57614c9a613f4d565b50506001821b610231565b5060208310610133831016604e8410600b8410161715614cc8575081810a610231565b614cd28383614c0a565b8060001904821115614ce657614ce6613f4d565b029392505050565b6000612c978383614c4d565b600360fc1b815260008251614b898160018501602087016139c6565b60008351614d288184602088016139c6565b601760f91b9083019081528351614d468160018401602088016139c6565b01600101949350505050565b600081614d6157614d61613f4d565b506000190190565b60008351614d7b8184602088016139c6565b835190830190614d8f8183602088016139c6565b0194935050505056fea26469706673582212200bede6c2fd14d9777b1f2a93c2b7e9c38cd1e28475e132481a6b6b474e0a5f9064736f6c63430008160033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000000000000000000447e69651d841bd8d104bed493
-----Decoded View---------------
Arg [0] : delegateRegistry_ (address): 0x00000000000000447e69651d841bD8D104Bed493
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 00000000000000000000000000000000000000447e69651d841bd8d104bed493
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 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.