Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
This contract has been verified via Sourcify.
View contract in Sourcify repository
- Contract name:
- MetadataRenderer
- Optimization enabled
- true
- Compiler version
- v0.8.16+commit.07a7930e
- Optimization runs
- 500000
- EVM Version
- london
- Verified at
- 2026-04-25T23:21:22.495097Z
Constructor Arguments
000000000000000000000000d310a3041dfcf14def5ccbc508668974b5da7174
Arg [0] (address) : 0xd310a3041dfcf14def5ccbc508668974b5da7174
src/token/metadata/MetadataRenderer.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { UriEncode } from "sol-uriencode/src/UriEncode.sol";
import { MetadataBuilder } from "micro-onchain-metadata-utils/MetadataBuilder.sol";
import { MetadataJSONKeys } from "micro-onchain-metadata-utils/MetadataJSONKeys.sol";
import { UUPS } from "../../lib/proxy/UUPS.sol";
import { Initializable } from "../../lib/utils/Initializable.sol";
import { IOwnable } from "../../lib/interfaces/IOwnable.sol";
import { ERC721 } from "../../lib/token/ERC721.sol";
import { MetadataRendererStorageV1 } from "./storage/MetadataRendererStorageV1.sol";
import { MetadataRendererStorageV2 } from "./storage/MetadataRendererStorageV2.sol";
import { IToken } from "../../token/IToken.sol";
import { IPropertyIPFSMetadataRenderer } from "./interfaces/IPropertyIPFSMetadataRenderer.sol";
import { IManager } from "../../manager/IManager.sol";
import { VersionedContract } from "../../VersionedContract.sol";
/// @title Metadata Renderer
/// @author Iain Nash & Rohan Kulkarni
/// @notice A DAO's artwork generator and renderer
/// @custom:repo github.com/ourzora/nouns-protocol
contract MetadataRenderer is
IPropertyIPFSMetadataRenderer,
VersionedContract,
Initializable,
UUPS,
MetadataRendererStorageV1,
MetadataRendererStorageV2
{
/// ///
/// IMMUTABLES ///
/// ///
/// @notice The contract upgrade manager
IManager private immutable manager;
/// ///
/// MODIFIERS ///
/// ///
/// @notice Checks the token owner if the current action is allowed
modifier onlyOwner() {
if (owner() != msg.sender) {
revert IOwnable.ONLY_OWNER();
}
_;
}
/// ///
/// CONSTRUCTOR ///
/// ///
/// @param _manager The contract upgrade manager address
constructor(address _manager) payable initializer {
manager = IManager(_manager);
}
/// ///
/// INITIALIZER ///
/// ///
/// @notice Initializes a DAO's token metadata renderer
/// @param _initStrings The encoded token and metadata initialization strings
/// @param _token The ERC-721 token address
function initialize(bytes calldata _initStrings, address _token) external initializer {
// Ensure the caller is the contract manager
if (msg.sender != address(manager)) {
revert ONLY_MANAGER();
}
// Decode the token initialization strings
(, , string memory _description, string memory _contractImage, string memory _projectURI, string memory _rendererBase) = abi.decode(
_initStrings,
(string, string, string, string, string, string)
);
// Store the renderer settings
settings.projectURI = _projectURI;
settings.description = _description;
settings.contractImage = _contractImage;
settings.rendererBase = _rendererBase;
settings.projectURI = _projectURI;
settings.token = _token;
}
/// ///
/// PROPERTIES & ITEMS ///
/// ///
/// @notice The number of properties
/// @return properties array length
function propertiesCount() external view returns (uint256) {
return properties.length;
}
/// @notice The number of items in a property
/// @param _propertyId The property id
/// @return items array length
function itemsCount(uint256 _propertyId) external view returns (uint256) {
return properties[_propertyId].items.length;
}
/// @notice The number of items in the IPFS data store
/// @return ipfs data array size
function ipfsDataCount() external view returns (uint256) {
return ipfsData.length;
}
/// @notice Updates the additional token properties associated with the metadata.
/// @dev Be careful to not conflict with already used keys such as "name", "description", "properties",
function setAdditionalTokenProperties(AdditionalTokenProperty[] memory _additionalTokenProperties) external onlyOwner {
delete additionalTokenProperties;
for (uint256 i = 0; i < _additionalTokenProperties.length; i++) {
additionalTokenProperties.push(_additionalTokenProperties[i]);
}
emit AdditionalTokenPropertiesSet(_additionalTokenProperties);
}
/// @notice Adds properties and/or items to be pseudo-randomly chosen from during token minting
/// @param _names The names of the properties to add
/// @param _items The items to add to each property
/// @param _ipfsGroup The IPFS base URI and extension
function addProperties(
string[] calldata _names,
ItemParam[] calldata _items,
IPFSGroup calldata _ipfsGroup
) external onlyOwner {
_addProperties(_names, _items, _ipfsGroup);
}
/// @notice Deletes existing properties and/or items to be pseudo-randomly chosen from during token minting, replacing them with provided properties. WARNING: This function can alter or break existing token metadata if the number of properties for this renderer change before/after the upsert. If the properties selected in any tokens do not exist in the new version those token will not render
/// @dev We do not require the number of properties for an reset to match the existing property length, to allow multi-stage property additions (for e.g. when there are more properties than can fit in a single transaction)
/// @param _names The names of the properties to add
/// @param _items The items to add to each property
/// @param _ipfsGroup The IPFS base URI and extension
function deleteAndRecreateProperties(
string[] calldata _names,
ItemParam[] calldata _items,
IPFSGroup calldata _ipfsGroup
) external onlyOwner {
delete ipfsData;
delete properties;
_addProperties(_names, _items, _ipfsGroup);
}
function _addProperties(
string[] calldata _names,
ItemParam[] calldata _items,
IPFSGroup calldata _ipfsGroup
) internal {
// Cache the existing amount of IPFS data stored
uint256 dataLength = ipfsData.length;
// Add the IPFS group information
ipfsData.push(_ipfsGroup);
// Cache the number of existing properties
uint256 numStoredProperties = properties.length;
// Cache the number of new properties
uint256 numNewProperties = _names.length;
// Cache the number of new items
uint256 numNewItems = _items.length;
// If this is the first time adding metadata:
if (numStoredProperties == 0) {
// Ensure at least one property and one item are included
if (numNewProperties == 0 || numNewItems == 0) {
revert ONE_PROPERTY_AND_ITEM_REQUIRED();
}
}
unchecked {
// Check if not too many items are stored
if (numStoredProperties + numNewProperties > 15) {
revert TOO_MANY_PROPERTIES();
}
// For each new property:
for (uint256 i = 0; i < numNewProperties; ++i) {
// Append storage space
properties.push();
// Get the new property id
uint256 propertyId = numStoredProperties + i;
// Store the property name
properties[propertyId].name = _names[i];
emit PropertyAdded(propertyId, _names[i]);
}
// For each new item:
for (uint256 i = 0; i < numNewItems; ++i) {
// Cache the id of the associated property
uint256 _propertyId = _items[i].propertyId;
// Offset the id if the item is for a new property
// Note: Property ids under the hood are offset by 1
if (_items[i].isNewProperty) {
_propertyId += numStoredProperties;
}
// Ensure the item is for a valid property
if (_propertyId >= properties.length) {
revert INVALID_PROPERTY_SELECTED(_propertyId);
}
// Get the pointer to the other items for the property
Item[] storage items = properties[_propertyId].items;
// Append storage space
items.push();
// Get the index of the new item
// Cannot underflow as the items array length is ensured to be at least 1
uint256 newItemIndex = items.length - 1;
// Store the new item
Item storage newItem = items[newItemIndex];
// Store the new item's name and reference slot
newItem.name = _items[i].name;
newItem.referenceSlot = uint16(dataLength);
}
}
}
/// ///
/// ATTRIBUTE GENERATION ///
/// ///
/// @notice Generates attributes for a token upon mint
/// @param _tokenId The ERC-721 token id
function onMinted(uint256 _tokenId) external override returns (bool) {
// Ensure the caller is the token contract
if (msg.sender != settings.token) revert ONLY_TOKEN();
// Compute some randomness for the token id
uint256 seed = _generateSeed(_tokenId);
// Get the pointer to store generated attributes
uint16[16] storage tokenAttributes = attributes[_tokenId];
// Cache the total number of properties available
uint256 numProperties = properties.length;
if (numProperties == 0) {
return false;
}
// Store the total as reference in the first slot of the token's array of attributes
tokenAttributes[0] = uint16(numProperties);
unchecked {
// For each property:
for (uint256 i = 0; i < numProperties; ++i) {
// Get the number of items to choose from
uint256 numItems = properties[i].items.length;
// Use the token's seed to select an item
tokenAttributes[i + 1] = uint16(seed % numItems);
// Adjust the randomness
seed >>= 16;
}
}
return true;
}
/// @notice The properties and query string for a generated token
/// @param _tokenId The ERC-721 token id
function getAttributes(uint256 _tokenId) public view returns (string memory resultAttributes, string memory queryString) {
// Get the token's query string
queryString = string.concat(
"?contractAddress=",
Strings.toHexString(uint256(uint160(address(this))), 20),
"&tokenId=",
Strings.toString(_tokenId)
);
// Get the token's generated attributes
uint16[16] memory tokenAttributes = attributes[_tokenId];
// Cache the number of properties when the token was minted
uint256 numProperties = tokenAttributes[0];
// Ensure the given token was minted
if (numProperties == 0) revert TOKEN_NOT_MINTED(_tokenId);
// Get an array to store the token's generated attribtues
MetadataBuilder.JSONItem[] memory arrayAttributesItems = new MetadataBuilder.JSONItem[](numProperties);
unchecked {
// For each of the token's properties:
for (uint256 i = 0; i < numProperties; ++i) {
// Get its name and list of associated items
Property memory property = properties[i];
// Get the randomly generated index of the item to select for this token
uint256 attribute = tokenAttributes[i + 1];
// Get the associated item data
Item memory item = property.items[attribute];
// Store the encoded attributes and query string
MetadataBuilder.JSONItem memory itemJSON = arrayAttributesItems[i];
itemJSON.key = property.name;
itemJSON.value = item.name;
itemJSON.quote = true;
queryString = string.concat(queryString, "&images=", _getItemImage(item, property.name));
}
resultAttributes = MetadataBuilder.generateJSON(arrayAttributesItems);
}
}
/// @dev Generates a psuedo-random seed for a token id
function _generateSeed(uint256 _tokenId) private view returns (uint256) {
return uint256(keccak256(abi.encode(_tokenId, blockhash(block.number), block.coinbase, block.timestamp)));
}
/// @dev Encodes the reference URI of an item
function _getItemImage(Item memory _item, string memory _propertyName) private view returns (string memory) {
return
UriEncode.uriEncode(
string(
abi.encodePacked(ipfsData[_item.referenceSlot].baseUri, _propertyName, "/", _item.name, ipfsData[_item.referenceSlot].extension)
)
);
}
/// ///
/// URIs ///
/// ///
/// @notice Internal getter function for token name
function _name() internal view returns (string memory) {
return ERC721(settings.token).name();
}
/// @notice The contract URI
function contractURI() external view override returns (string memory) {
MetadataBuilder.JSONItem[] memory items = new MetadataBuilder.JSONItem[](4);
items[0] = MetadataBuilder.JSONItem({ key: MetadataJSONKeys.keyName, value: _name(), quote: true });
items[1] = MetadataBuilder.JSONItem({ key: MetadataJSONKeys.keyDescription, value: settings.description, quote: true });
items[2] = MetadataBuilder.JSONItem({ key: MetadataJSONKeys.keyImage, value: settings.contractImage, quote: true });
items[3] = MetadataBuilder.JSONItem({ key: "external_url", value: settings.projectURI, quote: true });
return MetadataBuilder.generateEncodedJSON(items);
}
/// @notice The token URI
/// @param _tokenId The ERC-721 token id
function tokenURI(uint256 _tokenId) external view returns (string memory) {
(string memory _attributes, string memory queryString) = getAttributes(_tokenId);
MetadataBuilder.JSONItem[] memory items = new MetadataBuilder.JSONItem[](4 + additionalTokenProperties.length);
items[0] = MetadataBuilder.JSONItem({
key: MetadataJSONKeys.keyName,
value: string.concat(_name(), " #", Strings.toString(_tokenId)),
quote: true
});
items[1] = MetadataBuilder.JSONItem({ key: MetadataJSONKeys.keyDescription, value: settings.description, quote: true });
items[2] = MetadataBuilder.JSONItem({
key: MetadataJSONKeys.keyImage,
value: string.concat(settings.rendererBase, queryString),
quote: true
});
items[3] = MetadataBuilder.JSONItem({ key: MetadataJSONKeys.keyProperties, value: _attributes, quote: false });
for (uint256 i = 0; i < additionalTokenProperties.length; i++) {
AdditionalTokenProperty memory tokenProperties = additionalTokenProperties[i];
items[4 + i] = MetadataBuilder.JSONItem({ key: tokenProperties.key, value: tokenProperties.value, quote: tokenProperties.quote });
}
return MetadataBuilder.generateEncodedJSON(items);
}
/// ///
/// METADATA SETTINGS ///
/// ///
/// @notice The associated ERC-721 token
function token() external view returns (address) {
return settings.token;
}
/// @notice The contract image
function contractImage() external view returns (string memory) {
return settings.contractImage;
}
/// @notice The renderer base
function rendererBase() external view returns (string memory) {
return settings.rendererBase;
}
/// @notice The collection description
function description() external view returns (string memory) {
return settings.description;
}
/// @notice The collection description
function projectURI() external view returns (string memory) {
return settings.projectURI;
}
/// @notice Get the owner of the metadata (here delegated to the token owner)
function owner() public view returns (address) {
return IOwnable(settings.token).owner();
}
/// ///
/// UPDATE SETTINGS ///
/// ///
/// @notice Updates the contract image
/// @param _newContractImage The new contract image
function updateContractImage(string memory _newContractImage) external onlyOwner {
emit ContractImageUpdated(settings.contractImage, _newContractImage);
settings.contractImage = _newContractImage;
}
/// @notice Updates the renderer base
/// @param _newRendererBase The new renderer base
function updateRendererBase(string memory _newRendererBase) external onlyOwner {
emit RendererBaseUpdated(settings.rendererBase, _newRendererBase);
settings.rendererBase = _newRendererBase;
}
/// @notice Updates the collection description
/// @param _newDescription The new description
function updateDescription(string memory _newDescription) external onlyOwner {
emit DescriptionUpdated(settings.description, _newDescription);
settings.description = _newDescription;
}
function updateProjectURI(string memory _newProjectURI) external onlyOwner {
emit WebsiteURIUpdated(settings.projectURI, _newProjectURI);
settings.projectURI = _newProjectURI;
}
/// ///
/// METADATA UPGRADE ///
/// ///
/// @notice Ensures the caller is authorized to upgrade the contract to a valid implementation
/// @dev This function is called in UUPS `upgradeTo` & `upgradeToAndCall`
/// @param _impl The address of the new implementation
function _authorizeUpgrade(address _impl) internal view override onlyOwner {
if (!manager.isRegisteredUpgrade(_getImplementation(), _impl)) revert INVALID_UPGRADE(_impl);
}
}
/
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
library MetadataMIMETypes {
string constant mimeJSON = "application/json";
string constant mimeSVG = "image/svg+xml";
string constant mimeTextPlain = "text/plain";
}
/
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
library MetadataJSONKeys {
string constant keyName = "name";
string constant keyDescription = "description";
string constant keyImage = "image";
string constant keyAnimationURL = "animation_url";
string constant keyAttributes = "attributes";
string constant keyProperties = "properties";
}
/MetadataRendererStorageV2.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { MetadataRendererTypesV2 } from "../types/MetadataRendererTypesV2.sol";
/// @title MetadataRendererTypesV1
/// @author Iain Nash & Rohan Kulkarni
/// @notice The Metadata Renderer storage contract
contract MetadataRendererStorageV2 is MetadataRendererTypesV2 {
/// @notice Additional JSON key/value properties for each token.
/// @dev While strings are quoted, JSON needs to be escaped.
AdditionalTokenProperty[] internal additionalTokenProperties;
}
/draft-IERC1822.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
pragma solidity ^0.8.0;
/**
* @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
* proxy whose upgrades are fully controlled by the current implementation.
*/
interface IERC1822Proxiable {
/**
* @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
* address.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy.
*/
function proxiableUUID() external view returns (bytes32);
}
/
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
import {Base64} from "./lib/Base64.sol";
import {Strings} from "./lib/Strings.sol";
import {MetadataMIMETypes} from "./MetadataMIMETypes.sol";
library MetadataBuilder {
struct JSONItem {
string key;
string value;
bool quote;
}
function generateSVG(
string memory contents,
string memory viewBox,
string memory width,
string memory height
) internal pure returns (string memory) {
return
string.concat(
'<svg viewBox="',
viewBox,
'" xmlns="http://www.w3.org/2000/svg" width="',
width,
'" height="',
height,
'">',
contents,
"</svg>"
);
}
/// @notice prefer to use properties with key-value object instead of list
function generateAttributes(string memory displayType, string memory traitType, string memory value) internal pure returns (string memory) {
}
function generateEncodedSVG(
string memory contents,
string memory viewBox,
string memory width,
string memory height
) internal pure returns (string memory) {
return
encodeURI(
MetadataMIMETypes.mimeSVG,
generateSVG(contents, viewBox, width, height)
);
}
function encodeURI(string memory uriType, string memory result)
internal
pure
returns (string memory)
{
return
string.concat(
"data:",
uriType,
";base64,",
string(Base64.encode(bytes(result)))
);
}
function generateJSONArray(JSONItem[] memory items)
internal
pure
returns (string memory result)
{
result = "[";
uint256 added = 0;
for (uint256 i = 0; i < items.length; i++) {
if (bytes(items[i].value).length == 0) {
continue;
}
if (items[i].quote) {
result = string.concat(
result,
added == 0 ? "" : ",",
'"',
items[i].value,
'"'
);
} else {
result = string.concat(
result,
added == 0 ? "" : ",",
items[i].value
);
}
added += 1;
}
result = string.concat(result, "]");
}
function generateJSON(JSONItem[] memory items)
internal
pure
returns (string memory result)
{
result = "{";
uint256 added = 0;
for (uint256 i = 0; i < items.length; i++) {
if (bytes(items[i].value).length == 0) {
continue;
}
if (items[i].quote) {
result = string.concat(
result,
added == 0 ? "" : ",",
'"',
items[i].key,
'": "',
items[i].value,
'"'
);
} else {
result = string.concat(
result,
added == 0 ? "" : ",",
'"',
items[i].key,
'": ',
items[i].value
);
}
added += 1;
}
result = string.concat(result, "}");
}
function generateEncodedJSON(JSONItem[] memory items)
internal
pure
returns (string memory)
{
return encodeURI(MetadataMIMETypes.mimeJSON, generateJSON(items));
}
}
/IPropertyIPFSMetadataRenderer.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { MetadataRendererTypesV1 } from "../types/MetadataRendererTypesV1.sol";
import { MetadataRendererTypesV2 } from "../types/MetadataRendererTypesV2.sol";
import { IBaseMetadata } from "./IBaseMetadata.sol";
/// @title IPropertyIPFSMetadataRenderer
/// @author Iain Nash & Rohan Kulkarni
/// @notice The external Metadata Renderer events, errors, and functions
interface IPropertyIPFSMetadataRenderer is IBaseMetadata, MetadataRendererTypesV1, MetadataRendererTypesV2 {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when a property is added
event PropertyAdded(uint256 id, string name);
/// @notice Additional token properties have been set
event AdditionalTokenPropertiesSet(AdditionalTokenProperty[] _additionalJsonProperties);
/// @notice Emitted when the contract image is updated
event ContractImageUpdated(string prevImage, string newImage);
/// @notice Emitted when the renderer base is updated
event RendererBaseUpdated(string prevRendererBase, string newRendererBase);
/// @notice Emitted when the collection description is updated
event DescriptionUpdated(string prevDescription, string newDescription);
/// @notice Emitted when the collection uri is updated
event WebsiteURIUpdated(string lastURI, string newURI);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if the caller isn't the token contract
error ONLY_TOKEN();
/// @dev Reverts if querying attributes for a token not minted
error TOKEN_NOT_MINTED(uint256 tokenId);
/// @dev Reverts if the founder does not include both a property and item during the initial artwork upload
error ONE_PROPERTY_AND_ITEM_REQUIRED();
/// @dev Reverts if an item is added for a non-existent property
error INVALID_PROPERTY_SELECTED(uint256 selectedPropertyId);
///
error TOO_MANY_PROPERTIES();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice Adds properties and/or items to be pseudo-randomly chosen from during token minting
/// @param names The names of the properties to add
/// @param items The items to add to each property
/// @param ipfsGroup The IPFS base URI and extension
function addProperties(
string[] calldata names,
ItemParam[] calldata items,
IPFSGroup calldata ipfsGroup
) external;
/// @notice The number of properties
function propertiesCount() external view returns (uint256);
/// @notice The number of items in a property
/// @param propertyId The property id
function itemsCount(uint256 propertyId) external view returns (uint256);
/// @notice The properties and query string for a generated token
/// @param tokenId The ERC-721 token id
function getAttributes(uint256 tokenId) external view returns (string memory resultAttributes, string memory queryString);
/// @notice The contract image
function contractImage() external view returns (string memory);
/// @notice The renderer base
function rendererBase() external view returns (string memory);
/// @notice The collection description
function description() external view returns (string memory);
/// @notice Updates the contract image
/// @param newContractImage The new contract image
function updateContractImage(string memory newContractImage) external;
/// @notice Updates the renderer base
/// @param newRendererBase The new renderer base
function updateRendererBase(string memory newRendererBase) external;
/// @notice Updates the collection description
/// @param newDescription The new description
function updateDescription(string memory newDescription) external;
}
/Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
}
/Base64.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Base64.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides a set of functions to operate with Base64 strings.
*
* _Available since v4.5._
*/
library Base64 {
/**
* @dev Base64 Encoding/Decoding Table
*/
string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/**
* @dev Converts a `bytes` to its Bytes64 `string` representation.
*/
function encode(bytes memory data) internal pure returns (string memory) {
/**
* Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
* https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
*/
if (data.length == 0) return "";
// Loads the table into memory
string memory table = _TABLE;
// Encoding takes 3 bytes chunks of binary data from `bytes` data parameter
// and split into 4 numbers of 6 bits.
// The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up
// - `data.length + 2` -> Round up
// - `/ 3` -> Number of 3-bytes chunks
// - `4 *` -> 4 characters for each chunk
string memory result = new string(4 * ((data.length + 2) / 3));
/// @solidity memory-safe-assembly
assembly {
// Prepare the lookup table (skip the first "length" byte)
let tablePtr := add(table, 1)
// Prepare result pointer, jump over length
let resultPtr := add(result, 32)
// Run over the input, 3 bytes at a time
for {
let dataPtr := data
let endPtr := add(data, mload(data))
} lt(dataPtr, endPtr) {
} {
// Advance 3 bytes
dataPtr := add(dataPtr, 3)
let input := mload(dataPtr)
// To write each character, shift the 3 bytes (18 bits) chunk
// 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
// and apply logical AND with 0x3F which is the number of
// the previous character in the ASCII table prior to the Base64 Table
// The result is then added to the table to get the character to write,
// and finally write it in the result pointer but with a left shift
// of 256 (1 byte) - 8 (1 ASCII char) = 248 bits
mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
}
// When data `bytes` is not exactly 3 bytes long
// it is padded with `=` characters at the end
switch mod(mload(data), 3)
case 1 {
mstore8(sub(resultPtr, 1), 0x3d)
mstore8(sub(resultPtr, 2), 0x3d)
}
case 2 {
mstore8(sub(resultPtr, 1), 0x3d)
}
}
return result;
}
}
/StorageSlot.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)
pragma solidity ^0.8.0;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
}
/MetadataRendererStorageV1.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { MetadataRendererTypesV1 } from "../types/MetadataRendererTypesV1.sol";
/// @title MetadataRendererTypesV1
/// @author Iain Nash & Rohan Kulkarni
/// @notice The Metadata Renderer storage contract
contract MetadataRendererStorageV1 is MetadataRendererTypesV1 {
/// @notice The metadata renderer settings
Settings public settings;
/// @notice The properties chosen from upon generation
Property[] public properties;
/// @notice The IPFS data of all property items
IPFSGroup[] public ipfsData;
/// @notice The attributes generated for a token - mapping of tokenID uint16 array
/// @dev Array of size 16 1st element [0] used for number of attributes chosen, next N elements for those selections
/// @dev token ID
mapping(uint256 => uint16[16]) public attributes;
}
/Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
}
/Base64.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Base64.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides a set of functions to operate with Base64 strings.
*
* _Available since v4.5._
*/
library Base64 {
/**
* @dev Base64 Encoding/Decoding Table
*/
string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/**
* @dev Converts a `bytes` to its Bytes64 `string` representation.
*/
function encode(bytes memory data) internal pure returns (string memory) {
/**
* Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
* https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
*/
if (data.length == 0) return "";
// Loads the table into memory
string memory table = _TABLE;
// Encoding takes 3 bytes chunks of binary data from `bytes` data parameter
// and split into 4 numbers of 6 bits.
// The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up
// - `data.length + 2` -> Round up
// - `/ 3` -> Number of 3-bytes chunks
// - `4 *` -> 4 characters for each chunk
string memory result = new string(4 * ((data.length + 2) / 3));
/// @solidity memory-safe-assembly
assembly {
// Prepare the lookup table (skip the first "length" byte)
let tablePtr := add(table, 1)
// Prepare result pointer, jump over length
let resultPtr := add(result, 32)
// Run over the input, 3 bytes at a time
for {
let dataPtr := data
let endPtr := add(data, mload(data))
} lt(dataPtr, endPtr) {
} {
// Advance 3 bytes
dataPtr := add(dataPtr, 3)
let input := mload(dataPtr)
// To write each character, shift the 3 bytes (18 bits) chunk
// 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
// and apply logical AND with 0x3F which is the number of
// the previous character in the ASCII table prior to the Base64 Table
// The result is then added to the table to get the character to write,
// and finally write it in the result pointer but with a left shift
// of 256 (1 byte) - 8 (1 ASCII char) = 248 bits
mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
}
// When data `bytes` is not exactly 3 bytes long
// it is padded with `=` characters at the end
switch mod(mload(data), 3)
case 1 {
mstore8(sub(resultPtr, 1), 0x3d)
mstore8(sub(resultPtr, 2), 0x3d)
}
case 2 {
mstore8(sub(resultPtr, 1), 0x3d)
}
}
return result;
}
}
/MetadataRendererTypesV2.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title MetadataRendererTypesV2
/// @author Iain Nash & Rohan Kulkarni
/// @notice The Metadata Renderer custom data types
interface MetadataRendererTypesV2 {
struct AdditionalTokenProperty {
string key;
string value;
bool quote;
}
}
/MetadataRendererTypesV1.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title MetadataRendererTypesV1
/// @author Iain Nash & Rohan Kulkarni
/// @notice The Metadata Renderer custom data types
interface MetadataRendererTypesV1 {
struct ItemParam {
uint256 propertyId;
string name;
bool isNewProperty;
}
struct IPFSGroup {
string baseUri;
string extension;
}
struct Item {
uint16 referenceSlot;
string name;
}
struct Property {
string name;
Item[] items;
}
struct Settings {
address token;
string projectURI;
string description;
string contractImage;
string rendererBase;
}
}
/IBaseMetadata.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IUUPS } from "../../../lib/interfaces/IUUPS.sol";
/// @title IBaseMetadata
/// @author Rohan Kulkarni
/// @notice The external Base Metadata errors and functions
interface IBaseMetadata is IUUPS {
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if the caller was not the contract manager
error ONLY_MANAGER();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice Initializes a DAO's token metadata renderer
/// @param initStrings The encoded token and metadata initialization strings
/// @param token The associated ERC-721 token address
function initialize(
bytes calldata initStrings,
address token
) external;
/// @notice Generates attributes for a token upon mint
/// @param tokenId The ERC-721 token id
function onMinted(uint256 tokenId) external returns (bool);
/// @notice The token URI
/// @param tokenId The ERC-721 token id
function tokenURI(uint256 tokenId) external view returns (string memory);
/// @notice The contract URI
function contractURI() external view returns (string memory);
/// @notice The associated ERC-721 token
function token() external view returns (address);
/// @notice Get metadata owner address
function owner() external view returns (address);
}
/
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (token/ERC721/utils/ERC721Holder.sol)
abstract contract ERC721TokenReceiver {
function onERC721Received(
address,
address,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return this.onERC721Received.selector;
}
}
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (token/ERC1155/utils/ERC1155Holder.sol)
abstract contract ERC1155TokenReceiver {
function onERC1155Received(
address,
address,
uint256,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return this.onERC1155Received.selector;
}
function onERC1155BatchReceived(
address,
address,
uint256[] calldata,
uint256[] calldata,
bytes calldata
) external virtual returns (bytes4) {
return this.onERC1155BatchReceived.selector;
}
}
/
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
library UriEncode {
string internal constant _TABLE = "0123456789abcdef";
function uriEncode(string memory uri)
internal
pure
returns (string memory)
{
bytes memory bytesUri = bytes(uri);
string memory table = _TABLE;
// Max size is worse case all chars need to be encoded
bytes memory result = new bytes(3 * bytesUri.length);
/// @solidity memory-safe-assembly
assembly {
// Get the lookup table
let tablePtr := add(table, 1)
// Prepare result pointer, jump over length
let resultPtr := add(result, 32)
// Keep track of the final result size string length
let resultSize := 0
for {
let dataPtr := bytesUri
let endPtr := add(bytesUri, mload(bytesUri))
} lt(dataPtr, endPtr) {
} {
// advance 1 byte
dataPtr := add(dataPtr, 1)
// bytemask out a char
let input := and(mload(dataPtr), 255)
// Check if is valid URI character
let isValidUriChar := or(
and(gt(input, 96), lt(input, 134)), // a 97 / z 133
or(
and(gt(input, 64), lt(input, 91)), // A 65 / Z 90
or(
and(gt(input, 47), lt(input, 58)), // 0 48 / 9 57
or(
or(
eq(input, 46), // . 46
eq(input, 95) // _ 95
),
or(
eq(input, 45), // - 45
eq(input, 126) // ~ 126
)
)
)
)
)
switch isValidUriChar
// If is valid uri character copy character over and increment the result
case 1 {
mstore8(resultPtr, input)
resultPtr := add(resultPtr, 1)
resultSize := add(resultSize, 1)
}
// If the char is not a valid uri character, uriencode the character
case 0 {
mstore8(resultPtr, 37)
resultPtr := add(resultPtr, 1)
// table[character >> 4] (take the last 4 bits)
mstore8(resultPtr, mload(add(tablePtr, shr(4, input))))
resultPtr := add(resultPtr, 1)
// table & 15 (take the first 4 bits)
mstore8(resultPtr, mload(add(tablePtr, and(input, 15))))
resultPtr := add(resultPtr, 1)
resultSize := add(resultSize, 3)
}
}
// Set size of result string in memory
mstore(result, resultSize)
}
return string(result);
}
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title IERC1967Upgrade
/// @author Rohan Kulkarni
/// @notice The external ERC1967Upgrade events and errors
interface IERC1967Upgrade {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when the implementation is upgraded
/// @param impl The address of the implementation
event Upgraded(address impl);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if an implementation is an invalid upgrade
/// @param impl The address of the invalid implementation
error INVALID_UPGRADE(address impl);
/// @dev Reverts if an implementation upgrade is not stored at the storage slot of the original
error UNSUPPORTED_UUID();
/// @dev Reverts if an implementation does not support ERC1822 proxiableUUID()
error ONLY_UUPS();
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title IInitializable
/// @author Rohan Kulkarni
/// @notice The external Initializable events and errors
interface IInitializable {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when the contract has been initialized or reinitialized
event Initialized(uint256 version);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if incorrectly initialized with address(0)
error ADDRESS_ZERO();
/// @dev Reverts if disabling initializers during initialization
error INITIALIZING();
/// @dev Reverts if calling an initialization function outside of initialization
error NOT_INITIALIZING();
/// @dev Reverts if reinitializing incorrectly
error ALREADY_INITIALIZED();
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IERC721 } from "./IERC721.sol";
import { IEIP712 } from "./IEIP712.sol";
/// @title IERC721Votes
/// @author Rohan Kulkarni
/// @notice The external ERC721Votes events, errors, and functions
interface IERC721Votes is IERC721, IEIP712 {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when an account changes their delegate
event DelegateChanged(address indexed delegator, address indexed from, address indexed to);
/// @notice Emitted when a delegate's number of votes is updated
event DelegateVotesChanged(address indexed delegate, uint256 prevTotalVotes, uint256 newTotalVotes);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if the timestamp provided isn't in the past
error INVALID_TIMESTAMP();
/// ///
/// STRUCTS ///
/// ///
/// @notice The checkpoint data type
/// @param timestamp The recorded timestamp
/// @param votes The voting weight
struct Checkpoint {
uint64 timestamp;
uint192 votes;
}
/// ///
/// FUNCTIONS ///
/// ///
/// @notice The current number of votes for an account
/// @param account The account address
function getVotes(address account) external view returns (uint256);
/// @notice The number of votes for an account at a past timestamp
/// @param account The account address
/// @param timestamp The past timestamp
function getPastVotes(address account, uint256 timestamp) external view returns (uint256);
/// @notice The delegate for an account
/// @param account The account address
function delegates(address account) external view returns (address);
/// @notice Delegates votes to an account
/// @param to The address delegating votes to
function delegate(address to) external;
/// @notice Delegates votes from a signer to an account
/// @param from The address delegating votes from
/// @param to The address delegating votes to
/// @param deadline The signature deadline
/// @param v The 129th byte and chain id of the signature
/// @param r The first 64 bytes of the signature
/// @param s Bytes 64-128 of the signature
function delegateBySig(
address from,
address to,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title TokenTypesV2
/// @author James Geary
/// @notice The Token custom data types
interface TokenTypesV2 {
struct MinterParams {
address minter;
bool allowed;
}
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IBaseMetadata } from "../metadata/interfaces/IBaseMetadata.sol";
/// @title TokenTypesV1
/// @author Rohan Kulkarni
/// @notice The Token custom data types
interface TokenTypesV1 {
/// @notice The settings type
/// @param auction The DAO auction house
/// @param totalSupply The number of active tokens
/// @param numFounders The number of vesting recipients
/// @param metadatarenderer The token metadata renderer
/// @param mintCount The number of minted tokens
/// @param totalPercentage The total percentage owned by founders
struct Settings {
address auction;
uint88 totalSupply;
uint8 numFounders;
IBaseMetadata metadataRenderer;
uint88 mintCount;
uint8 totalOwnership;
}
/// @notice The founder type
/// @param wallet The address where tokens are sent
/// @param ownershipPct The percentage of token ownership
/// @param vestExpiry The timestamp when vesting ends
struct Founder {
address wallet;
uint8 ownershipPct;
uint32 vestExpiry;
}
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IERC1822Proxiable } from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import { StorageSlot } from "@openzeppelin/contracts/utils/StorageSlot.sol";
import { IERC1967Upgrade } from "../interfaces/IERC1967Upgrade.sol";
import { Address } from "../utils/Address.sol";
/// @title ERC1967Upgrade
/// @author Rohan Kulkarni
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (proxy/ERC1967/ERC1967Upgrade.sol)
/// - Uses custom errors declared in IERC1967Upgrade
/// - Removes ERC1967 admin and beacon support
abstract contract ERC1967Upgrade is IERC1967Upgrade {
/// ///
/// CONSTANTS ///
/// ///
/// @dev bytes32(uint256(keccak256('eip1967.proxy.rollback')) - 1)
bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
/// @dev bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/// ///
/// FUNCTIONS ///
/// ///
/// @dev Upgrades to an implementation with security checks for UUPS proxies and an additional function call
/// @param _newImpl The new implementation address
/// @param _data The encoded function call
function _upgradeToAndCallUUPS(
address _newImpl,
bytes memory _data,
bool _forceCall
) internal {
if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
_setImplementation(_newImpl);
} else {
try IERC1822Proxiable(_newImpl).proxiableUUID() returns (bytes32 slot) {
if (slot != _IMPLEMENTATION_SLOT) revert UNSUPPORTED_UUID();
} catch {
revert ONLY_UUPS();
}
_upgradeToAndCall(_newImpl, _data, _forceCall);
}
}
/// @dev Upgrades to an implementation with an additional function call
/// @param _newImpl The new implementation address
/// @param _data The encoded function call
function _upgradeToAndCall(
address _newImpl,
bytes memory _data,
bool _forceCall
) internal {
_upgradeTo(_newImpl);
if (_data.length > 0 || _forceCall) {
Address.functionDelegateCall(_newImpl, _data);
}
}
/// @dev Performs an implementation upgrade
/// @param _newImpl The new implementation address
function _upgradeTo(address _newImpl) internal {
_setImplementation(_newImpl);
emit Upgraded(_newImpl);
}
/// @dev Stores the address of an implementation
/// @param _impl The implementation address
function _setImplementation(address _impl) private {
if (!Address.isContract(_impl)) revert INVALID_UPGRADE(_impl);
StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = _impl;
}
/// @dev The address of the current implementation
function _getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
}
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IInitializable } from "../interfaces/IInitializable.sol";
import { Address } from "../utils/Address.sol";
/// @title Initializable
/// @author Rohan Kulkarni
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (proxy/utils/Initializable.sol)
/// - Uses custom errors declared in IInitializable
abstract contract Initializable is IInitializable {
/// ///
/// STORAGE ///
/// ///
/// @dev Indicates the contract has been initialized
uint8 internal _initialized;
/// @dev Indicates the contract is being initialized
bool internal _initializing;
/// ///
/// MODIFIERS ///
/// ///
/// @dev Ensures an initialization function is only called within an `initializer` or `reinitializer` function
modifier onlyInitializing() {
if (!_initializing) revert NOT_INITIALIZING();
_;
}
/// @dev Enables initializing upgradeable contracts
modifier initializer() {
bool isTopLevelCall = !_initializing;
if ((!isTopLevelCall || _initialized != 0) && (Address.isContract(address(this)) || _initialized != 1)) revert ALREADY_INITIALIZED();
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/// @dev Enables initializer versioning
/// @param _version The version to set
modifier reinitializer(uint8 _version) {
if (_initializing || _initialized >= _version) revert ALREADY_INITIALIZED();
_initialized = _version;
_initializing = true;
_;
_initializing = false;
emit Initialized(_version);
}
/// ///
/// FUNCTIONS ///
/// ///
/// @dev Prevents future initialization
function _disableInitializers() internal virtual {
if (_initializing) revert INITIALIZING();
if (_initialized < type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title IOwnable
/// @author Rohan Kulkarni
/// @notice The external Ownable events, errors, and functions
interface IOwnable {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when ownership has been updated
/// @param prevOwner The previous owner address
/// @param newOwner The new owner address
event OwnerUpdated(address indexed prevOwner, address indexed newOwner);
/// @notice Emitted when an ownership transfer is pending
/// @param owner The current owner address
/// @param pendingOwner The pending new owner address
event OwnerPending(address indexed owner, address indexed pendingOwner);
/// @notice Emitted when a pending ownership transfer has been canceled
/// @param owner The current owner address
/// @param canceledOwner The canceled owner address
event OwnerCanceled(address indexed owner, address indexed canceledOwner);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if an unauthorized user calls an owner function
error ONLY_OWNER();
/// @dev Reverts if an unauthorized user calls a pending owner function
error ONLY_PENDING_OWNER();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice The address of the owner
function owner() external view returns (address);
/// @notice The address of the pending owner
function pendingOwner() external view returns (address);
/// @notice Forces an ownership transfer
/// @param newOwner The new owner address
function transferOwnership(address newOwner) external;
/// @notice Initiates a two-step ownership transfer
/// @param newOwner The new owner address
function safeTransferOwnership(address newOwner) external;
/// @notice Accepts an ownership transfer
function acceptOwnership() external;
/// @notice Cancels a pending ownership transfer
function cancelOwnershipTransfer() external;
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title IERC721
/// @author Rohan Kulkarni
/// @notice The external ERC721 events, errors, and functions
interface IERC721 {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when a token is transferred from sender to recipient
/// @param from The sender address
/// @param to The recipient address
/// @param tokenId The ERC-721 token id
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/// @notice Emitted when an owner approves an account to manage a token
/// @param owner The owner address
/// @param approved The account address
/// @param tokenId The ERC-721 token id
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/// @notice Emitted when an owner sets an approval for a spender to manage all tokens
/// @param owner The owner address
/// @param operator The spender address
/// @param approved If the approval is being set or removed
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if a caller is not authorized to approve or transfer a token
error INVALID_APPROVAL();
/// @dev Reverts if a transfer is called with the incorrect token owner
error INVALID_OWNER();
/// @dev Reverts if a transfer is attempted to address(0)
error INVALID_RECIPIENT();
/// @dev Reverts if an existing token is called to be minted
error ALREADY_MINTED();
/// @dev Reverts if a non-existent token is called to be burned
error NOT_MINTED();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice The number of tokens owned
/// @param owner The owner address
function balanceOf(address owner) external view returns (uint256);
/// @notice The owner of a token
/// @param tokenId The ERC-721 token id
function ownerOf(uint256 tokenId) external view returns (address);
/// @notice The account approved to manage a token
/// @param tokenId The ERC-721 token id
function getApproved(uint256 tokenId) external view returns (address);
/// @notice If an operator is authorized to manage all of an owner's tokens
/// @param owner The owner address
/// @param operator The operator address
function isApprovedForAll(address owner, address operator) external view returns (bool);
/// @notice Authorizes an account to manage a token
/// @param to The account address
/// @param tokenId The ERC-721 token id
function approve(address to, uint256 tokenId) external;
/// @notice Authorizes an account to manage all tokens
/// @param operator The account address
/// @param approved If permission is being given or removed
function setApprovalForAll(address operator, bool approved) external;
/// @notice Safe transfers a token from sender to recipient with additional data
/// @param from The sender address
/// @param to The recipient address
/// @param tokenId The ERC-721 token id
/// @param data The additional data sent in the call to the recipient
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
/// @notice Safe transfers a token from sender to recipient
/// @param from The sender address
/// @param to The recipient address
/// @param tokenId The ERC-721 token id
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
/// @notice Transfers a token from sender to recipient
/// @param from The sender address
/// @param to The recipient address
/// @param tokenId The ERC-721 token id
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title IEIP712
/// @author Rohan Kulkarni
/// @notice The external EIP712 errors and functions
interface IEIP712 {
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if the deadline has passed to submit a signature
error EXPIRED_SIGNATURE();
/// @dev Reverts if the recovered signature is invalid
error INVALID_SIGNATURE();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice The sig nonce for an account
/// @param account The account address
function nonce(address account) external view returns (uint256);
/// @notice The EIP-712 domain separator
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
/
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
import { IERC1822Proxiable } from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import { IERC1967Upgrade } from "./IERC1967Upgrade.sol";
/// @title IUUPS
/// @author Rohan Kulkarni
/// @notice The external UUPS errors and functions
interface IUUPS is IERC1967Upgrade, IERC1822Proxiable {
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if not called directly
error ONLY_CALL();
/// @dev Reverts if not called via delegatecall
error ONLY_DELEGATECALL();
/// @dev Reverts if not called via proxy
error ONLY_PROXY();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice Upgrades to an implementation
/// @param newImpl The new implementation address
function upgradeTo(address newImpl) external;
/// @notice Upgrades to an implementation with an additional function call
/// @param newImpl The new implementation address
/// @param data The encoded function call
function upgradeToAndCall(address newImpl, bytes memory data) external payable;
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title EIP712
/// @author Rohan Kulkarni
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (utils/Address.sol)
/// - Uses custom errors `INVALID_TARGET()` & `DELEGATE_CALL_FAILED()`
/// - Adds util converting address to bytes32
library Address {
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if the target of a delegatecall is not a contract
error INVALID_TARGET();
/// @dev Reverts if a delegatecall has failed
error DELEGATE_CALL_FAILED();
/// ///
/// FUNCTIONS ///
/// ///
/// @dev Utility to convert an address to bytes32
function toBytes32(address _account) internal pure returns (bytes32) {
return bytes32(uint256(uint160(_account)) << 96);
}
/// @dev If an address is a contract
function isContract(address _account) internal view returns (bool rv) {
assembly {
rv := gt(extcodesize(_account), 0)
}
}
/// @dev Performs a delegatecall on an address
function functionDelegateCall(address _target, bytes memory _data) internal returns (bytes memory) {
if (!isContract(_target)) revert INVALID_TARGET();
(bool success, bytes memory returndata) = _target.delegatecall(_data);
return verifyCallResult(success, returndata);
}
/// @dev Verifies a delegatecall was successful
function verifyCallResult(bool _success, bytes memory _returndata) internal pure returns (bytes memory) {
if (_success) {
return _returndata;
} else {
if (_returndata.length > 0) {
assembly {
let returndata_size := mload(_returndata)
revert(add(32, _returndata), returndata_size)
}
} else {
revert DELEGATE_CALL_FAILED();
}
}
}
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
abstract contract VersionedContract {
function contractVersion() external pure returns (string memory) {
return "1.2.0";
}
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IUUPS } from "../lib/interfaces/IUUPS.sol";
import { IOwnable } from "../lib/interfaces/IOwnable.sol";
/// @title IManager
/// @author Rohan Kulkarni
/// @notice The external Manager events, errors, structs and functions
interface IManager is IUUPS, IOwnable {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when a DAO is deployed
/// @param token The ERC-721 token address
/// @param metadata The metadata renderer address
/// @param auction The auction address
/// @param treasury The treasury address
/// @param governor The governor address
event DAODeployed(address token, address metadata, address auction, address treasury, address governor);
/// @notice Emitted when an upgrade is registered by the Builder DAO
/// @param baseImpl The base implementation address
/// @param upgradeImpl The upgrade implementation address
event UpgradeRegistered(address baseImpl, address upgradeImpl);
/// @notice Emitted when an upgrade is unregistered by the Builder DAO
/// @param baseImpl The base implementation address
/// @param upgradeImpl The upgrade implementation address
event UpgradeRemoved(address baseImpl, address upgradeImpl);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if at least one founder is not provided upon deploy
error FOUNDER_REQUIRED();
/// ///
/// STRUCTS ///
/// ///
/// @notice The founder parameters
/// @param wallet The wallet address
/// @param ownershipPct The percent ownership of the token
/// @param vestExpiry The timestamp that vesting expires
struct FounderParams {
address wallet;
uint256 ownershipPct;
uint256 vestExpiry;
}
/// @notice DAO Version Information information struct
struct DAOVersionInfo {
string token;
string metadata;
string auction;
string treasury;
string governor;
}
/// @notice The ERC-721 token parameters
/// @param initStrings The encoded token name, symbol, collection description, collection image uri, renderer base uri
struct TokenParams {
bytes initStrings;
}
/// @notice The auction parameters
/// @param reservePrice The reserve price of each auction
/// @param duration The duration of each auction
struct AuctionParams {
uint256 reservePrice;
uint256 duration;
}
/// @notice The governance parameters
/// @param timelockDelay The time delay to execute a queued transaction
/// @param votingDelay The time delay to vote on a created proposal
/// @param votingPeriod The time period to vote on a proposal
/// @param proposalThresholdBps The basis points of the token supply required to create a proposal
/// @param quorumThresholdBps The basis points of the token supply required to reach quorum
/// @param vetoer The address authorized to veto proposals (address(0) if none desired)
struct GovParams {
uint256 timelockDelay;
uint256 votingDelay;
uint256 votingPeriod;
uint256 proposalThresholdBps;
uint256 quorumThresholdBps;
address vetoer;
}
/// ///
/// FUNCTIONS ///
/// ///
/// @notice The token implementation address
function tokenImpl() external view returns (address);
/// @notice The metadata renderer implementation address
function metadataImpl() external view returns (address);
/// @notice The auction house implementation address
function auctionImpl() external view returns (address);
/// @notice The treasury implementation address
function treasuryImpl() external view returns (address);
/// @notice The governor implementation address
function governorImpl() external view returns (address);
/// @notice Deploys a DAO with custom token, auction, and governance settings
/// @param founderParams The DAO founder(s)
/// @param tokenParams The ERC-721 token settings
/// @param auctionParams The auction settings
/// @param govParams The governance settings
function deploy(
FounderParams[] calldata founderParams,
TokenParams calldata tokenParams,
AuctionParams calldata auctionParams,
GovParams calldata govParams
)
external
returns (
address token,
address metadataRenderer,
address auction,
address treasury,
address governor
);
/// @notice A DAO's remaining contract addresses from its token address
/// @param token The ERC-721 token address
function getAddresses(address token)
external
returns (
address metadataRenderer,
address auction,
address treasury,
address governor
);
/// @notice If an implementation is registered by the Builder DAO as an optional upgrade
/// @param baseImpl The base implementation address
/// @param upgradeImpl The upgrade implementation address
function isRegisteredUpgrade(address baseImpl, address upgradeImpl) external view returns (bool);
/// @notice Called by the Builder DAO to offer opt-in implementation upgrades for all other DAOs
/// @param baseImpl The base implementation address
/// @param upgradeImpl The upgrade implementation address
function registerUpgrade(address baseImpl, address upgradeImpl) external;
/// @notice Called by the Builder DAO to remove an upgrade
/// @param baseImpl The base implementation address
/// @param upgradeImpl The upgrade implementation address
function removeUpgrade(address baseImpl, address upgradeImpl) external;
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IERC721 } from "../interfaces/IERC721.sol";
import { Initializable } from "../utils/Initializable.sol";
import { ERC721TokenReceiver } from "../utils/TokenReceiver.sol";
import { Address } from "../utils/Address.sol";
/// @title ERC721
/// @author Rohan Kulkarni
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (token/ERC721/ERC721Upgradeable.sol)
/// - Uses custom errors declared in IERC721
abstract contract ERC721 is IERC721, Initializable {
/// ///
/// STORAGE ///
/// ///
/// @notice The token name
string public name;
/// @notice The token symbol
string public symbol;
/// @notice The token owners
/// @dev ERC-721 token id => Owner
mapping(uint256 => address) internal owners;
/// @notice The owner balances
/// @dev Owner => Balance
mapping(address => uint256) internal balances;
/// @notice The token approvals
/// @dev ERC-721 token id => Manager
mapping(uint256 => address) internal tokenApprovals;
/// @notice The balance approvals
/// @dev Owner => Operator => Approved
mapping(address => mapping(address => bool)) internal operatorApprovals;
/// ///
/// FUNCTIONS ///
/// ///
/// @dev Initializes an ERC-721 token
/// @param _name The ERC-721 token name
/// @param _symbol The ERC-721 token symbol
function __ERC721_init(string memory _name, string memory _symbol) internal onlyInitializing {
name = _name;
symbol = _symbol;
}
/// @notice The token URI
/// @param _tokenId The ERC-721 token id
function tokenURI(uint256 _tokenId) public view virtual returns (string memory) {}
/// @notice The contract URI
function contractURI() public view virtual returns (string memory) {}
/// @notice If the contract implements an interface
/// @param _interfaceId The interface id
function supportsInterface(bytes4 _interfaceId) external pure returns (bool) {
return
_interfaceId == 0x01ffc9a7 || // ERC165 Interface ID
_interfaceId == 0x80ac58cd || // ERC721 Interface ID
_interfaceId == 0x5b5e139f; // ERC721Metadata Interface ID
}
/// @notice The account approved to manage a token
/// @param _tokenId The ERC-721 token id
function getApproved(uint256 _tokenId) external view returns (address) {
return tokenApprovals[_tokenId];
}
/// @notice If an operator is authorized to manage all of an owner's tokens
/// @param _owner The owner address
/// @param _operator The operator address
function isApprovedForAll(address _owner, address _operator) external view returns (bool) {
return operatorApprovals[_owner][_operator];
}
/// @notice The number of tokens owned
/// @param _owner The owner address
function balanceOf(address _owner) public view returns (uint256) {
if (_owner == address(0)) revert ADDRESS_ZERO();
return balances[_owner];
}
/// @notice The owner of a token
/// @param _tokenId The ERC-721 token id
function ownerOf(uint256 _tokenId) public view returns (address) {
address owner = owners[_tokenId];
if (owner == address(0)) revert INVALID_OWNER();
return owner;
}
/// @notice Authorizes an account to manage a token
/// @param _to The account address
/// @param _tokenId The ERC-721 token id
function approve(address _to, uint256 _tokenId) external {
address owner = owners[_tokenId];
if (msg.sender != owner && !operatorApprovals[owner][msg.sender]) revert INVALID_APPROVAL();
tokenApprovals[_tokenId] = _to;
emit Approval(owner, _to, _tokenId);
}
/// @notice Authorizes an account to manage all tokens
/// @param _operator The account address
/// @param _approved If permission is being given or removed
function setApprovalForAll(address _operator, bool _approved) external {
operatorApprovals[msg.sender][_operator] = _approved;
emit ApprovalForAll(msg.sender, _operator, _approved);
}
/// @notice Transfers a token from sender to recipient
/// @param _from The sender address
/// @param _to The recipient address
/// @param _tokenId The ERC-721 token id
function transferFrom(
address _from,
address _to,
uint256 _tokenId
) public {
if (_from != owners[_tokenId]) revert INVALID_OWNER();
if (_to == address(0)) revert ADDRESS_ZERO();
if (msg.sender != _from && !operatorApprovals[_from][msg.sender] && msg.sender != tokenApprovals[_tokenId]) revert INVALID_APPROVAL();
_beforeTokenTransfer(_from, _to, _tokenId);
unchecked {
--balances[_from];
++balances[_to];
}
owners[_tokenId] = _to;
delete tokenApprovals[_tokenId];
emit Transfer(_from, _to, _tokenId);
_afterTokenTransfer(_from, _to, _tokenId);
}
/// @notice Safe transfers a token from sender to recipient
/// @param _from The sender address
/// @param _to The recipient address
/// @param _tokenId The ERC-721 token id
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId
) external {
transferFrom(_from, _to, _tokenId);
if (
Address.isContract(_to) &&
ERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, "") != ERC721TokenReceiver.onERC721Received.selector
) revert INVALID_RECIPIENT();
}
/// @notice Safe transfers a token from sender to recipient with additional data
/// @param _from The sender address
/// @param _to The recipient address
/// @param _tokenId The ERC-721 token id
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId,
bytes calldata _data
) external {
transferFrom(_from, _to, _tokenId);
if (
Address.isContract(_to) &&
ERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data) != ERC721TokenReceiver.onERC721Received.selector
) revert INVALID_RECIPIENT();
}
/// @dev Mints a token to a recipient
/// @param _to The recipient address
/// @param _tokenId The ERC-721 token id
function _mint(address _to, uint256 _tokenId) internal virtual {
if (_to == address(0)) revert ADDRESS_ZERO();
if (owners[_tokenId] != address(0)) revert ALREADY_MINTED();
_beforeTokenTransfer(address(0), _to, _tokenId);
unchecked {
++balances[_to];
}
owners[_tokenId] = _to;
emit Transfer(address(0), _to, _tokenId);
_afterTokenTransfer(address(0), _to, _tokenId);
}
/// @dev Burns a token to a recipient
/// @param _tokenId The ERC-721 token id
function _burn(uint256 _tokenId) internal virtual {
address owner = owners[_tokenId];
if (owner == address(0)) revert NOT_MINTED();
_beforeTokenTransfer(owner, address(0), _tokenId);
unchecked {
--balances[owner];
}
delete owners[_tokenId];
delete tokenApprovals[_tokenId];
emit Transfer(owner, address(0), _tokenId);
_afterTokenTransfer(owner, address(0), _tokenId);
}
/// @dev Hook called before a token transfer
/// @param _from The sender address
/// @param _to The recipient address
/// @param _tokenId The ERC-721 token id
function _beforeTokenTransfer(
address _from,
address _to,
uint256 _tokenId
) internal virtual {}
/// @dev Hook called after a token transfer
/// @param _from The sender address
/// @param _to The recipient address
/// @param _tokenId The ERC-721 token id
function _afterTokenTransfer(
address _from,
address _to,
uint256 _tokenId
) internal virtual {}
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IUUPS } from "../interfaces/IUUPS.sol";
import { ERC1967Upgrade } from "./ERC1967Upgrade.sol";
/// @title UUPS
/// @author Rohan Kulkarni
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (proxy/utils/UUPSUpgradeable.sol)
/// - Uses custom errors declared in IUUPS
/// - Inherits a modern, minimal ERC1967Upgrade
abstract contract UUPS is IUUPS, ERC1967Upgrade {
/// ///
/// IMMUTABLES ///
/// ///
/// @dev The address of the implementation
address private immutable __self = address(this);
/// ///
/// MODIFIERS ///
/// ///
/// @dev Ensures that execution is via proxy delegatecall with the correct implementation
modifier onlyProxy() {
if (address(this) == __self) revert ONLY_DELEGATECALL();
if (_getImplementation() != __self) revert ONLY_PROXY();
_;
}
/// @dev Ensures that execution is via direct call
modifier notDelegated() {
if (address(this) != __self) revert ONLY_CALL();
_;
}
/// ///
/// FUNCTIONS ///
/// ///
/// @dev Hook to authorize an implementation upgrade
/// @param _newImpl The new implementation address
function _authorizeUpgrade(address _newImpl) internal virtual;
/// @notice Upgrades to an implementation
/// @param _newImpl The new implementation address
function upgradeTo(address _newImpl) external onlyProxy {
_authorizeUpgrade(_newImpl);
_upgradeToAndCallUUPS(_newImpl, "", false);
}
/// @notice Upgrades to an implementation with an additional function call
/// @param _newImpl The new implementation address
/// @param _data The encoded function call
function upgradeToAndCall(address _newImpl, bytes memory _data) external payable onlyProxy {
_authorizeUpgrade(_newImpl);
_upgradeToAndCallUUPS(_newImpl, _data, true);
}
/// @notice The storage slot of the implementation address
function proxiableUUID() external view notDelegated returns (bytes32) {
return _IMPLEMENTATION_SLOT;
}
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IUUPS } from "../lib/interfaces/IUUPS.sol";
import { IERC721Votes } from "../lib/interfaces/IERC721Votes.sol";
import { IManager } from "../manager/IManager.sol";
import { TokenTypesV1 } from "./types/TokenTypesV1.sol";
import { TokenTypesV2 } from "./types/TokenTypesV2.sol";
/// @title IToken
/// @author Rohan Kulkarni
/// @notice The external Token events, errors and functions
interface IToken is IUUPS, IERC721Votes, TokenTypesV1, TokenTypesV2 {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when a token is scheduled to be allocated
/// @param baseTokenId The
/// @param founderId The founder's id
/// @param founder The founder's vesting details
event MintScheduled(uint256 baseTokenId, uint256 founderId, Founder founder);
/// @notice Emitted when a token allocation is unscheduled (removed)
/// @param baseTokenId The token ID % 100
/// @param founderId The founder's id
/// @param founder The founder's vesting details
event MintUnscheduled(uint256 baseTokenId, uint256 founderId, Founder founder);
/// @notice Emitted when a tokens founders are deleted from storage
/// @param newFounders the list of founders
event FounderAllocationsCleared(IManager.FounderParams[] newFounders);
/// @notice Emitted when minters are updated
/// @param minter Address of added or removed minter
/// @param allowed Whether address is allowed to mint
event MinterUpdated(address minter, bool allowed);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if the founder ownership exceeds 100 percent
error INVALID_FOUNDER_OWNERSHIP();
/// @dev Reverts if the caller was not the auction contract
error ONLY_AUCTION();
/// @dev Reverts if the caller was not a minter
error ONLY_AUCTION_OR_MINTER();
/// @dev Reverts if the caller was not the token owner
error ONLY_TOKEN_OWNER();
/// @dev Reverts if no metadata was generated upon mint
error NO_METADATA_GENERATED();
/// @dev Reverts if the caller was not the contract manager
error ONLY_MANAGER();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice Initializes a DAO's ERC-721 token
/// @param founders The founding members to receive vesting allocations
/// @param initStrings The encoded token and metadata initialization strings
/// @param metadataRenderer The token's metadata renderer
/// @param auction The token's auction house
function initialize(
IManager.FounderParams[] calldata founders,
bytes calldata initStrings,
address metadataRenderer,
address auction,
address initialOwner
) external;
/// @notice Mints tokens to the caller and handles founder vesting
function mint() external returns (uint256 tokenId);
/// @notice Mints tokens to the recipient and handles founder vesting
function mintTo(address recipient) external returns (uint256 tokenId);
/// @notice Mints the specified amount of tokens to the recipient and handles founder vesting
function mintBatchTo(uint256 amount, address recipient) external returns (uint256[] memory tokenIds);
/// @notice Burns a token owned by the caller
/// @param tokenId The ERC-721 token id
function burn(uint256 tokenId) external;
/// @notice The URI for a token
/// @param tokenId The ERC-721 token id
function tokenURI(uint256 tokenId) external view returns (string memory);
/// @notice The URI for the contract
function contractURI() external view returns (string memory);
/// @notice The number of founders
function totalFounders() external view returns (uint256);
/// @notice The founders total percent ownership
function totalFounderOwnership() external view returns (uint256);
/// @notice The vesting details of a founder
/// @param founderId The founder id
function getFounder(uint256 founderId) external view returns (Founder memory);
/// @notice The vesting details of all founders
function getFounders() external view returns (Founder[] memory);
/// @notice Update the list of allocation owners
/// @param newFounders the full list of FounderParam structs
function updateFounders(IManager.FounderParams[] calldata newFounders) external;
/// @notice The founder scheduled to receive the given token id
/// NOTE: If a founder is returned, there's no guarantee they'll receive the token as vesting expiration is not considered
/// @param tokenId The ERC-721 token id
function getScheduledRecipient(uint256 tokenId) external view returns (Founder memory);
/// @notice The total supply of tokens
function totalSupply() external view returns (uint256);
/// @notice The token's auction house
function auction() external view returns (address);
/// @notice The token's metadata renderer
function metadataRenderer() external view returns (address);
/// @notice The owner of the token and metadata renderer
function owner() external view returns (address);
/// @notice Update minters
/// @param _minters Array of structs containing address status as a minter
function updateMinters(MinterParams[] calldata _minters) external;
/// @notice Check if an address is a minter
/// @param _minter Address to check
function isMinter(address _minter) external view returns (bool);
/// @notice Callback called by auction on first auction started to transfer ownership to treasury from founder
function onFirstAuctionStarted() external;
}
Compiler Settings
{"remappings":[":@openzeppelin/=node_modules/@openzeppelin/",":ds-test/=node_modules/ds-test/src/",":forge-std/=node_modules/forge-std/src/",":micro-onchain-metadata-utils/=node_modules/micro-onchain-metadata-utils/src/",":sol-uriencode/=node_modules/sol-uriencode/",":sol2string/=node_modules/sol2string/"],"optimizer":{"runs":500000,"enabled":true},"metadata":{"bytecodeHash":"ipfs"},"libraries":{},"evmVersion":"london","compilationTarget":{"src/token/metadata/MetadataRenderer.sol":"MetadataRenderer"}}
Contract ABI
[{"type":"constructor","stateMutability":"payable","inputs":[{"type":"address","name":"_manager","internalType":"address"}]},{"type":"error","name":"ADDRESS_ZERO","inputs":[]},{"type":"error","name":"ALREADY_INITIALIZED","inputs":[]},{"type":"error","name":"DELEGATE_CALL_FAILED","inputs":[]},{"type":"error","name":"INITIALIZING","inputs":[]},{"type":"error","name":"INVALID_PROPERTY_SELECTED","inputs":[{"type":"uint256","name":"selectedPropertyId","internalType":"uint256"}]},{"type":"error","name":"INVALID_TARGET","inputs":[]},{"type":"error","name":"INVALID_UPGRADE","inputs":[{"type":"address","name":"impl","internalType":"address"}]},{"type":"error","name":"NOT_INITIALIZING","inputs":[]},{"type":"error","name":"ONE_PROPERTY_AND_ITEM_REQUIRED","inputs":[]},{"type":"error","name":"ONLY_CALL","inputs":[]},{"type":"error","name":"ONLY_DELEGATECALL","inputs":[]},{"type":"error","name":"ONLY_MANAGER","inputs":[]},{"type":"error","name":"ONLY_OWNER","inputs":[]},{"type":"error","name":"ONLY_PROXY","inputs":[]},{"type":"error","name":"ONLY_TOKEN","inputs":[]},{"type":"error","name":"ONLY_UUPS","inputs":[]},{"type":"error","name":"TOKEN_NOT_MINTED","inputs":[{"type":"uint256","name":"tokenId","internalType":"uint256"}]},{"type":"error","name":"TOO_MANY_PROPERTIES","inputs":[]},{"type":"error","name":"UNSUPPORTED_UUID","inputs":[]},{"type":"event","name":"AdditionalTokenPropertiesSet","inputs":[{"type":"tuple[]","name":"_additionalJsonProperties","internalType":"struct MetadataRendererTypesV2.AdditionalTokenProperty[]","indexed":false,"components":[{"type":"string","name":"key","internalType":"string"},{"type":"string","name":"value","internalType":"string"},{"type":"bool","name":"quote","internalType":"bool"}]}],"anonymous":false},{"type":"event","name":"ContractImageUpdated","inputs":[{"type":"string","name":"prevImage","internalType":"string","indexed":false},{"type":"string","name":"newImage","internalType":"string","indexed":false}],"anonymous":false},{"type":"event","name":"DescriptionUpdated","inputs":[{"type":"string","name":"prevDescription","internalType":"string","indexed":false},{"type":"string","name":"newDescription","internalType":"string","indexed":false}],"anonymous":false},{"type":"event","name":"Initialized","inputs":[{"type":"uint256","name":"version","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"PropertyAdded","inputs":[{"type":"uint256","name":"id","internalType":"uint256","indexed":false},{"type":"string","name":"name","internalType":"string","indexed":false}],"anonymous":false},{"type":"event","name":"RendererBaseUpdated","inputs":[{"type":"string","name":"prevRendererBase","internalType":"string","indexed":false},{"type":"string","name":"newRendererBase","internalType":"string","indexed":false}],"anonymous":false},{"type":"event","name":"Upgraded","inputs":[{"type":"address","name":"impl","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"WebsiteURIUpdated","inputs":[{"type":"string","name":"lastURI","internalType":"string","indexed":false},{"type":"string","name":"newURI","internalType":"string","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addProperties","inputs":[{"type":"string[]","name":"_names","internalType":"string[]"},{"type":"tuple[]","name":"_items","internalType":"struct MetadataRendererTypesV1.ItemParam[]","components":[{"type":"uint256","name":"propertyId","internalType":"uint256"},{"type":"string","name":"name","internalType":"string"},{"type":"bool","name":"isNewProperty","internalType":"bool"}]},{"type":"tuple","name":"_ipfsGroup","internalType":"struct MetadataRendererTypesV1.IPFSGroup","components":[{"type":"string","name":"baseUri","internalType":"string"},{"type":"string","name":"extension","internalType":"string"}]}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint16","name":"","internalType":"uint16"}],"name":"attributes","inputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"contractImage","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"contractURI","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"contractVersion","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"deleteAndRecreateProperties","inputs":[{"type":"string[]","name":"_names","internalType":"string[]"},{"type":"tuple[]","name":"_items","internalType":"struct MetadataRendererTypesV1.ItemParam[]","components":[{"type":"uint256","name":"propertyId","internalType":"uint256"},{"type":"string","name":"name","internalType":"string"},{"type":"bool","name":"isNewProperty","internalType":"bool"}]},{"type":"tuple","name":"_ipfsGroup","internalType":"struct MetadataRendererTypesV1.IPFSGroup","components":[{"type":"string","name":"baseUri","internalType":"string"},{"type":"string","name":"extension","internalType":"string"}]}]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"description","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"resultAttributes","internalType":"string"},{"type":"string","name":"queryString","internalType":"string"}],"name":"getAttributes","inputs":[{"type":"uint256","name":"_tokenId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialize","inputs":[{"type":"bytes","name":"_initStrings","internalType":"bytes"},{"type":"address","name":"_token","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"baseUri","internalType":"string"},{"type":"string","name":"extension","internalType":"string"}],"name":"ipfsData","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"ipfsDataCount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"itemsCount","inputs":[{"type":"uint256","name":"_propertyId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"onMinted","inputs":[{"type":"uint256","name":"_tokenId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"projectURI","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"name","internalType":"string"}],"name":"properties","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"propertiesCount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"proxiableUUID","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"rendererBase","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setAdditionalTokenProperties","inputs":[{"type":"tuple[]","name":"_additionalTokenProperties","internalType":"struct MetadataRendererTypesV2.AdditionalTokenProperty[]","components":[{"type":"string","name":"key","internalType":"string"},{"type":"string","name":"value","internalType":"string"},{"type":"bool","name":"quote","internalType":"bool"}]}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"token","internalType":"address"},{"type":"string","name":"projectURI","internalType":"string"},{"type":"string","name":"description","internalType":"string"},{"type":"string","name":"contractImage","internalType":"string"},{"type":"string","name":"rendererBase","internalType":"string"}],"name":"settings","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"token","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"tokenURI","inputs":[{"type":"uint256","name":"_tokenId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateContractImage","inputs":[{"type":"string","name":"_newContractImage","internalType":"string"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateDescription","inputs":[{"type":"string","name":"_newDescription","internalType":"string"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateProjectURI","inputs":[{"type":"string","name":"_newProjectURI","internalType":"string"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateRendererBase","inputs":[{"type":"string","name":"_newRendererBase","internalType":"string"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"upgradeTo","inputs":[{"type":"address","name":"_newImpl","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"upgradeToAndCall","inputs":[{"type":"address","name":"_newImpl","internalType":"address"},{"type":"bytes","name":"_data","internalType":"bytes"}]}]
Contract Creation Code
0x60c060408190523060805262004f533881900390819083398101604081905262000029916200011c565b600054610100900460ff161580158062000047575060005460ff1615155b801562000077575062000065306200011660201b6200245a1760201c565b8062000077575060005460ff16600114155b15620000965760405163439a74c960e01b815260040160405180910390fd5b6000805460ff191660011790558015620000ba576000805461ff0019166101001790555b6001600160a01b03821660a05280156200010e576000805461ff0019169055604051600181527fbe9b076dc5b65990cca9dd9d7366682482e7817a6f6bc7f4faf4dc32af497f329060200160405180910390a15b50506200014e565b3b151590565b6000602082840312156200012f57600080fd5b81516001600160a01b03811681146200014757600080fd5b9392505050565b60805160a051614dbc620001976000396000818161199601526127ef015260008181610a7a01528181610ad401528181610ff20152818161104c015261113b0152614dbc6000f3fe6080604052600436106101c25760003560e01c80638da5cb5b116100f7578063cce2df0311610095578063eaf1cdf311610064578063eaf1cdf314610517578063ecb12ca814610537578063f7b1080814610557578063fc0c546a1461057757600080fd5b8063cce2df031461049c578063e06174e4146104bc578063e735b48a146104e2578063e8a3d4851461050257600080fd5b8063a802047c116100d1578063a802047c14610432578063b2846b8114610452578063c042f64714610467578063c87b56dd1461047c57600080fd5b80638da5cb5b1461039d5780638eab84ee146103d7578063a0a8e460146103ec57600080fd5b80633659cfe61161016457806352d1902d1161013e57806352d1902d146103315780636e6fb49f146103465780637284e4161461036857806381341fd21461037d57600080fd5b80633659cfe6146102de5780634378a6e3146102fe5780634f1ef2861461031e57600080fd5b8063228b5679116101a0578063228b56791461022d57806325b4e7be146102605780632658fd0a146102905780632b313ab8146102be57600080fd5b80630af1ec7a146101c75780631117b207146101e957806311447e8714610209575b600080fd5b3480156101d357600080fd5b506101e76101e2366004613a0a565b6105a2565b005b3480156101f557600080fd5b506101e7610204366004613a8b565b610643565b34801561021557600080fd5b506006545b6040519081526020015b60405180910390f35b34801561023957600080fd5b5061024d610248366004613b26565b6106c5565b60405161ffff9091168152602001610224565b34801561026c57600080fd5b5061028061027b366004613b48565b610702565b6040519015158152602001610224565b34801561029c57600080fd5b506102b06102ab366004613b48565b610882565b604051610224929190613bcf565b3480156102ca57600080fd5b506101e76102d9366004613a0a565b6109c6565b3480156102ea57600080fd5b506101e76102f9366004613c16565b610a63565b34801561030a57600080fd5b506102b0610319366004613b48565b610bbb565b6101e761032c366004613c33565b610fdb565b34801561033d57600080fd5b5061021a611121565b34801561035257600080fd5b5061035b6111b7565b6040516102249190613c97565b34801561037457600080fd5b5061035b61124b565b34801561038957600080fd5b5061021a610398366004613b48565b61125d565b3480156103a957600080fd5b506103b261128c565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610224565b3480156103e357600080fd5b5061035b611325565b3480156103f857600080fd5b5060408051808201909152600581527f312e322e30000000000000000000000000000000000000000000000000000000602082015261035b565b34801561043e57600080fd5b506101e761044d366004613a8b565b611337565b34801561045e57600080fd5b5061035b61138d565b34801561047357600080fd5b5060075461021a565b34801561048857600080fd5b5061035b610497366004613b48565b61139f565b3480156104a857600080fd5b506101e76104b7366004613caa565b6118b0565b3480156104c857600080fd5b506104d1611afb565b604051610224959493929190613d2d565b3480156104ee57600080fd5b506101e76104fd366004613a0a565b611d53565b34801561050e57600080fd5b5061035b611df0565b34801561052357600080fd5b506101e7610532366004613a0a565b6121b7565b34801561054357600080fd5b506101e7610552366004613db0565b612254565b34801561056357600080fd5b5061035b610572366004613b48565b6123a4565b34801561058357600080fd5b5060015473ffffffffffffffffffffffffffffffffffffffff166103b2565b336105ab61128c565b73ffffffffffffffffffffffffffffffffffffffff16146105f8576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fa4a51e61fd2a8836d039388e309e7301de08bb26931de504cded9cf6e5a90a0e9061062b906004908490613f54565b60405180910390a1600461063f8282614056565b5050565b3361064c61128c565b73ffffffffffffffffffffffffffffffffffffffff1614610699576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6106a5600760006136f7565b6106b160066000613718565b6106be8585858585612460565b5050505050565b600860205281600052604060002081601081106106e157600080fd5b60109182820401919006600202915091509054906101000a900461ffff1681565b60015460009073ffffffffffffffffffffffffffffffffffffffff163314610756576040517f3ff0b6ce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516020808201859052434082840152416060830152426080808401919091528351808403909101815260a090920183528151918101919091206000858152600890925291812060065490918190036107b657506000949350505050565b81547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001661ffff821617825560005b818110156108765760006006828154811061080257610802614170565b90600052602060002090600202016001018054905090508085816108285761082861419f565b0684836001016010811061083e5761083e614170565b601091828204019190066002026101000a81548161ffff021916908361ffff160217905550601085901c9450508060010190506107e5565b50600195945050505050565b6007818154811061089257600080fd5b90600052602060002090600202016000915090508060000180546108b590613f01565b80601f01602080910402602001604051908101604052809291908181526020018280546108e190613f01565b801561092e5780601f106109035761010080835404028352916020019161092e565b820191906000526020600020905b81548152906001019060200180831161091157829003601f168201915b50505050509080600101805461094390613f01565b80601f016020809104026020016040519081016040528092919081815260200182805461096f90613f01565b80156109bc5780601f10610991576101008083540402835291602001916109bc565b820191906000526020600020905b81548152906001019060200180831161099f57829003601f168201915b5050505050905082565b336109cf61128c565b73ffffffffffffffffffffffffffffffffffffffff1614610a1c576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fcc0881496fc7df5b2e39876ca077298fecc92707f462d28e9fc8f14161b3107390610a4f906002908490613f54565b60405180910390a1600261063f8282614056565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163003610ad2576040517f43d22ee900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16610b477f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614610b94576040517fe74d90a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610b9d81612797565b610bb88160405180602001604052806000815250600061294a565b50565b606080610bc9306014612aa2565b610bd284612cee565b604051602001610be39291906141ce565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00181526000868152600860205282812061020085019384905291945092916010908285855b82829054906101000a900461ffff1661ffff1681526020019060020190602082600101049283019260010382029150808411610c3057905050505050509050600081600060108110610c8457610c84614170565b602002015161ffff16905080600003610cd1576040517f531c95b1000000000000000000000000000000000000000000000000000000008152600481018690526024015b60405180910390fd5b60008167ffffffffffffffff811115610cec57610cec6138bf565b604051908082528060200260200182016040528015610d3957816020015b60408051606080820183528082526020820152600091810191909152815260200190600190039081610d0a5790505b50905060005b82811015610fc757600060068281548110610d5c57610d5c614170565b9060005260206000209060020201604051806040016040529081600082018054610d8590613f01565b80601f0160208091040260200160405190810160405280929190818152602001828054610db190613f01565b8015610dfe5780601f10610dd357610100808354040283529160200191610dfe565b820191906000526020600020905b815481529060010190602001808311610de157829003601f168201915b5050505050815260200160018201805480602002602001604051908101604052809291908181526020016000905b82821015610f005760008481526020908190206040805180820190915260028502909101805461ffff1682526001810180549293919291840191610e6f90613f01565b80601f0160208091040260200160405190810160405280929190818152602001828054610e9b90613f01565b8015610ee85780601f10610ebd57610100808354040283529160200191610ee8565b820191906000526020600020905b815481529060010190602001808311610ecb57829003601f168201915b50505050508152505081526020019060010190610e2c565b505050508152505090506000858360010160108110610f2157610f21614170565b602002015161ffff169050600082602001518281518110610f4457610f44614170565b602002602001015190506000858581518110610f6257610f62614170565b6020908102919091018101518551815283820151918101919091526001604082015284519091508990610f96908490612e2b565b604051602001610fa792919061424f565b604051602081830303815290604052985050505050806001019050610d3f565b50610fd181612eb5565b9450505050915091565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016300361104a576040517f43d22ee900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166110bf7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff161461110c576040517fe74d90a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61111582612797565b61063f8282600161294a565b60003073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611192576040517f575bc92e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90565b60606001800180546111c890613f01565b80601f01602080910402602001604051908101604052809291908181526020018280546111f490613f01565b80156112415780601f1061121657610100808354040283529160200191611241565b820191906000526020600020905b81548152906001019060200180831161122457829003601f168201915b5050505050905090565b6060600160020180546111c890613f01565b60006006828154811061127257611272614170565b600091825260209091206001600290920201015492915050565b600154604080517f8da5cb5b000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff1691638da5cb5b9160048083019260209291908290030181865afa1580156112fc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061132091906142a7565b905090565b6060600160030180546111c890613f01565b3361134061128c565b73ffffffffffffffffffffffffffffffffffffffff16146106b1576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060600160040180546111c890613f01565b60606000806113ad84610bbb565b60095491935091506000906113c39060046142f3565b67ffffffffffffffff8111156113db576113db6138bf565b60405190808252806020026020018201604052801561142857816020015b604080516060808201835280825260208201526000918101919091528152602001906001900390816113f95790505b506040805160a081019091526004606082019081527f6e616d650000000000000000000000000000000000000000000000000000000060808301528152909150602081016114746130f5565b61147d88612cee565b60405160200161148e929190614306565b604051602081830303815290604052815260200160011515815250816000815181106114bc576114bc614170565b602002602001018190525060405180606001604052806040518060400160405280600b81526020017f6465736372697074696f6e00000000000000000000000000000000000000000081525081526020016001600201805461151d90613f01565b80601f016020809104026020016040519081016040528092919081815260200182805461154990613f01565b80156115965780601f1061156b57610100808354040283529160200191611596565b820191906000526020600020905b81548152906001019060200180831161157957829003601f168201915b5050505050815260200160011515815250816001815181106115ba576115ba614170565b602002602001018190525060405180606001604052806040518060400160405280600581526020017f696d61676500000000000000000000000000000000000000000000000000000081525081526020016001600401846040516020016116229291906143ef565b6040516020818303038152906040528152602001600115158152508160028151811061165057611650614170565b602002602001018190525060405180606001604052806040518060400160405280600a81526020017f70726f7065727469657300000000000000000000000000000000000000000000815250815260200184815260200160001515815250816003815181106116c1576116c1614170565b602002602001018190525060005b60095481101561189d576000600982815481106116ee576116ee614170565b906000526020600020906003020160405180606001604052908160008201805461171790613f01565b80601f016020809104026020016040519081016040528092919081815260200182805461174390613f01565b80156117905780601f1061176557610100808354040283529160200191611790565b820191906000526020600020905b81548152906001019060200180831161177357829003601f168201915b505050505081526020016001820180546117a990613f01565b80601f01602080910402602001604051908101604052809291908181526020018280546117d590613f01565b80156118225780601f106117f757610100808354040283529160200191611822565b820191906000526020600020905b81548152906001019060200180831161180557829003601f168201915b50505091835250506002919091015460ff16151560209182015260408051606081018252835181528383015192810192909252808301511515908201529091508361186e8460046142f3565b8151811061187e5761187e614170565b602002602001018190525050808061189590614414565b9150506116cf565b506118a7816131ab565b95945050505050565b600054610100900460ff16158015806118cd575060005460ff1615155b80156118e95750303b1515806118e9575060005460ff16600114155b15611920576040517f439a74c900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055801561197e57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146119ed576040517fa2ddd97100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008080806119fe8789018961444c565b9298509096509450925060029150611a1890508382614056565b506003611a258582614056565b506004611a328482614056565b506005611a3f8282614056565b506002611a4c8382614056565b5050600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8716179055505081159050611af557600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527fbe9b076dc5b65990cca9dd9d7366682482e7817a6f6bc7f4faf4dc32af497f329060200160405180910390a15b50505050565b600180546002805473ffffffffffffffffffffffffffffffffffffffff9092169291611b2690613f01565b80601f0160208091040260200160405190810160405280929190818152602001828054611b5290613f01565b8015611b9f5780601f10611b7457610100808354040283529160200191611b9f565b820191906000526020600020905b815481529060010190602001808311611b8257829003601f168201915b505050505090806002018054611bb490613f01565b80601f0160208091040260200160405190810160405280929190818152602001828054611be090613f01565b8015611c2d5780601f10611c0257610100808354040283529160200191611c2d565b820191906000526020600020905b815481529060010190602001808311611c1057829003601f168201915b505050505090806003018054611c4290613f01565b80601f0160208091040260200160405190810160405280929190818152602001828054611c6e90613f01565b8015611cbb5780601f10611c9057610100808354040283529160200191611cbb565b820191906000526020600020905b815481529060010190602001808311611c9e57829003601f168201915b505050505090806004018054611cd090613f01565b80601f0160208091040260200160405190810160405280929190818152602001828054611cfc90613f01565b8015611d495780601f10611d1e57610100808354040283529160200191611d49565b820191906000526020600020905b815481529060010190602001808311611d2c57829003601f168201915b5050505050905085565b33611d5c61128c565b73ffffffffffffffffffffffffffffffffffffffff1614611da9576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fe21432e1fe2b572d5803dd7316b7a854952317b42017f920a616ec70cdb8a5c190611ddc906003908490613f54565b60405180910390a1600361063f8282614056565b60408051600480825260a0820190925260609160009190816020015b60408051606080820183528082526020820152600091810191909152815260200190600190039081611e0c5750506040805160a081019091526004606082019081527f6e616d65000000000000000000000000000000000000000000000000000000006080830152815290915060208101611e856130f5565b81526020016001151581525081600081518110611ea457611ea4614170565b602002602001018190525060405180606001604052806040518060400160405280600b81526020017f6465736372697074696f6e000000000000000000000000000000000000000000815250815260200160016002018054611f0590613f01565b80601f0160208091040260200160405190810160405280929190818152602001828054611f3190613f01565b8015611f7e5780601f10611f5357610100808354040283529160200191611f7e565b820191906000526020600020905b815481529060010190602001808311611f6157829003601f168201915b505050505081526020016001151581525081600181518110611fa257611fa2614170565b602002602001018190525060405180606001604052806040518060400160405280600581526020017f696d61676500000000000000000000000000000000000000000000000000000081525081526020016001600301805461200390613f01565b80601f016020809104026020016040519081016040528092919081815260200182805461202f90613f01565b801561207c5780601f106120515761010080835404028352916020019161207c565b820191906000526020600020905b81548152906001019060200180831161205f57829003601f168201915b5050505050815260200160011515815250816002815181106120a0576120a0614170565b602002602001018190525060405180606001604052806040518060400160405280600c81526020017f65787465726e616c5f75726c0000000000000000000000000000000000000000815250815260200160018001805461210090613f01565b80601f016020809104026020016040519081016040528092919081815260200182805461212c90613f01565b80156121795780601f1061214e57610100808354040283529160200191612179565b820191906000526020600020905b81548152906001019060200180831161215c57829003601f168201915b50505050508152602001600115158152508160038151811061219d5761219d614170565b60200260200101819052506121b1816131ab565b91505090565b336121c061128c565b73ffffffffffffffffffffffffffffffffffffffff161461220d576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fc5d098cca4f56032ca7e04c4bf7fccc90f26576acaa949a5f67bc9eba4ef414990612240906005908490613f54565b60405180910390a1600561063f8282614056565b3361225d61128c565b73ffffffffffffffffffffffffffffffffffffffff16146122aa576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6122b660096000613739565b60005b81518110156123695760098282815181106122d6576122d6614170565b602090810291909101810151825460018101845560009384529190922082516003909202019081906123089082614056565b506020820151600182019061231d9082614056565b5060409190910151600290910180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169115159190911790558061236181614414565b9150506122b9565b507fbf5f7405282e2c6b004f386e14b57c8a744e1a3e7f221b9af8031b911f6db59a816040516123999190614541565b60405180910390a150565b600681815481106123b457600080fd5b90600052602060002090600202016000915090508060000180546123d790613f01565b80601f016020809104026020016040519081016040528092919081815260200182805461240390613f01565b80156124505780601f1061242557610100808354040283529160200191612450565b820191906000526020600020905b81548152906001019060200180831161243357829003601f168201915b5050505050905081565b3b151590565b6007805460018101825560009190915281600282027fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688016124a18282614771565b5050600654858460008390036124f3578115806124bc575080155b156124f3576040517f279ec20e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600f8284011115612530576040517f8c90470900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b82811015612606576006805460010181556000528381018a8a8381811061255c5761255c614170565b905060200281019061256e91906145f2565b6006838154811061258157612581614170565b9060005260206000209060020201600001918261259f929190614657565b507febf63270254042d3527778299325aabb702431e95ad5e9055f4cb3b047ecdccc818c8c858181106125d4576125d4614170565b90506020028101906125e691906145f2565b6040516125f5939291906148ae565b60405180910390a150600101612533565b5060005b8181101561278b57600088888381811061262657612626614170565b90506020028101906126389190614902565b35905088888381811061264d5761264d614170565b905060200281019061265f9190614902565b612670906060810190604001614940565b156126785784015b60065481106126b6576040517f0dfb793000000000000000000000000000000000000000000000000000000000815260048101829052602401610cc8565b6000600682815481106126cb576126cb614170565b60009182526020822060016002909202018101805491820180825581845290935090919083908390811061270157612701614170565b906000526020600020906002020190508b8b8681811061272357612723614170565b90506020028101906127359190614902565b6127439060208101906145f2565b6001830191612753919083614657565b5080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001661ffff8a1617905550505060010161260a565b50505050505050505050565b336127a061128c565b73ffffffffffffffffffffffffffffffffffffffff16146127ed576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16639bb8dcfd6128677f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff91821660048201529084166024820152604401602060405180830381865afa1580156128d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128fc919061495d565b610bb8576040517fc40d973400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610cc8565b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff16156129825761297d836131f4565b505050565b8273ffffffffffffffffffffffffffffffffffffffff166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015612a07575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252612a049181019061497a565b60015b612a3d576040517fc0bb20b200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8114612a96576040517f0849b49600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5061297d8383836132aa565b60606000612ab1836002614993565b612abc9060026142f3565b67ffffffffffffffff811115612ad457612ad46138bf565b6040519080825280601f01601f191660200182016040528015612afe576020820181803683370190505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110612b3557612b35614170565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110612b9857612b98614170565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506000612bd4846002614993565b612bdf9060016142f3565b90505b6001811115612c7c577f303132333435363738396162636465660000000000000000000000000000000085600f1660108110612c2057612c20614170565b1a60f81b828281518110612c3657612c36614170565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049490941c93612c75816149d0565b9050612be2565b508315612ce5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610cc8565b90505b92915050565b606081600003612d3157505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b8115612d5b5780612d4581614414565b9150612d549050600a83614a05565b9150612d35565b60008167ffffffffffffffff811115612d7657612d766138bf565b6040519080825280601f01601f191660200182016040528015612da0576020820181803683370190505b5090505b8415612e2357612db5600183614a19565b9150612dc2600a86614a2c565b612dcd9060306142f3565b60f81b818381518110612de257612de2614170565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350612e1c600a86614a05565b9450612da4565b949350505050565b6060612ce56007846000015161ffff1681548110612e4b57612e4b614170565b90600052602060002090600202016000018385602001516007876000015161ffff1681548110612e7d57612e7d614170565b9060005260206000209060020201600101604051602001612ea19493929190614a40565b6040516020818303038152906040526132cf565b60408051808201909152600181527f7b0000000000000000000000000000000000000000000000000000000000000060208201526000805b83518110156130cc57838181518110612f0857612f08614170565b60200260200101516020015151600003156130ba57838181518110612f2f57612f2f614170565b60200260200101516040015115612ff857828215612f82576040518060400160405280600181526020017f2c00000000000000000000000000000000000000000000000000000000000000815250612f93565b604051806020016040528060008152505b858381518110612fa557612fa5614170565b602002602001015160000151868481518110612fc357612fc3614170565b602002602001015160200151604051602001612fe29493929190614aa4565b60405160208183030381529060405292506130ac565b82821561303a576040518060400160405280600181526020017f2c0000000000000000000000000000000000000000000000000000000000000081525061304b565b604051806020016040528060008152505b85838151811061305d5761305d614170565b60200260200101516000015186848151811061307b5761307b614170565b60200260200101516020015160405160200161309a9493929190614b59565b60405160208183030381529060405292505b6130b76001836142f3565b91505b806130c481614414565b915050612eed565b50816040516020016130de9190614c04565b604051602081830303815290604052915050919050565b600154604080517f06fdde03000000000000000000000000000000000000000000000000000000008152905160609273ffffffffffffffffffffffffffffffffffffffff16916306fdde039160048083019260009291908290030181865afa158015613165573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526113209190810190614c45565b6060612ce86040518060400160405280601081526020017f6170706c69636174696f6e2f6a736f6e000000000000000000000000000000008152506131ef84612eb5565b61341f565b803b613244576040517fc40d973400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610cc8565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6132b383613453565b6000825111806132c05750805b1561297d57611af583836134a2565b60408051808201909152601081527f3031323334353637383961626364656600000000000000000000000000000000602082015281516060918391600090613318906003614993565b67ffffffffffffffff811115613330576133306138bf565b6040519080825280601f01601f19166020018201604052801561335a576020820181803683370190505b5090506001820160208201600085865187015b808210156134105760018201915060ff825116607e8114602d821417605f8214602e83141717603a8210602f83111617605b82106040831116176086821060608311161780600181146133c55780156133d957613408565b828753600187019650600186019550613408565b602587536001870196508260041c8801518753600187019650600f831688015187536001870196506003860195505b50505061336d565b50508352509095945050505050565b60608261342b83613553565b60405160200161343c929190614cb3565b604051602081830303815290604052905092915050565b61345c816131f4565b60405173ffffffffffffffffffffffffffffffffffffffff821681527fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90602001612399565b6060823b6134dc576040517f37f2022900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808473ffffffffffffffffffffffffffffffffffffffff16846040516135049190614d34565b600060405180830381855af49150503d806000811461353f576040519150601f19603f3d011682016040523d82523d6000602084013e613544565b606091505b50915091506118a782826136a6565b6060815160000361357257505060408051602081019091526000815290565b6000604051806060016040528060408152602001614d4760409139905060006003845160026135a191906142f3565b6135ab9190614a05565b6135b6906004614993565b67ffffffffffffffff8111156135ce576135ce6138bf565b6040519080825280601f01601f1916602001820160405280156135f8576020820181803683370190505b509050600182016020820185865187015b80821015613664576003820191508151603f8160121c168501518453600184019350603f81600c1c168501518453600184019350603f8160061c168501518453600184019350603f8116850151845350600183019250613609565b505060038651066001811461368057600281146136935761369b565b603d6001830353603d600283035361369b565b603d60018303535b509195945050505050565b606082156136b5575080612ce8565b8151156136c55781518083602001fd5b6040517f62536b1000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5080546000825560020290600052602060002090810190610bb8919061375a565b5080546000825560020290600052602060002090810190610bb89190613789565b5080546000825560030290600052602060002090810190610bb891906137b4565b8082111561378557600061376e8282613809565b61377c600183016000613809565b5060020161375a565b5090565b8082111561378557600061379d8282613809565b6137ab600183016000613843565b50600201613789565b808211156137855760006137c88282613809565b6137d6600183016000613809565b506002810180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690556003016137b4565b50805461381590613f01565b6000825580601f10613825575050565b601f016020900490600052602060002090810190610bb89190613864565b5080546000825560020290600052602060002090810190610bb89190613879565b5b808211156137855760008155600101613865565b808211156137855780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016815560006138b66001830182613809565b50600201613879565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516060810167ffffffffffffffff81118282101715613911576139116138bf565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561395e5761395e6138bf565b604052919050565b600067ffffffffffffffff821115613980576139806138bf565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b60006139bf6139ba84613966565b613917565b90508281528383830111156139d357600080fd5b828260208301376000602084830101529392505050565b600082601f8301126139fb57600080fd5b612ce5838335602085016139ac565b600060208284031215613a1c57600080fd5b813567ffffffffffffffff811115613a3357600080fd5b612e23848285016139ea565b60008083601f840112613a5157600080fd5b50813567ffffffffffffffff811115613a6957600080fd5b6020830191508360208260051b8501011115613a8457600080fd5b9250929050565b600080600080600060608688031215613aa357600080fd5b853567ffffffffffffffff80821115613abb57600080fd5b613ac789838a01613a3f565b90975095506020880135915080821115613ae057600080fd5b613aec89838a01613a3f565b90955093506040880135915080821115613b0557600080fd5b50860160408189031215613b1857600080fd5b809150509295509295909350565b60008060408385031215613b3957600080fd5b50508035926020909101359150565b600060208284031215613b5a57600080fd5b5035919050565b60005b83811015613b7c578181015183820152602001613b64565b50506000910152565b60008151808452613b9d816020860160208601613b61565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b604081526000613be26040830185613b85565b82810360208401526118a78185613b85565b73ffffffffffffffffffffffffffffffffffffffff81168114610bb857600080fd5b600060208284031215613c2857600080fd5b8135612ce581613bf4565b60008060408385031215613c4657600080fd5b8235613c5181613bf4565b9150602083013567ffffffffffffffff811115613c6d57600080fd5b8301601f81018513613c7e57600080fd5b613c8d858235602084016139ac565b9150509250929050565b602081526000612ce56020830184613b85565b600080600060408486031215613cbf57600080fd5b833567ffffffffffffffff80821115613cd757600080fd5b818601915086601f830112613ceb57600080fd5b813581811115613cfa57600080fd5b876020828501011115613d0c57600080fd5b60209283019550935050840135613d2281613bf4565b809150509250925092565b73ffffffffffffffffffffffffffffffffffffffff8616815260a060208201526000613d5c60a0830187613b85565b8281036040840152613d6e8187613b85565b90508281036060840152613d828186613b85565b90508281036080840152613d968185613b85565b98975050505050505050565b8015158114610bb857600080fd5b60006020808385031215613dc357600080fd5b823567ffffffffffffffff80821115613ddb57600080fd5b818501915085601f830112613def57600080fd5b813581811115613e0157613e016138bf565b8060051b613e10858201613917565b9182528381018501918581019089841115613e2a57600080fd5b86860192505b83831015613ef457823585811115613e485760008081fd5b86016060818c037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001811315613e7e5760008081fd5b613e866138ee565b8983013588811115613e985760008081fd5b613ea68e8c838701016139ea565b82525060408084013589811115613ebd5760008081fd5b613ecb8f8d838801016139ea565b838d0152509282013592613ede84613da2565b8101929092525082529186019190860190613e30565b9998505050505050505050565b600181811c90821680613f1557607f821691505b602082108103613f4e577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b604081526000808454613f6681613f01565b8060408601526060600180841660008114613f885760018114613fc057613ff1565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008516838901528284151560051b8901019550613ff1565b8960005260208060002060005b86811015613fe85781548b8201870152908401908201613fcd565b8a018501975050505b505050505082810360208401526118a78185613b85565b601f82111561297d57600081815260208120601f850160051c8101602086101561402f5750805b601f850160051c820191505b8181101561404e5782815560010161403b565b505050505050565b815167ffffffffffffffff811115614070576140706138bf565b6140848161407e8454613f01565b84614008565b602080601f8311600181146140d757600084156140a15750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b17855561404e565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561412457888601518255948401946001909101908401614105565b508582101561416057878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f3f636f6e7472616374416464726573733d000000000000000000000000000000815260008351614206816011850160208801613b61565b7f26746f6b656e49643d0000000000000000000000000000000000000000000000601191840191820152835161424381601a840160208801613b61565b01601a01949350505050565b60008351614261818460208801613b61565b7f26696d616765733d000000000000000000000000000000000000000000000000908301908152835161429b816008840160208801613b61565b01600801949350505050565b6000602082840312156142b957600080fd5b8151612ce581613bf4565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820180821115612ce857612ce86142c4565b60008351614318818460208801613b61565b7f20230000000000000000000000000000000000000000000000000000000000009083019081528351614352816002840160208801613b61565b01600201949350505050565b6000815461436b81613f01565b6001828116801561438357600181146143b6576143e5565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00841687528215158302870194506143e5565b8560005260208060002060005b858110156143dc5781548a8201529084019082016143c3565b50505082870194505b5050505092915050565b60006143fb828561435e565b835161440b818360208801613b61565b01949350505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614445576144456142c4565b5060010190565b60008060008060008060c0878903121561446557600080fd5b863567ffffffffffffffff8082111561447d57600080fd5b6144898a838b016139ea565b9750602089013591508082111561449f57600080fd5b6144ab8a838b016139ea565b965060408901359150808211156144c157600080fd5b6144cd8a838b016139ea565b955060608901359150808211156144e357600080fd5b6144ef8a838b016139ea565b9450608089013591508082111561450557600080fd5b6145118a838b016139ea565b935060a089013591508082111561452757600080fd5b5061453489828a016139ea565b9150509295509295509295565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b838110156145e4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08984030185528151606081518186526145ac82870182613b85565b915050888201518582038a8701526145c48282613b85565b928901511515958901959095525094870194925090860190600101614568565b509098975050505050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261462757600080fd5b83018035915067ffffffffffffffff82111561464257600080fd5b602001915036819003821315613a8457600080fd5b67ffffffffffffffff83111561466f5761466f6138bf565b6146838361467d8354613f01565b83614008565b6000601f8411600181146146d5576000851561469f5750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b1783556106be565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156147245786850135825560209485019460019092019101614704565b508682101561475f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555050505050565b61477b82836145f2565b67ffffffffffffffff811115614793576147936138bf565b6147a7816147a18554613f01565b85614008565b6000601f8211600181146147f957600083156147c35750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600385901b1c1916600184901b17855561488f565b6000858152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0841690835b828110156148485786850135825560209485019460019092019101614828565b5084821015614883577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88660031b161c19848701351681555b505060018360011b0185555b505050506148a060208301836145f2565b611af5818360018601614657565b83815260406020820152816040820152818360608301376000818301606090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016010192915050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa183360301811261493657600080fd5b9190910192915050565b60006020828403121561495257600080fd5b8135612ce581613da2565b60006020828403121561496f57600080fd5b8151612ce581613da2565b60006020828403121561498c57600080fd5b5051919050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156149cb576149cb6142c4565b500290565b6000816149df576149df6142c4565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b600082614a1457614a1461419f565b500490565b81810381811115612ce857612ce86142c4565b600082614a3b57614a3b61419f565b500690565b6000614a4c828761435e565b8551614a5c818360208a01613b61565b7f2f0000000000000000000000000000000000000000000000000000000000000091019081528451614a95816001840160208901613b61565b613d966001828401018661435e565b60008551614ab6818460208a01613b61565b855190830190614aca818360208a01613b61565b7f220000000000000000000000000000000000000000000000000000000000000091018181528551909190614b06816001850160208a01613b61565b7f223a202200000000000000000000000000000000000000000000000000000000600193909101928301528451614b44816005850160208901613b61565b60059201918201526006019695505050505050565b60008551614b6b818460208a01613b61565b855190830190614b7f818360208a01613b61565b7f220000000000000000000000000000000000000000000000000000000000000091019081528451614bb8816001840160208901613b61565b7f223a200000000000000000000000000000000000000000000000000000000000600192909101918201528351614bf6816004840160208801613b61565b016004019695505050505050565b60008251614c16818460208701613b61565b7f7d00000000000000000000000000000000000000000000000000000000000000920191825250600101919050565b600060208284031215614c5757600080fd5b815167ffffffffffffffff811115614c6e57600080fd5b8201601f81018413614c7f57600080fd5b8051614c8d6139ba82613966565b818152856020838501011115614ca257600080fd5b6118a7826020830160208601613b61565b7f646174613a000000000000000000000000000000000000000000000000000000815260008351614ceb816005850160208801613b61565b7f3b6261736536342c0000000000000000000000000000000000000000000000006005918401918201528351614d2881600d840160208801613b61565b01600d01949350505050565b60008251614936818460208701613b6156fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa264697066735822122021bd4085e21d7ea8312d8a669802ee4a5e946121e844a34dac1876a9474eca5564736f6c63430008100033000000000000000000000000d310a3041dfcf14def5ccbc508668974b5da7174
Deployed ByteCode
0x6080604052600436106101c25760003560e01c80638da5cb5b116100f7578063cce2df0311610095578063eaf1cdf311610064578063eaf1cdf314610517578063ecb12ca814610537578063f7b1080814610557578063fc0c546a1461057757600080fd5b8063cce2df031461049c578063e06174e4146104bc578063e735b48a146104e2578063e8a3d4851461050257600080fd5b8063a802047c116100d1578063a802047c14610432578063b2846b8114610452578063c042f64714610467578063c87b56dd1461047c57600080fd5b80638da5cb5b1461039d5780638eab84ee146103d7578063a0a8e460146103ec57600080fd5b80633659cfe61161016457806352d1902d1161013e57806352d1902d146103315780636e6fb49f146103465780637284e4161461036857806381341fd21461037d57600080fd5b80633659cfe6146102de5780634378a6e3146102fe5780634f1ef2861461031e57600080fd5b8063228b5679116101a0578063228b56791461022d57806325b4e7be146102605780632658fd0a146102905780632b313ab8146102be57600080fd5b80630af1ec7a146101c75780631117b207146101e957806311447e8714610209575b600080fd5b3480156101d357600080fd5b506101e76101e2366004613a0a565b6105a2565b005b3480156101f557600080fd5b506101e7610204366004613a8b565b610643565b34801561021557600080fd5b506006545b6040519081526020015b60405180910390f35b34801561023957600080fd5b5061024d610248366004613b26565b6106c5565b60405161ffff9091168152602001610224565b34801561026c57600080fd5b5061028061027b366004613b48565b610702565b6040519015158152602001610224565b34801561029c57600080fd5b506102b06102ab366004613b48565b610882565b604051610224929190613bcf565b3480156102ca57600080fd5b506101e76102d9366004613a0a565b6109c6565b3480156102ea57600080fd5b506101e76102f9366004613c16565b610a63565b34801561030a57600080fd5b506102b0610319366004613b48565b610bbb565b6101e761032c366004613c33565b610fdb565b34801561033d57600080fd5b5061021a611121565b34801561035257600080fd5b5061035b6111b7565b6040516102249190613c97565b34801561037457600080fd5b5061035b61124b565b34801561038957600080fd5b5061021a610398366004613b48565b61125d565b3480156103a957600080fd5b506103b261128c565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610224565b3480156103e357600080fd5b5061035b611325565b3480156103f857600080fd5b5060408051808201909152600581527f312e322e30000000000000000000000000000000000000000000000000000000602082015261035b565b34801561043e57600080fd5b506101e761044d366004613a8b565b611337565b34801561045e57600080fd5b5061035b61138d565b34801561047357600080fd5b5060075461021a565b34801561048857600080fd5b5061035b610497366004613b48565b61139f565b3480156104a857600080fd5b506101e76104b7366004613caa565b6118b0565b3480156104c857600080fd5b506104d1611afb565b604051610224959493929190613d2d565b3480156104ee57600080fd5b506101e76104fd366004613a0a565b611d53565b34801561050e57600080fd5b5061035b611df0565b34801561052357600080fd5b506101e7610532366004613a0a565b6121b7565b34801561054357600080fd5b506101e7610552366004613db0565b612254565b34801561056357600080fd5b5061035b610572366004613b48565b6123a4565b34801561058357600080fd5b5060015473ffffffffffffffffffffffffffffffffffffffff166103b2565b336105ab61128c565b73ffffffffffffffffffffffffffffffffffffffff16146105f8576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fa4a51e61fd2a8836d039388e309e7301de08bb26931de504cded9cf6e5a90a0e9061062b906004908490613f54565b60405180910390a1600461063f8282614056565b5050565b3361064c61128c565b73ffffffffffffffffffffffffffffffffffffffff1614610699576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6106a5600760006136f7565b6106b160066000613718565b6106be8585858585612460565b5050505050565b600860205281600052604060002081601081106106e157600080fd5b60109182820401919006600202915091509054906101000a900461ffff1681565b60015460009073ffffffffffffffffffffffffffffffffffffffff163314610756576040517f3ff0b6ce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516020808201859052434082840152416060830152426080808401919091528351808403909101815260a090920183528151918101919091206000858152600890925291812060065490918190036107b657506000949350505050565b81547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001661ffff821617825560005b818110156108765760006006828154811061080257610802614170565b90600052602060002090600202016001018054905090508085816108285761082861419f565b0684836001016010811061083e5761083e614170565b601091828204019190066002026101000a81548161ffff021916908361ffff160217905550601085901c9450508060010190506107e5565b50600195945050505050565b6007818154811061089257600080fd5b90600052602060002090600202016000915090508060000180546108b590613f01565b80601f01602080910402602001604051908101604052809291908181526020018280546108e190613f01565b801561092e5780601f106109035761010080835404028352916020019161092e565b820191906000526020600020905b81548152906001019060200180831161091157829003601f168201915b50505050509080600101805461094390613f01565b80601f016020809104026020016040519081016040528092919081815260200182805461096f90613f01565b80156109bc5780601f10610991576101008083540402835291602001916109bc565b820191906000526020600020905b81548152906001019060200180831161099f57829003601f168201915b5050505050905082565b336109cf61128c565b73ffffffffffffffffffffffffffffffffffffffff1614610a1c576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fcc0881496fc7df5b2e39876ca077298fecc92707f462d28e9fc8f14161b3107390610a4f906002908490613f54565b60405180910390a1600261063f8282614056565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005a28eef0ed8cce44cda9d7097ecce041bb51b9d4163003610ad2576040517f43d22ee900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000005a28eef0ed8cce44cda9d7097ecce041bb51b9d473ffffffffffffffffffffffffffffffffffffffff16610b477f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614610b94576040517fe74d90a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610b9d81612797565b610bb88160405180602001604052806000815250600061294a565b50565b606080610bc9306014612aa2565b610bd284612cee565b604051602001610be39291906141ce565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00181526000868152600860205282812061020085019384905291945092916010908285855b82829054906101000a900461ffff1661ffff1681526020019060020190602082600101049283019260010382029150808411610c3057905050505050509050600081600060108110610c8457610c84614170565b602002015161ffff16905080600003610cd1576040517f531c95b1000000000000000000000000000000000000000000000000000000008152600481018690526024015b60405180910390fd5b60008167ffffffffffffffff811115610cec57610cec6138bf565b604051908082528060200260200182016040528015610d3957816020015b60408051606080820183528082526020820152600091810191909152815260200190600190039081610d0a5790505b50905060005b82811015610fc757600060068281548110610d5c57610d5c614170565b9060005260206000209060020201604051806040016040529081600082018054610d8590613f01565b80601f0160208091040260200160405190810160405280929190818152602001828054610db190613f01565b8015610dfe5780601f10610dd357610100808354040283529160200191610dfe565b820191906000526020600020905b815481529060010190602001808311610de157829003601f168201915b5050505050815260200160018201805480602002602001604051908101604052809291908181526020016000905b82821015610f005760008481526020908190206040805180820190915260028502909101805461ffff1682526001810180549293919291840191610e6f90613f01565b80601f0160208091040260200160405190810160405280929190818152602001828054610e9b90613f01565b8015610ee85780601f10610ebd57610100808354040283529160200191610ee8565b820191906000526020600020905b815481529060010190602001808311610ecb57829003601f168201915b50505050508152505081526020019060010190610e2c565b505050508152505090506000858360010160108110610f2157610f21614170565b602002015161ffff169050600082602001518281518110610f4457610f44614170565b602002602001015190506000858581518110610f6257610f62614170565b6020908102919091018101518551815283820151918101919091526001604082015284519091508990610f96908490612e2b565b604051602001610fa792919061424f565b604051602081830303815290604052985050505050806001019050610d3f565b50610fd181612eb5565b9450505050915091565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005a28eef0ed8cce44cda9d7097ecce041bb51b9d416300361104a576040517f43d22ee900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000005a28eef0ed8cce44cda9d7097ecce041bb51b9d473ffffffffffffffffffffffffffffffffffffffff166110bf7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff161461110c576040517fe74d90a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61111582612797565b61063f8282600161294a565b60003073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005a28eef0ed8cce44cda9d7097ecce041bb51b9d41614611192576040517f575bc92e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90565b60606001800180546111c890613f01565b80601f01602080910402602001604051908101604052809291908181526020018280546111f490613f01565b80156112415780601f1061121657610100808354040283529160200191611241565b820191906000526020600020905b81548152906001019060200180831161122457829003601f168201915b5050505050905090565b6060600160020180546111c890613f01565b60006006828154811061127257611272614170565b600091825260209091206001600290920201015492915050565b600154604080517f8da5cb5b000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff1691638da5cb5b9160048083019260209291908290030181865afa1580156112fc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061132091906142a7565b905090565b6060600160030180546111c890613f01565b3361134061128c565b73ffffffffffffffffffffffffffffffffffffffff16146106b1576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060600160040180546111c890613f01565b60606000806113ad84610bbb565b60095491935091506000906113c39060046142f3565b67ffffffffffffffff8111156113db576113db6138bf565b60405190808252806020026020018201604052801561142857816020015b604080516060808201835280825260208201526000918101919091528152602001906001900390816113f95790505b506040805160a081019091526004606082019081527f6e616d650000000000000000000000000000000000000000000000000000000060808301528152909150602081016114746130f5565b61147d88612cee565b60405160200161148e929190614306565b604051602081830303815290604052815260200160011515815250816000815181106114bc576114bc614170565b602002602001018190525060405180606001604052806040518060400160405280600b81526020017f6465736372697074696f6e00000000000000000000000000000000000000000081525081526020016001600201805461151d90613f01565b80601f016020809104026020016040519081016040528092919081815260200182805461154990613f01565b80156115965780601f1061156b57610100808354040283529160200191611596565b820191906000526020600020905b81548152906001019060200180831161157957829003601f168201915b5050505050815260200160011515815250816001815181106115ba576115ba614170565b602002602001018190525060405180606001604052806040518060400160405280600581526020017f696d61676500000000000000000000000000000000000000000000000000000081525081526020016001600401846040516020016116229291906143ef565b6040516020818303038152906040528152602001600115158152508160028151811061165057611650614170565b602002602001018190525060405180606001604052806040518060400160405280600a81526020017f70726f7065727469657300000000000000000000000000000000000000000000815250815260200184815260200160001515815250816003815181106116c1576116c1614170565b602002602001018190525060005b60095481101561189d576000600982815481106116ee576116ee614170565b906000526020600020906003020160405180606001604052908160008201805461171790613f01565b80601f016020809104026020016040519081016040528092919081815260200182805461174390613f01565b80156117905780601f1061176557610100808354040283529160200191611790565b820191906000526020600020905b81548152906001019060200180831161177357829003601f168201915b505050505081526020016001820180546117a990613f01565b80601f01602080910402602001604051908101604052809291908181526020018280546117d590613f01565b80156118225780601f106117f757610100808354040283529160200191611822565b820191906000526020600020905b81548152906001019060200180831161180557829003601f168201915b50505091835250506002919091015460ff16151560209182015260408051606081018252835181528383015192810192909252808301511515908201529091508361186e8460046142f3565b8151811061187e5761187e614170565b602002602001018190525050808061189590614414565b9150506116cf565b506118a7816131ab565b95945050505050565b600054610100900460ff16158015806118cd575060005460ff1615155b80156118e95750303b1515806118e9575060005460ff16600114155b15611920576040517f439a74c900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055801561197e57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000d310a3041dfcf14def5ccbc508668974b5da717416146119ed576040517fa2ddd97100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008080806119fe8789018961444c565b9298509096509450925060029150611a1890508382614056565b506003611a258582614056565b506004611a328482614056565b506005611a3f8282614056565b506002611a4c8382614056565b5050600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8716179055505081159050611af557600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527fbe9b076dc5b65990cca9dd9d7366682482e7817a6f6bc7f4faf4dc32af497f329060200160405180910390a15b50505050565b600180546002805473ffffffffffffffffffffffffffffffffffffffff9092169291611b2690613f01565b80601f0160208091040260200160405190810160405280929190818152602001828054611b5290613f01565b8015611b9f5780601f10611b7457610100808354040283529160200191611b9f565b820191906000526020600020905b815481529060010190602001808311611b8257829003601f168201915b505050505090806002018054611bb490613f01565b80601f0160208091040260200160405190810160405280929190818152602001828054611be090613f01565b8015611c2d5780601f10611c0257610100808354040283529160200191611c2d565b820191906000526020600020905b815481529060010190602001808311611c1057829003601f168201915b505050505090806003018054611c4290613f01565b80601f0160208091040260200160405190810160405280929190818152602001828054611c6e90613f01565b8015611cbb5780601f10611c9057610100808354040283529160200191611cbb565b820191906000526020600020905b815481529060010190602001808311611c9e57829003601f168201915b505050505090806004018054611cd090613f01565b80601f0160208091040260200160405190810160405280929190818152602001828054611cfc90613f01565b8015611d495780601f10611d1e57610100808354040283529160200191611d49565b820191906000526020600020905b815481529060010190602001808311611d2c57829003601f168201915b5050505050905085565b33611d5c61128c565b73ffffffffffffffffffffffffffffffffffffffff1614611da9576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fe21432e1fe2b572d5803dd7316b7a854952317b42017f920a616ec70cdb8a5c190611ddc906003908490613f54565b60405180910390a1600361063f8282614056565b60408051600480825260a0820190925260609160009190816020015b60408051606080820183528082526020820152600091810191909152815260200190600190039081611e0c5750506040805160a081019091526004606082019081527f6e616d65000000000000000000000000000000000000000000000000000000006080830152815290915060208101611e856130f5565b81526020016001151581525081600081518110611ea457611ea4614170565b602002602001018190525060405180606001604052806040518060400160405280600b81526020017f6465736372697074696f6e000000000000000000000000000000000000000000815250815260200160016002018054611f0590613f01565b80601f0160208091040260200160405190810160405280929190818152602001828054611f3190613f01565b8015611f7e5780601f10611f5357610100808354040283529160200191611f7e565b820191906000526020600020905b815481529060010190602001808311611f6157829003601f168201915b505050505081526020016001151581525081600181518110611fa257611fa2614170565b602002602001018190525060405180606001604052806040518060400160405280600581526020017f696d61676500000000000000000000000000000000000000000000000000000081525081526020016001600301805461200390613f01565b80601f016020809104026020016040519081016040528092919081815260200182805461202f90613f01565b801561207c5780601f106120515761010080835404028352916020019161207c565b820191906000526020600020905b81548152906001019060200180831161205f57829003601f168201915b5050505050815260200160011515815250816002815181106120a0576120a0614170565b602002602001018190525060405180606001604052806040518060400160405280600c81526020017f65787465726e616c5f75726c0000000000000000000000000000000000000000815250815260200160018001805461210090613f01565b80601f016020809104026020016040519081016040528092919081815260200182805461212c90613f01565b80156121795780601f1061214e57610100808354040283529160200191612179565b820191906000526020600020905b81548152906001019060200180831161215c57829003601f168201915b50505050508152602001600115158152508160038151811061219d5761219d614170565b60200260200101819052506121b1816131ab565b91505090565b336121c061128c565b73ffffffffffffffffffffffffffffffffffffffff161461220d576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fc5d098cca4f56032ca7e04c4bf7fccc90f26576acaa949a5f67bc9eba4ef414990612240906005908490613f54565b60405180910390a1600561063f8282614056565b3361225d61128c565b73ffffffffffffffffffffffffffffffffffffffff16146122aa576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6122b660096000613739565b60005b81518110156123695760098282815181106122d6576122d6614170565b602090810291909101810151825460018101845560009384529190922082516003909202019081906123089082614056565b506020820151600182019061231d9082614056565b5060409190910151600290910180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169115159190911790558061236181614414565b9150506122b9565b507fbf5f7405282e2c6b004f386e14b57c8a744e1a3e7f221b9af8031b911f6db59a816040516123999190614541565b60405180910390a150565b600681815481106123b457600080fd5b90600052602060002090600202016000915090508060000180546123d790613f01565b80601f016020809104026020016040519081016040528092919081815260200182805461240390613f01565b80156124505780601f1061242557610100808354040283529160200191612450565b820191906000526020600020905b81548152906001019060200180831161243357829003601f168201915b5050505050905081565b3b151590565b6007805460018101825560009190915281600282027fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688016124a18282614771565b5050600654858460008390036124f3578115806124bc575080155b156124f3576040517f279ec20e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600f8284011115612530576040517f8c90470900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b82811015612606576006805460010181556000528381018a8a8381811061255c5761255c614170565b905060200281019061256e91906145f2565b6006838154811061258157612581614170565b9060005260206000209060020201600001918261259f929190614657565b507febf63270254042d3527778299325aabb702431e95ad5e9055f4cb3b047ecdccc818c8c858181106125d4576125d4614170565b90506020028101906125e691906145f2565b6040516125f5939291906148ae565b60405180910390a150600101612533565b5060005b8181101561278b57600088888381811061262657612626614170565b90506020028101906126389190614902565b35905088888381811061264d5761264d614170565b905060200281019061265f9190614902565b612670906060810190604001614940565b156126785784015b60065481106126b6576040517f0dfb793000000000000000000000000000000000000000000000000000000000815260048101829052602401610cc8565b6000600682815481106126cb576126cb614170565b60009182526020822060016002909202018101805491820180825581845290935090919083908390811061270157612701614170565b906000526020600020906002020190508b8b8681811061272357612723614170565b90506020028101906127359190614902565b6127439060208101906145f2565b6001830191612753919083614657565b5080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001661ffff8a1617905550505060010161260a565b50505050505050505050565b336127a061128c565b73ffffffffffffffffffffffffffffffffffffffff16146127ed576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000d310a3041dfcf14def5ccbc508668974b5da717473ffffffffffffffffffffffffffffffffffffffff16639bb8dcfd6128677f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff91821660048201529084166024820152604401602060405180830381865afa1580156128d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128fc919061495d565b610bb8576040517fc40d973400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610cc8565b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff16156129825761297d836131f4565b505050565b8273ffffffffffffffffffffffffffffffffffffffff166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015612a07575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252612a049181019061497a565b60015b612a3d576040517fc0bb20b200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8114612a96576040517f0849b49600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5061297d8383836132aa565b60606000612ab1836002614993565b612abc9060026142f3565b67ffffffffffffffff811115612ad457612ad46138bf565b6040519080825280601f01601f191660200182016040528015612afe576020820181803683370190505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110612b3557612b35614170565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110612b9857612b98614170565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506000612bd4846002614993565b612bdf9060016142f3565b90505b6001811115612c7c577f303132333435363738396162636465660000000000000000000000000000000085600f1660108110612c2057612c20614170565b1a60f81b828281518110612c3657612c36614170565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049490941c93612c75816149d0565b9050612be2565b508315612ce5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610cc8565b90505b92915050565b606081600003612d3157505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b8115612d5b5780612d4581614414565b9150612d549050600a83614a05565b9150612d35565b60008167ffffffffffffffff811115612d7657612d766138bf565b6040519080825280601f01601f191660200182016040528015612da0576020820181803683370190505b5090505b8415612e2357612db5600183614a19565b9150612dc2600a86614a2c565b612dcd9060306142f3565b60f81b818381518110612de257612de2614170565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350612e1c600a86614a05565b9450612da4565b949350505050565b6060612ce56007846000015161ffff1681548110612e4b57612e4b614170565b90600052602060002090600202016000018385602001516007876000015161ffff1681548110612e7d57612e7d614170565b9060005260206000209060020201600101604051602001612ea19493929190614a40565b6040516020818303038152906040526132cf565b60408051808201909152600181527f7b0000000000000000000000000000000000000000000000000000000000000060208201526000805b83518110156130cc57838181518110612f0857612f08614170565b60200260200101516020015151600003156130ba57838181518110612f2f57612f2f614170565b60200260200101516040015115612ff857828215612f82576040518060400160405280600181526020017f2c00000000000000000000000000000000000000000000000000000000000000815250612f93565b604051806020016040528060008152505b858381518110612fa557612fa5614170565b602002602001015160000151868481518110612fc357612fc3614170565b602002602001015160200151604051602001612fe29493929190614aa4565b60405160208183030381529060405292506130ac565b82821561303a576040518060400160405280600181526020017f2c0000000000000000000000000000000000000000000000000000000000000081525061304b565b604051806020016040528060008152505b85838151811061305d5761305d614170565b60200260200101516000015186848151811061307b5761307b614170565b60200260200101516020015160405160200161309a9493929190614b59565b60405160208183030381529060405292505b6130b76001836142f3565b91505b806130c481614414565b915050612eed565b50816040516020016130de9190614c04565b604051602081830303815290604052915050919050565b600154604080517f06fdde03000000000000000000000000000000000000000000000000000000008152905160609273ffffffffffffffffffffffffffffffffffffffff16916306fdde039160048083019260009291908290030181865afa158015613165573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526113209190810190614c45565b6060612ce86040518060400160405280601081526020017f6170706c69636174696f6e2f6a736f6e000000000000000000000000000000008152506131ef84612eb5565b61341f565b803b613244576040517fc40d973400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610cc8565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6132b383613453565b6000825111806132c05750805b1561297d57611af583836134a2565b60408051808201909152601081527f3031323334353637383961626364656600000000000000000000000000000000602082015281516060918391600090613318906003614993565b67ffffffffffffffff811115613330576133306138bf565b6040519080825280601f01601f19166020018201604052801561335a576020820181803683370190505b5090506001820160208201600085865187015b808210156134105760018201915060ff825116607e8114602d821417605f8214602e83141717603a8210602f83111617605b82106040831116176086821060608311161780600181146133c55780156133d957613408565b828753600187019650600186019550613408565b602587536001870196508260041c8801518753600187019650600f831688015187536001870196506003860195505b50505061336d565b50508352509095945050505050565b60608261342b83613553565b60405160200161343c929190614cb3565b604051602081830303815290604052905092915050565b61345c816131f4565b60405173ffffffffffffffffffffffffffffffffffffffff821681527fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90602001612399565b6060823b6134dc576040517f37f2022900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808473ffffffffffffffffffffffffffffffffffffffff16846040516135049190614d34565b600060405180830381855af49150503d806000811461353f576040519150601f19603f3d011682016040523d82523d6000602084013e613544565b606091505b50915091506118a782826136a6565b6060815160000361357257505060408051602081019091526000815290565b6000604051806060016040528060408152602001614d4760409139905060006003845160026135a191906142f3565b6135ab9190614a05565b6135b6906004614993565b67ffffffffffffffff8111156135ce576135ce6138bf565b6040519080825280601f01601f1916602001820160405280156135f8576020820181803683370190505b509050600182016020820185865187015b80821015613664576003820191508151603f8160121c168501518453600184019350603f81600c1c168501518453600184019350603f8160061c168501518453600184019350603f8116850151845350600183019250613609565b505060038651066001811461368057600281146136935761369b565b603d6001830353603d600283035361369b565b603d60018303535b509195945050505050565b606082156136b5575080612ce8565b8151156136c55781518083602001fd5b6040517f62536b1000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5080546000825560020290600052602060002090810190610bb8919061375a565b5080546000825560020290600052602060002090810190610bb89190613789565b5080546000825560030290600052602060002090810190610bb891906137b4565b8082111561378557600061376e8282613809565b61377c600183016000613809565b5060020161375a565b5090565b8082111561378557600061379d8282613809565b6137ab600183016000613843565b50600201613789565b808211156137855760006137c88282613809565b6137d6600183016000613809565b506002810180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690556003016137b4565b50805461381590613f01565b6000825580601f10613825575050565b601f016020900490600052602060002090810190610bb89190613864565b5080546000825560020290600052602060002090810190610bb89190613879565b5b808211156137855760008155600101613865565b808211156137855780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016815560006138b66001830182613809565b50600201613879565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516060810167ffffffffffffffff81118282101715613911576139116138bf565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561395e5761395e6138bf565b604052919050565b600067ffffffffffffffff821115613980576139806138bf565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b60006139bf6139ba84613966565b613917565b90508281528383830111156139d357600080fd5b828260208301376000602084830101529392505050565b600082601f8301126139fb57600080fd5b612ce5838335602085016139ac565b600060208284031215613a1c57600080fd5b813567ffffffffffffffff811115613a3357600080fd5b612e23848285016139ea565b60008083601f840112613a5157600080fd5b50813567ffffffffffffffff811115613a6957600080fd5b6020830191508360208260051b8501011115613a8457600080fd5b9250929050565b600080600080600060608688031215613aa357600080fd5b853567ffffffffffffffff80821115613abb57600080fd5b613ac789838a01613a3f565b90975095506020880135915080821115613ae057600080fd5b613aec89838a01613a3f565b90955093506040880135915080821115613b0557600080fd5b50860160408189031215613b1857600080fd5b809150509295509295909350565b60008060408385031215613b3957600080fd5b50508035926020909101359150565b600060208284031215613b5a57600080fd5b5035919050565b60005b83811015613b7c578181015183820152602001613b64565b50506000910152565b60008151808452613b9d816020860160208601613b61565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b604081526000613be26040830185613b85565b82810360208401526118a78185613b85565b73ffffffffffffffffffffffffffffffffffffffff81168114610bb857600080fd5b600060208284031215613c2857600080fd5b8135612ce581613bf4565b60008060408385031215613c4657600080fd5b8235613c5181613bf4565b9150602083013567ffffffffffffffff811115613c6d57600080fd5b8301601f81018513613c7e57600080fd5b613c8d858235602084016139ac565b9150509250929050565b602081526000612ce56020830184613b85565b600080600060408486031215613cbf57600080fd5b833567ffffffffffffffff80821115613cd757600080fd5b818601915086601f830112613ceb57600080fd5b813581811115613cfa57600080fd5b876020828501011115613d0c57600080fd5b60209283019550935050840135613d2281613bf4565b809150509250925092565b73ffffffffffffffffffffffffffffffffffffffff8616815260a060208201526000613d5c60a0830187613b85565b8281036040840152613d6e8187613b85565b90508281036060840152613d828186613b85565b90508281036080840152613d968185613b85565b98975050505050505050565b8015158114610bb857600080fd5b60006020808385031215613dc357600080fd5b823567ffffffffffffffff80821115613ddb57600080fd5b818501915085601f830112613def57600080fd5b813581811115613e0157613e016138bf565b8060051b613e10858201613917565b9182528381018501918581019089841115613e2a57600080fd5b86860192505b83831015613ef457823585811115613e485760008081fd5b86016060818c037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001811315613e7e5760008081fd5b613e866138ee565b8983013588811115613e985760008081fd5b613ea68e8c838701016139ea565b82525060408084013589811115613ebd5760008081fd5b613ecb8f8d838801016139ea565b838d0152509282013592613ede84613da2565b8101929092525082529186019190860190613e30565b9998505050505050505050565b600181811c90821680613f1557607f821691505b602082108103613f4e577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b604081526000808454613f6681613f01565b8060408601526060600180841660008114613f885760018114613fc057613ff1565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008516838901528284151560051b8901019550613ff1565b8960005260208060002060005b86811015613fe85781548b8201870152908401908201613fcd565b8a018501975050505b505050505082810360208401526118a78185613b85565b601f82111561297d57600081815260208120601f850160051c8101602086101561402f5750805b601f850160051c820191505b8181101561404e5782815560010161403b565b505050505050565b815167ffffffffffffffff811115614070576140706138bf565b6140848161407e8454613f01565b84614008565b602080601f8311600181146140d757600084156140a15750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b17855561404e565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561412457888601518255948401946001909101908401614105565b508582101561416057878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f3f636f6e7472616374416464726573733d000000000000000000000000000000815260008351614206816011850160208801613b61565b7f26746f6b656e49643d0000000000000000000000000000000000000000000000601191840191820152835161424381601a840160208801613b61565b01601a01949350505050565b60008351614261818460208801613b61565b7f26696d616765733d000000000000000000000000000000000000000000000000908301908152835161429b816008840160208801613b61565b01600801949350505050565b6000602082840312156142b957600080fd5b8151612ce581613bf4565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820180821115612ce857612ce86142c4565b60008351614318818460208801613b61565b7f20230000000000000000000000000000000000000000000000000000000000009083019081528351614352816002840160208801613b61565b01600201949350505050565b6000815461436b81613f01565b6001828116801561438357600181146143b6576143e5565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00841687528215158302870194506143e5565b8560005260208060002060005b858110156143dc5781548a8201529084019082016143c3565b50505082870194505b5050505092915050565b60006143fb828561435e565b835161440b818360208801613b61565b01949350505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614445576144456142c4565b5060010190565b60008060008060008060c0878903121561446557600080fd5b863567ffffffffffffffff8082111561447d57600080fd5b6144898a838b016139ea565b9750602089013591508082111561449f57600080fd5b6144ab8a838b016139ea565b965060408901359150808211156144c157600080fd5b6144cd8a838b016139ea565b955060608901359150808211156144e357600080fd5b6144ef8a838b016139ea565b9450608089013591508082111561450557600080fd5b6145118a838b016139ea565b935060a089013591508082111561452757600080fd5b5061453489828a016139ea565b9150509295509295509295565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b838110156145e4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08984030185528151606081518186526145ac82870182613b85565b915050888201518582038a8701526145c48282613b85565b928901511515958901959095525094870194925090860190600101614568565b509098975050505050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261462757600080fd5b83018035915067ffffffffffffffff82111561464257600080fd5b602001915036819003821315613a8457600080fd5b67ffffffffffffffff83111561466f5761466f6138bf565b6146838361467d8354613f01565b83614008565b6000601f8411600181146146d5576000851561469f5750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b1783556106be565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156147245786850135825560209485019460019092019101614704565b508682101561475f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555050505050565b61477b82836145f2565b67ffffffffffffffff811115614793576147936138bf565b6147a7816147a18554613f01565b85614008565b6000601f8211600181146147f957600083156147c35750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600385901b1c1916600184901b17855561488f565b6000858152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0841690835b828110156148485786850135825560209485019460019092019101614828565b5084821015614883577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88660031b161c19848701351681555b505060018360011b0185555b505050506148a060208301836145f2565b611af5818360018601614657565b83815260406020820152816040820152818360608301376000818301606090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016010192915050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa183360301811261493657600080fd5b9190910192915050565b60006020828403121561495257600080fd5b8135612ce581613da2565b60006020828403121561496f57600080fd5b8151612ce581613da2565b60006020828403121561498c57600080fd5b5051919050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156149cb576149cb6142c4565b500290565b6000816149df576149df6142c4565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b600082614a1457614a1461419f565b500490565b81810381811115612ce857612ce86142c4565b600082614a3b57614a3b61419f565b500690565b6000614a4c828761435e565b8551614a5c818360208a01613b61565b7f2f0000000000000000000000000000000000000000000000000000000000000091019081528451614a95816001840160208901613b61565b613d966001828401018661435e565b60008551614ab6818460208a01613b61565b855190830190614aca818360208a01613b61565b7f220000000000000000000000000000000000000000000000000000000000000091018181528551909190614b06816001850160208a01613b61565b7f223a202200000000000000000000000000000000000000000000000000000000600193909101928301528451614b44816005850160208901613b61565b60059201918201526006019695505050505050565b60008551614b6b818460208a01613b61565b855190830190614b7f818360208a01613b61565b7f220000000000000000000000000000000000000000000000000000000000000091019081528451614bb8816001840160208901613b61565b7f223a200000000000000000000000000000000000000000000000000000000000600192909101918201528351614bf6816004840160208801613b61565b016004019695505050505050565b60008251614c16818460208701613b61565b7f7d00000000000000000000000000000000000000000000000000000000000000920191825250600101919050565b600060208284031215614c5757600080fd5b815167ffffffffffffffff811115614c6e57600080fd5b8201601f81018413614c7f57600080fd5b8051614c8d6139ba82613966565b818152856020838501011115614ca257600080fd5b6118a7826020830160208601613b61565b7f646174613a000000000000000000000000000000000000000000000000000000815260008351614ceb816005850160208801613b61565b7f3b6261736536342c0000000000000000000000000000000000000000000000006005918401918201528351614d2881600d840160208801613b61565b01600d01949350505050565b60008251614936818460208701613b6156fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa264697066735822122021bd4085e21d7ea8312d8a669802ee4a5e946121e844a34dac1876a9474eca5564736f6c63430008100033