Source Code
More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 25 from a total of 59 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Add Minter | 40041187 | 18 days ago | IN | 0 ETH | 0.00000002 | ||||
| Resolve Epoch If... | 40041187 | 18 days ago | IN | 0 ETH | 0.00000001 | ||||
| Set Marketplace | 39044463 | 41 days ago | IN | 0 ETH | 0.00000001 | ||||
| Set Marketplace | 39043381 | 41 days ago | IN | 0 ETH | 0.00000013 | ||||
| Set Marketplace | 39037436 | 41 days ago | IN | 0 ETH | 0.00000013 | ||||
| Add Minter | 36958286 | 89 days ago | IN | 0 ETH | 0.00000021 | ||||
| Resolve Epoch If... | 35772187 | 116 days ago | IN | 0 ETH | 0.00000004 | ||||
| Resolve Epoch If... | 35772152 | 116 days ago | IN | 0 ETH | 0.00000011 | ||||
| Resolve Epoch If... | 35772117 | 116 days ago | IN | 0 ETH | 0.00000004 | ||||
| Resolve Epoch If... | 35772015 | 116 days ago | IN | 0 ETH | 0.00000011 | ||||
| Resolve Epoch If... | 35771840 | 116 days ago | IN | 0 ETH | 0.00000013 | ||||
| Resolve Epoch If... | 35771720 | 116 days ago | IN | 0 ETH | 0.00000005 | ||||
| Resolve Epoch If... | 35771706 | 116 days ago | IN | 0 ETH | 0.00000013 | ||||
| Resolve Epoch If... | 35771650 | 116 days ago | IN | 0 ETH | 0.00000014 | ||||
| Resolve Epoch If... | 35771609 | 116 days ago | IN | 0 ETH | 0.00000007 | ||||
| Resolve Epoch If... | 35771585 | 117 days ago | IN | 0 ETH | 0.00000006 | ||||
| Resolve Epoch If... | 35771567 | 117 days ago | IN | 0 ETH | 0.00000015 | ||||
| Resolve Epoch If... | 35771452 | 117 days ago | IN | 0 ETH | 0.00000012 | ||||
| Resolve Epoch If... | 35771345 | 117 days ago | IN | 0 ETH | 0.00000012 | ||||
| Resolve Epoch If... | 35771007 | 117 days ago | IN | 0 ETH | 0.00000006 | ||||
| Resolve Epoch If... | 35770995 | 117 days ago | IN | 0 ETH | 0.00000013 | ||||
| Resolve Epoch If... | 35770982 | 117 days ago | IN | 0 ETH | 0.00000006 | ||||
| Resolve Epoch If... | 35770976 | 117 days ago | IN | 0 ETH | 0.00000006 | ||||
| Resolve Epoch If... | 35770965 | 117 days ago | IN | 0 ETH | 0.00000006 | ||||
| Resolve Epoch If... | 35770887 | 117 days ago | IN | 0 ETH | 0.00000014 |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
ChonkTraits
Compiler Version
v0.8.22+commit.4fc1097e
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
// OpenZeppelin Imports
import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import { ERC721Burnable } from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
import { ERC721Enumerable } from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { Ownable } from "solady/auth/Ownable.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
// Associated Interfaces and Libraries
import { CommitReveal } from "./common/CommitReveal.sol";
import { IERC4906 } from "./interfaces/IERC4906.sol";
import { IChonkStorage } from "./interfaces/IChonkStorage.sol";
import { ITraitStorage } from "./interfaces/ITraitStorage.sol";
import { TraitCategory } from "./TraitCategory.sol";
// Renderer
import { TraitRenderer } from "./renderers/TraitRenderer.sol";
// Other Chonks Associated Contracts
import { ChonksMain } from "./ChonksMain.sol";
import { ChonksMarket } from "./ChonksMarket.sol";
interface IRenderMinterV1 {
function explainTrait(
ITraitStorage.StoredTrait calldata storedTrait,
uint128 randomness
) external view returns (ITraitStorage.StoredTrait memory);
}
interface IChonkTraitsV1 {
function getTrait(uint256 _tokenId) external view returns (ITraitStorage.StoredTrait memory);
function getTraitMetadata(uint256 _tokenId) external view returns (ITraitStorage.TraitMetadata memory);
function getStoredTraitForTokenId(uint256 _tokenId) external view returns (ITraitStorage.StoredTrait memory);
function getTraitIndexToMetadata(uint256 _traitIndex) external view returns (ITraitStorage.TraitMetadata memory);
function getTraitImageSvg(uint256 index) external view returns (string memory svg);
function renderAsDataUri(uint256 _tokenId) external view returns (string memory);
function getZMapForTokenId(uint256 _tokenId) external view returns (string memory);
function getGhostSvg() external view returns (string memory);
function getEpochData(uint256 index) external view returns (CommitReveal.Epoch memory);
function getSvgAndMetadataTrait(ITraitStorage.StoredTrait memory _trait, uint256 _traitId) external view returns (string memory traitSvg, string memory traitAttributes);
function getSVGZmapAndMetadataTrait(ITraitStorage.StoredTrait memory _trait, uint256 _traitId) external view returns (string memory traitSvg, bytes memory traitZmap, string memory traitAttributes);
function callGetSvgAndMetadataTrait(uint256 _traitId, string memory _traitsSvg, string memory _traitsAttributes) external view returns (string memory traitsSvg, string memory traitsAttributes);
function callGetSVGZmapAndMetadataTrait(uint256 _traitId, string memory _traitsSvg, string memory _traitsAttributes, bytes memory _traitZMaps) external view returns (string memory traitsSvg, string memory traitsAttributes, bytes memory traitZMaps);
function totalSupply() external view returns (uint256);
}
/*
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:cllllllllllllllllllllllllllllllllc:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:okOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOko:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:clllxOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOdlllc:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:okOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOko:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ccldOOOOOOO0KKKxllldOOOOOOOOOOOO0KKKxllldOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ldxxkOOOOOOOXWMNl ;kOOOOOOOOOOOXWMWl ;kOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ldxxkOOOOOOOXMMWl ;kkkkkkkkkkkOXMMWl ;kOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ldxxkOOOOOOOXMMWl ,dxxxxxxxxxxxKWMWl ;kOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:coooxOOOOOOOKNNXd'.'cxkkkxxkkkkkk0XNXd'.'lkOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:;:oOOOOOOOOOOOOOkkOOOOOOOOOOOOOOOOOOkkOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:lddxkOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOkxxdl:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::oOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOo::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:okkkkkkkkkkkkkkkkkkkkkkkkkkkkxxddl:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:lxxxxxxxxxxxxxxxxxxxxxxxxxxxxl::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:cllldxkxxxxkkkkkkkkkkkkkkkkkkkkkxdlllc:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:okOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOko:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;clllxOOOOOOOOkkkOOOOOOOOOOOOOOkkkOOOOOOOOdlllc:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:okOOOOOOOOOOkxxxkOOOOOOOOOOOOkxxxkOOOOOOOOOOko:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOkkkOOOOkkkkkOOOOOOOOOOOOkkxkkOOOOkkkOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOkxxxkOOOOOOOOOOOOOOOOOOOOOOOOOOOOkxxxkOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOkxxxkOOOOOOOOOOOOOOOOOOOOOOOOOOOOkxxxkOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOkxxxkOOOOOOOOOOOOOOOOOOOOOOOOOOOOkxxxkOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ldxddoooxOOOOOOOOOOOOOOOOOOOOOOOOOOOOxoooddxdl:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::::::oOOOOOOOOOOOOOOOOOOOOOOOOOOOOo::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOOOOOkxdxkOOOOOOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOOOOOo:::okOOOOOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOkxddl:;:lddxxkOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOo::::;;;:::::oOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ldddddxdl:;;;;;;;;;;:ldxxxxxdl:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::::::::;;;;;;;;;;;;:::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
*/
contract ChonkTraits is IERC165, ERC721Enumerable, ERC721Burnable, ITraitStorage, Ownable, IERC4906, ReentrancyGuard {
// We use this database for persistent storage
Traits public traitTokens;
// The renderer contract for the Traits
TraitRenderer public traitRenderer = TraitRenderer(0x785AfED7Ce24E76Ac1d603be09C1fD20e0E1E6b7);
// The ChonksMain contract
ChonksMain public constant chonksMain = ChonksMain(0x07152bfde079b5319e5308C43fB1Dbc9C76cb4F9);
// The old Traits contract
IChonkTraitsV1 public constant chonkTraitsV1 = IChonkTraitsV1(0x6B8f34E0559aa9A5507e74aD93374D9745CdbF09);
// The ChonksMarket contract
ChonksMarket public marketplace;
// Metadata for each Trait by index
mapping(uint256 => TraitMetadata) public traitIndexToMetadata;
// Contract addresses that are approved to create Traits
mapping (address => bool) public isMinter;
// The next token ID to be minted
uint256 public nextTokenId = 340_646; // Begins where the original contract left off
uint256 constant LEGACY_CONTRACT_TRAIT_COUNT = 340_646;
// The description parts
string[2] descriptionParts;
// If the replaceMint permissions have been revoked
bool public replaceMintPermisssionsRevoked;
// Mapping from TBA to Owner to operator approvals. Pulled this mapping from ERC721.sol and added an additional mapping
// Thanks to Nix.eth for the inspiration for this
mapping(address => mapping(address => mapping(address => bool))) private _operatorApprovals;
/// Errors
error AddressCantBurn();
error CantTransfer();
error CantTransferEquipped();
error NotATBA();
error NotAValidMinterContract();
error NotYourTrait();
error ReplaceMintPermissionsRevoked();
error SetChonksMainAddress();
error SetMarketplaceAddress();
error TraitAlreadyExists();
error TraitIDTooLow();
error TraitNotFound(uint256 _tokenId);
error TraitTokenDoesntExist();
/// Modifier
modifier onlyMinter(address _address) {
// Add DataMinter contract first via `AddMinter`.
if (!isMinter[_address]) revert NotAValidMinterContract();
_;
}
/// Event
event TBAApprovalForAll(address indexed tba, address indexed owner, address indexed operator, bool approved);
/// Constructor
constructor() ERC721("Chonk Traits", "CHONK TRAITS") {
_initializeOwner(msg.sender);
}
function getTraitIndexToMetadata(uint256 _traitIndex) public view returns (TraitMetadata memory) {
TraitMetadata memory metadata = chonkTraitsV1.getTraitIndexToMetadata(_traitIndex);
if (metadata.dataMinterContract != address(0)) return metadata;
return traitIndexToMetadata[_traitIndex];
}
// Called by DataMinter contracts to set the trait for a tokenId
function setTraitForTokenId(uint256 _tokenId, ITraitStorage.StoredTrait memory _trait) public onlyMinter(msg.sender) {
if (_tokenId <= LEGACY_CONTRACT_TRAIT_COUNT) revert TraitIDTooLow();
traitTokens.all[_tokenId] = _trait;
}
/// @dev Called in DataMinter contracts to add Traits
function setTraitIndexToMetadata(uint256 _traitIndex, TraitMetadata memory _metadata) public onlyMinter(msg.sender) {
TraitMetadata memory oldMetadata = chonkTraitsV1.getTraitIndexToMetadata(_traitIndex);
if (oldMetadata.dataMinterContract != address(0)) revert TraitAlreadyExists();
traitIndexToMetadata[_traitIndex] = _metadata;
}
/// @dev NOTE: Mints to a smart contract address that implements onERC721Received
function safeMint(address _to) public onlyMinter(msg.sender) returns (uint256) {
resolveEpochIfNecessary();
uint tokenId = ++nextTokenId;
_safeMint(_to, tokenId);
return tokenId;
}
function updateEpochOnce() public onlyMinter(msg.sender) {
if (traitTokens.epoch == 0) traitTokens.epoch = 778;
}
/// @dev Used to replace Traits from old Traits contract
function replaceMint(address _to, uint256 _tokenId) public onlyMinter(msg.sender) {
if (replaceMintPermisssionsRevoked) revert ReplaceMintPermissionsRevoked();
_safeMint(_to, _tokenId);
}
function burn(uint256 _tokenId) public override {
if (!isMinter[msg.sender]) revert AddressCantBurn();
_burn(_tokenId);
}
function burnBatch(uint256[] memory tokenIds) public nonReentrant {
if (!isMinter[msg.sender]) revert AddressCantBurn();
for (uint256 i; i < tokenIds.length; ++i) {
_burn(tokenIds[i]);
}
}
/// @notice Initializes and closes epochs. Thank you Jalil & MouseDev.
/// @dev Based on the commit-reveal scheme proposed by MouseDev in Checks.
function resolveEpochIfNecessary() public {
CommitReveal.Epoch storage currentEpoch = traitTokens.epochs[traitTokens.epoch];
if (
// If epoch has not been committed,
!currentEpoch.committed ||
// Or the reveal commitment timed out.
(!currentEpoch.revealed && currentEpoch.revealBlock < block.number - 256)
) {
// This means the epoch has not been committed, OR the epoch was committed but has expired.
// Set committed to true, and record the reveal block:
// this was 50 x 12, 600 seconds - 10 mins on L1
// but on L2 it's more like 50 x 2, 100 seconds - 1.6 mins on L2
currentEpoch.revealBlock = uint64(block.number + 50);
currentEpoch.committed = true;
} else if (block.number > currentEpoch.revealBlock) {
// Epoch has been committed and is within range to be revealed.
// Set its randomness to the target block hash.
currentEpoch.randomness = uint128(uint256(keccak256(
abi.encodePacked(
blockhash(currentEpoch.revealBlock),
block.prevrandao
))) % (2 ** 128 - 1)
);
currentEpoch.revealed = true;
// Notify dApps about the new epoch.
emit CommitReveal.NewEpoch(traitTokens.epoch, currentEpoch.revealBlock);
// Notify OS to update all tokens
emit BatchMetadataUpdate(0, type(uint256).max);
// Initialize the next epoch
++traitTokens.epoch;
resolveEpochIfNecessary();
}
}
/// @notice Get the data for a given epoch
/// @param _index The identifier of the epoch to fetch
function getEpochData(uint256 _index) view public returns(CommitReveal.Epoch memory) {
if (_index <= 777) return chonkTraitsV1.getEpochData(_index);
return traitTokens.epochs[_index];
}
function tokenURI(uint256 _tokenId) public view override returns (string memory) {
if (!_exists(_tokenId)) revert TraitTokenDoesntExist();
return renderAsDataUri(_tokenId);
}
function getTrait(uint256 _tokenId) public view returns (ITraitStorage.StoredTrait memory) {
if (_tokenId <= LEGACY_CONTRACT_TRAIT_COUNT) return chonkTraitsV1.getTrait(_tokenId);
ITraitStorage.StoredTrait memory storedTrait = traitTokens.all[_tokenId];
uint128 randomness = traitTokens.epochs[storedTrait.epoch].randomness;
IRenderMinterV1 dataContract = IRenderMinterV1(storedTrait.dataMinterContract);
if (storedTrait.dataMinterContract == address(0) && storedTrait.seed == 0)
revert TraitNotFound(_tokenId);
return dataContract.explainTrait(storedTrait, randomness);
}
/// @notice Lets you easily go from the Trait token id to the Trait Metadata, as explained by the DataMinter contract the Trait was minted with
function getTraitMetadata(uint256 _tokenId) public view returns (TraitMetadata memory) {
if (_tokenId <= LEGACY_CONTRACT_TRAIT_COUNT) return chonkTraitsV1.getTraitMetadata(_tokenId);
StoredTrait memory trait = getTrait(_tokenId);
return traitIndexToMetadata[trait.traitIndex];
}
function getStoredTraitForTokenId(uint256 _tokenId) public view returns (ITraitStorage.StoredTrait memory) {
if (_tokenId <= LEGACY_CONTRACT_TRAIT_COUNT) return chonkTraitsV1.getStoredTraitForTokenId(_tokenId);
return traitTokens.all[_tokenId];
}
/// @notice The identifier of the current epoch
function getCurrentEpoch() public view returns (uint256) {
return traitTokens.epoch;
}
function renderAsDataUri(uint256 _tokenId) public view returns (string memory) {
if (_tokenId <= LEGACY_CONTRACT_TRAIT_COUNT) return chonkTraitsV1.renderAsDataUri(_tokenId);
StoredTrait memory trait = getTrait(_tokenId);
string memory traitSvg = trait.isRevealed ? getTraitImageSvg(trait.traitIndex) : '<svg></svg>';
return traitRenderer.renderAsDataUri(
_tokenId,
trait,
traitIndexToMetadata[trait.traitIndex],
getGhostSvg(),
traitSvg,
descriptionParts
);
}
function getSvgForTokenId(uint256 _tokenId) public view returns (string memory traitSvg) {
StoredTrait memory trait = getTrait(_tokenId);
if (trait.isRevealed) {
traitSvg = getTraitImageSvg(trait.traitIndex);
} else {
traitSvg = '<svg></svg>';
}
}
function getZMapForTokenId(uint256 _tokenId) public view returns (string memory) {
if (_tokenId <= LEGACY_CONTRACT_TRAIT_COUNT) return chonkTraitsV1.getZMapForTokenId(_tokenId);
StoredTrait memory trait = getTrait(_tokenId);
return string(traitIndexToMetadata[trait.traitIndex].zMap);
}
function getColorMapForTokenId(uint256 _tokenId) public view returns (bytes memory) {
return getTraitMetadata(_tokenId).colorMap;
}
function getTraitImageSvg(uint256 _index) public view returns (string memory svg) {
svg = chonkTraitsV1.getTraitImageSvg(_index);
if (bytes(svg).length > 0 && keccak256(bytes(svg)) != keccak256(bytes("<g id=\"Trait\"></g>")))
return svg;
bytes memory colorMap = traitIndexToMetadata[_index].colorMap;
svg = traitRenderer.getTraitImageSvg(colorMap);
}
function getGhostSvg() public view returns (string memory) {
return traitRenderer.getGhostSvg();
}
function createSvgFromPixels(bytes memory _pixels) public view returns (bytes memory svgParts) {
return traitRenderer.createSvgFromPixels(_pixels);
}
function getSvgAndMetadataTrait(StoredTrait memory _trait, uint256 _traitId) public view returns (string memory traitSvg, string memory traitAttributes) {
if (_traitId <= LEGACY_CONTRACT_TRAIT_COUNT) return chonkTraitsV1.getSvgAndMetadataTrait(_trait, _traitId);
return traitRenderer.getSvgAndMetadataTrait(
_trait,
_traitId,
traitIndexToMetadata[_trait.traitIndex]
);
}
function getSVGZmapAndMetadataTrait(StoredTrait memory _trait, uint256 _traitId) public view returns(string memory traitSvg, bytes memory traitZmap, string memory traitAttributes) {
if (_traitId <= LEGACY_CONTRACT_TRAIT_COUNT) return chonkTraitsV1.getSVGZmapAndMetadataTrait(_trait, _traitId);
return traitRenderer.getSVGZmapAndMetadataTrait(
_trait,
_traitId,
traitIndexToMetadata[_trait.traitIndex]
);
}
function getSvgAndMetadata(IChonkStorage.StoredChonk memory _storedChonk) public view returns (string memory traitsSvg, string memory traitsAttributes) {
return traitRenderer.getSvgAndMetadata(_storedChonk, this.callGetSvgAndMetadataTrait);
}
function getSvgZmapsAndMetadata(IChonkStorage.StoredChonk memory _storedChonk) public view returns (string memory traitsSvg, bytes memory traitZMaps, string memory traitsAttributes) {
return traitRenderer.getSvgZmapsAndMetadata(_storedChonk, this.callGetSVGZmapAndMetadataTrait);
}
function callGetSvgAndMetadataTrait(uint256 _traitId, string memory _traitsSvg, string memory _traitsAttributes) public view returns (string memory traitsSvg, string memory traitsAttributes) {
if (_traitId <= LEGACY_CONTRACT_TRAIT_COUNT) return chonkTraitsV1.callGetSvgAndMetadataTrait(_traitId, _traitsSvg, _traitsAttributes);
StoredTrait memory storedTrait = getTrait(_traitId);
return traitRenderer.callGetSvgAndMetadataTrait(
_traitId,
_traitsSvg,
_traitsAttributes,
storedTrait,
traitIndexToMetadata[storedTrait.traitIndex]
);
}
function callGetSVGZmapAndMetadataTrait(
uint256 _traitId,
string memory _traitsSvg,
string memory _traitsAttributes,
bytes memory _traitZMaps
) public view returns (string memory traitsSvg, string memory traitsAttributes, bytes memory traitZMaps) {
if (_traitId <= LEGACY_CONTRACT_TRAIT_COUNT) return chonkTraitsV1.callGetSVGZmapAndMetadataTrait(_traitId, _traitsSvg, _traitsAttributes, _traitZMaps);
StoredTrait memory storedTrait = getTrait(_traitId);
return traitRenderer.callGetSVGZmapAndMetadataTrait(
_traitId,
_traitsSvg,
_traitsAttributes,
_traitZMaps,
storedTrait,
traitIndexToMetadata[storedTrait.traitIndex]
);
}
function walletOfOwner(address _owner) public view returns(uint256[] memory) {
uint256 tokenCount = balanceOf(_owner);
uint256[] memory tokensId = new uint256[](tokenCount);
for (uint256 i; i < tokenCount; ++i){
tokensId[i] = tokenOfOwnerByIndex(_owner, i);
}
return tokensId;
}
/// Setters/OnlyOwner
function setMarketplace(address _marketplace) public onlyOwner {
marketplace = ChonksMarket(_marketplace);
}
function addMinter(address _minter) public onlyOwner {
isMinter[_minter] = true;
}
function removeMinter(address _minter) public onlyOwner {
isMinter[_minter] = false;
}
function setTraitRenderer(address _traitRenderer) public onlyOwner {
traitRenderer = TraitRenderer(_traitRenderer);
}
function setGhostMaps(bytes memory _colorMap, bytes memory _zMap) public onlyOwner {
traitRenderer.setGhostMaps(_colorMap, _zMap);
}
function setDescriptionParts(string[2] memory _descriptionParts) public onlyOwner {
descriptionParts = _descriptionParts;
}
function revokeReplaceMintPermissions() public onlyOwner {
replaceMintPermisssionsRevoked = true;
}
/// Boilerplate
function supportsInterface(bytes4 interfaceId) public view override(IERC165, ERC721Enumerable, ERC721) returns (bool) {
return super.supportsInterface(interfaceId);
}
function _cleanUpMarketplaceOffersAndBids(uint256 _tokenId, address _to) internal {
// Delete the Offer on Chonk ID before the transfer
address tba = ownerOf(_tokenId);
uint256 chonkId = chonksMain.tbaAddressToTokenId(tba);
marketplace.setChonkCooldownPeriod(chonkId);
marketplace.removeChonkOfferOnTraitTransfer(chonkId);
marketplace.deleteTraitOfferBeforeTokenTransferFromTraits(_tokenId);
marketplace.deleteTraitBidsBeforeTokenTransfer(_tokenId, _to);
}
// Override functions for marketplace compatibility
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal override(ERC721, ERC721Enumerable) {
super._beforeTokenTransfer(from, to, tokenId);
// If minting
if (from == address(0)) return;
// Ensure the `to` address is a TBA
if (to != address(0) && chonksMain.tbaAddressToTokenId(to) == 0)
revert NotATBA();
// Check if the Trait is equipped on the Chonk, revert if so
(,,, bool isEquipped) = chonksMain.getFullPictureForTrait(tokenId);
if (isEquipped) revert CantTransferEquipped();
_cleanUpMarketplaceOffersAndBids(tokenId, to);
}
// Approvals
/// @notice Override setApprovalForAll to track operator approvals
function setApprovalForAll(address _operator, bool _approved) public override(ERC721, IERC721) {
// Cannot approve self as operator
require(_operator != msg.sender, "ERC721: approve to caller");
uint256 chonkId = chonksMain.getChonkIdForTBAAddress(msg.sender);
// This will revert if owner is address(0)
address owner = chonksMain.ownerOf(chonkId);
_operatorApprovals[msg.sender][owner][_operator] = _approved;
emit TBAApprovalForAll(msg.sender, owner, _operator, _approved);
}
function isApprovedForAll(address _tba, address _operator) public view override(ERC721, IERC721) returns (bool) {
uint256 chonkId = chonksMain.getChonkIdForTBAAddress(_tba);
address owner = chonksMain.ownerOf(chonkId);
return _operatorApprovals[_tba][owner][_operator];
}
/// @notice Invalidates all operator approvals for a specific token
function invalidateAllOperatorApprovals(uint256 _tokenId) public {
(,, address owner,) = chonksMain.getFullPictureForTrait(_tokenId);
// We allow ChonksMain to invalidate all operator approvals for a token or the tba owner or the owner of the tba
if (ownerOf(_tokenId) != msg.sender && owner != msg.sender && msg.sender != address(chonksMain))
revert NotYourTrait();
// Remove individual token approval
_approve(address(0), _tokenId);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/ERC721.sol)
pragma solidity ^0.8.0;
import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "./extensions/IERC721Metadata.sol";
import "../../utils/Address.sol";
import "../../utils/Context.sol";
import "../../utils/Strings.sol";
import "../../utils/introspection/ERC165.sol";
/**
* @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
* the Metadata extension, but not including the Enumerable extension, which is available separately as
* {ERC721Enumerable}.
*/
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
using Address for address;
using Strings for uint256;
// Token name
string private _name;
// Token symbol
string private _symbol;
// Mapping from token ID to owner address
mapping(uint256 => address) private _owners;
// Mapping owner address to token count
mapping(address => uint256) private _balances;
// Mapping from token ID to approved address
mapping(uint256 => address) private _tokenApprovals;
// Mapping from owner to operator approvals
mapping(address => mapping(address => bool)) private _operatorApprovals;
/**
* @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC721-balanceOf}.
*/
function balanceOf(address owner) public view virtual override returns (uint256) {
require(owner != address(0), "ERC721: address zero is not a valid owner");
return _balances[owner];
}
/**
* @dev See {IERC721-ownerOf}.
*/
function ownerOf(uint256 tokenId) public view virtual override returns (address) {
address owner = _owners[tokenId];
require(owner != address(0), "ERC721: invalid token ID");
return owner;
}
/**
* @dev See {IERC721Metadata-name}.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev See {IERC721Metadata-symbol}.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev See {IERC721Metadata-tokenURI}.
*/
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
_requireMinted(tokenId);
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
}
/**
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, can be overridden in child contracts.
*/
function _baseURI() internal view virtual returns (string memory) {
return "";
}
/**
* @dev See {IERC721-approve}.
*/
function approve(address to, uint256 tokenId) public virtual override {
address owner = ERC721.ownerOf(tokenId);
require(to != owner, "ERC721: approval to current owner");
require(
_msgSender() == owner || isApprovedForAll(owner, _msgSender()),
"ERC721: approve caller is not token owner nor approved for all"
);
_approve(to, tokenId);
}
/**
* @dev See {IERC721-getApproved}.
*/
function getApproved(uint256 tokenId) public view virtual override returns (address) {
_requireMinted(tokenId);
return _tokenApprovals[tokenId];
}
/**
* @dev See {IERC721-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual override {
_setApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC721-isApprovedForAll}.
*/
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev See {IERC721-transferFrom}.
*/
function transferFrom(
address from,
address to,
uint256 tokenId
) public virtual override {
//solhint-disable-next-line max-line-length
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner nor approved");
_transfer(from, to, tokenId);
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) public virtual override {
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory data
) public virtual override {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner nor approved");
_safeTransfer(from, to, tokenId, data);
}
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* `data` is additional data, it has no specified format and it is sent in call to `to`.
*
* This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
* implement alternative mechanisms to perform token transfer, such as signature-based.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeTransfer(
address from,
address to,
uint256 tokenId,
bytes memory data
) internal virtual {
_transfer(from, to, tokenId);
require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
}
/**
* @dev Returns whether `tokenId` exists.
*
* Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
*
* Tokens start existing when they are minted (`_mint`),
* and stop existing when they are burned (`_burn`).
*/
function _exists(uint256 tokenId) internal view virtual returns (bool) {
return _owners[tokenId] != address(0);
}
/**
* @dev Returns whether `spender` is allowed to manage `tokenId`.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
address owner = ERC721.ownerOf(tokenId);
return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
}
/**
* @dev Safely mints `tokenId` and transfers it to `to`.
*
* Requirements:
*
* - `tokenId` must not exist.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeMint(address to, uint256 tokenId) internal virtual {
_safeMint(to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/
function _safeMint(
address to,
uint256 tokenId,
bytes memory data
) internal virtual {
_mint(to, tokenId);
require(
_checkOnERC721Received(address(0), to, tokenId, data),
"ERC721: transfer to non ERC721Receiver implementer"
);
}
/**
* @dev Mints `tokenId` and transfers it to `to`.
*
* WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
*
* Requirements:
*
* - `tokenId` must not exist.
* - `to` cannot be the zero address.
*
* Emits a {Transfer} event.
*/
function _mint(address to, uint256 tokenId) internal virtual {
require(to != address(0), "ERC721: mint to the zero address");
require(!_exists(tokenId), "ERC721: token already minted");
_beforeTokenTransfer(address(0), to, tokenId);
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(address(0), to, tokenId);
_afterTokenTransfer(address(0), to, tokenId);
}
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/
function _burn(uint256 tokenId) internal virtual {
address owner = ERC721.ownerOf(tokenId);
_beforeTokenTransfer(owner, address(0), tokenId);
// Clear approvals
_approve(address(0), tokenId);
_balances[owner] -= 1;
delete _owners[tokenId];
emit Transfer(owner, address(0), tokenId);
_afterTokenTransfer(owner, address(0), tokenId);
}
/**
* @dev Transfers `tokenId` from `from` to `to`.
* As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
*
* Emits a {Transfer} event.
*/
function _transfer(
address from,
address to,
uint256 tokenId
) internal virtual {
require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
require(to != address(0), "ERC721: transfer to the zero address");
_beforeTokenTransfer(from, to, tokenId);
// Clear approvals from the previous owner
_approve(address(0), tokenId);
_balances[from] -= 1;
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
_afterTokenTransfer(from, to, tokenId);
}
/**
* @dev Approve `to` to operate on `tokenId`
*
* Emits an {Approval} event.
*/
function _approve(address to, uint256 tokenId) internal virtual {
_tokenApprovals[tokenId] = to;
emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Emits an {ApprovalForAll} event.
*/
function _setApprovalForAll(
address owner,
address operator,
bool approved
) internal virtual {
require(owner != operator, "ERC721: approve to caller");
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
/**
* @dev Reverts if the `tokenId` has not been minted yet.
*/
function _requireMinted(uint256 tokenId) internal view virtual {
require(_exists(tokenId), "ERC721: invalid token ID");
}
/**
* @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
* The call is not executed if the target address is not a contract.
*
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param data bytes optional data to send along with the call
* @return bool whether the call correctly returned the expected magic value
*/
function _checkOnERC721Received(
address from,
address to,
uint256 tokenId,
bytes memory data
) private returns (bool) {
if (to.isContract()) {
try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
return retval == IERC721Receiver.onERC721Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
revert("ERC721: transfer to non ERC721Receiver implementer");
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
} else {
return true;
}
}
/**
* @dev Hook that is called before any token transfer. This includes minting
* and burning.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
* transferred to `to`.
* - When `from` is zero, `tokenId` will be minted for `to`.
* - When `to` is zero, ``from``'s `tokenId` will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 tokenId
) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(
address from,
address to,
uint256 tokenId
) internal virtual {}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/extensions/ERC721Burnable.sol)
pragma solidity ^0.8.0;
import "../ERC721.sol";
import "../../../utils/Context.sol";
/**
* @title ERC721 Burnable Token
* @dev ERC721 Token that can be burned (destroyed).
*/
abstract contract ERC721Burnable is Context, ERC721 {
/**
* @dev Burns `tokenId`. See {ERC721-_burn}.
*
* Requirements:
*
* - The caller must own `tokenId` or be an approved operator.
*/
function burn(uint256 tokenId) public virtual {
//solhint-disable-next-line max-line-length
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner nor approved");
_burn(tokenId);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/ERC721Enumerable.sol)
pragma solidity ^0.8.0;
import "../ERC721.sol";
import "./IERC721Enumerable.sol";
/**
* @dev This implements an optional extension of {ERC721} defined in the EIP that adds
* enumerability of all the token ids in the contract as well as all token ids owned by each
* account.
*/
abstract contract ERC721Enumerable is ERC721, IERC721Enumerable {
// Mapping from owner to list of owned token IDs
mapping(address => mapping(uint256 => uint256)) private _ownedTokens;
// Mapping from token ID to index of the owner tokens list
mapping(uint256 => uint256) private _ownedTokensIndex;
// Array with all token ids, used for enumeration
uint256[] private _allTokens;
// Mapping from token id to position in the allTokens array
mapping(uint256 => uint256) private _allTokensIndex;
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) {
return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
return _ownedTokens[owner][index];
}
/**
* @dev See {IERC721Enumerable-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _allTokens.length;
}
/**
* @dev See {IERC721Enumerable-tokenByIndex}.
*/
function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds");
return _allTokens[index];
}
/**
* @dev Hook that is called before any token transfer. This includes minting
* and burning.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
* transferred to `to`.
* - When `from` is zero, `tokenId` will be minted for `to`.
* - When `to` is zero, ``from``'s `tokenId` will be burned.
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 tokenId
) internal virtual override {
super._beforeTokenTransfer(from, to, tokenId);
if (from == address(0)) {
_addTokenToAllTokensEnumeration(tokenId);
} else if (from != to) {
_removeTokenFromOwnerEnumeration(from, tokenId);
}
if (to == address(0)) {
_removeTokenFromAllTokensEnumeration(tokenId);
} else if (to != from) {
_addTokenToOwnerEnumeration(to, tokenId);
}
}
/**
* @dev Private function to add a token to this extension's ownership-tracking data structures.
* @param to address representing the new owner of the given token ID
* @param tokenId uint256 ID of the token to be added to the tokens list of the given address
*/
function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
uint256 length = ERC721.balanceOf(to);
_ownedTokens[to][length] = tokenId;
_ownedTokensIndex[tokenId] = length;
}
/**
* @dev Private function to add a token to this extension's token tracking data structures.
* @param tokenId uint256 ID of the token to be added to the tokens list
*/
function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
_allTokensIndex[tokenId] = _allTokens.length;
_allTokens.push(tokenId);
}
/**
* @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
* while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
* gas optimizations e.g. when performing a transfer operation (avoiding double writes).
* This has O(1) time complexity, but alters the order of the _ownedTokens array.
* @param from address representing the previous owner of the given token ID
* @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
*/
function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
// To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
// then delete the last slot (swap and pop).
uint256 lastTokenIndex = ERC721.balanceOf(from) - 1;
uint256 tokenIndex = _ownedTokensIndex[tokenId];
// When the token to delete is the last token, the swap operation is unnecessary
if (tokenIndex != lastTokenIndex) {
uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
_ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
_ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
}
// This also deletes the contents at the last position of the array
delete _ownedTokensIndex[tokenId];
delete _ownedTokens[from][lastTokenIndex];
}
/**
* @dev Private function to remove a token from this extension's token tracking data structures.
* This has O(1) time complexity, but alters the order of the _allTokens array.
* @param tokenId uint256 ID of the token to be removed from the tokens list
*/
function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
// To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
// then delete the last slot (swap and pop).
uint256 lastTokenIndex = _allTokens.length - 1;
uint256 tokenIndex = _allTokensIndex[tokenId];
// When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
// rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
// an 'if' statement (like in _removeTokenFromOwnerEnumeration)
uint256 lastTokenId = _allTokens[lastTokenIndex];
_allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
_allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
// This also deletes the contents at the last position of the array
delete _allTokensIndex[tokenId];
_allTokens.pop();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool _approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The caller is not authorized to call the function.
error Unauthorized();
/// @dev The `newOwner` cannot be the zero address.
error NewOwnerIsZeroAddress();
/// @dev The `pendingOwner` does not have a valid handover request.
error NoHandoverRequest();
/// @dev Cannot double-initialize.
error AlreadyInitialized();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ownership is transferred from `oldOwner` to `newOwner`.
/// This event is intentionally kept the same as OpenZeppelin's Ownable to be
/// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
/// despite it not being as lightweight as a single argument event.
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
/// @dev An ownership handover to `pendingOwner` has been requested.
event OwnershipHandoverRequested(address indexed pendingOwner);
/// @dev The ownership handover to `pendingOwner` has been canceled.
event OwnershipHandoverCanceled(address indexed pendingOwner);
/// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
/// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
/// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The owner slot is given by:
/// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
/// It is intentionally chosen to be a high value
/// to avoid collision with lower slots.
/// The choice of manual storage layout is to enable compatibility
/// with both regular and upgradeable contracts.
bytes32 internal constant _OWNER_SLOT =
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;
/// The ownership handover slot of `newOwner` is given by:
/// ```
/// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
/// let handoverSlot := keccak256(0x00, 0x20)
/// ```
/// It stores the expiry timestamp of the two-step ownership handover.
uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
function _guardInitializeOwner() internal pure virtual returns (bool guard) {}
/// @dev Initializes the owner directly without authorization guard.
/// This function must be called upon initialization,
/// regardless of whether the contract is upgradeable or not.
/// This is to enable generalization to both regular and upgradeable contracts,
/// and to save gas in case the initial owner is not the caller.
/// For performance reasons, this function will not check if there
/// is an existing owner.
function _initializeOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
if sload(ownerSlot) {
mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
revert(0x1c, 0x04)
}
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
} else {
/// @solidity memory-safe-assembly
assembly {
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(_OWNER_SLOT, newOwner)
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
}
}
/// @dev Sets the owner directly without authorization guard.
function _setOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
}
} else {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, newOwner)
}
}
}
/// @dev Throws if the sender is not the owner.
function _checkOwner() internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// If the caller is not the stored owner, revert.
if iszero(eq(caller(), sload(_OWNER_SLOT))) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Returns how long a two-step ownership handover is valid for in seconds.
/// Override to return a different value if needed.
/// Made internal to conserve bytecode. Wrap it in a public function if needed.
function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
return 48 * 3600;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC UPDATE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Allows the owner to transfer the ownership to `newOwner`.
function transferOwnership(address newOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
if iszero(shl(96, newOwner)) {
mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
revert(0x1c, 0x04)
}
}
_setOwner(newOwner);
}
/// @dev Allows the owner to renounce their ownership.
function renounceOwnership() public payable virtual onlyOwner {
_setOwner(address(0));
}
/// @dev Request a two-step ownership handover to the caller.
/// The request will automatically expire in 48 hours (172800 seconds) by default.
function requestOwnershipHandover() public payable virtual {
unchecked {
uint256 expires = block.timestamp + _ownershipHandoverValidFor();
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to `expires`.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), expires)
// Emit the {OwnershipHandoverRequested} event.
log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
}
}
}
/// @dev Cancels the two-step ownership handover to the caller, if any.
function cancelOwnershipHandover() public payable virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), 0)
// Emit the {OwnershipHandoverCanceled} event.
log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
}
}
/// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
/// Reverts if there is no existing ownership handover requested by `pendingOwner`.
function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
let handoverSlot := keccak256(0x0c, 0x20)
// If the handover does not exist, or has expired.
if gt(timestamp(), sload(handoverSlot)) {
mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
revert(0x1c, 0x04)
}
// Set the handover slot to 0.
sstore(handoverSlot, 0)
}
_setOwner(pendingOwner);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC READ FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the owner of the contract.
function owner() public view virtual returns (address result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(_OWNER_SLOT)
}
}
/// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
function ownershipHandoverExpiresAt(address pendingOwner)
public
view
virtual
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
// Compute the handover slot.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
// Load the handover slot.
result := sload(keccak256(0x0c, 0x20))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Marks a function as only callable by the owner.
modifier onlyOwner() virtual {
_checkOwner();
_;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
library CommitReveal {
event NewEpoch(uint256 indexed epoch, uint64 indexed revealBlock);
// copy pasta from the legendary Checks contract by jalil + mouseDev
struct Epoch {
// The source of randomness for tokens from this epoch
uint128 randomness;
// The block at which this epoch was / is revealed
uint64 revealBlock;
// Whether the epoch has been instantiated
bool committed;
// Whether the epoch has been revealed
bool revealed;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
/// @title EIP-721 Metadata Update Extension
interface IERC4906 is IERC165, IERC721 {
/// @dev This event emits when the metadata of a token is changed.
/// Third-party platforms such as NFT marketplaces can listen to
/// the event and auto-update the tokens in their apps.
event MetadataUpdate(uint256 _tokenId);
event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
/// A shared interface for data storage of the Chonks
interface IChonkStorage {
struct StoredChonk {
// The token id of the Chonk
uint256 tokenId;
// The token id of the head, 0 if unequipped
uint256 headId;
// The token id of the hair, 0 if unequipped
uint256 hairId;
// The token id of the face, 0 if unequipped
uint256 faceId;
// The token id of the accessory, 0 if unequipped
uint256 accessoryId;
// The token id of the top, 0 if unequipped
uint256 topId;
// The token id of the bottom, 0 if unequipped
uint256 bottomId;
// The token id of the shoes, 0 if unequipped
uint256 shoesId;
// Randomly set in ChonksMain.mint() but can be updated by holder at any time
uint8 bodyIndex;
// RRGGBB colour of the background, default blue #0D6E9D set in ChonksMain.sol mint(), and setBackgroundColor()
string backgroundColor;
// Bool to determine whether to render in 3D or not
bool render3D;
}
struct BodyMetadata {
// Refers to the number used in ChonksMain.addNewBody; Not token id
uint256 bodyIndex;
// e.g. 'Skin Tone 1'
string bodyName;
// bytes memory colorMap = new bytes(2700); 30x30 grid by 3 bytes (rgb, each colour is a byte, or 2 hex digits);
bytes colorMap;
// The map of possible 3D traits
bytes zMap;
}
struct ChonkData {
string backgroundColor;
string bodyName;
// string rendererSet;
uint256 numOfItemsInBackpack;
string[2] descriptionParts;
}
/// Events
event Mint(address indexed owner, uint256 indexed tokenId);
event Equip(address indexed owner, uint256 indexed tokenId, uint256 indexed traitTokenId, uint8 traitCategory);
event Unequip(address indexed owner, uint256 indexed tokenId, uint8 traitCategory);
event EquipAll(address indexed owner, uint256 indexed tokenId);
event UnequipAll(address indexed owner, uint256 indexed tokenId);
event BackgroundColor(address indexed owner, uint256 indexed tokenId, string color);
event BodyIndex(address indexed owner, uint256 indexed tokenId, uint8 _bodyIndex);
event Render3D(address indexed owner, uint256 indexed tokenId, bool renderZ);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
import { CommitReveal } from "../common/CommitReveal.sol";
import { TraitCategory } from "../TraitCategory.sol";
interface ITraitStorage {
struct StoredTrait {
// The epoch when it was minted
uint256 epoch;
// If the trait has been revealed or not
bool isRevealed;
// Data used to calculate the commit-reveal
uint256 seed;
// The RenderMinter contract that can `explain` the trait
address dataMinterContract;
// A sequential numbering of the traits that exist in the collection
uint256 traitIndex;
// e.g. Head, Top, Shoes, etc.
TraitCategory.Name traitType;
}
struct Traits {
// A mapping of each token ID to what it actually is (the StoredTrait)
mapping(uint256 => StoredTrait) all;
// Collection-wide epoch; The current epoch index of the mapping below
uint256 epoch;
// A mapping of the above epoch (or past epochs) to the commit-reveal scheme. The epoch in StoredTrait is the epoch when that trait was *minted*
mapping(uint256 => CommitReveal.Epoch) epochs;
}
// With Bodies, we just hardcode 5 Bodies in contracts
// But with Traits, we want to be able to add them, hence this struct
struct TraitMetadata {
// Refers to the number used in ChonkTraits.addNewTrait; not a token ID
uint256 traitIndex;
// e.g. 'Blue top'
string traitName;
// e.g. TraitCategory.Name.Top
TraitCategory.Name traitType;
// The row-major byte array of the 2d version of a Trait
bytes colorMap;
// The row-major byte array of the 3d version of a Trait
bytes zMap;
// The DataMinter contract responsible for this trait
/// @dev Cast as not an address
address dataMinterContract;
// Address of creator
address creatorAddress;
// Name of creator
string creatorName;
// Which Release the Trait was in
string release;
}
// Event for when all approvals are invalidated
event AllOperatorApprovalsInvalidated(uint256 indexed tokenId);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
library TraitCategory {
enum Name {
None, // 0
Head, // 1
Hair, // 2
Face, // 3
Accessory, // 4
Top, // 5
Bottom, // 6
Shoes // 7
}
function toString(Name name) public pure returns (string memory) {
if (name == Name.Head) return "Head";
if (name == Name.Hair) return "Hair";
if (name == Name.Face) return "Face";
if (name == Name.Accessory) return "Accessory";
if (name == Name.Top) return "Top";
if (name == Name.Bottom) return "Bottom";
if (name == Name.Shoes) return "Shoes";
return "";
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
import { IChonkStorage } from "../interfaces/IChonkStorage.sol";
import { ITraitStorage } from "../interfaces/ITraitStorage.sol";
import { TraitCategory } from "../TraitCategory.sol";
import { Utils } from "../common/Utils.sol";
contract TraitRenderer {
struct Ghost {
bytes colorMap;
bytes zMap;
}
Ghost public ghost;
string private constant SVG_START = '<svg shape-rendering="crispEdges" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg"><style>rect{width:1px; height: 1px;} .bg{width:30px; height: 30px;} </style><rect class="bg" fill="#0D6E9D"/>';
function renderAsDataUri(
uint256 _tokenId,
ITraitStorage.StoredTrait memory trait,
ITraitStorage.TraitMetadata memory metadata,
string memory ghostSvg,
string memory traitSvg,
string[2] memory descriptionParts
) public pure returns (string memory) {
string memory fullSvg;
string memory attributes;
if (trait.isRevealed) {
attributes = string.concat(
'"attributes":[',
stringTrait(
TraitCategory.toString(trait.traitType),
metadata.traitName
),
',',
stringTrait(
'Creator',
metadata.creatorName
),
',',
stringTrait(
'Release',
metadata.release
),
']'
);
} else {
attributes = '"attributes":[]';
traitSvg = '<svg></svg>';
}
fullSvg = wrapWithSvgTag(string.concat(ghostSvg, traitSvg));
string memory image = string.concat(
'"image":"data:image/svg+xml;base64,',
Utils.encode(bytes(fullSvg)),
'"'
);
string memory json = string.concat(
'{"name":"Chonk Trait #',
Utils.toString(_tokenId),
'","description":"',
descriptionParts[0],
Utils.toString(_tokenId),
descriptionParts[1],
'",',
attributes,
',',
image,
'}'
);
return string.concat("data:application/json;base64,", Utils.encode(bytes(json)));
}
// Update any functions that need the SVG_START, for example:
function wrapWithSvgTag(string memory content) public pure returns (string memory) {
return string.concat(SVG_START, content, '</svg>');
}
function createSvgFromPixels(bytes memory _pixels) public pure returns (bytes memory svgParts) {
string[16] memory hexSymbols = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"];
string[30] memory coords = ["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29"];
for (uint i; i < 4500; i += 5) {
if (_pixels[i] > 0) {
uint x = (i / 5) % 30;
uint y = (i / 5) / 30;
bytes memory color = abi.encodePacked(
hexSymbols[uint8(_pixels[i + 2]) >> 4],
hexSymbols[uint8(_pixels[i + 2]) & 0xf],
hexSymbols[uint8(_pixels[i + 3]) >> 4],
hexSymbols[uint8(_pixels[i + 3]) & 0xf],
hexSymbols[uint8(_pixels[i + 4]) >> 4],
hexSymbols[uint8(_pixels[i + 4]) & 0xf]
);
svgParts = abi.encodePacked(
svgParts,
'<rect x="', coords[x],
'" y="', coords[y],
'" width="1" height="1" fill="#', color, '"/>'
);
}
}
}
function getTraitImage(bytes memory colorMap) public pure returns (bytes memory) {
uint256 length = colorMap.length;
// require(length > 0 && length % 5 == 0, "Invalid trait bytes length"); //TODO: put back in... only we can add a colorMap so do we need it?
bytes memory pixels = new bytes(30 * 30 * 5); // 30x30 grid with 5 bytes per pixel
uint256 pixelCount = length / 5;
for (uint256 i; i < pixelCount; i++) {
uint256 offset = i * 5;
uint8 x = uint8(colorMap[offset]);
uint8 y = uint8(colorMap[offset + 1]);
uint256 index = (uint256(y) * 30 + uint256(x)) * 5;
// Set the pixel data in the pixels array
unchecked {
pixels[index] = colorMap[offset];
pixels[index + 1] = colorMap[offset + 1];
pixels[index + 2] = colorMap[offset + 2];
pixels[index + 3] = colorMap[offset + 3];
pixels[index + 4] = colorMap[offset + 4];
}
}
return pixels;
}
function getGhostSvg() public view returns (string memory svg) {
bytes memory pixels = getTraitImage(ghost.colorMap);
bytes memory svgParts = createSvgFromPixels(pixels);
return string(abi.encodePacked('<g id="ghost" class="g" style="opacity: 50%;">', svgParts, '</g>'));
}
function setGhostMaps(bytes memory _colorMap, bytes memory _zMap) public {
ghost.colorMap = _colorMap;
ghost.zMap = _zMap;
}
function getTraitImageSvg(bytes memory colorMap) public pure returns (string memory svg) {
bytes memory pixels = getTraitImage(colorMap);
bytes memory svgParts = createSvgFromPixels(pixels);
return string(abi.encodePacked('<g id="Trait">', svgParts, '</g>'));
}
function getSvgAndMetadataTrait(
ITraitStorage.StoredTrait memory trait,
uint256 traitId,
ITraitStorage.TraitMetadata memory metadata
) public pure returns(string memory traitSvg, string memory traitAttributes) {
if (trait.isRevealed && traitId > 0) {
traitAttributes = stringTrait(
TraitCategory.toString(metadata.traitType),
metadata.traitName
);
traitSvg = getTraitImageSvg(metadata.colorMap);
} else {
traitAttributes = '{}';
traitSvg = '<svg></svg>';
}
}
function getSVGZmapAndMetadataTrait(
ITraitStorage.StoredTrait memory trait,
uint256 traitId,
ITraitStorage.TraitMetadata memory metadata
) public pure returns(
string memory traitSvg,
bytes memory traitZmap,
string memory traitAttributes
) {
if (trait.isRevealed && traitId > 0) {
traitSvg = getTraitImageSvg(metadata.colorMap);
traitAttributes = stringTrait(
TraitCategory.toString(metadata.traitType),
metadata.traitName
);
traitZmap = metadata.zMap;
} else {
traitSvg = '<svg></svg>';
traitAttributes = '{}';
traitZmap = '';
}
}
function callGetSvgAndMetadataTrait(
uint256 traitId,
string memory _traitsSvg,
string memory _traitsAttributes,
ITraitStorage.StoredTrait memory storedTrait,
ITraitStorage.TraitMetadata memory metadata
) public pure returns (string memory traitsSvg, string memory traitsAttributes) {
string memory traitAttribute;
string memory traitSvg;
(traitSvg, traitAttribute) = getSvgAndMetadataTrait(storedTrait, traitId, metadata);
if (bytes(_traitsAttributes).length == 0) {
traitsSvg = traitSvg;
traitsAttributes = traitAttribute;
} else {
traitsSvg = string.concat(_traitsSvg, traitSvg);
traitsAttributes = string.concat(_traitsAttributes, ',', traitAttribute);
}
}
function callGetSVGZmapAndMetadataTrait(
uint256 traitId,
string memory _traitsSvg,
string memory _traitsAttributes,
bytes memory _traitZMaps,
ITraitStorage.StoredTrait memory storedTrait,
ITraitStorage.TraitMetadata memory metadata
) public pure returns (
string memory traitsSvg,
string memory traitsAttributes,
bytes memory traitZMaps
) {
string memory traitAttribute;
string memory traitSvg;
bytes memory traitZMap;
(traitSvg, traitZMap, traitAttribute) = getSVGZmapAndMetadataTrait(storedTrait, traitId, metadata);
if (bytes(_traitsAttributes).length == 0) {
traitsSvg = traitSvg;
traitsAttributes = traitAttribute;
traitZMaps = traitZMap;
} else {
traitsSvg = string.concat(_traitsSvg, traitSvg);
traitsAttributes = string.concat(_traitsAttributes, ',', traitAttribute);
traitZMaps = bytes.concat(_traitZMaps, traitZMap);
}
}
function getSvgAndMetadata(
IChonkStorage.StoredChonk memory storedChonk,
function(uint256, string memory, string memory) external view returns (string memory, string memory) callGetSvgAndMetadataTraitFn
) public view returns (string memory traitsSvg, string memory traitsAttributes) {
if (storedChonk.shoesId > 0)
(traitsSvg, traitsAttributes) = callGetSvgAndMetadataTraitFn(storedChonk.shoesId, traitsSvg, traitsAttributes);
if (storedChonk.bottomId > 0)
(traitsSvg, traitsAttributes) = callGetSvgAndMetadataTraitFn(storedChonk.bottomId, traitsSvg, traitsAttributes);
if (storedChonk.topId > 0)
(traitsSvg, traitsAttributes) = callGetSvgAndMetadataTraitFn(storedChonk.topId, traitsSvg, traitsAttributes);
if (storedChonk.hairId > 0)
(traitsSvg, traitsAttributes) = callGetSvgAndMetadataTraitFn(storedChonk.hairId, traitsSvg, traitsAttributes);
if (storedChonk.faceId > 0)
(traitsSvg, traitsAttributes) = callGetSvgAndMetadataTraitFn(storedChonk.faceId, traitsSvg, traitsAttributes);
if (storedChonk.headId > 0)
(traitsSvg, traitsAttributes) = callGetSvgAndMetadataTraitFn(storedChonk.headId, traitsSvg, traitsAttributes);
if (storedChonk.accessoryId > 0)
(traitsSvg, traitsAttributes) = callGetSvgAndMetadataTraitFn(storedChonk.accessoryId, traitsSvg, traitsAttributes);
}
function getSvgZmapsAndMetadata(
IChonkStorage.StoredChonk memory storedChonk,
function(uint256, string memory, string memory, bytes memory) external view returns (string memory, string memory, bytes memory) callGetSVGZmapAndMetadataTraitFn
) public view returns (string memory traitsSvg, bytes memory traitZMaps, string memory traitsAttributes) {
if (storedChonk.shoesId > 0)
(traitsSvg, traitsAttributes, traitZMaps) = callGetSVGZmapAndMetadataTraitFn(storedChonk.shoesId, traitsSvg, traitsAttributes, traitZMaps);
if (storedChonk.bottomId > 0)
(traitsSvg, traitsAttributes, traitZMaps) = callGetSVGZmapAndMetadataTraitFn(storedChonk.bottomId, traitsSvg, traitsAttributes, traitZMaps);
if (storedChonk.topId > 0)
(traitsSvg, traitsAttributes, traitZMaps) = callGetSVGZmapAndMetadataTraitFn(storedChonk.topId, traitsSvg, traitsAttributes, traitZMaps);
if (storedChonk.hairId > 0)
(traitsSvg, traitsAttributes, traitZMaps) = callGetSVGZmapAndMetadataTraitFn(storedChonk.hairId, traitsSvg, traitsAttributes, traitZMaps);
if (storedChonk.faceId > 0)
(traitsSvg, traitsAttributes, traitZMaps) = callGetSVGZmapAndMetadataTraitFn(storedChonk.faceId, traitsSvg, traitsAttributes, traitZMaps);
if (storedChonk.headId > 0)
(traitsSvg, traitsAttributes, traitZMaps) = callGetSVGZmapAndMetadataTraitFn(storedChonk.headId, traitsSvg, traitsAttributes, traitZMaps);
if (storedChonk.accessoryId > 0)
(traitsSvg, traitsAttributes, traitZMaps) = callGetSVGZmapAndMetadataTraitFn(storedChonk.accessoryId, traitsSvg, traitsAttributes, traitZMaps);
}
function stringTrait(string memory traitName, string memory traitValue) internal pure returns (string memory) {
return string.concat(
'{"trait_type":"',
traitName,
'","value":"',
traitValue,
'"}'
);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
// OpenZeppelin/Solady Imports
import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import { ERC721Enumerable } from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { MerkleProofLib } from "solady/utils/MerkleProofLib.sol";
import { Ownable } from "solady/auth/Ownable.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
// ERC-6551 Imports
import { IAccountImplementation } from "./interfaces/TBABoilerplate/IAccountImplementation.sol";
import { IAccountProxy } from "./interfaces/TBABoilerplate/IAccountProxy.sol";
import { IRegistry } from "./interfaces/TBABoilerplate/IRegistry.sol";
// Renderers
import { MainRenderer2D } from "./renderers/MainRenderer2D.sol";
import { MainRenderer3D } from "./renderers/MainRenderer3D.sol";
// The Traits ERC-721 Contract
import { ChonkTraits } from "./ChonkTraits.sol";
import { ChonkEquipHelper } from "./ChonkEquipHelper.sol";
// Associated Interfaces and Libraries
import { IERC4906 } from "./interfaces/IERC4906.sol";
import { IChonkStorage } from "./interfaces/IChonkStorage.sol";
import { ITraitStorage } from "./interfaces/ITraitStorage.sol";
import { TraitCategory } from "./TraitCategory.sol";
// Other Chonks Associated Contracts
import { ChonksMarket } from "./ChonksMarket.sol";
import { FirstReleaseDataMinter } from "./FirstReleaseDataMinter.sol";
// NOTE: Console logging here wont really work bc we're testing against the deployed ChonksMain rather than any changes that woudl happen in here
// import { console } from "forge-std/console.sol";
/*
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:cllllllllllllllllllllllllllllllllc:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:okOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOko:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:clllxOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOdlllc:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:okOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOko:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ccldOOOOOOO0KKKxllldOOOOOOOOOOOO0KKKxllldOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ldxxkOOOOOOOXWMNl ;kOOOOOOOOOOOXWMWl ;kOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ldxxkOOOOOOOXMMWl ;kkkkkkkkkkkOXMMWl ;kOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ldxxkOOOOOOOXMMWl ,dxxxxxxxxxxxKWMWl ;kOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:coooxOOOOOOOKNNXd'.'cxkkkxxkkkkkk0XNXd'.'lkOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:;:oOOOOOOOOOOOOOkkOOOOOOOOOOOOOOOOOOkkOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:lddxkOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOkxxdl:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::oOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOo::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:okkkkkkkkkkkkkkkkkkkkkkkkkkkkxxddl:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:lxxxxxxxxxxxxxxxxxxxxxxxxxxxxl::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:cllldxkxxxxkkkkkkkkkkkkkkkkkkkkkxdlllc:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:okOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOko:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;clllxOOOOOOOOkkkOOOOOOOOOOOOOOkkkOOOOOOOOdlllc:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:okOOOOOOOOOOkxxxkOOOOOOOOOOOOkxxxkOOOOOOOOOOko:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOkkkOOOOkkkkkOOOOOOOOOOOOkkxkkOOOOkkkOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOkxxxkOOOOOOOOOOOOOOOOOOOOOOOOOOOOkxxxkOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOkxxxkOOOOOOOOOOOOOOOOOOOOOOOOOOOOkxxxkOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOkxxxkOOOOOOOOOOOOOOOOOOOOOOOOOOOOkxxxkOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ldxddoooxOOOOOOOOOOOOOOOOOOOOOOOOOOOOxoooddxdl:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::::::oOOOOOOOOOOOOOOOOOOOOOOOOOOOOo::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOOOOOkxdxkOOOOOOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOOOOOo:::okOOOOOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOkxddl:;:lddxxkOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOo::::;;;:::::oOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ldddddxdl:;;;;;;;;;;:ldxxxxxdl:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::::::::;;;;;;;;;;;;:::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
*/
contract ChonksMain is IChonkStorage, IERC165, ERC721Enumerable, Ownable, IERC4906, ReentrancyGuard {
// ERC-6551 Boilerplate addresses
IRegistry constant REGISTRY = IRegistry(0x000000006551c19487814612e58FE06813775758);
address constant ACCOUNT_PROXY = 0x55266d75D1a14E4572138116aF39863Ed6596E7F;
address constant ACCOUNT_IMPLEMENTATION = 0x41C8f39463A868d3A88af00cd0fe7102F30E44eC;
// constants
uint8 constant MAX_MINT_AMOUNT = 10;
// Storage for Body metadata
mapping(uint256 => IChonkStorage.BodyMetadata) public bodyIndexToMetadata;
/// The address of the ERC-721 Traits contract
ChonkTraits public traitsContract;
// The address of the ChonksMarket contract
ChonksMarket public marketplace;
// The address of the ChonkEquipHelper helper contract
ChonkEquipHelper public chonkEquipHelper;
// The contract that handles rendering and minting the first release of traits
FirstReleaseDataMinter public firstReleaseDataMinter;
// The render contract that handles SVG generation
MainRenderer2D public mainRenderer2D;
// The render contract that handles 3d generation
MainRenderer3D public mainRenderer3D;
uint256 public maxTraitsToOutput = 99;
uint256 public nextTokenId;
address public withdrawAddress;
uint256 public price;
uint256 public initialMintStartTime;
string[2] descriptionParts;
// The date when contract was deployed, a year after which, certain functions can't be called by the owner
uint256 public immutable deploymentTime;
mapping(uint256 tokenID => StoredChonk chonk) public chonkTokens;
// Mapping of tokenID to the TBA account address
mapping(uint256 => address) public tokenIdToTBAAccountAddress;
// Mapping of the TBA account address to its tokenId. Great for getting from Trait Token ID to Chonk Token ID or Owner
mapping(address => uint256) public tbaAddressToTokenId;
// Chonk ID to approved addresses
mapping(uint256 chonkId => address[] operators) public chonkIdToApprovedOperators;
// Mappings for Merkles
mapping(address => bool) public collectionsAddressDidUse;
mapping(address => bool) public friendsAddressDidUse;
mapping(address => bool) public creatorsAddressDidUse;
/// Merkle Roots
bytes32 public collectionsMerkle;
bytes32 public friendsMerkle;
bytes32 public creatorsMerkle;
/// Errors
error CanOnlyReserveFirstTwo();
error CantTransferDuringMint();
error CantTransferToTBAs();
error ChonkDoesntExist();
error FirstReleaseDataMinterNotSet();
error IncorrectChonkOwner();
error IncorrectTBAOwner();
error IncorrectTraitType();
error InsufficientFunds();
error InvalidBodyIndex();
error InvalidColor();
error InvalidTraitCount();
error InvalidTraitCategory();
error InvalidMintAmount();
error MintEnded();
error MintNotStarted();
error MintStartTimeAlreadySet();
error Timelocked();
error TraitLengthsMustMatch();
error UseUnequip();
error WithdrawFailed();
/// Modifier
modifier onlyChonkOwner(uint256 _chonkId) {
if (msg.sender != ownerOf(_chonkId)) revert IncorrectChonkOwner();
_;
}
/// Constructor
constructor() ERC721("Chonks", "CHONKS") {
_initializeOwner(msg.sender);
deploymentTime = block.timestamp;
}
// Don't need these anymore post-mint
// function teamReserve() public onlyOwner {
// if (totalSupply() > 2) revert CanOnlyReserveFirstTwo();
// _mintInternal(msg.sender, 2, 7);
// }
// function teamMint(address _to, uint256 _amount, uint8 _traitCount) public onlyOwner {
// if (_traitCount < 4 || _traitCount > 7) revert InvalidTraitCount();
// if (initialMintStartTime == 0 || block.timestamp < initialMintStartTime) revert MintNotStarted();
// if (block.timestamp > initialMintStartTime + 26 hours) revert MintEnded();
// _mintInternal(_to, _amount, _traitCount);
// }
// function mint(uint256 _amount, bytes32[] memory _merkleProof) public payable {
// if (address(firstReleaseDataMinter) == address(0)) revert FirstReleaseDataMinterNotSet();
// if (_amount == 0 || _amount > MAX_MINT_AMOUNT) revert InvalidMintAmount();
// if (initialMintStartTime == 0 || block.timestamp < initialMintStartTime) revert MintNotStarted();
// if (block.timestamp > initialMintStartTime + 24 hours) revert MintEnded();
// if (msg.value != price * _amount) revert InsufficientFunds();
// uint8 traitCount = 4;
// bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
// if (MerkleProofLib.verify(_merkleProof, collectionsMerkle, leaf)) {
// if (!collectionsAddressDidUse[msg.sender]) {
// traitCount = 5;
// collectionsAddressDidUse[msg.sender] = true;
// }
// } else if (MerkleProofLib.verify(_merkleProof, friendsMerkle, leaf)) {
// if (!friendsAddressDidUse[msg.sender]) {
// traitCount = 6;
// friendsAddressDidUse[msg.sender] = true;
// }
// } else if (MerkleProofLib.verify(_merkleProof, creatorsMerkle, leaf)) {
// if (!creatorsAddressDidUse[msg.sender]) {
// traitCount = 7;
// creatorsAddressDidUse[msg.sender] = true;
// }
// }
// _mintInternal(msg.sender, _amount, traitCount);
// }
// function _mintInternal(address _to, uint256 _amount, uint8 _traitCount) internal {
// for (uint i; i < _amount; ++i) {
// uint256 tokenId = ++nextTokenId;
// _mint(_to, tokenId);
// address tokenBoundAccountAddress = REGISTRY.createAccount(
// ACCOUNT_PROXY, // implementation address
// 0, // salt
// 8453, // chainId
// address(this), // tokenContract
// tokenId // tokenId
// );
// // Set the cross-reference between tokenId and TBA account address
// tokenIdToTBAAccountAddress[tokenId] = tokenBoundAccountAddress;
// tbaAddressToTokenId[tokenBoundAccountAddress] = tokenId;
// // Initialize the TBA
// IAccountProxy(payable(tokenBoundAccountAddress)).initialize(address(ACCOUNT_IMPLEMENTATION));
// // Mint Traits to equip below
// uint256[] memory traitsIds = firstReleaseDataMinter.safeMintMany(tokenBoundAccountAddress, _traitCount);
// // Initialize the Chonk
// StoredChonk storage chonk = chonkTokens[tokenId];
// chonk.tokenId = tokenId;
// // This randomly picks your Chonk skin color but you can change it any time.
// chonk.bodyIndex = uint8(uint256(keccak256(abi.encodePacked(tokenId))) % 5); // even chance for 5 different bodies
// // Set the default background color
// chonk.backgroundColor = "0D6E9D";
// chonk.shoesId = traitsIds[0];
// chonk.bottomId = traitsIds[1];
// chonk.topId = traitsIds[2];
// chonk.hairId = traitsIds[3];
// }
// }
function getOwnerAndTBAAddressForChonkId(uint256 _chonkId) public view returns (address owner, address tbaAddress) {
owner = ownerOf(_chonkId);
tbaAddress = tokenIdToTBAAccountAddress[_chonkId];
}
/// Equip/Unequip Traits
function equip(uint256 _chonkTokenId, uint256 _traitTokenId) public onlyChonkOwner(_chonkTokenId) {
if (_traitTokenId == 0) revert UseUnequip();
TraitCategory.Name traitType = chonkEquipHelper.equipValidation(_chonkTokenId, _traitTokenId);
_setTrait(_chonkTokenId, traitType, _traitTokenId);
emit Equip(ownerOf(_chonkTokenId), _chonkTokenId, _traitTokenId, uint8(traitType));
}
function unequip(uint256 _chonkTokenId, TraitCategory.Name traitType) public onlyChonkOwner(_chonkTokenId) {
_setTrait(_chonkTokenId, traitType, 0);
emit Unequip(ownerOf(_chonkTokenId), _chonkTokenId, uint8(traitType));
}
function _setTrait(uint256 _chonkTokenId, TraitCategory.Name traitType, uint256 _traitTokenId) internal {
if (traitType == TraitCategory.Name.Head) chonkTokens[_chonkTokenId].headId = _traitTokenId;
else if (traitType == TraitCategory.Name.Hair) chonkTokens[_chonkTokenId].hairId = _traitTokenId;
else if (traitType == TraitCategory.Name.Face) chonkTokens[_chonkTokenId].faceId = _traitTokenId;
else if (traitType == TraitCategory.Name.Accessory) chonkTokens[_chonkTokenId].accessoryId = _traitTokenId;
else if (traitType == TraitCategory.Name.Top) chonkTokens[_chonkTokenId].topId = _traitTokenId;
else if (traitType == TraitCategory.Name.Bottom) chonkTokens[_chonkTokenId].bottomId = _traitTokenId;
else if (traitType == TraitCategory.Name.Shoes) chonkTokens[_chonkTokenId].shoesId = _traitTokenId;
}
function unequipAll(uint256 _chonkTokenId) public onlyChonkOwner(_chonkTokenId) {
StoredChonk storage chonk = chonkTokens[_chonkTokenId];
chonk.headId = 0;
chonk.hairId = 0;
chonk.faceId = 0;
chonk.accessoryId = 0;
chonk.topId = 0;
chonk.bottomId = 0;
chonk.shoesId = 0;
emit UnequipAll(ownerOf(_chonkTokenId), _chonkTokenId);
}
function equipMany(
uint256 _chonkTokenId,
uint256[] calldata _traitTokenIds,
uint8[] calldata _traitCategories
) public onlyChonkOwner(_chonkTokenId) {
if (_traitTokenIds.length != _traitCategories.length) revert TraitLengthsMustMatch();
StoredChonk storage chonk = chonkTokens[_chonkTokenId];
address owner = ownerOf(_chonkTokenId);
address tba = tokenIdToTBAAccountAddress[_chonkTokenId];
for (uint256 i; i < _traitTokenIds.length; i++) {
uint256 _traitTokenId = _traitTokenIds[i];
uint8 _traitCategory = _traitCategories[i];
if (_traitTokenId == 0) revert UseUnequip();
if (_traitCategory == 0 || _traitCategory > 7) revert InvalidTraitCategory();
TraitCategory.Name traitCategoryEnum = TraitCategory.Name(_traitCategory);
chonkEquipHelper.performValidations(tba, _traitTokenId, traitCategoryEnum);
if (_traitCategory == uint8(TraitCategory.Name.Head)) {
chonk.headId = _traitTokenId;
} else if (_traitCategory == uint8(TraitCategory.Name.Hair)) {
chonk.hairId = _traitTokenId;
} else if (_traitCategory == uint8(TraitCategory.Name.Face)) {
chonk.faceId = _traitTokenId;
} else if (_traitCategory == uint8(TraitCategory.Name.Accessory)) {
chonk.accessoryId = _traitTokenId;
} else if (_traitCategory == uint8(TraitCategory.Name.Top)) {
chonk.topId = _traitTokenId;
} else if (_traitCategory == uint8(TraitCategory.Name.Bottom)) {
chonk.bottomId = _traitTokenId;
} else if (_traitCategory == uint8(TraitCategory.Name.Shoes)) {
chonk.shoesId = _traitTokenId;
}
emit Equip(owner, _chonkTokenId, _traitTokenId, _traitCategory);
}
emit EquipAll(owner, _chonkTokenId);
}
/// tokenURI/Rendering
function tokenURI(uint256 _tokenId) public view override returns (string memory) {
if (!_exists(_tokenId)) revert ChonkDoesntExist();
return renderAsDataUri(_tokenId);
}
/// @param _index The index of the body to get the SVG for
/// @return svg The SVG for the body
function getBodyImageSvg(uint256 _index) public view returns (string memory) {
return mainRenderer2D.colorMapToSVG(bodyIndexToMetadata[_index].colorMap);
}
function getBodySVGZmapsAndMetadata(IChonkStorage.StoredChonk memory storedChonk) public view returns (string memory, bytes memory , string memory) {
return (
getBodyImageSvg(storedChonk.bodyIndex),
bodyIndexToMetadata[storedChonk.bodyIndex].zMap,
mainRenderer2D.stringTrait('Body Type', bodyIndexToMetadata[storedChonk.bodyIndex].bodyName)
);
}
function getBodySvgAndMetadata(IChonkStorage.StoredChonk memory storedChonk) public view returns (string memory, string memory) {
return (
getBodyImageSvg(storedChonk.bodyIndex),
mainRenderer2D.stringTrait('Body Type', bodyIndexToMetadata[storedChonk.bodyIndex].bodyName)
);
}
// Returns all necessary ownership info for a Trait
function getFullPictureForTrait(uint256 _chonkTraitTokenId) public view returns (
address traitOwnerTBA,
uint256 chonkTokenId,
address chonkOwner,
bool isEquipped
) {
traitOwnerTBA = traitsContract.ownerOf(_chonkTraitTokenId);
chonkTokenId = tbaAddressToTokenId[traitOwnerTBA];
chonkOwner = ownerOf(chonkTokenId);
isEquipped = checkIfTraitIsEquipped(chonkTokenId, _chonkTraitTokenId);
}
/// @notice Returns the TBA address for a Chonk
function getTBAAddressForChonkId(uint256 _chonkId) public view returns (address) {
return tokenIdToTBAAccountAddress[_chonkId];
}
/// @notice Returns the ChonkId for a TBA
function getChonkIdForTBAAddress(address _tbaAddress) public view returns (uint256) {
return tbaAddressToTokenId[_tbaAddress];
}
function getTraitsForChonkId(uint256 _chonkId) public view returns (uint256[] memory traitTokens) {
address tbaAddress = getTBAAddressForChonkId(_chonkId);
traitTokens = traitsContract.walletOfOwner(tbaAddress);
}
function getBackpackSVGs(uint256 _tokenId) public view returns (string memory) {
return mainRenderer2D.getBackpackSVGs(
address(traitsContract),
getTBAAddressForChonkId(_tokenId),
maxTraitsToOutput
);
}
function _gatherData(uint256 _tokenId) internal view returns (
string memory bodySvg,
bytes memory bodyZmap,
string memory traitsSvg,
bytes memory traitZmaps,
string memory traitsAttributes,
string memory backpackSVGs,
bytes memory fullZmap,
ChonkData memory chonkdata
) {
StoredChonk memory storedChonk = getChonk(_tokenId);
(bodySvg, bodyZmap,) = getBodySVGZmapsAndMetadata(storedChonk);
(traitsSvg, traitZmaps, traitsAttributes) = traitsContract.getSvgZmapsAndMetadata(storedChonk);
backpackSVGs = getBackpackSVGs(_tokenId);
chonkdata.backgroundColor = storedChonk.backgroundColor;
chonkdata.numOfItemsInBackpack = getTraitsForChonkId(_tokenId).length;
chonkdata.bodyName = bodyIndexToMetadata[storedChonk.bodyIndex].bodyName;
chonkdata.descriptionParts = descriptionParts; // stuffing descriptionParts in here to avoid stack too deep
fullZmap = bytes.concat(bodyZmap, traitZmaps);
}
function renderAsDataUri(uint256 _tokenId) public view returns (string memory) {
return (getChonk(_tokenId).render3D) ? renderAsDataUri3D(_tokenId) : renderAsDataUri2D(_tokenId);
}
function renderAsDataUri2D(uint256 _tokenId) public view returns (string memory) {
(
string memory bodySvg,,
string memory traitsSvg,,
string memory traitsAttributes,
string memory backpackSVGs,,
ChonkData memory chonkdata
) = _gatherData(_tokenId);
return mainRenderer2D.renderAsDataUri(
_tokenId,
bodySvg,
traitsSvg,
traitsAttributes,
backpackSVGs,
chonkdata
);
}
function renderAsDataUri3D(uint256 _tokenId) public view returns (string memory) {
(
string memory bodySvg,,
string memory traitsSvg,,
string memory traitsAttributes,,
bytes memory fullZmap,
ChonkData memory chonkdata
) = _gatherData(_tokenId);
return mainRenderer3D.renderAsDataUri(
_tokenId,
bodySvg,
traitsSvg,
traitsAttributes,
fullZmap,
chonkdata
);
}
function chonkMakeover(
uint256 _chonkTokenId,
uint256[] calldata _traitTokenIds,
uint8[] calldata _traitCategories,
uint8 _bodyIndex,
string memory _backgroundColor,
bool _render3D
) public onlyChonkOwner(_chonkTokenId) {
equipMany(_chonkTokenId, _traitTokenIds, _traitCategories);
setBodyIndex(_chonkTokenId, _bodyIndex);
setBackgroundColor(_chonkTokenId, _backgroundColor);
setTokenRender3D(_chonkTokenId, _render3D);
}
/// Getters
// Gets complete zMap for a Chonk, body and traits
function getChonkZMap(uint256 _tokenId) public view returns (string memory) {
bytes memory traitZmaps;
(, traitZmaps,) = traitsContract.getSvgZmapsAndMetadata(getChonk(_tokenId));
return string.concat(
getBodyZMap(_tokenId),
string(traitZmaps)
);
}
function getBodyZMap(uint256 _tokenId) public view returns (string memory) {
bytes memory bodyZmap;
(, bodyZmap,) = getBodySVGZmapsAndMetadata(getChonk(_tokenId));
return string(bodyZmap);
}
function getChonk(uint256 _tokenId) public view returns (IChonkStorage.StoredChonk memory) {
return chonkTokens[_tokenId];
}
function checkIfTraitIsEquipped(uint256 _chonkId, uint256 _traitId) public view returns (bool) {
IChonkStorage.StoredChonk memory storedChonk = getChonk(_chonkId);
return storedChonk.headId == _traitId ||
storedChonk.hairId == _traitId ||
storedChonk.faceId == _traitId ||
storedChonk.accessoryId == _traitId ||
storedChonk.topId == _traitId ||
storedChonk.bottomId == _traitId ||
storedChonk.shoesId == _traitId;
}
/// @dev Returns the token ids the end user's wallet owns
function walletOfOwner(address _owner) public view returns (uint256[] memory) {
uint256 tokenCount = balanceOf(_owner);
uint256[] memory tokensId = new uint256[](tokenCount);
for (uint256 i; i < tokenCount; ++i){
tokensId[i] = tokenOfOwnerByIndex(_owner, i);
}
return tokensId;
}
/// @notice Returns the timestamp one year after contract deployment
function oneYearFromDeployment() public view returns (uint256) {
return deploymentTime + 365 days;
}
function isTimelocked() public view returns (bool) {
return block.timestamp > oneYearFromDeployment();
}
/// Ownable Functions
function addNewBody(uint256 _bodyIndex, string memory _bodyName, bytes memory _colorMap, bytes memory _zMap) public onlyOwner {
if (isTimelocked()) revert Timelocked();
BodyMetadata storage metadata = bodyIndexToMetadata[_bodyIndex];
metadata.bodyIndex = _bodyIndex;
metadata.bodyName = _bodyName;
metadata.colorMap = _colorMap;
metadata.zMap = _zMap;
}
function setTraitsContract(ChonkTraits _address) public onlyOwner {
if (isTimelocked()) revert Timelocked();
traitsContract = _address;
}
function setFirstReleaseDataMinter(address _dataContract) public onlyOwner {
if (isTimelocked()) revert Timelocked();
firstReleaseDataMinter = FirstReleaseDataMinter(_dataContract);
}
function setMainRenderer2D(address _mainRenderer2D) public onlyOwner {
if (isTimelocked()) revert Timelocked();
mainRenderer2D = MainRenderer2D(_mainRenderer2D);
}
function setMainRenderer3D(address _mainRenderer3D) public onlyOwner {
if (isTimelocked()) revert Timelocked();
mainRenderer3D = MainRenderer3D(_mainRenderer3D);
}
function setMarketplace(address _marketplace) public onlyOwner {
if (isTimelocked()) revert Timelocked();
marketplace = ChonksMarket(_marketplace);
}
function setChonkEquipHelper(address _chonkEquipHelper) public onlyOwner {
if (isTimelocked()) revert Timelocked();
chonkEquipHelper = ChonkEquipHelper(_chonkEquipHelper);
}
function setMaxTraitsToOutput(uint256 _maxTraitsToOutput) public onlyOwner {
maxTraitsToOutput = _maxTraitsToOutput;
}
function setMintStartTime(uint256 _initialMintStartTime) public onlyOwner {
if (initialMintStartTime != 0) revert MintStartTimeAlreadySet();
initialMintStartTime = _initialMintStartTime;
}
function setWithdrawAddress(address _withdrawAddress) public onlyOwner {
withdrawAddress = _withdrawAddress;
}
function setPrice(uint256 _priceInWei) public onlyOwner {
price = _priceInWei;
}
function setFriendsMerkleRoot(bytes32 _merkleRoot) public onlyOwner {
friendsMerkle = _merkleRoot;
}
function setCollectionsMerkle(bytes32 _merkleRoot) public onlyOwner {
collectionsMerkle = _merkleRoot;
}
function setCreatorsMerkle(bytes32 _merkleRoot) public onlyOwner {
creatorsMerkle = _merkleRoot;
}
function setDescriptionParts(string[2] memory _descriptionParts) public onlyOwner {
descriptionParts = _descriptionParts;
}
/// Public Setters
function setBodyIndex(uint256 _chonkTokenId, uint8 _bodyIndex) public onlyChonkOwner(_chonkTokenId) {
if (_bodyIndex > 4) revert InvalidBodyIndex();
chonkTokens[_chonkTokenId].bodyIndex = _bodyIndex;
emit BodyIndex(ownerOf(_chonkTokenId), _chonkTokenId, _bodyIndex );
}
function setTokenRender3D(uint256 _tokenId, bool _render3D) public onlyChonkOwner(_tokenId) {
chonkTokens[_tokenId].render3D = _render3D;
emit Render3D(ownerOf(_tokenId), _tokenId, _render3D);
}
function validateColor(string memory _color) internal pure {
bytes memory colorBytes = bytes(_color);
if (colorBytes.length != 6) revert InvalidColor();
// Ensure all characters are valid hex characters (0-9, a-f, A-F)
for (uint i; i < 6; i++) {
if (
!(colorBytes[i] >= 0x30 && colorBytes[i] <= 0x39) && // 0-9
!(colorBytes[i] >= 0x41 && colorBytes[i] <= 0x46) && // A-F
!(colorBytes[i] >= 0x61 && colorBytes[i] <= 0x66) // a-f
) {
revert InvalidColor(); // Invalid character found
}
}
}
function setBackgroundColor(uint256 _chonkTokenId, string memory _color) public onlyChonkOwner(_chonkTokenId) {
validateColor(_color); // Call the helper function
chonkTokens[_chonkTokenId].backgroundColor = _color;
emit BackgroundColor(ownerOf(_chonkTokenId), _chonkTokenId, _color );
}
function setChonkAttributes(uint256 _tokenId, string memory _color, uint8 _bodyIndex, bool _render3D) public onlyChonkOwner(_tokenId) {
validateColor(_color); // Call the helper function
if (_bodyIndex > 4) revert InvalidBodyIndex();
chonkTokens[_tokenId].backgroundColor = _color;
chonkTokens[_tokenId].bodyIndex = _bodyIndex;
chonkTokens[_tokenId].render3D = _render3D;
}
// Boilerplate
function supportsInterface(bytes4 interfaceId) public view override(IERC165, ERC721Enumerable) returns (bool) {
return super.supportsInterface(interfaceId);
}
// Override functions for marketplace compatibility
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal override {
if (from == address(0)) {
super._beforeTokenTransfer(from, to, tokenId);
return;
}
if (block.timestamp < initialMintStartTime + 24 hours) revert CantTransferDuringMint();
// Ensure you can't transfer a Chonk to a TBA (Chonks can't hold Chonks)
if (tbaAddressToTokenId[to] != 0) revert CantTransferToTBAs();
// Cache TBA address and trait tokens to minimize external calls
address tbaAddress = tokenIdToTBAAccountAddress[tokenId];
uint256[] memory chonkIds = walletOfOwner(to);
address[] memory tbas = new address[](chonkIds.length);
for (uint256 j; j < chonkIds.length; ++j) {
tbas[j] = tokenIdToTBAAccountAddress[chonkIds[j]];
}
marketplace.deleteChonkOfferBeforeTokenTransfer(tokenId);
marketplace.deleteChonkBidsBeforeTokenTransfer(tokenId, to);
uint256[] memory traitTokenIds = traitsContract.walletOfOwner(tbaAddress);
for (uint256 i; i < traitTokenIds.length; ++i) {
uint256 traitTokenId = traitTokenIds[i];
// Clean up marketplace offers/bids
marketplace.deleteTraitOffersBeforeTokenTransfer(traitTokenId);
marketplace.deleteTraitBidsBeforeTokenTransfer(traitTokenId, tbas);
// Clean up past approvals for new TBA owner
traitsContract.invalidateAllOperatorApprovals(traitTokenId);
}
super._beforeTokenTransfer(from, to, tokenId);
}
function _afterTokenTransfer(address _from, address, uint256 _tokenId) internal virtual override {
_invalidateAllOperatorApprovals(_tokenId, _from);
}
/// Approvals
function getChonkIdToApprovedOperators(uint256 _chonkId) public view returns (address[] memory) {
return chonkIdToApprovedOperators[_chonkId];
}
function approve(address _operator, uint256 _chonkId) public override(IERC721, ERC721) {
if (msg.sender != ownerOf(_chonkId)) revert Unauthorized();
_incrementApprovals(_chonkId, _operator);
_approve(_operator, _chonkId);
}
function setApprovalForAllChonksMarketplace(uint256 _chonkId, address _operator, bool _approved) public {
address owner = ownerOf(_chonkId);
if (owner != msg.sender) revert Unauthorized();
if (_approved) _incrementApprovals(_chonkId, _operator);
_setApprovalForAll(owner, _operator, _approved);
}
// Please use the function above as it's more appropriate. Traditional marketplaces will use this
function setApprovalForAll(address _operator, bool _approved) public override(IERC721, ERC721) {
if (_approved) {
uint256[] memory chonkIds = walletOfOwner(msg.sender);
// Don't approve if the user doesn't own any Chonks
if (chonkIds.length == 0) revert Unauthorized();
for (uint i; i < chonkIds.length; ++i) {
_incrementApprovals(chonkIds[i], _operator);
}
}
_setApprovalForAll(msg.sender, _operator, _approved);
}
function _incrementApprovals(uint256 _chonkId, address _operator) private {
address[] storage operators = chonkIdToApprovedOperators[_chonkId];
operators.push(_operator);
}
/// @dev – Called on _afterTokenTransfer
/// Prevents subsequent owners from using the previous owner's approvals
function _invalidateAllOperatorApprovals(uint256 _chonkId, address _previousOwner) private {
address[] memory approvals = chonkIdToApprovedOperators[_chonkId];
address tbaForChonk = tokenIdToTBAAccountAddress[_chonkId];
// may need to use tbaAddressToTokenId w/ msg.sender value and check that?
// Invalidate all other approvals including the ChonksMarket
for (uint256 i; i < approvals.length; ++i) {
_setApprovalForAll(_previousOwner, approvals[i], false);
_setApprovalForAll(tbaForChonk, approvals[i], false);
}
delete chonkIdToApprovedOperators[_chonkId];
}
/// Withdraw
function withdraw() public onlyOwner {
(bool success,) = payable(withdrawAddress).call{ value: address(this).balance }("");
if (!success) revert WithdrawFailed();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
import { ChonksMain } from "./ChonksMain.sol";
import { ChonkTraits } from "./ChonkTraits.sol";
import { IChonkStorage } from "./interfaces/IChonkStorage.sol";
import { Ownable } from "solady/auth/Ownable.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract ChonksMarket is Ownable, ReentrancyGuard {
// Structs
// If a Chonk offer, sells the Chonk and all of its Traits, else just sells the Trait
struct ChonkOffer {
// How much for the Chonk
uint256 priceInWei;
// Who is selling (the end user wallet)
address seller;
// The TBA of the Chonk ID
address sellerTBA;
// An optional address to restrict the buyer to
address onlySellTo;
}
struct TraitOffer {
// How much for the Chonk
uint256 priceInWei;
// Who is selling (the end user wallet)
address seller;
// The TBA of the Chonk ID
address sellerTBA;
// An optional address to restrict the buyer to
address onlySellTo;
}
struct ChonkBid {
// The address of the bidder
address bidder;
// The amount in Wei
uint256 amountInWei;
// The block number of the bid
uint256 bidBlockNumber;
}
struct TraitBid {
// The address of the bidder
address bidder;
// Chonk TBA
address bidderTBA;
// The amount in Wei
uint256 amountInWei;
// The block number of the bid
uint256 bidBlockNumber;
}
// Storage
ChonksMain public immutable CHONKS_MAIN = ChonksMain(0x07152bfde079b5319e5308C43fB1Dbc9C76cb4F9);
ChonkTraits public immutable CHONK_TRAITS;
address public teamWallet;
bool public paused = true;
bool public pausabilityRevoked;
uint256 public royaltyPercentage; // starts at 250 (for 2.5%)
uint256 public gasStipend = 60_000;
// The number of blocks a Chonk must wait after a Trait transfer
uint256 public chonkCooldownPeriod = 50;
// Offers
mapping(uint256 chonkId => ChonkOffer chonkOffer) public chonkOffers;
mapping(uint256 traitId => TraitOffer traitOffer) public traitOffers;
// Bids
mapping(uint256 chonkId => ChonkBid chonkBid) public chonkBids;
mapping(uint256 traitId => TraitBid traitBid) public traitBids;
// Funds
mapping(address eoa => uint256 balance) public withdrawableFunds;
// Cooldown
mapping(uint256 chonkId => uint256 lastTraitTransferBlock) public chonkIdToLastTraitTransferBlock;
/// Errors
error ApproveTheMarketplace();
error BidderChanged();
error BidIsTooLow();
error CantAcceptYourOwnBid();
error CantBeZero();
error CantBidOnYourOwnChonk();
error CantBidOnYourOwnTrait();
error CantBuyYourOwnChonk();
error CantBuyYourOwnTrait();
error ChonkInCooldown();
error CMUnauthorized();
error MustWaitToWithdrawBid();
error NoBidToAccept();
error NoBidToWithdraw();
error NoOfferToCancel();
error NotYourBid();
error NotYourChonk();
error NotYourOffer();
error NotYourTrait();
error OfferDoesNotExist();
error OnlySellToEOAs();
error OnlyTraitsContract();
error Paused();
error PausabilityRevoked();
error SellerMustRelistTrait();
error TBANeedsToApproveMarketplace();
error TraitEquipped();
error WithdrawFailed();
error WrongAmount();
error YouCantBuyThatChonk();
error YouCantBuyThatTrait();
/// Events (These map to the order of the functions below)
// Chonk Events
event ChonkOfferCanceled(
uint256 indexed chonkId,
address indexed seller
);
event ChonkOffered(
uint256 indexed chonkId,
uint256 indexed price,
address indexed seller,
address sellerTBA
);
event ChonkOfferedToAddress(
uint256 indexed chonkId,
uint256 indexed price,
address indexed seller,
address sellerTBA,
address onlySellTo
);
event ChonkBought(
uint256 indexed chonkId,
address indexed buyer,
uint256 indexed amountInWei,
address seller
);
event ChonkBidWithdrawn(
uint256 indexed chonkId,
address indexed bidder,
uint256 indexed amountInWei
);
event ChonkBidEntered(
uint256 indexed chonkId,
address indexed bidder,
uint256 indexed amountInWei
);
event ChonkBidAccepted(
uint256 indexed chonkId,
uint256 indexed amountInWei,
address indexed buyer,
address seller
);
// Trait Events
event TraitOfferCanceled(
uint256 indexed traitId,
address indexed seller
);
event TraitOffered(
uint256 indexed traitId,
uint256 indexed price,
address indexed seller,
address sellerTBA
);
event TraitOfferedToAddress(
uint256 indexed traitId,
uint256 indexed price,
address indexed seller,
address sellerTBA,
address onlySellTo
);
event TraitBought(
uint256 indexed traitId,
address indexed buyerTBA,
uint256 indexed amountInWei,
address buyer,
address seller
);
event TraitBidWithdrawn(
uint256 indexed traitId,
address indexed bidder,
uint256 indexed amountInWei
);
event TraitBidEntered(
uint256 indexed traitId,
address indexed bidder,
uint256 indexed amountInWei
);
event TraitBidAccepted(
uint256 indexed traitId,
uint256 indexed amountInWei,
address indexed buyer,
address seller
);
/// Other Events
event ChonkCooldownPeriodExpiresAtBlock(
uint256 indexed chonkId,
uint256 indexed expiresAtBlock
);
/// Modifiers
modifier ensurePriceIsNotZero(uint256 _price) {
if (_price == 0) revert CantBeZero();
_;
}
modifier notPaused() {
if (paused) revert Paused();
_;
}
modifier onlyMainContract() {
if (msg.sender != address(CHONKS_MAIN)) revert CMUnauthorized();
_;
}
modifier onlyTraitsContract() {
if (msg.sender != address(CHONK_TRAITS)) revert OnlyTraitsContract();
_;
}
modifier ensureChonkIsNotInCooldown(uint256 _chonkId) {
if (chonkIdToLastTraitTransferBlock[_chonkId] != 0 &&
block.number - chonkIdToLastTraitTransferBlock[_chonkId] < chonkCooldownPeriod)
revert ChonkInCooldown();
_;
}
modifier ensureMarketplaceIsApproved(uint256 _chonkId) {
if (!CHONKS_MAIN.isApprovedForAll(msg.sender, address(this)) && CHONKS_MAIN.getApproved(_chonkId) != address(this))
revert ApproveTheMarketplace();
_;
}
// Ensures that the msg.sender owns the Chonk which owns the TBA that owns the Trait
modifier onlyTraitOwner(uint256 _traitId, uint256 _chonkId) { // TODO: move
address traitOwnerTBA = CHONK_TRAITS.ownerOf(_traitId);
(address chonkOwner, address tbaForChonkId) = CHONKS_MAIN.getOwnerAndTBAAddressForChonkId(_chonkId);
if (traitOwnerTBA != tbaForChonkId || chonkOwner != msg.sender) revert NotYourTrait();
_;
}
/// Constructor
constructor(address _chonkTraits, uint8 _royaltyPercentage, address _teamWallet) {
_initializeOwner(msg.sender);
CHONK_TRAITS = ChonkTraits(_chonkTraits);
royaltyPercentage = _royaltyPercentage;
teamWallet = _teamWallet;
}
// GETTERS
// Add a custom getter function
function getChonkOffer(uint256 _chonkId) public view returns (
uint256 priceInWei,
address seller,
address sellerTBA,
address onlySellTo
) {
ChonkOffer memory offer = chonkOffers[_chonkId];
return (
offer.priceInWei,
offer.seller,
offer.sellerTBA,
offer.onlySellTo
);
}
function getTraitOffer(uint256 _traitId) public view returns (
uint256 priceInWei,
address seller,
address sellerTBA,
address onlySellTo
) {
TraitOffer memory offer = traitOffers[_traitId];
return (
offer.priceInWei,
offer.seller,
offer.sellerTBA,
offer.onlySellTo
);
}
function getChonkBid(uint256 _chonkId) public view returns (address bidder, uint256 amountInWei, uint256 bidBlockNumber) {
ChonkBid memory bid = chonkBids[_chonkId];
return (bid.bidder, bid.amountInWei, bid.bidBlockNumber);
}
function getTraitBid(uint256 _traitId) public view returns (
address bidder,
address bidderTBA,
uint256 amountInWei,
uint256 bidBlockNumber
) {
TraitBid memory bid = traitBids[_traitId];
return (
bid.bidder,
bid.bidderTBA,
bid.amountInWei,
bid.bidBlockNumber
);
}
/*
Chonk
Cancel, Offer, Buy
Withdraw Bid, Bid, Accept Bid
*/
function cancelOfferChonk(uint256 _chonkId) public {
if (chonkOffers[_chonkId].seller != msg.sender) revert NotYourOffer();
delete chonkOffers[_chonkId];
emit ChonkOfferCanceled(_chonkId, msg.sender);
}
function offerChonk(uint256 _chonkId, uint256 _priceInWei) public notPaused ensurePriceIsNotZero(_priceInWei) ensureMarketplaceIsApproved(_chonkId) {
(address owner, address tbaAddress) = CHONKS_MAIN.getOwnerAndTBAAddressForChonkId(_chonkId);
_offerChonk(_chonkId, _priceInWei, address(0), owner, tbaAddress);
emit ChonkOffered(_chonkId, _priceInWei, owner, tbaAddress);
}
function offerChonkToAddress(
uint256 _chonkId,
uint256 _priceInWei,
address _onlySellTo
) public notPaused ensurePriceIsNotZero(_priceInWei) ensureMarketplaceIsApproved(_chonkId) {
(address owner, address tbaAddress) = CHONKS_MAIN.getOwnerAndTBAAddressForChonkId(_chonkId);
_offerChonk(_chonkId, _priceInWei, _onlySellTo, owner, tbaAddress);
emit ChonkOfferedToAddress(_chonkId, _priceInWei, owner, tbaAddress, _onlySellTo);
}
function _offerChonk(uint256 _chonkId, uint256 _priceInWei, address _onlySellTo, address _seller, address _sellerTBA) internal ensureChonkIsNotInCooldown(_chonkId) {
if (_seller != msg.sender) revert NotYourChonk();
chonkOffers[_chonkId] = ChonkOffer({
priceInWei: _priceInWei,
seller: _seller,
sellerTBA: _sellerTBA,
onlySellTo: _onlySellTo
});
}
function buyChonk(uint256 _chonkId) public payable notPaused nonReentrant ensureChonkIsNotInCooldown(_chonkId) {
ChonkOffer memory offer = chonkOffers[_chonkId];
// Ensure Offer
address seller = offer.seller;
if (seller == address(0)) revert OfferDoesNotExist();
if (seller == msg.sender) revert CantBuyYourOwnChonk();
if (offer.onlySellTo != address(0) && offer.onlySellTo != msg.sender)
revert YouCantBuyThatChonk();
// Ensure correct price
if (offer.priceInWei != msg.value) revert WrongAmount();
if (!CHONKS_MAIN.isApprovedForAll(offer.seller, address(this)) && CHONKS_MAIN.getApproved(_chonkId) != address(this))
revert ApproveTheMarketplace();
// Delete the Offer
delete chonkOffers[_chonkId];
// Refund and clear existing Bid if from buyer
uint256 refundAmount = 0;
ChonkBid memory existingBid = chonkBids[_chonkId];
if (existingBid.bidder == msg.sender) {
delete chonkBids[_chonkId];
refundAmount = existingBid.amountInWei;
}
// Transfer Chonk (Don't need to transfer Traits because they come with the Chonk)
CHONKS_MAIN.transferFrom(offer.seller, msg.sender, _chonkId);
if (refundAmount > 0)
_refundBid(existingBid.bidder, refundAmount);
// Pay Royalties and Seller
_calculateRoyaltiesAndTransferFunds(msg.value, seller);
emit ChonkBought(_chonkId, msg.sender, msg.value, seller);
}
///////////////////////////////////////////////////////////////////////
function withdrawBidOnChonk(uint256 _chonkId) public nonReentrant {
// Ensure bid and that it's yours
ChonkBid memory bid = chonkBids[_chonkId];
if (bid.bidder == address(0)) revert NoBidToWithdraw();
if (bid.bidder != msg.sender) revert NotYourBid();
if (block.number < bid.bidBlockNumber + chonkCooldownPeriod) revert MustWaitToWithdrawBid();
// Delete from mapping
delete chonkBids[_chonkId];
// Refund your bid
_refundBid(msg.sender, bid.amountInWei);
emit ChonkBidWithdrawn(_chonkId, msg.sender, bid.amountInWei);
}
function bidOnChonk(uint256 _chonkId) public payable ensurePriceIsNotZero(msg.value) notPaused nonReentrant {
address owner = CHONKS_MAIN.ownerOf(_chonkId);
if (owner == msg.sender) revert CantBidOnYourOwnChonk();
ChonkBid memory existingBid = chonkBids[_chonkId];
if (existingBid.amountInWei > 0) {
// New bid must be 5% higher than the existing bid
uint256 minBid = (existingBid.amountInWei * 105) / 100;
if (msg.value < minBid) revert BidIsTooLow();
}
chonkBids[_chonkId] = ChonkBid(
msg.sender,
msg.value,
block.number
);
if (existingBid.amountInWei > 0) {
_refundBid(existingBid.bidder, existingBid.amountInWei);
}
emit ChonkBidEntered(_chonkId, msg.sender, msg.value);
}
function acceptBidForChonk(uint256 _chonkId, address _bidder) public notPaused nonReentrant ensureChonkIsNotInCooldown(_chonkId) {
address owner = CHONKS_MAIN.ownerOf(_chonkId);
if (!CHONKS_MAIN.isApprovedForAll(owner, address(this)) && CHONKS_MAIN.getApproved(_chonkId) != address(this))
revert ApproveTheMarketplace();
if (owner != msg.sender) revert NotYourChonk();
ChonkBid memory bid = chonkBids[_chonkId];
address bidder = bid.bidder;
if (bidder == address(0)) revert NoBidToAccept();
if (bidder == msg.sender) revert CantAcceptYourOwnBid();
if (bidder != _bidder) revert BidderChanged();
delete chonkBids[_chonkId];
CHONKS_MAIN.transferFrom(msg.sender, bidder, _chonkId);
_calculateRoyaltiesAndTransferFunds(bid.amountInWei, owner);
emit ChonkBidAccepted(_chonkId, bid.amountInWei, bidder, owner);
}
/*
Trait
Cancel, Offer, Buy
Withdraw Bid, Bid, Accept Bid
*/
function cancelOfferTrait(uint256 _traitId, uint256 _chonkId) public onlyTraitOwner(_traitId, _chonkId) {
address seller = traitOffers[_traitId].seller;
if (seller == address(0)) revert NoOfferToCancel();
// Allows for the seller to cancel
if (seller != msg.sender && msg.sender != CHONKS_MAIN.ownerOf(_chonkId)) revert NotYourOffer();
delete traitOffers[_traitId];
emit TraitOfferCanceled(_traitId, msg.sender);
}
/// Note: Needs to be called by the EOA that owns the Chonk
function offerTrait(
uint256 _traitId,
uint256 _chonkId,
uint256 _priceInWei
) public notPaused ensurePriceIsNotZero(_priceInWei) onlyTraitOwner(_traitId, _chonkId) {
// Please unequip the trait if you want to sell it
if (CHONKS_MAIN.checkIfTraitIsEquipped(_chonkId, _traitId))
revert TraitEquipped();
// Remove the Chonk offer if it exists
if (chonkOffers[_chonkId].seller != address(0)) {
delete chonkOffers[_chonkId];
emit ChonkOfferCanceled(_chonkId, msg.sender);
}
address tbaTraitOwner = CHONK_TRAITS.ownerOf(_traitId);
(address tokenOwner, ) = CHONKS_MAIN.getOwnerAndTBAAddressForChonkId(_chonkId);
// Ensure the marketplace is approved for Chonk Traits
if (!CHONK_TRAITS.isApprovedForAll(tbaTraitOwner, address(this)) && CHONK_TRAITS.getApproved(_traitId) != address(this))
revert ApproveTheMarketplace();
traitOffers[_traitId] = TraitOffer(
_priceInWei,
tokenOwner,
tbaTraitOwner,
address(0)
);
emit TraitOffered(_traitId, _priceInWei, tokenOwner, tbaTraitOwner);
}
/// @notice This should be called by the EOA that owns the Chonk, not the TBA
/// @param _traitId The ID of the Trait you're selling
/// @param _chonkId The ID of the Chonk you're selling the Trait for. This Chonk ID must own the `_traitId`
/// @param _priceInWei The price of the Trait you're selling, in Wei
/// @param _onlySellTo should be the EOA that will be buying the Trait for their Chonk
function offerTraitToAddress(
uint256 _traitId,
uint256 _chonkId,
uint256 _priceInWei,
address _onlySellTo
) public notPaused ensurePriceIsNotZero(_priceInWei) onlyTraitOwner(_traitId, _chonkId) {
if (CHONKS_MAIN.tbaAddressToTokenId(_onlySellTo) != 0) revert OnlySellToEOAs();
// Please unequip the trait if you want to sell it
if (CHONKS_MAIN.checkIfTraitIsEquipped(_chonkId, _traitId))
revert TraitEquipped();
address tbaTraitOwner = CHONK_TRAITS.ownerOf(_traitId);
(address tokenOwner, ) = CHONKS_MAIN.getOwnerAndTBAAddressForChonkId(_chonkId);
if (!CHONK_TRAITS.isApprovedForAll(tbaTraitOwner, address(this)) && CHONK_TRAITS.getApproved(_traitId) != address(this))
revert ApproveTheMarketplace();
traitOffers[_traitId] = TraitOffer(
_priceInWei,
tokenOwner,
tbaTraitOwner,
_onlySellTo
);
emit TraitOfferedToAddress(_traitId, _priceInWei, tokenOwner, tbaTraitOwner, _onlySellTo);
}
/// @notice This should be called by the EOA that owns the Chonk
/// @param _traitId The ID of the Trait you're buying
/// @param _forChonkId should be your Chonk you're buying the Trait for
function buyTrait(uint256 _traitId, uint256 _forChonkId) public payable notPaused nonReentrant {
// Ensure msg.sender owns the Chonk token of the TBA
address owner = CHONKS_MAIN.ownerOf(_forChonkId);
if (owner != msg.sender) revert NotYourChonk();
// Ensure you don't own the Trait
address tba = CHONKS_MAIN.tokenIdToTBAAccountAddress(_forChonkId);
address traitOwnerTBAAddress = CHONK_TRAITS.ownerOf(_traitId);
if (traitOwnerTBAAddress == tba) revert CantBuyYourOwnTrait();
// Ensure Offer
TraitOffer memory offer = traitOffers[_traitId];
if (!CHONK_TRAITS.isApprovedForAll(offer.sellerTBA, address(this)) && CHONK_TRAITS.getApproved(_traitId) != address(this))
revert TBANeedsToApproveMarketplace();
address seller = offer.seller;
if (seller == address(0)) revert OfferDoesNotExist();
if (seller == msg.sender) revert CantBuyYourOwnTrait();
if (offer.onlySellTo != address(0) && offer.onlySellTo != msg.sender)
revert YouCantBuyThatTrait();
// This is triggered if the Chonk holding the Trait changed hands. We don't clear the Trait offers for gas reasons when a Chonk sells and its Traits are also for sale (if the TBA holds a ton of Traits, it can get costly gas-wise)
if (!traitOfferIsValid(_traitId)) revert SellerMustRelistTrait();
// Ensure correct price
if (offer.priceInWei != msg.value) revert WrongAmount();
(,,, bool isEquipped) = CHONKS_MAIN.getFullPictureForTrait(_traitId);
if (isEquipped) revert TraitEquipped();
// Delete the Offer
delete traitOffers[_traitId];
// Clear existing Bid if it exists
uint256 refundAmount = 0;
TraitBid memory existingBid = traitBids[_traitId];
if (existingBid.bidder == msg.sender) {
delete traitBids[_traitId];
refundAmount = existingBid.amountInWei;
}
CHONK_TRAITS.transferFrom(offer.sellerTBA, tba, _traitId);
if (refundAmount > 0)
_refundBid(existingBid.bidder, refundAmount);
_calculateRoyaltiesAndTransferFunds(msg.value, seller);
emit TraitBought(_traitId, tba, msg.value, msg.sender, seller);
}
///////////////////////////////////////////////////////////////////////
function withdrawBidOnTrait(uint256 _traitId) public nonReentrant {
// Ensure bid and that it's yours
TraitBid memory bid = traitBids[_traitId];
if (bid.bidder != msg.sender) revert NotYourBid();
if (block.number < bid.bidBlockNumber + chonkCooldownPeriod) revert MustWaitToWithdrawBid();
// Delete from mapping
delete traitBids[_traitId];
// Refund your bid
_refundBid(msg.sender, bid.amountInWei);
emit TraitBidWithdrawn(_traitId, msg.sender, bid.amountInWei);
}
function bidOnTrait(uint256 _traitId, uint256 _yourChonkId) public payable ensurePriceIsNotZero(msg.value) notPaused nonReentrant {
(address chonkOwner, address tbaAddressOfBiddersChonk) = CHONKS_MAIN.getOwnerAndTBAAddressForChonkId(_yourChonkId);
// Ensure msg.sender owns the Chonk trait will go to
if (chonkOwner != msg.sender) revert NotYourChonk();
// Ensure msg.sender does own Chonk or Trait
(address traitOwnerTBA, , address traitChonkOwner, ) = CHONKS_MAIN.getFullPictureForTrait(_traitId);
if (traitChonkOwner == msg.sender || traitOwnerTBA == msg.sender) revert CantBidOnYourOwnTrait();
TraitBid memory existingBid = traitBids[_traitId];
if (existingBid.amountInWei > 0) {
// New bid must be 5% higher than the existing bid
uint256 minBid = (existingBid.amountInWei * 105) / 100;
if (msg.value < minBid) revert BidIsTooLow();
}
traitBids[_traitId] = TraitBid(msg.sender, tbaAddressOfBiddersChonk, msg.value, block.number);
if (existingBid.amountInWei > 0) {
_refundBid(existingBid.bidder, existingBid.amountInWei);
}
emit TraitBidEntered(_traitId, msg.sender, msg.value);
}
function acceptBidForTrait(uint256 _traitId, address _bidder) public notPaused nonReentrant {
// Ensure Bid
TraitBid memory bid = traitBids[_traitId];
address bidder = bid.bidder;
if (bidder == address(0)) revert NoBidToAccept();
if (bidder == msg.sender) revert CantAcceptYourOwnBid();
if (bidder != _bidder) revert BidderChanged();
(address sellerTBA, , address seller, bool isEquipped) = CHONKS_MAIN.getFullPictureForTrait(_traitId);
if (seller != msg.sender) revert NotYourTrait();
if (isEquipped) revert TraitEquipped();
// Delete Offer for trait ID if present, delete Bid you're accepting
delete traitOffers[_traitId];
delete traitBids[_traitId];
CHONK_TRAITS.transferFrom(sellerTBA, bid.bidderTBA, _traitId);
_calculateRoyaltiesAndTransferFunds(bid.amountInWei, seller);
emit TraitBidAccepted(_traitId, bid.amountInWei, bidder, seller);
}
/// Helper Functions
function calculateRoyalty(uint256 _amount) public view returns (uint256) {
return (_amount * royaltyPercentage) / 10_000;
}
/// Before Token Transfer
function deleteChonkOfferBeforeTokenTransfer(uint256 _chonkId) public onlyMainContract {
ChonkOffer memory offer = chonkOffers[_chonkId];
if (offer.seller != address(0)) delete chonkOffers[_chonkId];
}
function deleteChonkBidsBeforeTokenTransfer(uint256 _chonkId, address _toEOA) public onlyMainContract {
ChonkBid memory bid = chonkBids[_chonkId];
if (bid.bidder == _toEOA) {
delete chonkBids[_chonkId];
_refundBid(bid.bidder, bid.amountInWei);
}
}
// /// @dev This is called by ChonksMain in a loop and was excessive. Unused.
function deleteTraitOffersBeforeTokenTransfer(uint256) view public {}
function deleteTraitOfferBeforeTokenTransferFromTraits(uint256 _traitId) public {
if (msg.sender != address(CHONK_TRAITS)) revert CMUnauthorized();
if (traitOffers[_traitId].seller != address(0)) {
delete traitOffers[_traitId];
}
}
/// @dev Legacy function. Called from ChonksMain. Unused.
function deleteTraitBidsBeforeTokenTransfer(uint256 _traitId, address[] memory _toTBAs) view public {}
function deleteTraitBidsBeforeTokenTransfer(uint256 _traitId, address _toTBA) public {
if (msg.sender != address(CHONK_TRAITS)) revert CMUnauthorized();
TraitBid memory bid = traitBids[_traitId];
if (bid.bidder != address(0)) {
if (bid.bidderTBA == _toTBA) {
delete traitBids[_traitId];
_refundBid(bid.bidder, bid.amountInWei);
}
}
}
function removeChonkOfferOnTraitTransfer(uint256 _chonkId) public onlyTraitsContract {
delete chonkOffers[_chonkId];
}
function setChonkCooldownPeriod(uint256 _chonkId) public {
// Only callable by the Traits contract
if (msg.sender != address(CHONK_TRAITS)) revert CMUnauthorized();
chonkIdToLastTraitTransferBlock[_chonkId] = block.number;
emit ChonkCooldownPeriodExpiresAtBlock(_chonkId, block.number + chonkCooldownPeriod);
}
/// @dev Returns true if the owner of the TBA is the same as the seller
/// This allows a Chonk to sell without invalidating the Trait offers in the
/// case where the Chonk holds many Traits.
function traitOfferIsValid(uint256 _traitId) public view returns (bool) {
if (traitOffers[_traitId].seller == address(0)) return false;
(,,address owner,) = CHONKS_MAIN.getFullPictureForTrait(_traitId);
return owner == traitOffers[_traitId].seller;
}
/// Withdraw
function withdrawFunds() public nonReentrant {
uint256 balance = withdrawableFunds[msg.sender];
withdrawableFunds[msg.sender] = 0;
(bool success, ) = msg.sender.call{value: balance, gas: gasStipend}("");
if (!success) revert WithdrawFailed();
}
/// Private
function _calculateRoyaltiesAndTransferFunds(uint256 _amount, address _to) private returns (bool success) {
uint256 royalties = calculateRoyalty(_amount);
uint256 amountForSeller = _amount - royalties;
(bool royaltyPayment, ) = payable(teamWallet).call{value: royalties}("");
if (!royaltyPayment) withdrawableFunds[teamWallet] += royalties;
(success, ) = payable(_to).call{value: amountForSeller, gas: gasStipend}("");
if (!success) withdrawableFunds[_to] += amountForSeller;
}
function _refundBid(address _to, uint256 _amount) private {
(bool success, ) = payable(_to).call{value: _amount, gas: gasStipend}("");
if (!success) withdrawableFunds[_to] += _amount;
}
/// Only Owner
// Set the royalty percentage
function setRoyaltyPercentage(uint256 _royaltyPercentage) public onlyOwner {
royaltyPercentage = _royaltyPercentage;
}
// Set the wallet to receive royalties
function setTeamWallet(address _teamWallet) public onlyOwner {
teamWallet = _teamWallet;
}
// Set the gas stipend for withdrawals
function setGasStipend(uint256 _gasStipend) public onlyOwner {
gasStipend = _gasStipend;
}
function setChonkCooldownPeriodBlocks(uint16 _chonkCooldownPeriod) public onlyOwner {
chonkCooldownPeriod = _chonkCooldownPeriod;
}
// Allows us to pause the market
function pause(bool _value) public onlyOwner {
if (pausabilityRevoked) revert PausabilityRevoked();
paused = _value;
}
// Allows us to revoke the pausability. Will happen after enough time has passed and we feel confident in the market
// CAUTION: This is irreversible. Ensure `paused` is `false` before revoking
function revokePausability() public onlyOwner {
if (pausabilityRevoked) revert PausabilityRevoked();
pausabilityRevoked = true;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.0;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Metadata is IERC721 {
/**
* @dev Returns the token collection name.
*/
function name() external view returns (string memory);
/**
* @dev Returns the token collection symbol.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) external view returns (string memory);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}// 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);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)
pragma solidity ^0.8.0;
import "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Enumerable is IERC721 {
/**
* @dev Returns the total amount of tokens stored by the contract.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns a token ID owned by `owner` at a given `index` of its token list.
* Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
/**
* @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
* Use along with {totalSupply} to enumerate all tokens.
*/
function tokenByIndex(uint256 index) external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
library Utils {
uint256 internal constant MULTIPLIER = 100;
uint256 internal constant GOLDEN_RATIO = 161803;
function toHexString(bytes memory data) internal pure returns (string memory) {
bytes memory alphabet = "0123456789abcdef";
bytes memory str = new bytes(2 + data.length * 2);
str[0] = "0";
str[1] = "x";
for (uint256 i = 0; i < data.length; i++) {
str[2 + i * 2] = alphabet[uint8(data[i] >> 4)];
str[2 + i * 2 + 1] = alphabet[uint8(data[i] & 0x0f)];
}
return string(str);
}
/// @dev Zero-index based pseudorandom number based on one input and max bound - thank you Checks
function random(uint256 input, uint256 _max) internal pure returns (uint256) {
return (uint256(keccak256(abi.encodePacked(input))) % _max);
}
/// @dev Zero-index based salted pseudorandom number based on two inputs and max bound - thank you Checks
function random(uint256 input, string memory salt, uint256 _max) internal pure returns (uint256) {
return (uint256(keccak256(abi.encodePacked(input, salt))) % _max);
}
/**
* Compute the largest integer smaller than or equal to the square root of `n`
*/
// function floorSqrt(uint256 n) internal pure returns (uint256) { unchecked {
// if (n > 0) {
// uint256 x = n / 2 + 1;
// uint256 y = (x + n / x) / 2;
// while (x > y) {
// x = y;
// y = (x + n / x) / 2;
// }
// return x;
// }
// return 0;
// }}
/**
* Compute the smallest integer larger than or equal to the square root of `n`
*/
// function ceilSqrt(uint256 n) internal pure returns (uint256) { unchecked {
// uint256 x = floorSqrt(n);
// return x ** 2 == n ? x : x + 1;
// }}
// function lerp(int256 targetFrom, int256 targetTo, int256 currentFrom, int256 currentTo, int current) internal pure returns (int256) { unchecked {
// int256 t = 0;a
// int256 divisor = currentTo - currentFrom - 1;
// if (divisor > 0) {
// t = (current - currentFrom) * int256(MULTIPLIER) / (divisor);
// }
// return targetFrom * int256(MULTIPLIER) + t * (targetTo - targetFrom);
// }}
function toByteArray(bytes32 _bytes32) internal pure returns (bytes memory result) {
uint8 i = 0;
while(i < 32 && _bytes32[i] != 0) {
i++;
}
bytes memory bytesArray = new bytes(i);
for (i = 0; i < 32 && _bytes32[i] != 0; i++) {
bytesArray[i] = _bytes32[i];
}
return bytesArray;
}
function toString(bytes32 _bytes32) internal pure returns (string memory result) {
return string(toByteArray(_bytes32));
}
function toStringBytes3(bytes3 _bytes) public pure returns (string memory) {
bytes memory hexChars = "0123456789abcdef";
bytes memory hexString = new bytes(6); // Since bytes3 contains 3 bytes, resulting in 6 hex characters
for (uint i = 0; i < 3; i++) {
hexString[i * 2] = hexChars[uint8(_bytes[i] >> 4)];
hexString[1 + i * 2] = hexChars[uint8(_bytes[i] & 0x0f)];
}
return string(hexString);
}
/*
Gas Efficient uint/int to string functions
Copied from: https://github.com/Vectorized/solady/blob/main/src/utils/LibString.sol
*/
/// @dev Returns the base 10 decimal representation of `value`.
function toString(uint256 value) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
// The maximum value of a uint256 contains 78 digits (1 byte per digit), but
// we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
// We will need 1 word for the trailing zeros padding, 1 word for the length,
// and 3 words for a maximum of 78 digits.
str := add(mload(0x40), 0x80)
// Update the free memory pointer to allocate.
mstore(0x40, add(str, 0x20))
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end of the memory to calculate the length later.
let end := str
let w := not(0) // Tsk.
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let temp := value } 1 {} {
str := add(str, w) // `sub(str, 1)`.
// Write the character to the pointer.
// The ASCII index of the '0' character is 48.
mstore8(str, add(48, mod(temp, 10)))
// Keep dividing `temp` until zero.
temp := div(temp, 10)
if iszero(temp) { break }
}
let length := sub(end, str)
// Move the pointer 32 bytes leftwards to make room for the length.
str := sub(str, 0x20)
// Store the length.
mstore(str, length)
}
}
/// @dev Returns the base 10 decimal representation of `value`.
function toString(int256 value) internal pure returns (string memory str) {
if (value >= 0) {
return toString(uint256(value));
}
unchecked {
str = toString(uint256(-value));
}
/// @solidity memory-safe-assembly
assembly {
// We still have some spare memory space on the left,
// as we have allocated 3 words (96 bytes) for up to 78 digits.
let length := mload(str) // Load the string length.
mstore(str, 0x2d) // Store the '-' character.
str := sub(str, 1) // Move back the string pointer by a byte.
mstore(str, add(length, 1)) // Update the string length.
}
}
/// @dev Encodes `data` using the base64 encoding described in RFC 4648.
/// See: https://datatracker.ietf.org/doc/html/rfc4648
/// @param fileSafe Whether to replace '+' with '-' and '/' with '_'.
/// @param noPadding Whether to strip away the padding.
function encode(bytes memory data, bool fileSafe, bool noPadding) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
let dataLength := mload(data)
if dataLength {
// Multiply by 4/3 rounded up.
// The `shl(2, ...)` is equivalent to multiplying by 4.
let encodedLength := shl(2, div(add(dataLength, 2), 3))
// Set `result` to point to the start of the free memory.
result := mload(0x40)
// Store the table into the scratch space.
// Offsetted by -1 byte so that the `mload` will load the character.
// We will rewrite the free memory pointer at `0x40` later with
// the allocated size.
// The magic constant 0x0670 will turn "-_" into "+/".
mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
mstore(0x3f, xor("ghijklmnopqrstuvwxyz0123456789-_", mul(iszero(fileSafe), 0x0670)))
// Skip the first slot, which stores the length.
let ptr := add(result, 0x20)
let end := add(ptr, encodedLength)
// Run over the input, 3 bytes at a time.
for {} 1 {} {
data := add(data, 3) // Advance 3 bytes.
let input := mload(data)
// Write 4 bytes. Optimized for fewer stack operations.
mstore8(0, mload(and(shr(18, input), 0x3F)))
mstore8(1, mload(and(shr(12, input), 0x3F)))
mstore8(2, mload(and(shr(6, input), 0x3F)))
mstore8(3, mload(and(input, 0x3F)))
mstore(ptr, mload(0x00))
ptr := add(ptr, 4) // Advance 4 bytes.
if iszero(lt(ptr, end)) { break }
}
mstore(0x40, add(end, 0x20)) // Allocate the memory.
// Equivalent to `o = [0, 2, 1][dataLength % 3]`.
let o := div(2, mod(dataLength, 3))
// Offset `ptr` and pad with '='. We can simply write over the end.
mstore(sub(ptr, o), shl(240, 0x3d3d))
// Set `o` to zero if there is padding.
o := mul(iszero(iszero(noPadding)), o)
mstore(sub(ptr, o), 0) // Zeroize the slot after the string.
mstore(result, sub(encodedLength, o)) // Store the length.
}
}
}
/// @dev Encodes `data` using the base64 encoding described in RFC 4648.
/// Equivalent to `encode(data, false, false)`.
function encode(bytes memory data) internal pure returns (string memory result) {
result = encode(data, false, false);
}
/// @dev Encodes `data` using the base64 encoding described in RFC 4648.
/// Equivalent to `encode(data, fileSafe, false)`.
function encode(bytes memory data, bool fileSafe) internal pure returns (string memory result) {
result = encode(data, fileSafe, false);
}
// function contains(string memory _str, string memory _searchStr) internal pure returns (bool) {
// bytes memory str = bytes(_str);
// bytes memory searchStr = bytes(_searchStr);
// for (uint i = 0; i <= str.length - searchStr.length; i++) {
// bool found = true;
// for (uint j = 0; j < searchStr.length; j++) {
// if (str[i + j] != searchStr[j]) {
// found = false;
// break;
// }
// }
// if (found) return true;
// }
// return false;
// }
function startsWith(string memory _str, string memory _prefix) internal pure returns (bool) {
bytes memory str = bytes(_str);
bytes memory prefix = bytes(_prefix);
if (str.length < prefix.length) return false;
for (uint i = 0; i < prefix.length; i++) {
if (str[i] != prefix[i]) return false;
}
return true;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol)
library MerkleProofLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MERKLE PROOF VERIFICATION OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf)
internal
pure
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
if mload(proof) {
// Initialize `offset` to the offset of `proof` elements in memory.
let offset := add(proof, 0x20)
// Left shift by 5 is equivalent to multiplying by 0x20.
let end := add(offset, shl(5, mload(proof)))
// Iterate over proof elements to compute root hash.
for {} 1 {} {
// Slot of `leaf` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(leaf, mload(offset)))
// Store elements to hash contiguously in scratch space.
// Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
mstore(scratch, leaf)
mstore(xor(scratch, 0x20), mload(offset))
// Reuse `leaf` to store the hash to reduce stack operations.
leaf := keccak256(0x00, 0x40)
offset := add(offset, 0x20)
if iszero(lt(offset, end)) { break }
}
}
isValid := eq(leaf, root)
}
}
/// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf)
internal
pure
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
if proof.length {
// Left shift by 5 is equivalent to multiplying by 0x20.
let end := add(proof.offset, shl(5, proof.length))
// Initialize `offset` to the offset of `proof` in the calldata.
let offset := proof.offset
// Iterate over proof elements to compute root hash.
for {} 1 {} {
// Slot of `leaf` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(leaf, calldataload(offset)))
// Store elements to hash contiguously in scratch space.
// Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
mstore(scratch, leaf)
mstore(xor(scratch, 0x20), calldataload(offset))
// Reuse `leaf` to store the hash to reduce stack operations.
leaf := keccak256(0x00, 0x40)
offset := add(offset, 0x20)
if iszero(lt(offset, end)) { break }
}
}
isValid := eq(leaf, root)
}
}
/// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
/// given `proof` and `flags`.
///
/// Note:
/// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
/// will always return false.
/// - The sum of the lengths of `proof` and `leaves` must never overflow.
/// - Any non-zero word in the `flags` array is treated as true.
/// - The memory offset of `proof` must be non-zero
/// (i.e. `proof` is not pointing to the scratch space).
function verifyMultiProof(
bytes32[] memory proof,
bytes32 root,
bytes32[] memory leaves,
bool[] memory flags
) internal pure returns (bool isValid) {
// Rebuilds the root by consuming and producing values on a queue.
// The queue starts with the `leaves` array, and goes into a `hashes` array.
// After the process, the last element on the queue is verified
// to be equal to the `root`.
//
// The `flags` array denotes whether the sibling
// should be popped from the queue (`flag == true`), or
// should be popped from the `proof` (`flag == false`).
/// @solidity memory-safe-assembly
assembly {
// Cache the lengths of the arrays.
let leavesLength := mload(leaves)
let proofLength := mload(proof)
let flagsLength := mload(flags)
// Advance the pointers of the arrays to point to the data.
leaves := add(0x20, leaves)
proof := add(0x20, proof)
flags := add(0x20, flags)
// If the number of flags is correct.
for {} eq(add(leavesLength, proofLength), add(flagsLength, 1)) {} {
// For the case where `proof.length + leaves.length == 1`.
if iszero(flagsLength) {
// `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
isValid := eq(mload(xor(leaves, mul(xor(proof, leaves), proofLength))), root)
break
}
// The required final proof offset if `flagsLength` is not zero, otherwise zero.
let proofEnd := add(proof, shl(5, proofLength))
// We can use the free memory space for the queue.
// We don't need to allocate, since the queue is temporary.
let hashesFront := mload(0x40)
// Copy the leaves into the hashes.
// Sometimes, a little memory expansion costs less than branching.
// Should cost less, even with a high free memory offset of 0x7d00.
leavesLength := shl(5, leavesLength)
for { let i := 0 } iszero(eq(i, leavesLength)) { i := add(i, 0x20) } {
mstore(add(hashesFront, i), mload(add(leaves, i)))
}
// Compute the back of the hashes.
let hashesBack := add(hashesFront, leavesLength)
// This is the end of the memory for the queue.
// We recycle `flagsLength` to save on stack variables (sometimes save gas).
flagsLength := add(hashesBack, shl(5, flagsLength))
for {} 1 {} {
// Pop from `hashes`.
let a := mload(hashesFront)
// Pop from `hashes`.
let b := mload(add(hashesFront, 0x20))
hashesFront := add(hashesFront, 0x40)
// If the flag is false, load the next proof,
// else, pops from the queue.
if iszero(mload(flags)) {
// Loads the next proof.
b := mload(proof)
proof := add(proof, 0x20)
// Unpop from `hashes`.
hashesFront := sub(hashesFront, 0x20)
}
// Advance to the next flag.
flags := add(flags, 0x20)
// Slot of `a` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(a, b))
// Hash the scratch space and push the result onto the queue.
mstore(scratch, a)
mstore(xor(scratch, 0x20), b)
mstore(hashesBack, keccak256(0x00, 0x40))
hashesBack := add(hashesBack, 0x20)
if iszero(lt(hashesBack, flagsLength)) { break }
}
isValid :=
and(
// Checks if the last value in the queue is same as the root.
eq(mload(sub(hashesBack, 0x20)), root),
// And whether all the proofs are used, if required.
eq(proofEnd, proof)
)
break
}
}
}
/// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
/// given `proof` and `flags`.
///
/// Note:
/// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
/// will always return false.
/// - Any non-zero word in the `flags` array is treated as true.
/// - The calldata offset of `proof` must be non-zero
/// (i.e. `proof` is from a regular Solidity function with a 4-byte selector).
function verifyMultiProofCalldata(
bytes32[] calldata proof,
bytes32 root,
bytes32[] calldata leaves,
bool[] calldata flags
) internal pure returns (bool isValid) {
// Rebuilds the root by consuming and producing values on a queue.
// The queue starts with the `leaves` array, and goes into a `hashes` array.
// After the process, the last element on the queue is verified
// to be equal to the `root`.
//
// The `flags` array denotes whether the sibling
// should be popped from the queue (`flag == true`), or
// should be popped from the `proof` (`flag == false`).
/// @solidity memory-safe-assembly
assembly {
// If the number of flags is correct.
for {} eq(add(leaves.length, proof.length), add(flags.length, 1)) {} {
// For the case where `proof.length + leaves.length == 1`.
if iszero(flags.length) {
// `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
// forgefmt: disable-next-item
isValid := eq(
calldataload(
xor(leaves.offset, mul(xor(proof.offset, leaves.offset), proof.length))
),
root
)
break
}
// The required final proof offset if `flagsLength` is not zero, otherwise zero.
let proofEnd := add(proof.offset, shl(5, proof.length))
// We can use the free memory space for the queue.
// We don't need to allocate, since the queue is temporary.
let hashesFront := mload(0x40)
// Copy the leaves into the hashes.
// Sometimes, a little memory expansion costs less than branching.
// Should cost less, even with a high free memory offset of 0x7d00.
calldatacopy(hashesFront, leaves.offset, shl(5, leaves.length))
// Compute the back of the hashes.
let hashesBack := add(hashesFront, shl(5, leaves.length))
// This is the end of the memory for the queue.
// We recycle `flagsLength` to save on stack variables (sometimes save gas).
flags.length := add(hashesBack, shl(5, flags.length))
// We don't need to make a copy of `proof.offset` or `flags.offset`,
// as they are pass-by-value (this trick may not always save gas).
for {} 1 {} {
// Pop from `hashes`.
let a := mload(hashesFront)
// Pop from `hashes`.
let b := mload(add(hashesFront, 0x20))
hashesFront := add(hashesFront, 0x40)
// If the flag is false, load the next proof,
// else, pops from the queue.
if iszero(calldataload(flags.offset)) {
// Loads the next proof.
b := calldataload(proof.offset)
proof.offset := add(proof.offset, 0x20)
// Unpop from `hashes`.
hashesFront := sub(hashesFront, 0x20)
}
// Advance to the next flag offset.
flags.offset := add(flags.offset, 0x20)
// Slot of `a` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(a, b))
// Hash the scratch space and push the result onto the queue.
mstore(scratch, a)
mstore(xor(scratch, 0x20), b)
mstore(hashesBack, keccak256(0x00, 0x40))
hashesBack := add(hashesBack, 0x20)
if iszero(lt(hashesBack, flags.length)) { break }
}
isValid :=
and(
// Checks if the last value in the queue is same as the root.
eq(mload(sub(hashesBack, 0x20)), root),
// And whether all the proofs are used, if required.
eq(proofEnd, proof.offset)
)
break
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EMPTY CALLDATA HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns an empty calldata bytes32 array.
function emptyProof() internal pure returns (bytes32[] calldata proof) {
/// @solidity memory-safe-assembly
assembly {
proof.length := 0
}
}
/// @dev Returns an empty calldata bytes32 array.
function emptyLeaves() internal pure returns (bytes32[] calldata leaves) {
/// @solidity memory-safe-assembly
assembly {
leaves.length := 0
}
}
/// @dev Returns an empty calldata bool array.
function emptyFlags() internal pure returns (bool[] calldata flags) {
/// @solidity memory-safe-assembly
assembly {
flags.length := 0
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
interface IAccountImplementation {
function owner() external view returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
interface IAccountProxy {
function initialize(address _implementation) external;
function execute(
address target,
uint256 value,
bytes calldata data,
uint256 operation
) external returns (bytes memory);
function executeCall(
address to,
uint256 value,
bytes calldata data
) external payable returns (bytes memory);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
interface IRegistry {
// function symbolScheme(uint256 index) external view returns (uint8);
// function tokenURI(uint256 index) external view returns (string memory);
function createAccount(
address implementation,
bytes32 salt,
uint256 chainId,
address tokenContract,
uint256 tokenId
) external returns (address);
function account(
address implementation,
bytes32 salt,
uint256 chainId,
address tokenContract,
uint256 tokenId
) external view returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
import { ChonkTraits } from "../ChonkTraits.sol";
import { IChonkStorage } from "../interfaces/IChonkStorage.sol";
import { Utils } from "../common/Utils.sol";
contract MainRenderer2D {
string constant SVG_BACKPACK = '<g id="All Traits"><g id="backpack" class="closed"><path d="M0 0 L30 0 L30 30 L0 30 Z" fill="rgb(12, 109, 157)" /><svg id="backpackUI" viewBox="0 0 120 120"> <style>.ui{width:1px; height: 1px; fill:white}</style> <g id="closeBtn" transform="translate(2,2)"> <rect x="1" y="1" class="ui"></rect> <rect x="2" y="2" class="ui"></rect> <rect x="3" y="3" class="ui"></rect> <rect x="4" y="4" class="ui"></rect> <rect x="5" y="5" class="ui"></rect> <rect x="5" y="1" class="ui"></rect> <rect x="4" y="2" class="ui"></rect> <!-- <rect x="3" y="3" width="1" height="1" fill="white"></rect> --> <rect x="2" y="4" class="ui"></rect> <rect x="1" y="5" class="ui"></rect> </g> <g id="leftBtn" class="button" transform="translate(45,110)"> <path d="M0 0 L6 0 L6 6 L0 6 Z" fill="transparent" /> <rect x="2" y="0" class="ui"></rect> <rect x="1" y="1" class="ui"></rect> <rect x="0" y="2" class="ui"></rect> <rect x="1" y="3" class="ui"></rect> <rect x="2" y="4" class="ui"></rect> </g> <g id="rightBtn" class="button" transform="translate(65,110)"> <path d="M0 0 L6 0 L6 6 L0 6 Z" fill="transparent" /> <rect x="3" y="0" class="ui"></rect> <rect x="4" y="1" class="ui"></rect> <rect x="5" y="2" class="ui"></rect> <rect x="4" y="3" class="ui"></rect> <rect x="3" y="4" class="ui"></rect> </g> </svg> ';
string private constant SVG_START = '<svg shape-rendering="crispEdges" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">';
string private constant SVG_STYLE = '<style> body{overflow: hidden; margin: 0;} svg{ max-width: 100vw; max-height: 100vh; width: 100%;} #main rect{width:1px; height: 1px;} .bg{width:30px; height: 30px;} .on { scale: 177%; transform: translate(-6px, -3px); } .off { scale: 100%; transform: translate(0px, 0px); } .button { cursor: pointer; fill: transparent; } .closed{ transform: translate(0px, 30px); } .open{ transform: translate(0px, 0px); } </style>';
string private constant SVG_BG_MAIN_START = '<rect class="bg"/><g id="main" class="off">';
string private constant SVG_TOGGLE = '<rect id="toggleMain" class="button" x="25" y="0" width="5" height="5" /><rect id="toggleBackpack" class="button" x="0" y="0" width="5" height="5" />';
string private constant SVG_TOGGLE_SCRIPT = '<script><![CDATA[ const maxTraitsPerScreen = 20; const mainGroup = document.getElementById("main"); const backpackGroup = document.getElementById("backpack"); const backpackTraits = document.getElementById("backpackTraits"); const backpackTraitsSvgs = Array.from(backpackTraits.getElementsByTagName("svg")); const ghostGroup = document.getElementById("ghost"); const leftBtn = document.getElementById("leftBtn"); const rightBtn = document.getElementById("rightBtn"); let curScreen = 0; const numScreens = Math.ceil(backpackTraitsSvgs.length / maxTraitsPerScreen); while (backpackTraits.firstChild) { backpackTraits.removeChild(backpackTraits.firstChild);} const ghostClone = ghostGroup.outerHTML; for (let i = 0; i < backpackTraitsSvgs.length; i += maxTraitsPerScreen) { const gElement = document.createElementNS("http://www.w3.org/2000/svg", "g"); gElement.setAttribute("transform", `translate(${(i / maxTraitsPerScreen) * 30} 0)`); for (let j = 0; j < maxTraitsPerScreen && i + j < backpackTraitsSvgs.length; ++j) { const svg = backpackTraitsSvgs[i + j]; const x = -(j % 5) * 30; const y = -(Math.floor(j / 5) * 30) - 10; svg.setAttribute("viewBox", `${x} ${y} 150 150`); svg.innerHTML = ghostClone + svg.innerHTML; gElement.appendChild(svg);} backpackTraits.appendChild(gElement); } ghostGroup.remove(); if (backpackTraitsSvgs.length <= maxTraitsPerScreen) { leftBtn.style.display = "none"; rightBtn.style.display = "none";} else {leftBtn.style.opacity = 0.1;} leftBtn.onclick = () => { if (curScreen === 0) return; curScreen--; backpackTraits.style.transform = `translate(-${curScreen * 100}%, 0)`; rightBtn.style.opacity = 1; if (curScreen === 0) { leftBtn.style.opacity = 0.1;} }; rightBtn.onclick = () => { if (curScreen >= numScreens - 1) return; curScreen++; backpackTraits.style.transform = `translate(-${curScreen * 100}%, 0)`;leftBtn.style.opacity = 1;if (curScreen >= numScreens - 1) { rightBtn.style.opacity = 0.1; }}; document.getElementById("toggleMain").onclick = () => { mainGroup.classList.toggle("on"); mainGroup.classList.toggle("off"); if (backpackGroup.classList.contains("open")) { backpackGroup.classList.toggle("open"); backpackGroup.classList.toggle("closed");}}; document.getElementById("toggleBackpack").onclick = () => { backpackGroup.classList.toggle("open"); backpackGroup.classList.toggle("closed"); if (mainGroup.classList.contains("on")) { mainGroup.classList.toggle("on"); mainGroup.classList.toggle("off"); } }; ]]></script>';
string private constant SVG_END = '</svg> ';
error InvalidBodyBytes();
function generateBackgroundColorStyles(IChonkStorage.ChonkData memory _chonkdata) internal pure returns (string memory backgroundColorStyles) {
backgroundColorStyles = string.concat(
'<style>',
'body, svg{ background: #', _chonkdata.backgroundColor, '; }',
'.bg { fill: #', _chonkdata.backgroundColor, '; }',
'</style>'
);
}
function generateChonkData(IChonkStorage.ChonkData memory _chonkdata) internal pure returns (string memory chonkDataJson) {
chonkDataJson = string.concat(
'"chonkdata":[',
'{ "background_color" : "#', _chonkdata.backgroundColor, '" },',
'{ "num_items_in_backpack" : "', Utils.toString(_chonkdata.numOfItemsInBackpack), '" },',
'{ "renderer" : "2D" },',
'{ "body_type" : "', _chonkdata.bodyName, '" }'
']'
);
}
function renderAsDataUri(
uint256 _tokenId,
string memory _bodySvg,
string memory _traitsSvg,
string memory _traitsAttributes,
string memory _backpackSVGs,
IChonkStorage.ChonkData memory _chonkdata
) public pure returns (string memory) {
string memory fullSvg;
string memory fullAttributes;
fullSvg = string.concat(
SVG_START,
SVG_STYLE,
generateBackgroundColorStyles(_chonkdata),
SVG_BG_MAIN_START,
_bodySvg,
_traitsSvg,
'</g>'
);
string memory image = string.concat(
'"image":"data:image/svg+xml;base64,',
Utils.encode(bytes(string.concat(fullSvg, SVG_END) )),
'"'
);
if (bytes(_traitsAttributes).length > 0) {
fullAttributes = string.concat('"attributes":[', _traitsAttributes, ']');
}
else {
fullAttributes = string.concat('"attributes":[]');
}
string memory combinedHTML = string.concat(
'<!DOCTYPE html><html><head>',
SVG_STYLE,
generateBackgroundColorStyles(_chonkdata),
'</head><body>',
SVG_START,
SVG_BG_MAIN_START,
_bodySvg,
_traitsSvg,
_backpackSVGs,
'</g></g></g>',
SVG_TOGGLE,
SVG_TOGGLE_SCRIPT,
'</body></html>'
);
string memory animationURL = string.concat(
'"animation_url":"data:text/html;base64,',
Utils.encode(bytes(combinedHTML)),
'"'
);
string memory json = string.concat(
'{"name":"Chonk #',
Utils.toString(_tokenId),
'","description":"',
_chonkdata.descriptionParts[0],
Utils.toString(_tokenId),
_chonkdata.descriptionParts[1],
'",',
fullAttributes,
',', generateChonkData(_chonkdata),
',', image,
',', animationURL, // comment out for qa collection
'}'
);
return string.concat("data:application/json;base64,", Utils.encode(bytes(json)));
}
function getBodyImageSvg(bytes memory _pixels) public pure returns (string memory) {
// optimised for hex and set 30 coords
string[16] memory hexSymbols = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"];
string[30] memory coords = ["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29"];
bytes memory svgParts;
for (uint i; i < 4500; i += 5) {
if (_pixels[i] > 0) {
uint x = (i / 5) % 30;
uint y = (i / 5) / 30;
bytes memory color = abi.encodePacked(
hexSymbols[uint8(_pixels[i + 2]) >> 4],
hexSymbols[uint8(_pixels[i + 2]) & 0xf],
hexSymbols[uint8(_pixels[i + 3]) >> 4],
hexSymbols[uint8(_pixels[i + 3]) & 0xf],
hexSymbols[uint8(_pixels[i + 4]) >> 4],
hexSymbols[uint8(_pixels[i + 4]) & 0xf]
);
svgParts = abi.encodePacked(
svgParts,
'<rect x="', coords[x],
'" y="', coords[y],
'" width="1" height="1" fill="#', color, '"/>'
);
}
}
return string(abi.encodePacked('<g id="Body">', svgParts, '</g>'));
}
function colorMapToSVG(bytes memory colorMap) public pure returns (string memory) {
bytes memory pixels = getBodyImage(colorMap);
return getBodyImageSvg(pixels);
}
function getBodyImage(bytes memory colorMap) public pure returns (bytes memory) {
uint256 length = colorMap.length;
if (length == 0 || length % 5 != 0) revert InvalidBodyBytes();
bytes memory pixels = new bytes(30 * 30 * 5); // 30x30 grid with 5 bytes per pixel
uint256 pixelCount = length / 5;
for (uint256 i; i < pixelCount; ++i) {
uint256 offset = i * 5;
uint8 x = uint8(colorMap[offset]);
uint8 y = uint8(colorMap[offset + 1]);
uint256 index = (uint256(y) * 30 + uint256(x)) * 5;
// Set the pixel data in the pixels array
unchecked {
pixels[index] = colorMap[offset];
pixels[index + 1] = colorMap[offset + 1];
pixels[index + 2] = colorMap[offset + 2];
pixels[index + 3] = colorMap[offset + 3];
pixels[index + 4] = colorMap[offset + 4];
}
}
return pixels;
}
function getBackpackSVGs(
address _traitsContract,
address _tbaAddress,
uint256 _maxTraitsToOutput
) public view returns (string memory backpackSVGs) {
ChonkTraits traitsContract = ChonkTraits(_traitsContract);
uint256[] memory traitTokens = traitsContract.walletOfOwner(_tbaAddress);
string memory bodyGhostSvg = traitsContract.getGhostSvg();
uint256 numTraits = traitTokens.length < _maxTraitsToOutput ? traitTokens.length : _maxTraitsToOutput;
string[] memory traitSvgs = new string[](numTraits);
for (uint256 i; i < numTraits; ++i) {
traitSvgs[i] = traitsContract.getSvgForTokenId(traitTokens[i]);
}
string memory baseSvgPart = '<svg viewBox="0 0 150 150">';
string memory closeSvgTag = '</svg>';
bytes memory buffer;
buffer = abi.encodePacked(
SVG_BACKPACK,
bodyGhostSvg,
'<g id="backpackTraits">'
);
for (uint256 i; i < numTraits; ++i) {
buffer = abi.encodePacked(
buffer,
baseSvgPart,
traitSvgs[i],
closeSvgTag
);
}
if (traitTokens.length > _maxTraitsToOutput) {
buffer = abi.encodePacked(
buffer,
baseSvgPart,
'<g id="MoreTraits"><rect style="width:10px; height:2px;" x="10" y="16" fill="#ffffff"></rect><rect style="height:10px; width:2px;" x="14" y="12" fill="#ffffff"></rect></g>',
closeSvgTag
);
}
buffer = abi.encodePacked(
buffer,
'</g>'
);
backpackSVGs = string(buffer);
}
function stringTrait(string memory traitName, string memory traitValue) public pure returns (string memory) {
return string.concat(
'{"trait_type":"',
traitName,
'","value":"',
traitValue,
'"}'
);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
import { EncodeURI } from "../EncodeURI.sol";
import { IChonkStorage } from "../interfaces/IChonkStorage.sol";
import { Ownable } from "solady/auth/Ownable.sol";
import { Utils } from "../common/Utils.sol";
// Scripty for 3D rendering
import { IScriptyBuilderV2, HTMLRequest, HTMLTagType, HTMLTag } from "../../lib/scripty/interfaces/IScriptyBuilderV2.sol";
contract MainRenderer3D is Ownable {
// Scripty & EthFS for 3D rendering
address immutable scriptyBuilderAddress = 0xD7587F110E08F4D120A231bA97d3B577A81Df022;
address immutable ethfsFileStorageAddress = 0x8FAA1AAb9DA8c75917C43Fb24fDdb513edDC3245;
// Encodes plain text as a URI-encoded string
EncodeURI public encodeURIContract;
// Three.js script for 3D rendering
bytes public base64ScriptContent;
string private constant SVG_START_STYLE = '<svg shape-rendering="crispEdges" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg"><style> body{overflow: hidden; margin: 0;} svg{ max-width: 100vw; max-height: 100vh; width: 100%;} #main rect{width:1px; height: 1px;} .bg{width:30px; height: 30px;} .on { scale: 177%; transform: translate(-6px, -3px); } .off { scale: 100%; transform: translate(0px, 0px); } .button { cursor: pointer; fill: transparent; } .closed{ transform: translate(0px, 30px); } .open{ transform: translate(0px, 0px); } </style>';
constructor() {
_initializeOwner(msg.sender);
}
function generateFullSvg(
string memory _bodySvg,
string memory _traitsSvg,
IChonkStorage.ChonkData memory _chonkdata
) internal pure returns (string memory image) {
string memory fullSvg = string.concat(
SVG_START_STYLE,
generateBackgroundColorStyles(_chonkdata),
'<g id="body">',
_bodySvg,
'</g>',
'<g id="traits">',
_traitsSvg,
'</g></svg>'
);
image = string.concat(
'"image":"data:image/svg+xml;base64,',
Utils.encode(bytes(fullSvg )),
'"'
);
}
function generateBackgroundColorStyles(IChonkStorage.ChonkData memory _chonkdata) internal pure returns (string memory backgroundColorStyles) {
backgroundColorStyles = string.concat(
'<style>',
'body, svg{ background: #', _chonkdata.backgroundColor, '; }',
'.bg { fill: #', _chonkdata.backgroundColor, '; }',
'</style>'
);
}
function generateChonkData(IChonkStorage.ChonkData memory _chonkdata) internal pure returns (string memory chonkDataJson) {
chonkDataJson = string.concat(
'"chonkdata":[',
'{ "background_color" : "#', _chonkdata.backgroundColor, '" },',
'{ "num_items_in_backpack" : "', Utils.toString(_chonkdata.numOfItemsInBackpack), '" },',
'{ "renderer" : "3D" },',
'{ "body_type" : "', _chonkdata.bodyName, '" }'
']'
);
}
function generateAttributes(string memory _traitsAttributes, IChonkStorage.ChonkData memory _chonkdata) internal pure returns (string memory fullAttributes) {
//todo: do we need this bodyAttributes check in here?
if (bytes(_traitsAttributes).length > 0) {
// fullAttributes = string.concat('"attributes":[', _bodyAttributes, ',', _traitsAttributes, ']');
fullAttributes = string.concat(
'"attributes":[',
_traitsAttributes,
'],',
generateChonkData(_chonkdata)
);
}
else {
fullAttributes = string.concat(
'"attributes":[],',
generateChonkData(_chonkdata)
);
}
}
function renderAsDataUri(
uint256 _tokenId,
string memory _bodySvg,
string memory _traitsSvg,
string memory _traitsAttributes,
bytes memory _fullZmap,
IChonkStorage.ChonkData memory _chonkdata
) public view returns (string memory) {
string memory image = generateFullSvg( _bodySvg, _traitsSvg, _chonkdata);
string memory fullAttributes = generateAttributes(_traitsAttributes, _chonkdata);
HTMLTag[] memory headTags = new HTMLTag[](1);
headTags[0].tagOpen = "%253Cstyle%253E";
headTags[0]
.tagContent = "html%257Bheight%253A100%2525%257Dbody%257Bmin-height%253A100%2525%253Bmargin%253A0%253Bpadding%253A0%257Dcanvas%257Bpadding%253A0%253Bmargin%253Aauto%253Bdisplay%253Ablock%253Bposition%253Aabsolute%253Btop%253A0%253Bbottom%253A0%253Bleft%253A0%253Bright%253A0%257D";
headTags[0].tagClose = "%253C%252Fstyle%253E";
// Shoutout to Sterling Crispin and BrainWorms
HTMLTag[] memory bodyTags = new HTMLTag[](12);
bodyTags[0].name = "gunzipScripts-0.0.1.js";
bodyTags[0].tagType = HTMLTagType.scriptBase64DataURI;
bodyTags[0].contractAddress = ethfsFileStorageAddress;
bodyTags[1].name = "es-module-shims.js.Base64.gz";
bodyTags[1].tagType = HTMLTagType.scriptGZIPBase64DataURI;
bodyTags[1].contractAddress = ethfsFileStorageAddress;
bodyTags[2].name = "fflate.module.js.Base64.gz";
bodyTags[2]
.tagOpen = "%253Cscript%253Evar%2520fflte%2520%253D%2520%2522";
bodyTags[2].tagClose = "%2522%253C%252Fscript%253E";
bodyTags[2].contractAddress = ethfsFileStorageAddress;
bodyTags[3].name = "three-v0.162.0-module.min.js.Base64.gz";
bodyTags[3].tagOpen = "%253Cscript%253Evar%2520t3%2520%253D%2520%2522";
bodyTags[3].tagClose = "%2522%253C%252Fscript%253E";
bodyTags[3].contractAddress = ethfsFileStorageAddress;
bodyTags[4].name = "three-v0.162.0-OrbitControls.js.Base64.gz";
bodyTags[4].tagOpen = "%253Cscript%253Evar%2520oc%2520%253D%2520%2522";
bodyTags[4].tagClose = "%2522%253C%252Fscript%253E";
bodyTags[4].contractAddress = ethfsFileStorageAddress;
bodyTags[5].name = "importHandler.js";
bodyTags[5].tagType = HTMLTagType.scriptBase64DataURI;
bodyTags[5].contractAddress = ethfsFileStorageAddress;
bodyTags[6].name = "";
bodyTags[6].tagType = HTMLTagType.script;
bodyTags[6]
.tagContent = 'injectImportMap([ ["fflate",fflte], ["three",t3], ["OrbitControls",oc] ],gunzipScripts)';
bodyTags[7].name = "canvas";
bodyTags[7].tagOpen = '%253Ccanvas%2520id%253D%2522theCanvas%2522%2520class%253D%2522webgl%2522%253E';
bodyTags[7].tagClose = "%253C%252Fcanvas%253E";
bodyTags[8].tagOpen = bytes(
string.concat(
"%253Cscript%253Evar%2520zMapFull%2520%253D%2527",
encodeURIContract.encodeURI(
encodeURIContract.encodeURI(string(_fullZmap))
)
)
);
bodyTags[8].tagClose = "%2527%253B%253C%252Fscript%253E";
bodyTags[9].tagOpen = bytes(
string.concat(
"%253Cscript%253Evar%2520bgColor%2520%253D%2527",
encodeURIContract.encodeURI(
encodeURIContract.encodeURI(string.concat("#", _chonkdata.backgroundColor))
)
)
);
bodyTags[9].tagClose = "%2527%253B%253C%252Fscript%253E";
bodyTags[10]
.tagOpen = "%253Cscript%2520type%253D%2522module%2522%2520src%253D%2522data%253Atext%252Fjavascript%253Bbase64%252C";
bodyTags[10].tagContent = base64ScriptContent;
bodyTags[10].tagClose = "%2522%253E%253C%252Fscript%253E";
HTMLRequest memory htmlRequest;
htmlRequest.headTags = headTags;
htmlRequest.bodyTags = bodyTags;
bytes memory doubleURLEncodedHTMLDataURI = IScriptyBuilderV2(scriptyBuilderAddress).getHTMLURLSafe(htmlRequest);
return generateJSON(_tokenId, fullAttributes, _chonkdata, image, doubleURLEncodedHTMLDataURI);
}
function generateJSON(uint256 _tokenId, string memory _attributes, IChonkStorage.ChonkData memory _chonkdata, string memory _image, bytes memory _doubleURLEncodedHTMLDataURI) internal view returns (string memory json) {
return
string(
abi.encodePacked(
"data:application/json,",
encodeURIContract.encodeURI('{"name":"Chonk #'),
Utils.toString(_tokenId),
generateDescription(_chonkdata, _tokenId),
encodeURIContract.encodeURI(_attributes),
encodeURIContract.encodeURI(','),
encodeURIContract.encodeURI(_image),
encodeURIContract.encodeURI(',"animation_url":"'),
_doubleURLEncodedHTMLDataURI,
encodeURIContract.encodeURI('"}')
)
);
}
function generateDescription(IChonkStorage.ChonkData memory _chonkdata, uint256 _tokenId) internal view returns (string memory description) {
description = string.concat(
encodeURIContract.encodeURI('", "description":"'),
encodeURIContract.encodeURI(_chonkdata.descriptionParts[0]),
Utils.toString(_tokenId),
encodeURIContract.encodeURI(_chonkdata.descriptionParts[1]),
encodeURIContract.encodeURI('",')
);
}
function setEncodeURI(address _encodeURIAddress) public onlyOwner {
encodeURIContract = EncodeURI(_encodeURIAddress);
}
function setScriptContent(bytes calldata _base64EncodedString) public onlyOwner {
base64ScriptContent = _base64EncodedString;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
import { ChonkTraits } from "./ChonkTraits.sol";
import { TraitCategory } from "./TraitCategory.sol";
interface IChonksMain {
function getTBAAddressForChonkId(uint256 _chonkId) external view returns (address);
}
interface IChonksMarket {
function getTraitOffer(uint256 _traitId) external view returns (uint256 priceInWei, address seller, address sellerTBA, address onlySellTo);
}
contract ChonkEquipHelper {
IChonksMain public immutable chonksMain;
ChonkTraits public immutable chonkTraits;
IChonksMarket public immutable chonksMarket;
error IncorrectTBAOwner();
error IncorrectTraitType();
error TraitIsOffered();
modifier traitIsNotOffered(uint256 _traitTokenId) {
(uint256 offerPrice,,,) = chonksMarket.getTraitOffer(_traitTokenId);
if (offerPrice > 0) revert TraitIsOffered();
_;
}
constructor(address _chonksMain, address _chonkTraits, address _chonksMarket) {
chonksMain = IChonksMain(_chonksMain);
chonkTraits = ChonkTraits(_chonkTraits);
chonksMarket = IChonksMarket(_chonksMarket);
}
function performValidations(
address _tbaForChonk,
uint256 _traitTokenId,
TraitCategory.Name _traitType
) view public traitIsNotOffered(_traitTokenId) {
_validateTBAOwnership(_tbaForChonk, _traitTokenId);
_validateTraitType(_traitTokenId, _traitType);
}
function equipValidation(
uint256 _chonkTokenId,
uint256 _traitTokenId
) view public traitIsNotOffered(_traitTokenId) returns (TraitCategory.Name traitType)
{
address tbaForChonk = chonksMain.getTBAAddressForChonkId(_chonkTokenId);
_validateTBAOwnership(tbaForChonk, _traitTokenId);
traitType = chonkTraits.getTraitMetadata(_traitTokenId).traitType;
_validateTraitType(_traitTokenId, traitType);
}
function _validateTBAOwnership(address _tbaForChonk, uint256 _traitTokenId) internal view {
address ownerOfTrait = chonkTraits.ownerOf(_traitTokenId);
if (ownerOfTrait != _tbaForChonk) revert IncorrectTBAOwner();
}
function _validateTraitType(uint256 _traitTokenId, TraitCategory.Name _traitType) internal view {
TraitCategory.Name traitTypeofTokenIdToBeSet = chonkTraits.getTraitMetadata(_traitTokenId).traitType;
if (keccak256(abi.encodePacked(uint(traitTypeofTokenIdToBeSet))) != keccak256(abi.encodePacked(uint(_traitType))))
revert IncorrectTraitType();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
import { ChonkTraits } from './ChonkTraits.sol';
import { ITraitStorage } from './interfaces/ITraitStorage.sol';
import { Ownable } from 'solady/auth/Ownable.sol';
import { TraitCategory } from './TraitCategory.sol';
import { Utils } from './common/Utils.sol';
contract FirstReleaseDataMinter is Ownable {
uint256[] public accessory = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
uint256[] internal accessoryProbability = [6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 82, 86, 90, 94, 96, 98, 99, 100];
uint256[] public head = [1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031];
uint256[] internal headProbability = [2, 14, 26, 29, 33, 35, 38, 41, 43, 45, 47, 48, 49, 50, 53, 56, 59, 62, 65, 68, 71, 72, 73, 76, 79, 82, 85, 88, 91, 94, 97, 100];
uint256[] public hair = [2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026, 2027, 2028, 2029, 2030, 2031, 2032, 2033, 2034, 2035, 2036];
uint256[] internal hairProbability = [3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 95, 96, 97, 98, 99, 100];
uint256[] public face = [3000, 3001, 3002, 3003, 3004, 3005, 3006, 3007, 3008, 3009, 3010, 3011];
uint256[] internal faceProbability = [2, 4, 12, 20, 28, 41, 52, 63, 74, 85, 96, 100];
uint256[] public top = [4000, 4001, 4002, 4003, 4004, 4005, 4006, 4007, 4008, 4009, 4010, 4011, 4012, 4013, 4014, 4015, 4016, 4017, 4018, 4019, 4020, 4021, 4022, 4023, 4024, 4025, 4026, 4027, 4028, 4029, 4030, 4031, 4032, 4033, 4034, 4035, 4036, 4037, 4038, 4039, 4040, 4041, 4042, 4043, 4044, 4045, 4046, 4047, 4048, 4049, 4050, 4051, 4052, 4053, 4054, 4055, 4056, 4057, 4058, 4059, 4060, 4061, 4062, 4063, 4064, 4065, 4066, 4067, 4068, 4069, 4070, 4071, 4072];
uint256[] public bottom = [5000, 5001, 5002, 5003, 5004, 5005, 5006, 5007, 5008, 5009, 5010, 5011, 5012, 5013, 5014, 5015, 5016, 5017, 5018, 5019, 5020, 5021, 5022, 5023, 5024, 5025, 5026, 5027, 5028, 5029, 5030, 5031, 5032, 5033, 5034, 5035, 5036, 5037, 5038, 5039, 5040, 5041, 5042, 5043, 5044];
uint256[] public shoes = [6000, 6001, 6002, 6003, 6004, 6005, 6006, 6007, 6008, 6009, 6010, 6011, 6012, 6013, 6014, 6015, 6016, 6017];
// The Main contract address
address public chonksMain;
// The Trait contract address
ChonkTraits public chonkTraits;
/// Errors
error OnlyChonksMain();
/// Constructor
constructor(address _chonksMain, address _chonkTraits) {
_initializeOwner(msg.sender);
chonksMain = _chonksMain;
chonkTraits = ChonkTraits(_chonkTraits);
}
function safeMintMany(address _toTBA, uint8 _traitCount) public returns (uint256[] memory) {
if (msg.sender != chonksMain) revert OnlyChonksMain();
uint256[] memory mintedIds = new uint256[](_traitCount);
for (uint256 i; i < _traitCount; ++i) {
// Creates a blank Trait token
uint256 tokenId = chonkTraits.safeMint(_toTBA);
mintedIds[i] = tokenId;
// Initialize our Trait
ITraitStorage.StoredTrait memory trait = chonkTraits.getStoredTraitForTokenId(tokenId);
// Set the current epoch
trait.epoch = chonkTraits.getCurrentEpoch();
// Set the seed to the tokenId
trait.seed = tokenId;
// Set the data render contract to this contract
trait.dataMinterContract = address(this);
// Assign the Trait Category
if (i == 0) {
trait.traitType = TraitCategory.Name.Shoes;
} else if (i == 1) {
trait.traitType = TraitCategory.Name.Bottom;
} else if (i == 2) {
trait.traitType = TraitCategory.Name.Top;
} else if (i == 3) {
trait.traitType = TraitCategory.Name.Hair;
} else if (i == 4) {
trait.traitType = TraitCategory.Name.Face;
} else if (i == 5) {
trait.traitType = TraitCategory.Name.Head;
} else if (i == 6) {
trait.traitType = TraitCategory.Name.Accessory;
} else {
// This should never happen
trait.traitType = TraitCategory.Name.Accessory;
}
chonkTraits.setTraitForTokenId(tokenId, trait);
}
return mintedIds;
}
/// @notice Ownership will be revoked after mint period
function addNewTrait(
uint256 _traitIndex,
string memory _traitName,
TraitCategory.Name _traitType,
bytes memory _colorMap,
bytes memory _zMap,
address _creatorAddress,
string memory _creatorName
) public onlyOwner {
ITraitStorage.TraitMetadata memory metadata = chonkTraits.getTraitIndexToMetadata(_traitIndex);
metadata.traitIndex = _traitIndex;
metadata.traitName = _traitName;
metadata.traitType = _traitType;
metadata.colorMap = _colorMap;
metadata.zMap = _zMap;
metadata.dataMinterContract = address(this);
metadata.creatorAddress = _creatorAddress;
metadata.creatorName = _creatorName;
metadata.release = "1";
chonkTraits.setTraitIndexToMetadata(_traitIndex, metadata);
}
// Shoutout to apex777.eth and Based OnChain Dinos:
function _pickTraitByProbability(uint256 seed, uint256[] memory traitArray, uint256[] memory traitProbability) internal pure returns (uint256) {
require(traitArray.length > 0, "Elements array is empty");
require(traitArray.length == traitProbability.length, "Elements and weights length mismatch");
for (uint256 i; i < traitProbability.length; i++) {
if (seed < traitProbability[i]) {
return i;
}
}
// Fallback, return first element as a safe default
return 0;
}
function explainTrait(
ITraitStorage.StoredTrait memory storedTrait,
uint128 randomness
) view public returns (ITraitStorage.StoredTrait memory) {
uint256 n; // number for randomness
storedTrait.seed = uint256(keccak256(abi.encodePacked(randomness, storedTrait.seed))) % type(uint256).max;
storedTrait.isRevealed = randomness > 0; // if randomness is > 0, epoch & hence Chonk is revealed
if (storedTrait.traitType == TraitCategory.Name.Accessory) {
storedTrait.traitIndex = 0 + _pickTraitByProbability( Utils.random(storedTrait.seed, 'acccesory', 100), accessory, accessoryProbability);
}
if (storedTrait.traitType == TraitCategory.Name.Head) {
storedTrait.traitIndex = 1000 + _pickTraitByProbability( Utils.random(storedTrait.seed, 'head', 100), head, headProbability);
}
if (storedTrait.traitType == TraitCategory.Name.Hair) {
storedTrait.traitIndex = 2000 + _pickTraitByProbability( Utils.random(storedTrait.seed, 'hair', 100), hair, hairProbability);
}
if (storedTrait.traitType == TraitCategory.Name.Face) {
storedTrait.traitIndex = 3000 + _pickTraitByProbability( Utils.random(storedTrait.seed, 'face', 100), face, faceProbability);
}
// tops: last 3 are rares, 1% chance of this tranche hitting
if (storedTrait.traitType == TraitCategory.Name.Top) {
n = Utils.random(storedTrait.seed, 'top', 100);
if (n < 99) {
storedTrait.traitIndex = 4000 + Utils.random(storedTrait.seed, 'top-common', top.length - 3 );
} else {
storedTrait.traitIndex = 4000 + (top.length - 3) + Utils.random(storedTrait.seed, 'top-rare', 3 );
}
}
// bottoms: last 4 are rares, 1% chance of this tranche hitting
if (storedTrait.traitType == TraitCategory.Name.Bottom) {
n = Utils.random(storedTrait.seed, 'bottom', 100);
if (n < 99) {
storedTrait.traitIndex = 5000 + Utils.random(storedTrait.seed, 'bottom-common', bottom.length - 4 );
} else {
storedTrait.traitIndex = 5000 + (bottom.length - 4) + Utils.random(storedTrait.seed, 'bottom-rare', 4 );
}
}
// shoes: last 4 are rares, 5% chance of this tranche hitting
if (storedTrait.traitType == TraitCategory.Name.Shoes) {
n = Utils.random(storedTrait.seed, 'shoes', 100);
if (n < 95) {
storedTrait.traitIndex = 6000 + Utils.random(storedTrait.seed, 'shoes-common', shoes.length - 4 );
} else {
storedTrait.traitIndex = 6000 + (shoes.length - 4) + Utils.random(storedTrait.seed, 'shoes-rare', 4 );
}
}
return storedTrait;
}
}// SPDX-License-Identifier: MIT
// via @iamwhitelights
pragma solidity ^0.8.17;
contract EncodeURI {
/**
* @dev URI Encoding/Decoding Hex Table
*/
bytes internal constant TABLE = "0123456789ABCDEF";
/**
* @dev URI encodes the provided string. Gas optimized like crazy.
*/
function encodeURI(string memory str_) public pure returns (string memory) {
bytes memory input = bytes(str_);
uint256 inputLength = input.length;
uint256 outputLength = 0;
for (uint256 i = 0; i < inputLength; i++) {
bytes1 b = input[i];
if (
(b >= 0x30 && b <= 0x39) ||
(b >= 0x41 && b <= 0x5a) ||
(b >= 0x61 && b <= 0x7a)
) {
outputLength++;
} else {
outputLength += 3;
}
}
bytes memory output = new bytes(outputLength);
uint256 j = 0;
for (uint256 i = 0; i < inputLength; i++) {
bytes1 b = input[i];
if (
(b >= 0x30 && b <= 0x39) ||
(b >= 0x41 && b <= 0x5a) ||
(b >= 0x61 && b <= 0x7a)
) {
output[j++] = b;
} else {
bytes1 b1 = TABLE[uint8(b) / 16];
bytes1 b2 = TABLE[uint8(b) % 16];
output[j++] = 0x25; // '%'
output[j++] = b1;
output[j++] = b2;
}
}
return string(output);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
///////////////////////////////////////////////////////////
// ░██████╗░█████╗░██████╗░██╗██████╗░████████╗██╗░░░██╗ //
// ██╔════╝██╔══██╗██╔══██╗██║██╔══██╗╚══██╔══╝╚██╗░██╔╝ //
// ╚█████╗░██║░░╚═╝██████╔╝██║██████╔╝░░░██║░░░░╚████╔╝░ //
// ░╚═══██╗██║░░██╗██╔══██╗██║██╔═══╝░░░░██║░░░░░╚██╔╝░░ //
// ██████╔╝╚█████╔╝██║░░██║██║██║░░░░░░░░██║░░░░░░██║░░░ //
// ╚═════╝░░╚════╝░╚═╝░░╚═╝╚═╝╚═╝░░░░░░░░╚═╝░░░░░░╚═╝░░░ //
///////////////////////////////////////////////////////////
/**
@title A generic HTML builder that fetches and assembles given JS requests.
@author @0xthedude
@author @xtremetom
Special thanks to @cxkoda and @frolic
*/
import "./IScriptyHTML.sol";
import "./IScriptyHTMLURLSafe.sol";
interface IScriptyBuilderV2 is IScriptyHTML, IScriptyHTMLURLSafe {}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
///////////////////////////////////////////////////////////
// ░██████╗░█████╗░██████╗░██╗██████╗░████████╗██╗░░░██╗ //
// ██╔════╝██╔══██╗██╔══██╗██║██╔══██╗╚══██╔══╝╚██╗░██╔╝ //
// ╚█████╗░██║░░╚═╝██████╔╝██║██████╔╝░░░██║░░░░╚████╔╝░ //
// ░╚═══██╗██║░░██╗██╔══██╗██║██╔═══╝░░░░██║░░░░░╚██╔╝░░ //
// ██████╔╝╚█████╔╝██║░░██║██║██║░░░░░░░░██║░░░░░░██║░░░ //
// ╚═════╝░░╚════╝░╚═╝░░╚═╝╚═╝╚═╝░░░░░░░░╚═╝░░░░░░╚═╝░░░ //
///////////////////////////////////////////////////////////
import {HTMLRequest, HTMLTagType, HTMLTag} from "./../core/ScriptyCore.sol";
interface IScriptyHTML {
// =============================================================
// RAW HTML GETTERS
// =============================================================
/**
* @notice Get HTML with requested head tags and body tags
* @dev Your HTML is returned in the following format:
* <html>
* <head>
* [tagOpen[0]][contractRequest[0] | tagContent[0]][tagClose[0]]
* [tagOpen[1]][contractRequest[0] | tagContent[1]][tagClose[1]]
* ...
* [tagOpen[n]][contractRequest[0] | tagContent[n]][tagClose[n]]
* </head>
* <body>
* [tagOpen[0]][contractRequest[0] | tagContent[0]][tagClose[0]]
* [tagOpen[1]][contractRequest[0] | tagContent[1]][tagClose[1]]
* ...
* [tagOpen[n]][contractRequest[0] | tagContent[n]][tagClose[n]]
* </body>
* </html>
* @param htmlRequest - HTMLRequest
* @return Full HTML with head and body tags
*/
function getHTML(
HTMLRequest memory htmlRequest
) external view returns (bytes memory);
// =============================================================
// ENCODED HTML GETTERS
// =============================================================
/**
* @notice Get {getHTML} and base64 encode it
* @param htmlRequest - HTMLRequest
* @return Full HTML with head and script tags, base64 encoded
*/
function getEncodedHTML(
HTMLRequest memory htmlRequest
) external view returns (bytes memory);
// =============================================================
// STRING UTILITIES
// =============================================================
/**
* @notice Convert {getHTML} output to a string
* @param htmlRequest - HTMLRequest
* @return {getHTMLWrapped} as a string
*/
function getHTMLString(
HTMLRequest memory htmlRequest
) external view returns (string memory);
/**
* @notice Convert {getEncodedHTML} output to a string
* @param htmlRequest - HTMLRequest
* @return {getEncodedHTML} as a string
*/
function getEncodedHTMLString(
HTMLRequest memory htmlRequest
) external view returns (string memory);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
///////////////////////////////////////////////////////////
// ░██████╗░█████╗░██████╗░██╗██████╗░████████╗██╗░░░██╗ //
// ██╔════╝██╔══██╗██╔══██╗██║██╔══██╗╚══██╔══╝╚██╗░██╔╝ //
// ╚█████╗░██║░░╚═╝██████╔╝██║██████╔╝░░░██║░░░░╚████╔╝░ //
// ░╚═══██╗██║░░██╗██╔══██╗██║██╔═══╝░░░░██║░░░░░╚██╔╝░░ //
// ██████╔╝╚█████╔╝██║░░██║██║██║░░░░░░░░██║░░░░░░██║░░░ //
// ╚═════╝░░╚════╝░╚═╝░░╚═╝╚═╝╚═╝░░░░░░░░╚═╝░░░░░░╚═╝░░░ //
///////////////////////////////////////////////////////////
import {HTMLRequest, HTMLTagType, HTMLTag} from "./../core/ScriptyCore.sol";
interface IScriptyHTMLURLSafe {
// =============================================================
// RAW HTML GETTERS
// =============================================================
/**
* @notice Get URL safe HTML with requested head tags and body tags
* @dev Any tags with tagType = 1/script are converted to base64 and wrapped
* with <script src="data:text/javascript;base64,[SCRIPT]"></script>
*
* [WARNING]: Large non-base64 libraries that need base64 encoding
* carry a high risk of causing a gas out. Highly advised the use
* of base64 encoded scripts where possible
*
* Your HTML is returned in the following format:
*
* <html>
* <head>
* [tagOpen[0]][contractRequest[0] | tagContent[0]][tagClose[0]]
* [tagOpen[1]][contractRequest[0] | tagContent[1]][tagClose[1]]
* ...
* [tagOpen[n]][contractRequest[0] | tagContent[n]][tagClose[n]]
* </head>
* <body>
* [tagOpen[0]][contractRequest[0] | tagContent[0]][tagClose[0]]
* [tagOpen[1]][contractRequest[0] | tagContent[1]][tagClose[1]]
* ...
* [tagOpen[n]][contractRequest[0] | tagContent[n]][tagClose[n]]
* </body>
* </html>
* @param htmlRequest - HTMLRequest
* @return Full HTML with head and body tags
*/
function getHTMLURLSafe(
HTMLRequest memory htmlRequest
) external view returns (bytes memory);
// =============================================================
// STRING UTILITIES
// =============================================================
/**
* @notice Convert {getHTMLURLSafe} output to a string
* @param htmlRequest - HTMLRequest
* @return {getHTMLURLSafe} as a string
*/
function getHTMLURLSafeString(
HTMLRequest memory htmlRequest
) external view returns (string memory);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
///////////////////////////////////////////////////////////
// ░██████╗░█████╗░██████╗░██╗██████╗░████████╗██╗░░░██╗ //
// ██╔════╝██╔══██╗██╔══██╗██║██╔══██╗╚══██╔══╝╚██╗░██╔╝ //
// ╚█████╗░██║░░╚═╝██████╔╝██║██████╔╝░░░██║░░░░╚████╔╝░ //
// ░╚═══██╗██║░░██╗██╔══██╗██║██╔═══╝░░░░██║░░░░░╚██╔╝░░ //
// ██████╔╝╚█████╔╝██║░░██║██║██║░░░░░░░░██║░░░░░░██║░░░ //
// ╚═════╝░░╚════╝░╚═╝░░╚═╝╚═╝╚═╝░░░░░░░░╚═╝░░░░░░╚═╝░░░ //
///////////////////////////////////////////////////////////
//░░░░░░░░░░░░░░░░░░░░░░ CORE ░░░░░░░░░░░░░░░░░░░░░//
///////////////////////////////////////////////////////////
import {HTMLRequest, HTMLTagType, HTMLTag} from "./ScriptyStructs.sol";
import {DynamicBuffer} from "./../utils/DynamicBuffer.sol";
import {IScriptyContractStorage} from "./../interfaces/IScriptyContractStorage.sol";
contract ScriptyCore {
using DynamicBuffer for bytes;
// =============================================================
// TAG CONSTANTS
// =============================================================
// data:text/html;base64,
// raw
// 22 bytes
bytes public constant DATA_HTML_BASE64_URI_RAW = "data:text/html;base64,";
// url encoded
// 21 bytes
bytes public constant DATA_HTML_URL_SAFE = "data%3Atext%2Fhtml%2C";
// <html>,
// raw
// 6 bytes
bytes public constant HTML_OPEN_RAW = "<html>";
// url encoded
// 10 bytes
bytes public constant HTML_OPEN_URL_SAFE = "%3Chtml%3E";
// <head>,
// raw
// 6 bytes
bytes public constant HEAD_OPEN_RAW = "<head>";
// url encoded
// 10 bytes
bytes public constant HEAD_OPEN_URL_SAFE = "%3Chead%3E";
// </head>,
// raw
// 7 bytes
bytes public constant HEAD_CLOSE_RAW = "</head>";
// url encoded
// 13 bytes
bytes public constant HEAD_CLOSE_URL_SAFE = "%3C%2Fhead%3E";
// <body>
// 6 bytes
bytes public constant BODY_OPEN_RAW = "<body>";
// url encoded
// 10 bytes
bytes public constant BODY_OPEN_URL_SAFE = "%3Cbody%3E";
// </body></html>
// 14 bytes
bytes public constant HTML_BODY_CLOSED_RAW = "</body></html>";
// 26 bytes
bytes public constant HTML_BODY_CLOSED_URL_SAFE =
"%3C%2Fbody%3E%3C%2Fhtml%3E";
// [RAW]
// HTML_OPEN + HEAD_OPEN + HEAD_CLOSE + BODY_OPEN + HTML_BODY_CLOSED
uint256 public constant URLS_RAW_BYTES = 39;
// [URL_SAFE]
// DATA_HTML_URL_SAFE + HTML_OPEN + HEAD_OPEN + HEAD_CLOSE + BODY_OPEN + HTML_BODY_CLOSED
uint256 public constant URLS_SAFE_BYTES = 90;
// [RAW]
// HTML_OPEN + HTML_CLOSE
uint256 public constant HTML_RAW_BYTES = 13;
// [RAW]
// HEAD_OPEN + HEAD_CLOSE
uint256 public constant HEAD_RAW_BYTES = 13;
// [RAW]
// BODY_OPEN + BODY_CLOSE
uint256 public constant BODY_RAW_BYTES = 13;
// All raw
// HTML_RAW_BYTES + HEAD_RAW_BYTES + BODY_RAW_BYTES
uint256 public constant RAW_BYTES = 39;
// [URL_SAFE]
// HTML_OPEN + HTML_CLOSE
uint256 public constant HTML_URL_SAFE_BYTES = 23;
// [URL_SAFE]
// HEAD_OPEN + HEAD_CLOSE
uint256 public constant HEAD_URL_SAFE_BYTES = 23;
// [URL_SAFE]
// BODY_OPEN + BODY_CLOSE
uint256 public constant BODY_SAFE_BYTES = 23;
// All url safe
// HTML_URL_SAFE_BYTES + HEAD_URL_SAFE_BYTES + BODY_URL_SAFE_BYTES
// %3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody%3E%3C%2Fbody%3E%3C%2Fhtml%3E
uint256 public constant URL_SAFE_BYTES = 69;
// data:text/html;base64,
uint256 public constant HTML_BASE64_DATA_URI_BYTES = 22;
// =============================================================
// TAG OPEN CLOSE TEMPLATES
// =============================================================
/**
* @notice Grab tag open and close depending on tag type
* @dev
* tagType: 0/HTMLTagType.useTagOpenAndClose or any other:
* [tagOpen][CONTENT][tagClose]
*
* tagType: 1/HTMLTagType.script:
* <script>[SCRIPT]</script>
*
* tagType: 2/HTMLTagType.scriptBase64DataURI:
* <script src="data:text/javascript;base64,[SCRIPT]"></script>
*
* tagType: 3/HTMLTagType.scriptGZIPBase64DataURI:
* <script type="text/javascript+gzip" src="data:text/javascript;base64,[SCRIPT]"></script>
*
* tagType: 4/HTMLTagType.scriptPNGBase64DataURI
* <script type="text/javascript+png" name="[NAME]" src="data:text/javascript;base64,[SCRIPT]"></script>
*
* [IMPORTANT NOTE]: The tags `text/javascript+gzip` and `text/javascript+png` are used to identify scripts
* during decompression
*
* @param htmlTag - HTMLTag data for code
* @return (tagOpen, tagClose) - Tag open and close as a tuple
*/
function tagOpenCloseForHTMLTag(
HTMLTag memory htmlTag
) public pure returns (bytes memory, bytes memory) {
if (htmlTag.tagType == HTMLTagType.script) {
return ("<script>", "</script>");
} else if (htmlTag.tagType == HTMLTagType.scriptBase64DataURI) {
return ('<script src="data:text/javascript;base64,', '"></script>');
} else if (htmlTag.tagType == HTMLTagType.scriptGZIPBase64DataURI) {
return (
'<script type="text/javascript+gzip" src="data:text/javascript;base64,',
'"></script>'
);
} else if (htmlTag.tagType == HTMLTagType.scriptPNGBase64DataURI) {
return (
'<script type="text/javascript+png" src="data:text/javascript;base64,',
'"></script>'
);
}
return (htmlTag.tagOpen, htmlTag.tagClose);
}
/**
* @notice Grab URL safe tag open and close depending on tag type
* @dev
* tagType: 0/HTMLTagType.useTagOpenAndClose or any other:
* [tagOpen][scriptContent or scriptFromContract][tagClose]
*
* tagType: 1/HTMLTagType.script:
* tagType: 2/HTMLTagType.scriptBase64DataURI:
* <script src="data:text/javascript;base64,[SCRIPT]"></script>
*
* tagType: 3/HTMLTagType.scriptGZIPBase64DataURI:
* <script type="text/javascript+gzip" src="data:text/javascript;base64,[SCRIPT]"></script>
*
* tagType: 4/HTMLTagType.scriptPNGBase64DataURI
* <script type="text/javascript+png" name="[NAME]" src="data:text/javascript;base64,[SCRIPT]"></script>
*
* [IMPORTANT NOTE]: The tags `text/javascript+gzip` and `text/javascript+png` are used to identify scripts
* during decompression
*
* @param htmlTag - HTMLTag data for code
* @return (tagOpen, tagClose) - Tag open and close as a tuple
*/
function tagOpenCloseForHTMLTagURLSafe(
HTMLTag memory htmlTag
) public pure returns (bytes memory, bytes memory) {
if (
htmlTag.tagType == HTMLTagType.script ||
htmlTag.tagType == HTMLTagType.scriptBase64DataURI
) {
// <script src="data:text/javascript;base64,
// "></script>
return (
"%253Cscript%2520src%253D%2522data%253Atext%252Fjavascript%253Bbase64%252C",
"%2522%253E%253C%252Fscript%253E"
);
} else if (htmlTag.tagType == HTMLTagType.scriptGZIPBase64DataURI) {
// <script type="text/javascript+gzip" src="data:text/javascript;base64,
// "></script>
return (
"%253Cscript%2520type%253D%2522text%252Fjavascript%252Bgzip%2522%2520src%253D%2522data%253Atext%252Fjavascript%253Bbase64%252C",
"%2522%253E%253C%252Fscript%253E"
);
} else if (htmlTag.tagType == HTMLTagType.scriptPNGBase64DataURI) {
// <script type="text/javascript+png" src="data:text/javascript;base64,
// "></script>
return (
"%253Cscript%2520type%253D%2522text%252Fjavascript%252Bpng%2522%2520src%253D%2522data%253Atext%252Fjavascript%253Bbase64%252C",
"%2522%253E%253C%252Fscript%253E"
);
}
return (htmlTag.tagOpen, htmlTag.tagClose);
}
// =============================================================
// TAG CONTENT FETCHER
// =============================================================
/**
* @notice Grabs requested tag content from storage
* @dev
* If given HTMLTag contains non empty contractAddress
* this method will fetch the content from given storage
* contract. Otherwise, it will return the tagContent
* from the given htmlTag.
*
* @param htmlTag - HTMLTag
*/
function fetchTagContent(
HTMLTag memory htmlTag
) public view returns (bytes memory) {
if (htmlTag.contractAddress != address(0)) {
return
IScriptyContractStorage(htmlTag.contractAddress).getContent(
htmlTag.name,
htmlTag.contractData
);
}
return htmlTag.tagContent;
}
// =============================================================
// SIZE OPERATIONS
// =============================================================
/**
* @notice Calculate the buffer size post base64 encoding
* @param value - Starting buffer size
* @return Final buffer size as uint256
*/
function sizeForBase64Encoding(
uint256 value
) public pure returns (uint256) {
unchecked {
return 4 * ((value + 2) / 3);
}
}
/**
* @notice Adds the required tag open/close and calculates buffer size of tags
* @dev Effectively multiple functions bundled into one as this saves gas
* @param htmlTags - Array of HTMLTag
* @param isURLSafe - Bool to handle tag content/open/close encoding
* @return Total buffersize of updated HTMLTags
*/
function _enrichHTMLTags(
HTMLTag[] memory htmlTags,
bool isURLSafe
) internal view returns (uint256) {
if (htmlTags.length == 0) {
return 0;
}
bytes memory tagOpen;
bytes memory tagClose;
bytes memory tagContent;
uint256 totalSize;
uint256 length = htmlTags.length;
uint256 i;
unchecked {
do {
tagContent = fetchTagContent(htmlTags[i]);
htmlTags[i].tagContent = tagContent;
if (isURLSafe && htmlTags[i].tagType == HTMLTagType.script) {
totalSize += sizeForBase64Encoding(tagContent.length);
} else {
totalSize += tagContent.length;
}
if (isURLSafe) {
(tagOpen, tagClose) = tagOpenCloseForHTMLTagURLSafe(
htmlTags[i]
);
} else {
(tagOpen, tagClose) = tagOpenCloseForHTMLTag(htmlTags[i]);
}
htmlTags[i].tagOpen = tagOpen;
htmlTags[i].tagClose = tagClose;
totalSize += tagOpen.length;
totalSize += tagClose.length;
} while (++i < length);
}
return totalSize;
}
// =============================================================
// HTML CONCATENATION
// =============================================================
/**
* @notice Append tags to the html buffer for tags
* @param htmlFile - bytes buffer
* @param htmlTags - Tags being added to buffer
* @param base64EncodeTagContent - Bool to handle tag content encoding
*/
function _appendHTMLTags(
bytes memory htmlFile,
HTMLTag[] memory htmlTags,
bool base64EncodeTagContent
) internal pure {
uint256 i;
unchecked {
do {
_appendHTMLTag(htmlFile, htmlTags[i], base64EncodeTagContent);
} while (++i < htmlTags.length);
}
}
/**
* @notice Append tag to the html buffer
* @param htmlFile - bytes buffer
* @param htmlTag - Request being added to buffer
* @param base64EncodeTagContent - Bool to handle tag content encoding
*/
function _appendHTMLTag(
bytes memory htmlFile,
HTMLTag memory htmlTag,
bool base64EncodeTagContent
) internal pure {
htmlFile.appendSafe(htmlTag.tagOpen);
if (base64EncodeTagContent) {
htmlFile.appendSafeBase64(htmlTag.tagContent, false, false);
} else {
htmlFile.appendSafe(htmlTag.tagContent);
}
htmlFile.appendSafe(htmlTag.tagClose);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
///////////////////////////////////////////////////////////
// ░██████╗░█████╗░██████╗░██╗██████╗░████████╗██╗░░░██╗ //
// ██╔════╝██╔══██╗██╔══██╗██║██╔══██╗╚══██╔══╝╚██╗░██╔╝ //
// ╚█████╗░██║░░╚═╝██████╔╝██║██████╔╝░░░██║░░░░╚████╔╝░ //
// ░╚═══██╗██║░░██╗██╔══██╗██║██╔═══╝░░░░██║░░░░░╚██╔╝░░ //
// ██████╔╝╚█████╔╝██║░░██║██║██║░░░░░░░░██║░░░░░░██║░░░ //
// ╚═════╝░░╚════╝░╚═╝░░╚═╝╚═╝╚═╝░░░░░░░░╚═╝░░░░░░╚═╝░░░ //
///////////////////////////////////////////////////////////
//░░░░░░░░░░░░░░░░░░░ REQUESTS ░░░░░░░░░░░░░░░░░░░░//
///////////////////////////////////////////////////////////
struct HTMLRequest {
HTMLTag[] headTags;
HTMLTag[] bodyTags;
}
enum HTMLTagType {
useTagOpenAndClose,
script,
scriptBase64DataURI,
scriptGZIPBase64DataURI,
scriptPNGBase64DataURI
}
struct HTMLTag {
string name;
address contractAddress;
bytes contractData;
HTMLTagType tagType;
bytes tagOpen;
bytes tagClose;
bytes tagContent;
}// SPDX-License-Identifier: MIT
// Copyright (c) 2021 the ethier authors (github.com/divergencetech/ethier)
pragma solidity ^0.8.22;
/// @title DynamicBuffer
/// @author David Huber (@cxkoda) and Simon Fremaux (@dievardump). See also
/// https://raw.githubusercontent.com/dievardump/solidity-dynamic-buffer
/// @notice This library is used to allocate a big amount of container memory
// which will be subsequently filled without needing to reallocate
/// memory.
/// @dev First, allocate memory.
/// Then use `buffer.appendUnchecked(theBytes)` or `appendSafe()` if
/// bounds checking is required.
library DynamicBuffer {
/// @notice Allocates container space for the DynamicBuffer
/// @param capacity_ The intended max amount of bytes in the buffer
/// @return buffer The memory location of the buffer
/// @dev Allocates `capacity_ + 0x60` bytes of space
/// The buffer array starts at the first container data position,
/// (i.e. `buffer = container + 0x20`)
function allocate(uint256 capacity_)
internal
pure
returns (bytes memory buffer)
{
assembly {
// Get next-free memory address
let container := mload(0x40)
// Allocate memory by setting a new next-free address
{
// Add 2 x 32 bytes in size for the two length fields
// Add 32 bytes safety space for 32B chunked copy
let size := add(capacity_, 0x60)
let newNextFree := add(container, size)
mstore(0x40, newNextFree)
}
// Set the correct container length
{
let length := add(capacity_, 0x40)
mstore(container, length)
}
// The buffer starts at idx 1 in the container (0 is length)
buffer := add(container, 0x20)
// Init content with length 0
mstore(buffer, 0)
}
return buffer;
}
/// @notice Appends data to buffer, and update buffer length
/// @param buffer the buffer to append the data to
/// @param data the data to append
/// @dev Does not perform out-of-bound checks (container capacity)
/// for efficiency.
function appendUnchecked(bytes memory buffer, bytes memory data)
internal
pure
{
assembly {
let length := mload(data)
for {
data := add(data, 0x20)
let dataEnd := add(data, length)
let copyTo := add(buffer, add(mload(buffer), 0x20))
} lt(data, dataEnd) {
data := add(data, 0x20)
copyTo := add(copyTo, 0x20)
} {
// Copy 32B chunks from data to buffer.
// This may read over data array boundaries and copy invalid
// bytes, which doesn't matter in the end since we will
// later set the correct buffer length, and have allocated an
// additional word to avoid buffer overflow.
mstore(copyTo, mload(data))
}
// Update buffer length
mstore(buffer, add(mload(buffer), length))
}
}
/// @notice Appends data to buffer, and update buffer length
/// @param buffer the buffer to append the data to
/// @param data the data to append
/// @dev Performs out-of-bound checks and calls `appendUnchecked`.
function appendSafe(bytes memory buffer, bytes memory data) internal pure {
checkOverflow(buffer, data.length);
appendUnchecked(buffer, data);
}
/// @notice Appends data encoded as Base64 to buffer.
/// @param fileSafe Whether to replace '+' with '-' and '/' with '_'.
/// @param noPadding Whether to strip away the padding.
/// @dev Encodes `data` using the base64 encoding described in RFC 4648.
/// See: https://datatracker.ietf.org/doc/html/rfc4648
/// Author: Modified from Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol)
/// Author: Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Base64.sol)
/// Author: Modified from (https://github.com/Brechtpd/base64/blob/main/base64.sol) by Brecht Devos.
function appendSafeBase64(
bytes memory buffer,
bytes memory data,
bool fileSafe,
bool noPadding
) internal pure {
uint256 dataLength = data.length;
if (data.length == 0) {
return;
}
uint256 encodedLength;
uint256 r;
assembly {
// For each 3 bytes block, we will have 4 bytes in the base64
// encoding: `encodedLength = 4 * divCeil(dataLength, 3)`.
// The `shl(2, ...)` is equivalent to multiplying by 4.
encodedLength := shl(2, div(add(dataLength, 2), 3))
r := mod(dataLength, 3)
if noPadding {
// if r == 0 => no modification
// if r == 1 => encodedLength -= 2
// if r == 2 => encodedLength -= 1
encodedLength := sub(
encodedLength,
add(iszero(iszero(r)), eq(r, 1))
)
}
}
checkOverflow(buffer, encodedLength);
assembly {
let nextFree := mload(0x40)
// Store the table into the scratch space.
// Offsetted by -1 byte so that the `mload` will load the character.
// We will rewrite the free memory pointer at `0x40` later with
// the allocated size.
mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
mstore(
0x3f,
sub(
"ghijklmnopqrstuvwxyz0123456789-_",
// The magic constant 0x0230 will translate "-_" + "+/".
mul(iszero(fileSafe), 0x0230)
)
)
// Skip the first slot, which stores the length.
let ptr := add(add(buffer, 0x20), mload(buffer))
let end := add(data, dataLength)
// Run over the input, 3 bytes at a time.
// prettier-ignore
// solhint-disable-next-line no-empty-blocks
for {} 1 {} {
data := add(data, 3) // Advance 3 bytes.
let input := mload(data)
// Write 4 bytes. Optimized for fewer stack operations.
mstore8( ptr , mload(and(shr(18, input), 0x3F)))
mstore8(add(ptr, 1), mload(and(shr(12, input), 0x3F)))
mstore8(add(ptr, 2), mload(and(shr( 6, input), 0x3F)))
mstore8(add(ptr, 3), mload(and( input , 0x3F)))
ptr := add(ptr, 4) // Advance 4 bytes.
// prettier-ignore
if iszero(lt(data, end)) { break }
}
if iszero(noPadding) {
// Offset `ptr` and pad with '='. We can simply write over the end.
mstore8(sub(ptr, iszero(iszero(r))), 0x3d) // Pad at `ptr - 1` if `r > 0`.
mstore8(sub(ptr, shl(1, eq(r, 1))), 0x3d) // Pad at `ptr - 2` if `r == 1`.
}
mstore(buffer, add(mload(buffer), encodedLength))
mstore(0x40, nextFree)
}
}
/// @notice Appends data encoded as Base64 to buffer.
/// @param fileSafe Whether to replace '+' with '-' and '/' with '_'.
/// @param noPadding Whether to strip away the padding.
/// @dev Encodes `data` using the base64 encoding described in RFC 4648.
/// See: https://datatracker.ietf.org/doc/html/rfc4648
/// Author: Modified from Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol)
/// Author: Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Base64.sol)
/// Author: Modified from (https://github.com/Brechtpd/base64/blob/main/base64.sol) by Brecht Devos.
function appendUncheckedBase64(
bytes memory buffer,
bytes memory data,
bool fileSafe,
bool noPadding
) internal pure {
uint256 dataLength = data.length;
if (data.length == 0) {
return;
}
uint256 encodedLength;
uint256 r;
assembly {
// For each 3 bytes block, we will have 4 bytes in the base64
// encoding: `encodedLength = 4 * divCeil(dataLength, 3)`.
// The `shl(2, ...)` is equivalent to multiplying by 4.
encodedLength := shl(2, div(add(dataLength, 2), 3))
r := mod(dataLength, 3)
if noPadding {
// if r == 0 => no modification
// if r == 1 => encodedLength -= 2
// if r == 2 => encodedLength -= 1
encodedLength := sub(
encodedLength,
add(iszero(iszero(r)), eq(r, 1))
)
}
}
assembly {
let nextFree := mload(0x40)
// Store the table into the scratch space.
// Offsetted by -1 byte so that the `mload` will load the character.
// We will rewrite the free memory pointer at `0x40` later with
// the allocated size.
mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
mstore(
0x3f,
sub(
"ghijklmnopqrstuvwxyz0123456789-_",
// The magic constant 0x0230 will translate "-_" + "+/".
mul(iszero(fileSafe), 0x0230)
)
)
// Skip the first slot, which stores the length.
let ptr := add(add(buffer, 0x20), mload(buffer))
let end := add(data, dataLength)
// Run over the input, 3 bytes at a time.
// prettier-ignore
// solhint-disable-next-line no-empty-blocks
for {} 1 {} {
data := add(data, 3) // Advance 3 bytes.
let input := mload(data)
// Write 4 bytes. Optimized for fewer stack operations.
mstore8( ptr , mload(and(shr(18, input), 0x3F)))
mstore8(add(ptr, 1), mload(and(shr(12, input), 0x3F)))
mstore8(add(ptr, 2), mload(and(shr( 6, input), 0x3F)))
mstore8(add(ptr, 3), mload(and( input , 0x3F)))
ptr := add(ptr, 4) // Advance 4 bytes.
// prettier-ignore
if iszero(lt(data, end)) { break }
}
if iszero(noPadding) {
// Offset `ptr` and pad with '='. We can simply write over the end.
mstore8(sub(ptr, iszero(iszero(r))), 0x3d) // Pad at `ptr - 1` if `r > 0`.
mstore8(sub(ptr, shl(1, eq(r, 1))), 0x3d) // Pad at `ptr - 2` if `r == 1`.
}
mstore(buffer, add(mload(buffer), encodedLength))
mstore(0x40, nextFree)
}
}
/// @notice Returns the capacity of a given buffer.
function capacity(bytes memory buffer) internal pure returns (uint256) {
uint256 cap;
assembly {
cap := sub(mload(sub(buffer, 0x20)), 0x40)
}
return cap;
}
/// @notice Reverts if the buffer will overflow after appending a given
/// number of bytes.
function checkOverflow(bytes memory buffer, uint256 addedLength)
internal
pure
{
uint256 cap = capacity(buffer);
uint256 newLength = buffer.length + addedLength;
if (cap < newLength) {
revert("DynamicBuffer: Appending out of bounds.");
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
///////////////////////////////////////////////////////////
// ░██████╗░█████╗░██████╗░██╗██████╗░████████╗██╗░░░██╗ //
// ██╔════╝██╔══██╗██╔══██╗██║██╔══██╗╚══██╔══╝╚██╗░██╔╝ //
// ╚█████╗░██║░░╚═╝██████╔╝██║██████╔╝░░░██║░░░░╚████╔╝░ //
// ░╚═══██╗██║░░██╗██╔══██╗██║██╔═══╝░░░░██║░░░░░╚██╔╝░░ //
// ██████╔╝╚█████╔╝██║░░██║██║██║░░░░░░░░██║░░░░░░██║░░░ //
// ╚═════╝░░╚════╝░╚═╝░░╚═╝╚═╝╚═╝░░░░░░░░╚═╝░░░░░░╚═╝░░░ //
///////////////////////////////////////////////////////////
interface IScriptyContractStorage {
// =============================================================
// GETTERS
// =============================================================
/**
* @notice Get the full content
* @param name - Name given to the script. Eg: threejs.min.js_r148
* @param data - Arbitrary data to be passed to storage
* @return script - Full script from merged chunks
*/
function getContent(string calldata name, bytes memory data)
external
view
returns (bytes memory script);
}{
"remappings": [
"ds-test/=lib/forge-std/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/",
"@openzeppelin/=lib/openzeppelin-contracts/",
"solady/=lib/solady/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"scripty/=lib/scripty/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "paris",
"viaIR": false,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AddressCantBurn","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"CantTransfer","type":"error"},{"inputs":[],"name":"CantTransferEquipped","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"NotATBA","type":"error"},{"inputs":[],"name":"NotAValidMinterContract","type":"error"},{"inputs":[],"name":"NotYourTrait","type":"error"},{"inputs":[],"name":"ReplaceMintPermissionsRevoked","type":"error"},{"inputs":[],"name":"SetChonksMainAddress","type":"error"},{"inputs":[],"name":"SetMarketplaceAddress","type":"error"},{"inputs":[],"name":"TraitAlreadyExists","type":"error"},{"inputs":[],"name":"TraitIDTooLow","type":"error"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"TraitNotFound","type":"error"},{"inputs":[],"name":"TraitTokenDoesntExist","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"AllOperatorApprovalsInvalidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_toTokenId","type":"uint256"}],"name":"BatchMetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"MetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":true,"internalType":"uint64","name":"revealBlock","type":"uint64"}],"name":"NewEpoch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"tba","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"TBAApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"_minter","type":"address"}],"name":"addMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"burnBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_traitId","type":"uint256"},{"internalType":"string","name":"_traitsSvg","type":"string"},{"internalType":"string","name":"_traitsAttributes","type":"string"},{"internalType":"bytes","name":"_traitZMaps","type":"bytes"}],"name":"callGetSVGZmapAndMetadataTrait","outputs":[{"internalType":"string","name":"traitsSvg","type":"string"},{"internalType":"string","name":"traitsAttributes","type":"string"},{"internalType":"bytes","name":"traitZMaps","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_traitId","type":"uint256"},{"internalType":"string","name":"_traitsSvg","type":"string"},{"internalType":"string","name":"_traitsAttributes","type":"string"}],"name":"callGetSvgAndMetadataTrait","outputs":[{"internalType":"string","name":"traitsSvg","type":"string"},{"internalType":"string","name":"traitsAttributes","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"chonkTraitsV1","outputs":[{"internalType":"contract IChonkTraitsV1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"chonksMain","outputs":[{"internalType":"contract ChonksMain","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_pixels","type":"bytes"}],"name":"createSvgFromPixels","outputs":[{"internalType":"bytes","name":"svgParts","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getColorMapForTokenId","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"getEpochData","outputs":[{"components":[{"internalType":"uint128","name":"randomness","type":"uint128"},{"internalType":"uint64","name":"revealBlock","type":"uint64"},{"internalType":"bool","name":"committed","type":"bool"},{"internalType":"bool","name":"revealed","type":"bool"}],"internalType":"struct CommitReveal.Epoch","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGhostSvg","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"bool","name":"isRevealed","type":"bool"},{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"address","name":"dataMinterContract","type":"address"},{"internalType":"uint256","name":"traitIndex","type":"uint256"},{"internalType":"enum TraitCategory.Name","name":"traitType","type":"uint8"}],"internalType":"struct ITraitStorage.StoredTrait","name":"_trait","type":"tuple"},{"internalType":"uint256","name":"_traitId","type":"uint256"}],"name":"getSVGZmapAndMetadataTrait","outputs":[{"internalType":"string","name":"traitSvg","type":"string"},{"internalType":"bytes","name":"traitZmap","type":"bytes"},{"internalType":"string","name":"traitAttributes","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getStoredTraitForTokenId","outputs":[{"components":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"bool","name":"isRevealed","type":"bool"},{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"address","name":"dataMinterContract","type":"address"},{"internalType":"uint256","name":"traitIndex","type":"uint256"},{"internalType":"enum TraitCategory.Name","name":"traitType","type":"uint8"}],"internalType":"struct ITraitStorage.StoredTrait","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"headId","type":"uint256"},{"internalType":"uint256","name":"hairId","type":"uint256"},{"internalType":"uint256","name":"faceId","type":"uint256"},{"internalType":"uint256","name":"accessoryId","type":"uint256"},{"internalType":"uint256","name":"topId","type":"uint256"},{"internalType":"uint256","name":"bottomId","type":"uint256"},{"internalType":"uint256","name":"shoesId","type":"uint256"},{"internalType":"uint8","name":"bodyIndex","type":"uint8"},{"internalType":"string","name":"backgroundColor","type":"string"},{"internalType":"bool","name":"render3D","type":"bool"}],"internalType":"struct IChonkStorage.StoredChonk","name":"_storedChonk","type":"tuple"}],"name":"getSvgAndMetadata","outputs":[{"internalType":"string","name":"traitsSvg","type":"string"},{"internalType":"string","name":"traitsAttributes","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"bool","name":"isRevealed","type":"bool"},{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"address","name":"dataMinterContract","type":"address"},{"internalType":"uint256","name":"traitIndex","type":"uint256"},{"internalType":"enum TraitCategory.Name","name":"traitType","type":"uint8"}],"internalType":"struct ITraitStorage.StoredTrait","name":"_trait","type":"tuple"},{"internalType":"uint256","name":"_traitId","type":"uint256"}],"name":"getSvgAndMetadataTrait","outputs":[{"internalType":"string","name":"traitSvg","type":"string"},{"internalType":"string","name":"traitAttributes","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getSvgForTokenId","outputs":[{"internalType":"string","name":"traitSvg","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"headId","type":"uint256"},{"internalType":"uint256","name":"hairId","type":"uint256"},{"internalType":"uint256","name":"faceId","type":"uint256"},{"internalType":"uint256","name":"accessoryId","type":"uint256"},{"internalType":"uint256","name":"topId","type":"uint256"},{"internalType":"uint256","name":"bottomId","type":"uint256"},{"internalType":"uint256","name":"shoesId","type":"uint256"},{"internalType":"uint8","name":"bodyIndex","type":"uint8"},{"internalType":"string","name":"backgroundColor","type":"string"},{"internalType":"bool","name":"render3D","type":"bool"}],"internalType":"struct IChonkStorage.StoredChonk","name":"_storedChonk","type":"tuple"}],"name":"getSvgZmapsAndMetadata","outputs":[{"internalType":"string","name":"traitsSvg","type":"string"},{"internalType":"bytes","name":"traitZMaps","type":"bytes"},{"internalType":"string","name":"traitsAttributes","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getTrait","outputs":[{"components":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"bool","name":"isRevealed","type":"bool"},{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"address","name":"dataMinterContract","type":"address"},{"internalType":"uint256","name":"traitIndex","type":"uint256"},{"internalType":"enum TraitCategory.Name","name":"traitType","type":"uint8"}],"internalType":"struct ITraitStorage.StoredTrait","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"getTraitImageSvg","outputs":[{"internalType":"string","name":"svg","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_traitIndex","type":"uint256"}],"name":"getTraitIndexToMetadata","outputs":[{"components":[{"internalType":"uint256","name":"traitIndex","type":"uint256"},{"internalType":"string","name":"traitName","type":"string"},{"internalType":"enum TraitCategory.Name","name":"traitType","type":"uint8"},{"internalType":"bytes","name":"colorMap","type":"bytes"},{"internalType":"bytes","name":"zMap","type":"bytes"},{"internalType":"address","name":"dataMinterContract","type":"address"},{"internalType":"address","name":"creatorAddress","type":"address"},{"internalType":"string","name":"creatorName","type":"string"},{"internalType":"string","name":"release","type":"string"}],"internalType":"struct ITraitStorage.TraitMetadata","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getTraitMetadata","outputs":[{"components":[{"internalType":"uint256","name":"traitIndex","type":"uint256"},{"internalType":"string","name":"traitName","type":"string"},{"internalType":"enum TraitCategory.Name","name":"traitType","type":"uint8"},{"internalType":"bytes","name":"colorMap","type":"bytes"},{"internalType":"bytes","name":"zMap","type":"bytes"},{"internalType":"address","name":"dataMinterContract","type":"address"},{"internalType":"address","name":"creatorAddress","type":"address"},{"internalType":"string","name":"creatorName","type":"string"},{"internalType":"string","name":"release","type":"string"}],"internalType":"struct ITraitStorage.TraitMetadata","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getZMapForTokenId","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"invalidateAllOperatorApprovals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tba","type":"address"},{"internalType":"address","name":"_operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isMinter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"marketplace","outputs":[{"internalType":"contract ChonksMarket","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_minter","type":"address"}],"name":"removeMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"renderAsDataUri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"replaceMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"replaceMintPermisssionsRevoked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"resolveEpochIfNecessary","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"revokeReplaceMintPermissions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"}],"name":"safeMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"bool","name":"_approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string[2]","name":"_descriptionParts","type":"string[2]"}],"name":"setDescriptionParts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_colorMap","type":"bytes"},{"internalType":"bytes","name":"_zMap","type":"bytes"}],"name":"setGhostMaps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_marketplace","type":"address"}],"name":"setMarketplace","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"components":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"bool","name":"isRevealed","type":"bool"},{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"address","name":"dataMinterContract","type":"address"},{"internalType":"uint256","name":"traitIndex","type":"uint256"},{"internalType":"enum TraitCategory.Name","name":"traitType","type":"uint8"}],"internalType":"struct ITraitStorage.StoredTrait","name":"_trait","type":"tuple"}],"name":"setTraitForTokenId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_traitIndex","type":"uint256"},{"components":[{"internalType":"uint256","name":"traitIndex","type":"uint256"},{"internalType":"string","name":"traitName","type":"string"},{"internalType":"enum TraitCategory.Name","name":"traitType","type":"uint8"},{"internalType":"bytes","name":"colorMap","type":"bytes"},{"internalType":"bytes","name":"zMap","type":"bytes"},{"internalType":"address","name":"dataMinterContract","type":"address"},{"internalType":"address","name":"creatorAddress","type":"address"},{"internalType":"string","name":"creatorName","type":"string"},{"internalType":"string","name":"release","type":"string"}],"internalType":"struct ITraitStorage.TraitMetadata","name":"_metadata","type":"tuple"}],"name":"setTraitIndexToMetadata","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_traitRenderer","type":"address"}],"name":"setTraitRenderer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"traitIndexToMetadata","outputs":[{"internalType":"uint256","name":"traitIndex","type":"uint256"},{"internalType":"string","name":"traitName","type":"string"},{"internalType":"enum TraitCategory.Name","name":"traitType","type":"uint8"},{"internalType":"bytes","name":"colorMap","type":"bytes"},{"internalType":"bytes","name":"zMap","type":"bytes"},{"internalType":"address","name":"dataMinterContract","type":"address"},{"internalType":"address","name":"creatorAddress","type":"address"},{"internalType":"string","name":"creatorName","type":"string"},{"internalType":"string","name":"release","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"traitRenderer","outputs":[{"internalType":"contract TraitRenderer","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"traitTokens","outputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"updateEpochOnce","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"walletOfOwner","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"}]Contract Creation Code
6080604052600e80546001600160a01b03191673785afed7ce24e76ac1d603be09c1fd20e0e1e6b7179055620532a66012553480156200003e57600080fd5b506040518060400160405280600c81526020016b43686f6e6b2054726169747360a01b8152506040518060400160405280600c81526020016b43484f4e4b2054524149545360a01b81525081600090816200009a9190620001a5565b506001620000a98282620001a5565b50506001600a5550620000bc33620000c2565b62000271565b6001600160a01b0316638b78c6d8198190558060007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a350565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200012957607f821691505b6020821081036200014a57634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620001a0576000816000526020600020601f850160051c810160208610156200017b5750805b601f850160051c820191505b818110156200019c5782815560010162000187565b5050505b505050565b81516001600160401b03811115620001c157620001c1620000fe565b620001d981620001d2845462000114565b8462000150565b602080601f831160018114620002115760008415620001f85750858301515b600019600386901b1c1916600185901b1785556200019c565b600085815260208120601f198616915b82811015620002425788860151825594840194600190910190840162000221565b5085821015620002615787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b615c1380620002816000396000f3fe6080604052600436106103ef5760003560e01c806390f6384211610208578063bc08503311610118578063e1defed9116100ab578063f04e283e1161007a578063f04e283e14610c16578063f127194214610c29578063f2fde38b14610c49578063f901667f14610c5c578063fee81cf414610c7c57600080fd5b8063e1defed914610b96578063e4623c1b14610bb6578063e985e9c514610bd6578063ea13954414610bf657600080fd5b8063d1bfc2af116100e7578063d1bfc2af14610b12578063dcbaaad514610b3f578063e08714a614610b5f578063e14d026f14610b7f57600080fd5b8063bc08503314610a90578063c779153114610ab2578063c876371b14610ad2578063c87b56dd14610af257600080fd5b8063a8498c741161019b578063acb586df1161016a578063acb586df146109f3578063b88d4fde14610a13578063b966c51d14610a33578063b97dd9e214610a53578063ba7270f314610a6857600080fd5b8063a8498c7414610963578063a9684c5814610983578063aa271e1a146109a3578063abc8c7af146109d357600080fd5b80639a6b9596116101d75780639a6b9596146108e9578063a1d6268014610903578063a22cb46514610923578063a5e2912a1461094357600080fd5b806390f638421461087f57806395d89b411461089f578063983b2d56146108b457806398d8d288146108d457600080fd5b806333a70328116103035780635c1ef2561161029657806373ad6c2d1161026557806373ad6c2d1461079257806375794a3c146107b2578063817a29a4146107c8578063859e7d32146107fd5780638da5cb5b1461086657600080fd5b80635c1ef256146107355780636352211e1461074a57806370a082311461076a578063715018a61461078a57600080fd5b806342966c68116102d257806342966c68146106c0578063438b6300146106e05780634f6ccce71461070d57806354d1f13d1461072d57600080fd5b806333a703281461063257806336d34ab91461066057806340d097c31461068057806342842e0e146106a057600080fd5b806321b12c2311610386578063256929621161035557806325692962146105b557806326f2052a146105bd5780632f745c59146105dd5780633092afd5146105fd57806331d2866a1461061d57600080fd5b806321b12c2314610531578063239ffa681461055157806323b872dd14610566578063250818ea1461058657600080fd5b8063120def24116103c2578063120def24146104a557806318160ddd146104c55780631f787d2f146104e4578063208c25a31461050457600080fd5b806301ffc9a7146103f457806306fdde0314610429578063081812fc1461044b578063095ea7b314610483575b600080fd5b34801561040057600080fd5b5061041461040f366004614432565b610caf565b60405190151581526020015b60405180910390f35b34801561043557600080fd5b5061043e610cc0565b604051610420919061449f565b34801561045757600080fd5b5061046b6104663660046144b2565b610d52565b6040516001600160a01b039091168152602001610420565b34801561048f57600080fd5b506104a361049e3660046144f0565b610d79565b005b3480156104b157600080fd5b506104a36104c036600461451c565b610e93565b3480156104d157600080fd5b506008545b604051908152602001610420565b3480156104f057600080fd5b506104a36104ff3660046144f0565b610ebd565b34801561051057600080fd5b5061052461051f3660046144b2565b610f1b565b60405161042091906145b9565b34801561053d57600080fd5b5061043e61054c36600461471a565b61105c565b34801561055d57600080fd5b506104a36110d2565b34801561057257600080fd5b506104a361058136600461474e565b6112ae565b34801561059257600080fd5b506105a66105a13660046147b9565b6112df565b604051610420939291906148a0565b6104a361136e565b3480156105c957600080fd5b506104a36105d83660046148e3565b6113bd565b3480156105e957600080fd5b506104d66105f83660046144f0565b6113d6565b34801561060957600080fd5b506104a361061836600461451c565b61146c565b34801561062957600080fd5b506104a3611495565b34801561063e57600080fd5b5061065261064d366004614a08565b6114d8565b604051610420929190614a33565b34801561066c57600080fd5b506104a361067b366004614a58565b6115fd565b34801561068c57600080fd5b506104d661069b36600461451c565b6116ea565b3480156106ac57600080fd5b506104a36106bb36600461474e565b611750565b3480156106cc57600080fd5b506104a36106db3660046144b2565b61176b565b3480156106ec57600080fd5b506107006106fb36600461451c565b6117a4565b6040516104209190614a85565b34801561071957600080fd5b506104d66107283660046144b2565b61183b565b6104a36118ce565b34801561074157600080fd5b5061043e61190a565b34801561075657600080fd5b5061046b6107653660046144b2565b611981565b34801561077657600080fd5b506104d661078536600461451c565b6119e1565b6104a3611a67565b34801561079e57600080fd5b506104a36107ad36600461451c565b611a7b565b3480156107be57600080fd5b506104d660125481565b3480156107d457600080fd5b506107e86107e33660046144b2565b611aa5565b60405161042099989796959493929190614ac9565b34801561080957600080fd5b5061081d6108183660046144b2565b611daf565b604051610420919081516001600160801b031681526020808301516001600160401b03169082015260408083015115159082015260609182015115159181019190915260800190565b34801561087257600080fd5b50638b78c6d8195461046b565b34801561088b57600080fd5b506104a361089a3660046144b2565b611eb3565b3480156108ab57600080fd5b5061043e611fab565b3480156108c057600080fd5b506104a36108cf36600461451c565b611fba565b3480156108e057600080fd5b506104a3611fe6565b3480156108f557600080fd5b506015546104149060ff1681565b34801561090f57600080fd5b5061043e61091e3660046144b2565b611ffd565b34801561092f57600080fd5b506104a361093e366004614b68565b61212f565b34801561094f57600080fd5b5061043e61095e3660046144b2565b6122f6565b34801561096f57600080fd5b5061043e61097e3660046144b2565b6124ee565b34801561098f57600080fd5b5061065261099e3660046147b9565b6125e2565b3480156109af57600080fd5b506104146109be36600461451c565b60116020526000908152604090205460ff1681565b3480156109df57600080fd5b50600f5461046b906001600160a01b031681565b3480156109ff57600080fd5b50610524610a0e3660046144b2565b61266d565b348015610a1f57600080fd5b506104a3610a2e366004614ba1565b612841565b348015610a3f57600080fd5b506104a3610a4e366004614c0c565b612879565b348015610a5f57600080fd5b50600c546104d6565b348015610a7457600080fd5b5061046b7307152bfde079b5319e5308c43fb1dbc9c76cb4f981565b348015610a9c57600080fd5b5061046b600080516020615bbe83398151915281565b348015610abe57600080fd5b5061043e610acd3660046144b2565b612a38565b348015610ade57600080fd5b50600e5461046b906001600160a01b031681565b348015610afe57600080fd5b5061043e610b0d3660046144b2565b612a8e565b348015610b1e57600080fd5b50610b32610b2d3660046144b2565b612acf565b6040516104209190614d54565b348015610b4b57600080fd5b5061043e610b5a3660046144b2565b612ec6565b348015610b6b57600080fd5b50610652610b7a366004614e38565b612edb565b348015610b8b57600080fd5b50600c546104d69081565b348015610ba257600080fd5b506104a3610bb1366004614ea4565b613016565b348015610bc257600080fd5b506104a3610bd1366004614f07565b613086565b348015610be257600080fd5b50610414610bf1366004614fac565b61314c565b348015610c0257600080fd5b50610b32610c113660046144b2565b613283565b6104a3610c2436600461451c565b613355565b348015610c3557600080fd5b506105a6610c44366004614fda565b613392565b6104a3610c5736600461451c565b6134da565b348015610c6857600080fd5b506105a6610c77366004614a08565b613501565b348015610c8857600080fd5b506104d6610c9736600461451c565b63389a75e1600c908152600091909152602090205490565b6000610cba8261362c565b92915050565b606060008054610ccf9061505f565b80601f0160208091040260200160405190810160405280929190818152602001828054610cfb9061505f565b8015610d485780601f10610d1d57610100808354040283529160200191610d48565b820191906000526020600020905b815481529060010190602001808311610d2b57829003601f168201915b5050505050905090565b6000610d5d82613651565b506000908152600460205260409020546001600160a01b031690565b6000610d8482611981565b9050806001600160a01b0316836001600160a01b031603610df65760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b60648201526084015b60405180910390fd5b336001600160a01b0382161480610e125750610e12813361314c565b610e845760405162461bcd60e51b815260206004820152603e60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206e6f7220617070726f76656420666f7220616c6c00006064820152608401610ded565b610e8e83836136b0565b505050565b610e9b61371e565b600e80546001600160a01b0319166001600160a01b0392909216919091179055565b3360008181526011602052604090205460ff16610eed5760405163090a0b6960e21b815260040160405180910390fd5b60155460ff1615610f115760405163cb207e0d60e01b815260040160405180910390fd5b610e8e8383613739565b610f516040805160c08101825260008082526020820181905291810182905260608101829052608081018290529060a082015290565b620532a68211610fca5760405163208c25a360e01b815260048101839052600080516020615bbe8339815191529063208c25a3906024015b60c060405180830381865afa158015610fa6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cba91906150a9565b6000828152600b6020908152604091829020825160c08101845281548152600182015460ff90811615159382019390935260028201549381019390935260038101546001600160a01b03166060840152600481015460808401526005810154909160a084019116600781111561104257611042614539565b600781111561105357611053614539565b90525092915050565b600e546040516321b12c2360e01b81526060916001600160a01b0316906321b12c239061108d90859060040161449f565b600060405180830381865afa1580156110aa573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610cba9190810190615160565b600c546000908152600d602052604090208054600160c01b900460ff16158061112a57508054600160c81b900460ff1615801561112a5750611116610100436151aa565b8154600160801b90046001600160401b0316105b156111755761113a4360326151bd565b815460ff60c01b196001600160401b0392909216600160801b029190911668ffffffffffffffffff60801b1990911617600160c01b17815550565b8054600160801b90046001600160401b03164311156112ab57805460408051600160801b9092046001600160401b031640602083015244908201526001600160801b03906060016040516020818303038152906040528051906020012060001c6111df91906151d0565b8154600160c81b6001600160801b039290921679ff000000000000000000ffffffffffffffffffffffffffffffff199091161717808255600c54604051600160801b9092046001600160401b0316917f78611aecfda8d341359c248df527c95aef93d446c92bb928b2a81b7abcb1d8d990600090a3604080516000815260001960208201527f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c910160405180910390a1600c805460009061129f906151f2565b909155506112ab6110d2565b50565b6112b83382613753565b6112d45760405162461bcd60e51b8152600401610ded9061520b565b610e8e8383836137b1565b600e5460405163d42f766360e01b8152606091829182916001600160a01b03169063d42f76639061131c908790309063f1271942906004016152f9565b600060405180830381865afa158015611339573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611361919081019061532b565b9250925092509193909250565b60006202a3006001600160401b03164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b6113c561371e565b6113d2601382600261430e565b5050565b60006113e1836119e1565b82106114435760405162461bcd60e51b815260206004820152602b60248201527f455243373231456e756d657261626c653a206f776e657220696e646578206f7560448201526a74206f6620626f756e647360a81b6064820152608401610ded565b506001600160a01b03919091166000908152600660209081526040808320938352929052205490565b61147461371e565b6001600160a01b03166000908152601160205260409020805460ff19169055565b3360008181526011602052604090205460ff166114c55760405163090a0b6960e21b815260040160405180910390fd5b600c546000036112ab5761030a600c5550565b606080620532a6831161156657604051630674e06560e31b8152600080516020615bbe833981519152906333a703289061151890879087906004016153a8565b600060405180830381865afa158015611535573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261155d91908101906153c3565b915091506115f6565b600e546080850151600090815260106020526040908190209051630bfb083b60e41b81526001600160a01b039092169163bfb083b0916115ac918891889160040161555f565b600060405180830381865afa1580156115c9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115f191908101906153c3565b915091505b9250929050565b3360008181526011602052604090205460ff1661162d5760405163090a0b6960e21b815260040160405180910390fd5b620532a683116116505760405163918c6c1560e01b815260040160405180910390fd5b6000838152600b602090815260409182902084518155908401516001808301805492151560ff1993841617905592850151600283015560608501516003830180546001600160a01b039092166001600160a01b03199092169190911790556080850151600483015560a08501516005830180548795929391929116908360078111156116de576116de614539565b02179055505050505050565b3360008181526011602052604081205490919060ff1661171d5760405163090a0b6960e21b815260040160405180910390fd5b6117256110d2565b6000601260008154611736906151f2565b918290555090506117478482613739565b91505b50919050565b610e8e83838360405180602001604052806000815250612841565b3360009081526011602052604090205460ff1661179b57604051631758e6d560e11b815260040160405180910390fd5b6112ab81613958565b606060006117b1836119e1565b90506000816001600160401b038111156117cd576117cd6145c7565b6040519080825280602002602001820160405280156117f6578160200160208202803683370190505b50905060005b828110156118335761180e85826113d6565b82828151811061182057611820615586565b60209081029190910101526001016117fc565b509392505050565b600061184660085490565b82106118a95760405162461bcd60e51b815260206004820152602c60248201527f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60448201526b7574206f6620626f756e647360a01b6064820152608401610ded565b600882815481106118bc576118bc615586565b90600052602060002001549050919050565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b600e5460408051632e0f792b60e11b815290516060926001600160a01b031691635c1ef2569160048083019260009291908290030181865afa158015611954573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261197c9190810190615160565b905090565b6000818152600260205260408120546001600160a01b031680610cba5760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b6044820152606401610ded565b60006001600160a01b038216611a4b5760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f7420612076616044820152683634b21037bbb732b960b91b6064820152608401610ded565b506001600160a01b031660009081526003602052604090205490565b611a6f61371e565b611a7960006139ff565b565b611a8361371e565b600f80546001600160a01b0319166001600160a01b0392909216919091179055565b60106020526000908152604090208054600182018054919291611ac79061505f565b80601f0160208091040260200160405190810160405280929190818152602001828054611af39061505f565b8015611b405780601f10611b1557610100808354040283529160200191611b40565b820191906000526020600020905b815481529060010190602001808311611b2357829003601f168201915b5050506002840154600385018054949560ff909216949193509150611b649061505f565b80601f0160208091040260200160405190810160405280929190818152602001828054611b909061505f565b8015611bdd5780601f10611bb257610100808354040283529160200191611bdd565b820191906000526020600020905b815481529060010190602001808311611bc057829003601f168201915b505050505090806004018054611bf29061505f565b80601f0160208091040260200160405190810160405280929190818152602001828054611c1e9061505f565b8015611c6b5780601f10611c4057610100808354040283529160200191611c6b565b820191906000526020600020905b815481529060010190602001808311611c4e57829003601f168201915b5050506005840154600685015460078601805495966001600160a01b0393841696929093169450919250611c9e9061505f565b80601f0160208091040260200160405190810160405280929190818152602001828054611cca9061505f565b8015611d175780601f10611cec57610100808354040283529160200191611d17565b820191906000526020600020905b815481529060010190602001808311611cfa57829003601f168201915b505050505090806008018054611d2c9061505f565b80601f0160208091040260200160405190810160405280929190818152602001828054611d589061505f565b8015611da55780601f10611d7a57610100808354040283529160200191611da5565b820191906000526020600020905b815481529060010190602001808311611d8857829003601f168201915b5050505050905089565b6040805160808101825260008082526020820181905291810182905260608101919091526103098211611e4a576040516342cf3e9960e11b815260048101839052600080516020615bbe8339815191529063859e7d3290602401608060405180830381865afa158015611e26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cba919061559c565b506000908152600d6020908152604091829020825160808101845290546001600160801b03811682526001600160401b03600160801b8204169282019290925260ff600160c01b83048116151593820193909352600160c81b9091049091161515606082015290565b604051636670434960e01b8152600481018290526000907307152bfde079b5319e5308c43fb1dbc9c76cb4f990636670434990602401608060405180830381865afa158015611f06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f2a9190615638565b5092505050336001600160a01b0316611f4283611981565b6001600160a01b031614158015611f6257506001600160a01b0381163314155b8015611f825750337307152bfde079b5319e5308c43fb1dbc9c76cb4f914155b15611fa057604051633e79b9f360e01b815260040160405180910390fd5b6113d26000836136b0565b606060018054610ccf9061505f565b611fc261371e565b6001600160a01b03166000908152601160205260409020805460ff19166001179055565b611fee61371e565b6015805460ff19166001179055565b6060620532a6821161203b57604051630143ac4d60e71b815260048101839052600080516020615bbe8339815191529063a1d626809060240161108d565b60006120468361266d565b90506000816020015161207c576040518060400160405280600b81526020016a1e39bb339f1e17b9bb339f60a91b815250612089565b61208982608001516122f6565b600e54608084015160009081526010602052604090209192506001600160a01b03169063ac499bbc90869085906120be61190a565b8660136040518763ffffffff1660e01b81526004016120e29695949392919061568d565b600060405180830381865afa1580156120ff573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526121279190810190615160565b949350505050565b336001600160a01b038316036121875760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610ded565b60405163423965e560e11b81523360048201526000907307152bfde079b5319e5308c43fb1dbc9c76cb4f990638472cbca90602401602060405180830381865afa1580156121d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121fd9190615730565b6040516331a9108f60e11b8152600481018290529091506000907307152bfde079b5319e5308c43fb1dbc9c76cb4f990636352211e90602401602060405180830381865afa158015612253573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122779190615749565b3360008181526016602090815260408083206001600160a01b03868116808652918452828520908b1680865290845293829020805460ff19168a15159081179091559151918252949550919392917fb35a3e86f41df9a708de503b8afccb6c992167394cba9e16a3b5c466d378d1aa910160405180910390a450505050565b6040516352f1489560e11b815260048101829052606090600080516020615bbe8339815191529063a5e2912a90602401600060405180830381865afa158015612343573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261236b9190810190615160565b9050600081511180156123cd57506040805180820190915260128152711e339034b21e912a3930b4ba111f1e17b39f60711b6020918201528151908201207fb6491396077b2019fd311df30a263f5b6f84c028f31b91717e9f140465bce24314155b156123d757919050565b600082815260106020526040812060030180546123f39061505f565b80601f016020809104026020016040519081016040528092919081815260200182805461241f9061505f565b801561246c5780601f106124415761010080835404028352916020019161246c565b820191906000526020600020905b81548152906001019060200180831161244f57829003601f168201915b5050600e54604051631349027b60e31b81529495506001600160a01b031693639a4813d893506124a2925085915060040161449f565b600060405180830381865afa1580156124bf573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526124e79190810190615160565b9392505050565b6060620532a6821161252c57604051632a12631d60e21b815260048101839052600080516020615bbe8339815191529063a8498c749060240161108d565b60006125378361266d565b6080810151600090815260106020526040902060040180549192509061255c9061505f565b80601f01602080910402602001604051908101604052809291908181526020018280546125889061505f565b80156125d55780601f106125aa576101008083540402835291602001916125d5565b820191906000526020600020905b8154815290600101906020018083116125b857829003601f168201915b5050505050915050919050565b600e54604051631b4751fb60e01b815260609182916001600160a01b0390911690631b4751fb9061261f908690309063e08714a6906004016152f9565b600060405180830381865afa15801561263c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261266491908101906153c3565b91509150915091565b6126a36040805160c08101825260008082526020820181905291810182905260608101829052608081018290529060a082015290565b620532a682116126df5760405163acb586df60e01b815260048101839052600080516020615bbe8339815191529063acb586df90602401610f89565b6000828152600b60209081526040808320815160c08101835281548152600182015460ff90811615159482019490945260028201549281019290925260038101546001600160a01b031660608301526004810154608083015260058101549192909160a084019116600781111561275857612758614539565b600781111561276957612769614539565b90525080516000908152600d602052604090205460608201519192506001600160801b0316906001600160a01b0381161580156127a857506040830151155b156127c95760405163082d451d60e01b815260048101869052602401610ded565b604051630ac7cb6f60e01b81526001600160a01b03821690630ac7cb6f906127f79086908690600401615766565b60c060405180830381865afa158015612814573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061283891906150a9565b95945050505050565b61284b3383613753565b6128675760405162461bcd60e51b8152600401610ded9061520b565b61287384848484613a3d565b50505050565b3360008181526011602052604090205460ff166128a95760405163090a0b6960e21b815260040160405180910390fd5b604051633a84e55160e21b815260048101849052600090600080516020615bbe8339815191529063ea13954490602401600060405180830381865afa1580156128f6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261291e919081019061578a565b60a08101519091506001600160a01b03161561294d5760405163cb391f5760e01b815260040160405180910390fd5b6000848152601060209081526040909120845181559084015184919060018201906129789082615910565b50604082015160028201805460ff1916600183600781111561299c5761299c614539565b0217905550606082015160038201906129b59082615910565b50608082015160048201906129ca9082615910565b5060a08201516005820180546001600160a01b039283166001600160a01b03199182161790915560c084015160068401805491909316911617905560e08201516007820190612a199082615910565b506101008201516008820190612a2f9082615910565b50505050505050565b60606000612a458361266d565b9050806020015115612a6557612a5e81608001516122f6565b915061174a565b505060408051808201909152600b81526a1e39bb339f1e17b9bb339f60a91b6020820152919050565b6000818152600260205260409020546060906001600160a01b0316612ac657604051632df0e24560e21b815260040160405180910390fd5b610cba82611ffd565b612ad7614357565b620532a68211612b535760405163d1bfc2af60e01b815260048101839052600080516020615bbe8339815191529063d1bfc2af90602401600060405180830381865afa158015612b2b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610cba919081019061578a565b6000612b5e8361266d565b905060106000826080015181526020019081526020016000206040518061012001604052908160008201548152602001600182018054612b9d9061505f565b80601f0160208091040260200160405190810160405280929190818152602001828054612bc99061505f565b8015612c165780601f10612beb57610100808354040283529160200191612c16565b820191906000526020600020905b815481529060010190602001808311612bf957829003601f168201915b5050509183525050600282015460209091019060ff166007811115612c3d57612c3d614539565b6007811115612c4e57612c4e614539565b8152602001600382018054612c629061505f565b80601f0160208091040260200160405190810160405280929190818152602001828054612c8e9061505f565b8015612cdb5780601f10612cb057610100808354040283529160200191612cdb565b820191906000526020600020905b815481529060010190602001808311612cbe57829003601f168201915b50505050508152602001600482018054612cf49061505f565b80601f0160208091040260200160405190810160405280929190818152602001828054612d209061505f565b8015612d6d5780601f10612d4257610100808354040283529160200191612d6d565b820191906000526020600020905b815481529060010190602001808311612d5057829003601f168201915b505050918352505060058201546001600160a01b0390811660208301526006830154166040820152600782018054606090920191612daa9061505f565b80601f0160208091040260200160405190810160405280929190818152602001828054612dd69061505f565b8015612e235780601f10612df857610100808354040283529160200191612e23565b820191906000526020600020905b815481529060010190602001808311612e0657829003601f168201915b50505050508152602001600882018054612e3c9061505f565b80601f0160208091040260200160405190810160405280929190818152602001828054612e689061505f565b8015612eb55780601f10612e8a57610100808354040283529160200191612eb5565b820191906000526020600020905b815481529060010190602001808311612e9857829003601f168201915b505050505081525050915050919050565b6060612ed182612acf565b6060015192915050565b606080620532a68511612f6b576040516370438a5360e11b8152600080516020615bbe8339815191529063e08714a690612f1d908890889088906004016159cf565b600060405180830381865afa158015612f3a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612f6291908101906153c3565b9150915061300e565b6000612f768661266d565b600e5460808201516000908152601060205260409081902090516354a2ab0560e01b81529293506001600160a01b03909116916354a2ab0591612fc3918a918a918a9188916004016159fa565b600060405180830381865afa158015612fe0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261300891908101906153c3565b92509250505b935093915050565b61301e61371e565b600e5460405163e1defed960e01b81526001600160a01b039091169063e1defed9906130509085908590600401614a33565b600060405180830381600087803b15801561306a57600080fd5b505af115801561307e573d6000803e3d6000fd5b505050505050565b6002600a54036130d85760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610ded565b6002600a553360009081526011602052604090205460ff1661310d57604051631758e6d560e11b815260040160405180910390fd5b60005b81518110156131435761313b82828151811061312e5761312e615586565b6020026020010151613958565b600101613110565b50506001600a55565b60405163423965e560e11b81526001600160a01b038316600482015260009081907307152bfde079b5319e5308c43fb1dbc9c76cb4f990638472cbca90602401602060405180830381865afa1580156131a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131cd9190615730565b6040516331a9108f60e11b8152600481018290529091506000907307152bfde079b5319e5308c43fb1dbc9c76cb4f990636352211e90602401602060405180830381865afa158015613223573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132479190615749565b6001600160a01b039586166000908152601660209081526040808320938916835292815282822096909716815294909552505050205460ff1690565b61328b614357565b604051633a84e55160e21b815260048101839052600090600080516020615bbe8339815191529063ea13954490602401600060405180830381865afa1580156132d8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613300919081019061578a565b60a08101519091506001600160a01b03161561331c5792915050565b601060008481526020019081526020016000206040518061012001604052908160008201548152602001600182018054612b9d9061505f565b61335d61371e565b63389a75e1600c52806000526020600c20805442111561338557636f5e88186000526004601cfd5b600090556112ab816139ff565b6060806060620532a68711613428576040516378938ca160e11b8152600080516020615bbe8339815191529063f1271942906133d8908a908a908a908a90600401615a4a565b600060405180830381865afa1580156133f5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261341d919081019061532b565b9250925092506134d0565b60006134338861266d565b600e546080820151600090815260106020526040908190209051631c1fc8db60e01b81529293506001600160a01b0390911691631c1fc8db91613483918c918c918c918c91899190600401615a94565b600060405180830381865afa1580156134a0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526134c8919081019061532b565b935093509350505b9450945094915050565b6134e261371e565b8060601b6134f857637448fbae6000526004601cfd5b6112ab816139ff565b6060806060620532a684116135935760405163f901667f60e01b8152600080516020615bbe8339815191529063f901667f9061354390889088906004016153a8565b600060405180830381865afa158015613560573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613588919081019061532b565b925092509250613625565b600e546080860151600090815260106020526040908190209051633c05553b60e01b81526001600160a01b0390921691633c05553b916135d9918991899160040161555f565b600060405180830381865afa1580156135f6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261361e919081019061532b565b9250925092505b9250925092565b60006001600160e01b0319821663780e9d6360e01b1480610cba5750610cba82613a70565b6000818152600260205260409020546001600160a01b03166112ab5760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b6044820152606401610ded565b600081815260046020526040902080546001600160a01b0319166001600160a01b03841690811790915581906136e582611981565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b638b78c6d819543314611a79576382b429006000526004601cfd5b6113d2828260405180602001604052806000815250613ac0565b60008061375f83611981565b9050806001600160a01b0316846001600160a01b031614806137865750613786818561314c565b806121275750836001600160a01b031661379f84610d52565b6001600160a01b031614949350505050565b826001600160a01b03166137c482611981565b6001600160a01b0316146138285760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201526437bbb732b960d91b6064820152608401610ded565b6001600160a01b03821661388a5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610ded565b613895838383613af3565b6138a06000826136b0565b6001600160a01b03831660009081526003602052604081208054600192906138c99084906151aa565b90915550506001600160a01b03821660009081526003602052604081208054600192906138f79084906151bd565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b600061396382611981565b905061397181600084613af3565b61397c6000836136b0565b6001600160a01b03811660009081526003602052604081208054600192906139a59084906151aa565b909155505060008281526002602052604080822080546001600160a01b0319169055518391906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b613a488484846137b1565b613a5484848484613c64565b6128735760405162461bcd60e51b8152600401610ded90615b05565b60006001600160e01b031982166380ac58cd60e01b1480613aa157506001600160e01b03198216635b5e139f60e01b145b80610cba57506301ffc9a760e01b6001600160e01b0319831614610cba565b613aca8383613d65565b613ad76000848484613c64565b610e8e5760405162461bcd60e51b8152600401610ded90615b05565b613afe838383613eb3565b6001600160a01b038316613b1157505050565b6001600160a01b03821615801590613ba1575060405162525fcd60e91b81526001600160a01b03831660048201527307152bfde079b5319e5308c43fb1dbc9c76cb4f99063a4bf9a0090602401602060405180830381865afa158015613b7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b9f9190615730565b155b15613bbf576040516326a45c0160e21b815260040160405180910390fd5b604051636670434960e01b8152600481018290526000907307152bfde079b5319e5308c43fb1dbc9c76cb4f990636670434990602401608060405180830381865afa158015613c12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c369190615638565b93505050508015613c5a5760405163290ea0ff60e01b815260040160405180910390fd5b6128738284613f6b565b60006001600160a01b0384163b15613d5a57604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290613ca8903390899088908890600401615b57565b6020604051808303816000875af1925050508015613ce3575060408051601f3d908101601f19168201909252613ce091810190615b8a565b60015b613d40573d808015613d11576040519150601f19603f3d011682016040523d82523d6000602084013e613d16565b606091505b508051600003613d385760405162461bcd60e51b8152600401610ded90615b05565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050612127565b506001949350505050565b6001600160a01b038216613dbb5760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610ded565b6000818152600260205260409020546001600160a01b031615613e205760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610ded565b613e2c60008383613af3565b6001600160a01b0382166000908152600360205260408120805460019290613e559084906151bd565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6001600160a01b038316613f0e57613f0981600880546000838152600960205260408120829055600182018355919091527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30155565b613f31565b816001600160a01b0316836001600160a01b031614613f3157613f31838261417e565b6001600160a01b038216613f4857610e8e8161421b565b826001600160a01b0316826001600160a01b031614610e8e57610e8e82826142ca565b6000613f7683611981565b60405162525fcd60e91b81526001600160a01b03821660048201529091506000907307152bfde079b5319e5308c43fb1dbc9c76cb4f99063a4bf9a0090602401602060405180830381865afa158015613fd3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ff79190615730565b600f5460405163125bc37160e11b8152600481018390529192506001600160a01b0316906324b786e290602401600060405180830381600087803b15801561403e57600080fd5b505af1158015614052573d6000803e3d6000fd5b5050600f5460405163236313ff60e11b8152600481018590526001600160a01b0390911692506346c627fe9150602401600060405180830381600087803b15801561409c57600080fd5b505af11580156140b0573d6000803e3d6000fd5b5050600f5460405163662c08cd60e01b8152600481018890526001600160a01b03909116925063662c08cd9150602401600060405180830381600087803b1580156140fa57600080fd5b505af115801561410e573d6000803e3d6000fd5b5050600f546040516302fbe78f60e41b8152600481018890526001600160a01b0387811660248301529091169250632fbe78f09150604401600060405180830381600087803b15801561416057600080fd5b505af1158015614174573d6000803e3d6000fd5b5050505050505050565b6000600161418b846119e1565b61419591906151aa565b6000838152600760205260409020549091508082146141e8576001600160a01b03841660009081526006602090815260408083208584528252808320548484528184208190558352600790915290208190555b5060009182526007602090815260408084208490556001600160a01b039094168352600681528383209183525290812055565b60085460009061422d906001906151aa565b6000838152600960205260408120546008805493945090928490811061425557614255615586565b90600052602060002001549050806008838154811061427657614276615586565b60009182526020808320909101929092558281526009909152604080822084905585825281205560088054806142ae576142ae615ba7565b6001900381819060005260206000200160009055905550505050565b60006142d5836119e1565b6001600160a01b039093166000908152600660209081526040808320868452825280832085905593825260079052919091209190915550565b8260028101928215614347579160200282015b8281111561434757825182906143379082615910565b5091602001919060010190614321565b506143539291506143b5565b5090565b604080516101208101825260008082526060602083015290918201908152602001606081526020016060815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160608152602001606081525090565b808211156143535760006143c982826143d2565b506001016143b5565b5080546143de9061505f565b6000825580601f106143ee575050565b601f0160209004906000526020600020908101906112ab91905b808211156143535760008155600101614408565b6001600160e01b0319811681146112ab57600080fd5b60006020828403121561444457600080fd5b81356124e78161441c565b60005b8381101561446a578181015183820152602001614452565b50506000910152565b6000815180845261448b81602086016020860161444f565b601f01601f19169290920160200192915050565b6020815260006124e76020830184614473565b6000602082840312156144c457600080fd5b5035919050565b6001600160a01b03811681146112ab57600080fd5b80356144eb816144cb565b919050565b6000806040838503121561450357600080fd5b823561450e816144cb565b946020939093013593505050565b60006020828403121561452e57600080fd5b81356124e7816144cb565b634e487b7160e01b600052602160045260246000fd5b6008811061456d57634e487b7160e01b600052602160045260246000fd5b9052565b805182526020810151151560208301526040810151604083015260018060a01b0360608201511660608301526080810151608083015260a0810151610e8e60a084018261454f565b60c08101610cba8284614571565b634e487b7160e01b600052604160045260246000fd5b60405161016081016001600160401b0381118282101715614600576146006145c7565b60405290565b604080519081016001600160401b0381118282101715614600576146006145c7565b60405160c081016001600160401b0381118282101715614600576146006145c7565b60405161012081016001600160401b0381118282101715614600576146006145c7565b604051601f8201601f191681016001600160401b0381118282101715614695576146956145c7565b604052919050565b60006001600160401b038211156146b6576146b66145c7565b50601f01601f191660200190565b600082601f8301126146d557600080fd5b81356146e86146e38261469d565b61466d565b8181528460208386010111156146fd57600080fd5b816020850160208301376000918101602001919091529392505050565b60006020828403121561472c57600080fd5b81356001600160401b0381111561474257600080fd5b612127848285016146c4565b60008060006060848603121561476357600080fd5b833561476e816144cb565b9250602084013561477e816144cb565b929592945050506040919091013590565b803560ff811681146144eb57600080fd5b80151581146112ab57600080fd5b80356144eb816147a0565b6000602082840312156147cb57600080fd5b81356001600160401b03808211156147e257600080fd5b9083019061016082860312156147f757600080fd5b6147ff6145dd565b823581526020830135602082015260408301356040820152606083013560608201526080830135608082015260a083013560a082015260c083013560c082015260e083013560e082015261010061485781850161478f565b90820152610120838101358381111561486f57600080fd5b61487b888287016146c4565b82840152505061014091506148918284016147ae565b91810191909152949350505050565b6060815260006148b36060830186614473565b82810360208401526148c58186614473565b905082810360408401526148d98185614473565b9695505050505050565b600060208083850312156148f657600080fd5b82356001600160401b038082111561490d57600080fd5b818501915085601f83011261492157600080fd5b614929614606565b80604084018881111561493b57600080fd5b845b8181101561496f578035858111156149555760008081fd5b6149618b8289016146c4565b85525092860192860161493d565b509098975050505050505050565b600881106112ab57600080fd5b80356144eb8161497d565b600060c082840312156149a757600080fd5b6149af614628565b90508135815260208201356149c3816147a0565b60208201526040828101359082015260608201356149e0816144cb565b60608201526080828101359082015260a08201356149fd8161497d565b60a082015292915050565b60008060e08385031215614a1b57600080fd5b614a258484614995565b9460c0939093013593505050565b604081526000614a466040830185614473565b82810360208401526128388185614473565b60008060e08385031215614a6b57600080fd5b82359150614a7c8460208501614995565b90509250929050565b6020808252825182820181905260009190848201906040850190845b81811015614abd57835183529284019291840191600101614aa1565b50909695505050505050565b60006101208b8352806020840152614ae38184018c614473565b9050614af2604084018b61454f565b8281036060840152614b04818a614473565b90508281036080840152614b188189614473565b6001600160a01b0388811660a0860152871660c085015283810360e08501529050614b438186614473565b9050828103610100840152614b588185614473565b9c9b505050505050505050505050565b60008060408385031215614b7b57600080fd5b8235614b86816144cb565b91506020830135614b96816147a0565b809150509250929050565b60008060008060808587031215614bb757600080fd5b8435614bc2816144cb565b93506020850135614bd2816144cb565b92506040850135915060608501356001600160401b03811115614bf457600080fd5b614c00878288016146c4565b91505092959194509250565b60008060408385031215614c1f57600080fd5b8235915060208301356001600160401b0380821115614c3d57600080fd5b908401906101208287031215614c5257600080fd5b614c5a61464a565b82358152602083013582811115614c7057600080fd5b614c7c888286016146c4565b602083015250614c8e6040840161498a565b6040820152606083013582811115614ca557600080fd5b614cb1888286016146c4565b606083015250608083013582811115614cc957600080fd5b614cd5888286016146c4565b608083015250614ce760a084016144e0565b60a0820152614cf860c084016144e0565b60c082015260e083013582811115614d0f57600080fd5b614d1b888286016146c4565b60e0830152506101008084013583811115614d3557600080fd5b614d41898287016146c4565b8284015250508093505050509250929050565b602081528151602082015260006020830151610120806040850152614d7d610140850183614473565b91506040850151614d91606086018261454f565b506060850151601f1980868503016080870152614dae8483614473565b935060808701519150808685030160a0870152614dcb8483614473565b935060a08701519150614de960c08701836001600160a01b03169052565b60c08701516001600160a01b03811660e0880152915060e08701519150610100818786030181880152614e1c8584614473565b9088015187820390920184880152935090506148d98382614473565b600080600060608486031215614e4d57600080fd5b8335925060208401356001600160401b0380821115614e6b57600080fd5b614e77878388016146c4565b93506040860135915080821115614e8d57600080fd5b50614e9a868287016146c4565b9150509250925092565b60008060408385031215614eb757600080fd5b82356001600160401b0380821115614ece57600080fd5b614eda868387016146c4565b93506020850135915080821115614ef057600080fd5b50614efd858286016146c4565b9150509250929050565b60006020808385031215614f1a57600080fd5b82356001600160401b0380821115614f3157600080fd5b818501915085601f830112614f4557600080fd5b813581811115614f5757614f576145c7565b8060051b9150614f6884830161466d565b8181529183018401918481019088841115614f8257600080fd5b938501935b83851015614fa057843582529385019390850190614f87565b98975050505050505050565b60008060408385031215614fbf57600080fd5b8235614fca816144cb565b91506020830135614b96816144cb565b60008060008060808587031215614ff057600080fd5b8435935060208501356001600160401b038082111561500e57600080fd5b61501a888389016146c4565b9450604087013591508082111561503057600080fd5b61503c888389016146c4565b9350606087013591508082111561505257600080fd5b50614c00878288016146c4565b600181811c9082168061507357607f821691505b60208210810361174a57634e487b7160e01b600052602260045260246000fd5b80516144eb816144cb565b80516144eb8161497d565b600060c082840312156150bb57600080fd5b6150c3614628565b8251815260208301516150d5816147a0565b60208201526040838101519082015260608301516150f2816144cb565b60608201526080838101519082015260a083015161510f8161497d565b60a08201529392505050565b600082601f83011261512c57600080fd5b815161513a6146e38261469d565b81815284602083860101111561514f57600080fd5b61212782602083016020870161444f565b60006020828403121561517257600080fd5b81516001600160401b0381111561518857600080fd5b6121278482850161511b565b634e487b7160e01b600052601160045260246000fd5b81810381811115610cba57610cba615194565b80820180821115610cba57610cba615194565b6000826151ed57634e487b7160e01b600052601260045260246000fd5b500690565b60006001820161520457615204615194565b5060010190565b6020808252602e908201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560408201526d1c881b9bdc88185c1c1c9bdd995960921b606082015260800190565b6000610160825184526020830151602085015260408301516040850152606083015160608501526080830151608085015260a083015160a085015260c083015160c085015260e083015160e0850152610100808401516152bd8287018260ff169052565b50506101208084015182828701526152d783870182614473565b92505050610140808401516152ef8287018215159052565b5090949350505050565b60408152600061530c6040830186615259565b905061212760208301848660201b63ffffffff919091161760401b9052565b60008060006060848603121561534057600080fd5b83516001600160401b038082111561535757600080fd5b6153638783880161511b565b9450602086015191508082111561537957600080fd5b6153858783880161511b565b9350604086015191508082111561539b57600080fd5b50614e9a8682870161511b565b60e081016153b68285614571565b8260c08301529392505050565b600080604083850312156153d657600080fd5b82516001600160401b03808211156153ed57600080fd5b6153f98683870161511b565b9350602085015191508082111561540f57600080fd5b50614efd8582860161511b565b600081546154298161505f565b80855260206001838116801561544657600181146154605761548e565b60ff1985168884015283151560051b88018301955061548e565b866000528260002060005b858110156154865781548a820186015290830190840161546b565b890184019650505b505050505092915050565b6000610120825484528060208501526154b78185016001850161541c565b90506154c7600284015460ff1690565b6154d4604086018261454f565b5083810360608501526154ea816003850161541c565b90508381036080850152615501816004850161541c565b905061551760058401546001600160a01b031690565b6001600160a01b0390811660a086015260068401541660c085015283810360e0850152615547816007850161541c565b9050838103610100850152612127816008850161541c565b600061010061556e8387614571565b8460c08401528060e08401526148d981840185615499565b634e487b7160e01b600052603260045260246000fd5b6000608082840312156155ae57600080fd5b604051608081016001600160401b0382821081831117156155d1576155d16145c7565b81604052845191506001600160801b03821682146155ee57600080fd5b908252602084015190808216821461560557600080fd5b5060208201526040830151615619816147a0565b6040820152606083015161562c816147a0565b60608201529392505050565b6000806000806080858703121561564e57600080fd5b8451615659816144cb565b602086015160408701519195509350615671816144cb565b6060860151909250615682816147a0565b939692955090935050565b600061016088835260206156a4602085018a614571565b8160e08501526156b682850189615499565b91508382036101008501526156cb8288614473565b91508382036101208501526156e08287614473565b848103610140860152915081604081018660005b600281101561571e57858303845261570c838361541c565b938501939250600191820191016156f4565b50909c9b505050505050505050505050565b60006020828403121561574257600080fd5b5051919050565b60006020828403121561575b57600080fd5b81516124e7816144cb565b60e081016157748285614571565b6001600160801b03831660c08301529392505050565b60006020828403121561579c57600080fd5b81516001600160401b03808211156157b357600080fd5b9083019061012082860312156157c857600080fd5b6157d061464a565b825181526020830151828111156157e657600080fd5b6157f28782860161511b565b6020830152506158046040840161509e565b604082015260608301518281111561581b57600080fd5b6158278782860161511b565b60608301525060808301518281111561583f57600080fd5b61584b8782860161511b565b60808301525061585d60a08401615093565b60a082015261586e60c08401615093565b60c082015260e08301518281111561588557600080fd5b6158918782860161511b565b60e08301525061010080840151838111156158ab57600080fd5b6158b78882870161511b565b918301919091525095945050505050565b601f821115610e8e576000816000526020600020601f850160051c810160208610156158f15750805b601f850160051c820191505b8181101561307e578281556001016158fd565b81516001600160401b03811115615929576159296145c7565b61593d81615937845461505f565b846158c8565b602080601f831160018114615972576000841561595a5750858301515b600019600386901b1c1916600185901b17855561307e565b600085815260208120601f198616915b828110156159a157888601518255948401946001909101908401615982565b50858210156159bf5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b8381526060602082015260006159e86060830185614473565b82810360408401526148d98185614473565b6000610140878352806020840152615a1481840188614473565b90508281036040840152615a288187614473565b9050615a376060840186614571565b828103610120840152614fa08185615499565b848152608060208201526000615a636080830186614473565b8281036040840152615a758186614473565b90508281036060840152615a898185614473565b979650505050505050565b6000610160888352806020840152615aae81840189614473565b90508281036040840152615ac28188614473565b90508281036060840152615ad68187614473565b9050615ae56080840186614571565b828103610140840152615af88185615499565b9998505050505050505050565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b6001600160a01b03858116825284166020820152604081018390526080606082018190526000906148d990830184614473565b600060208284031215615b9c57600080fd5b81516124e78161441c565b634e487b7160e01b600052603160045260246000fdfe0000000000000000000000006b8f34e0559aa9a5507e74ad93374d9745cdbf09a2646970667358221220f0fd37db28ac57615de11a9c1fadad4504b5186d6335bb14f2c99d22c56c297a64736f6c63430008160033
Deployed Bytecode
0x6080604052600436106103ef5760003560e01c806390f6384211610208578063bc08503311610118578063e1defed9116100ab578063f04e283e1161007a578063f04e283e14610c16578063f127194214610c29578063f2fde38b14610c49578063f901667f14610c5c578063fee81cf414610c7c57600080fd5b8063e1defed914610b96578063e4623c1b14610bb6578063e985e9c514610bd6578063ea13954414610bf657600080fd5b8063d1bfc2af116100e7578063d1bfc2af14610b12578063dcbaaad514610b3f578063e08714a614610b5f578063e14d026f14610b7f57600080fd5b8063bc08503314610a90578063c779153114610ab2578063c876371b14610ad2578063c87b56dd14610af257600080fd5b8063a8498c741161019b578063acb586df1161016a578063acb586df146109f3578063b88d4fde14610a13578063b966c51d14610a33578063b97dd9e214610a53578063ba7270f314610a6857600080fd5b8063a8498c7414610963578063a9684c5814610983578063aa271e1a146109a3578063abc8c7af146109d357600080fd5b80639a6b9596116101d75780639a6b9596146108e9578063a1d6268014610903578063a22cb46514610923578063a5e2912a1461094357600080fd5b806390f638421461087f57806395d89b411461089f578063983b2d56146108b457806398d8d288146108d457600080fd5b806333a70328116103035780635c1ef2561161029657806373ad6c2d1161026557806373ad6c2d1461079257806375794a3c146107b2578063817a29a4146107c8578063859e7d32146107fd5780638da5cb5b1461086657600080fd5b80635c1ef256146107355780636352211e1461074a57806370a082311461076a578063715018a61461078a57600080fd5b806342966c68116102d257806342966c68146106c0578063438b6300146106e05780634f6ccce71461070d57806354d1f13d1461072d57600080fd5b806333a703281461063257806336d34ab91461066057806340d097c31461068057806342842e0e146106a057600080fd5b806321b12c2311610386578063256929621161035557806325692962146105b557806326f2052a146105bd5780632f745c59146105dd5780633092afd5146105fd57806331d2866a1461061d57600080fd5b806321b12c2314610531578063239ffa681461055157806323b872dd14610566578063250818ea1461058657600080fd5b8063120def24116103c2578063120def24146104a557806318160ddd146104c55780631f787d2f146104e4578063208c25a31461050457600080fd5b806301ffc9a7146103f457806306fdde0314610429578063081812fc1461044b578063095ea7b314610483575b600080fd5b34801561040057600080fd5b5061041461040f366004614432565b610caf565b60405190151581526020015b60405180910390f35b34801561043557600080fd5b5061043e610cc0565b604051610420919061449f565b34801561045757600080fd5b5061046b6104663660046144b2565b610d52565b6040516001600160a01b039091168152602001610420565b34801561048f57600080fd5b506104a361049e3660046144f0565b610d79565b005b3480156104b157600080fd5b506104a36104c036600461451c565b610e93565b3480156104d157600080fd5b506008545b604051908152602001610420565b3480156104f057600080fd5b506104a36104ff3660046144f0565b610ebd565b34801561051057600080fd5b5061052461051f3660046144b2565b610f1b565b60405161042091906145b9565b34801561053d57600080fd5b5061043e61054c36600461471a565b61105c565b34801561055d57600080fd5b506104a36110d2565b34801561057257600080fd5b506104a361058136600461474e565b6112ae565b34801561059257600080fd5b506105a66105a13660046147b9565b6112df565b604051610420939291906148a0565b6104a361136e565b3480156105c957600080fd5b506104a36105d83660046148e3565b6113bd565b3480156105e957600080fd5b506104d66105f83660046144f0565b6113d6565b34801561060957600080fd5b506104a361061836600461451c565b61146c565b34801561062957600080fd5b506104a3611495565b34801561063e57600080fd5b5061065261064d366004614a08565b6114d8565b604051610420929190614a33565b34801561066c57600080fd5b506104a361067b366004614a58565b6115fd565b34801561068c57600080fd5b506104d661069b36600461451c565b6116ea565b3480156106ac57600080fd5b506104a36106bb36600461474e565b611750565b3480156106cc57600080fd5b506104a36106db3660046144b2565b61176b565b3480156106ec57600080fd5b506107006106fb36600461451c565b6117a4565b6040516104209190614a85565b34801561071957600080fd5b506104d66107283660046144b2565b61183b565b6104a36118ce565b34801561074157600080fd5b5061043e61190a565b34801561075657600080fd5b5061046b6107653660046144b2565b611981565b34801561077657600080fd5b506104d661078536600461451c565b6119e1565b6104a3611a67565b34801561079e57600080fd5b506104a36107ad36600461451c565b611a7b565b3480156107be57600080fd5b506104d660125481565b3480156107d457600080fd5b506107e86107e33660046144b2565b611aa5565b60405161042099989796959493929190614ac9565b34801561080957600080fd5b5061081d6108183660046144b2565b611daf565b604051610420919081516001600160801b031681526020808301516001600160401b03169082015260408083015115159082015260609182015115159181019190915260800190565b34801561087257600080fd5b50638b78c6d8195461046b565b34801561088b57600080fd5b506104a361089a3660046144b2565b611eb3565b3480156108ab57600080fd5b5061043e611fab565b3480156108c057600080fd5b506104a36108cf36600461451c565b611fba565b3480156108e057600080fd5b506104a3611fe6565b3480156108f557600080fd5b506015546104149060ff1681565b34801561090f57600080fd5b5061043e61091e3660046144b2565b611ffd565b34801561092f57600080fd5b506104a361093e366004614b68565b61212f565b34801561094f57600080fd5b5061043e61095e3660046144b2565b6122f6565b34801561096f57600080fd5b5061043e61097e3660046144b2565b6124ee565b34801561098f57600080fd5b5061065261099e3660046147b9565b6125e2565b3480156109af57600080fd5b506104146109be36600461451c565b60116020526000908152604090205460ff1681565b3480156109df57600080fd5b50600f5461046b906001600160a01b031681565b3480156109ff57600080fd5b50610524610a0e3660046144b2565b61266d565b348015610a1f57600080fd5b506104a3610a2e366004614ba1565b612841565b348015610a3f57600080fd5b506104a3610a4e366004614c0c565b612879565b348015610a5f57600080fd5b50600c546104d6565b348015610a7457600080fd5b5061046b7307152bfde079b5319e5308c43fb1dbc9c76cb4f981565b348015610a9c57600080fd5b5061046b600080516020615bbe83398151915281565b348015610abe57600080fd5b5061043e610acd3660046144b2565b612a38565b348015610ade57600080fd5b50600e5461046b906001600160a01b031681565b348015610afe57600080fd5b5061043e610b0d3660046144b2565b612a8e565b348015610b1e57600080fd5b50610b32610b2d3660046144b2565b612acf565b6040516104209190614d54565b348015610b4b57600080fd5b5061043e610b5a3660046144b2565b612ec6565b348015610b6b57600080fd5b50610652610b7a366004614e38565b612edb565b348015610b8b57600080fd5b50600c546104d69081565b348015610ba257600080fd5b506104a3610bb1366004614ea4565b613016565b348015610bc257600080fd5b506104a3610bd1366004614f07565b613086565b348015610be257600080fd5b50610414610bf1366004614fac565b61314c565b348015610c0257600080fd5b50610b32610c113660046144b2565b613283565b6104a3610c2436600461451c565b613355565b348015610c3557600080fd5b506105a6610c44366004614fda565b613392565b6104a3610c5736600461451c565b6134da565b348015610c6857600080fd5b506105a6610c77366004614a08565b613501565b348015610c8857600080fd5b506104d6610c9736600461451c565b63389a75e1600c908152600091909152602090205490565b6000610cba8261362c565b92915050565b606060008054610ccf9061505f565b80601f0160208091040260200160405190810160405280929190818152602001828054610cfb9061505f565b8015610d485780601f10610d1d57610100808354040283529160200191610d48565b820191906000526020600020905b815481529060010190602001808311610d2b57829003601f168201915b5050505050905090565b6000610d5d82613651565b506000908152600460205260409020546001600160a01b031690565b6000610d8482611981565b9050806001600160a01b0316836001600160a01b031603610df65760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b60648201526084015b60405180910390fd5b336001600160a01b0382161480610e125750610e12813361314c565b610e845760405162461bcd60e51b815260206004820152603e60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206e6f7220617070726f76656420666f7220616c6c00006064820152608401610ded565b610e8e83836136b0565b505050565b610e9b61371e565b600e80546001600160a01b0319166001600160a01b0392909216919091179055565b3360008181526011602052604090205460ff16610eed5760405163090a0b6960e21b815260040160405180910390fd5b60155460ff1615610f115760405163cb207e0d60e01b815260040160405180910390fd5b610e8e8383613739565b610f516040805160c08101825260008082526020820181905291810182905260608101829052608081018290529060a082015290565b620532a68211610fca5760405163208c25a360e01b815260048101839052600080516020615bbe8339815191529063208c25a3906024015b60c060405180830381865afa158015610fa6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cba91906150a9565b6000828152600b6020908152604091829020825160c08101845281548152600182015460ff90811615159382019390935260028201549381019390935260038101546001600160a01b03166060840152600481015460808401526005810154909160a084019116600781111561104257611042614539565b600781111561105357611053614539565b90525092915050565b600e546040516321b12c2360e01b81526060916001600160a01b0316906321b12c239061108d90859060040161449f565b600060405180830381865afa1580156110aa573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610cba9190810190615160565b600c546000908152600d602052604090208054600160c01b900460ff16158061112a57508054600160c81b900460ff1615801561112a5750611116610100436151aa565b8154600160801b90046001600160401b0316105b156111755761113a4360326151bd565b815460ff60c01b196001600160401b0392909216600160801b029190911668ffffffffffffffffff60801b1990911617600160c01b17815550565b8054600160801b90046001600160401b03164311156112ab57805460408051600160801b9092046001600160401b031640602083015244908201526001600160801b03906060016040516020818303038152906040528051906020012060001c6111df91906151d0565b8154600160c81b6001600160801b039290921679ff000000000000000000ffffffffffffffffffffffffffffffff199091161717808255600c54604051600160801b9092046001600160401b0316917f78611aecfda8d341359c248df527c95aef93d446c92bb928b2a81b7abcb1d8d990600090a3604080516000815260001960208201527f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c910160405180910390a1600c805460009061129f906151f2565b909155506112ab6110d2565b50565b6112b83382613753565b6112d45760405162461bcd60e51b8152600401610ded9061520b565b610e8e8383836137b1565b600e5460405163d42f766360e01b8152606091829182916001600160a01b03169063d42f76639061131c908790309063f1271942906004016152f9565b600060405180830381865afa158015611339573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611361919081019061532b565b9250925092509193909250565b60006202a3006001600160401b03164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b6113c561371e565b6113d2601382600261430e565b5050565b60006113e1836119e1565b82106114435760405162461bcd60e51b815260206004820152602b60248201527f455243373231456e756d657261626c653a206f776e657220696e646578206f7560448201526a74206f6620626f756e647360a81b6064820152608401610ded565b506001600160a01b03919091166000908152600660209081526040808320938352929052205490565b61147461371e565b6001600160a01b03166000908152601160205260409020805460ff19169055565b3360008181526011602052604090205460ff166114c55760405163090a0b6960e21b815260040160405180910390fd5b600c546000036112ab5761030a600c5550565b606080620532a6831161156657604051630674e06560e31b8152600080516020615bbe833981519152906333a703289061151890879087906004016153a8565b600060405180830381865afa158015611535573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261155d91908101906153c3565b915091506115f6565b600e546080850151600090815260106020526040908190209051630bfb083b60e41b81526001600160a01b039092169163bfb083b0916115ac918891889160040161555f565b600060405180830381865afa1580156115c9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115f191908101906153c3565b915091505b9250929050565b3360008181526011602052604090205460ff1661162d5760405163090a0b6960e21b815260040160405180910390fd5b620532a683116116505760405163918c6c1560e01b815260040160405180910390fd5b6000838152600b602090815260409182902084518155908401516001808301805492151560ff1993841617905592850151600283015560608501516003830180546001600160a01b039092166001600160a01b03199092169190911790556080850151600483015560a08501516005830180548795929391929116908360078111156116de576116de614539565b02179055505050505050565b3360008181526011602052604081205490919060ff1661171d5760405163090a0b6960e21b815260040160405180910390fd5b6117256110d2565b6000601260008154611736906151f2565b918290555090506117478482613739565b91505b50919050565b610e8e83838360405180602001604052806000815250612841565b3360009081526011602052604090205460ff1661179b57604051631758e6d560e11b815260040160405180910390fd5b6112ab81613958565b606060006117b1836119e1565b90506000816001600160401b038111156117cd576117cd6145c7565b6040519080825280602002602001820160405280156117f6578160200160208202803683370190505b50905060005b828110156118335761180e85826113d6565b82828151811061182057611820615586565b60209081029190910101526001016117fc565b509392505050565b600061184660085490565b82106118a95760405162461bcd60e51b815260206004820152602c60248201527f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60448201526b7574206f6620626f756e647360a01b6064820152608401610ded565b600882815481106118bc576118bc615586565b90600052602060002001549050919050565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b600e5460408051632e0f792b60e11b815290516060926001600160a01b031691635c1ef2569160048083019260009291908290030181865afa158015611954573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261197c9190810190615160565b905090565b6000818152600260205260408120546001600160a01b031680610cba5760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b6044820152606401610ded565b60006001600160a01b038216611a4b5760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f7420612076616044820152683634b21037bbb732b960b91b6064820152608401610ded565b506001600160a01b031660009081526003602052604090205490565b611a6f61371e565b611a7960006139ff565b565b611a8361371e565b600f80546001600160a01b0319166001600160a01b0392909216919091179055565b60106020526000908152604090208054600182018054919291611ac79061505f565b80601f0160208091040260200160405190810160405280929190818152602001828054611af39061505f565b8015611b405780601f10611b1557610100808354040283529160200191611b40565b820191906000526020600020905b815481529060010190602001808311611b2357829003601f168201915b5050506002840154600385018054949560ff909216949193509150611b649061505f565b80601f0160208091040260200160405190810160405280929190818152602001828054611b909061505f565b8015611bdd5780601f10611bb257610100808354040283529160200191611bdd565b820191906000526020600020905b815481529060010190602001808311611bc057829003601f168201915b505050505090806004018054611bf29061505f565b80601f0160208091040260200160405190810160405280929190818152602001828054611c1e9061505f565b8015611c6b5780601f10611c4057610100808354040283529160200191611c6b565b820191906000526020600020905b815481529060010190602001808311611c4e57829003601f168201915b5050506005840154600685015460078601805495966001600160a01b0393841696929093169450919250611c9e9061505f565b80601f0160208091040260200160405190810160405280929190818152602001828054611cca9061505f565b8015611d175780601f10611cec57610100808354040283529160200191611d17565b820191906000526020600020905b815481529060010190602001808311611cfa57829003601f168201915b505050505090806008018054611d2c9061505f565b80601f0160208091040260200160405190810160405280929190818152602001828054611d589061505f565b8015611da55780601f10611d7a57610100808354040283529160200191611da5565b820191906000526020600020905b815481529060010190602001808311611d8857829003601f168201915b5050505050905089565b6040805160808101825260008082526020820181905291810182905260608101919091526103098211611e4a576040516342cf3e9960e11b815260048101839052600080516020615bbe8339815191529063859e7d3290602401608060405180830381865afa158015611e26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cba919061559c565b506000908152600d6020908152604091829020825160808101845290546001600160801b03811682526001600160401b03600160801b8204169282019290925260ff600160c01b83048116151593820193909352600160c81b9091049091161515606082015290565b604051636670434960e01b8152600481018290526000907307152bfde079b5319e5308c43fb1dbc9c76cb4f990636670434990602401608060405180830381865afa158015611f06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f2a9190615638565b5092505050336001600160a01b0316611f4283611981565b6001600160a01b031614158015611f6257506001600160a01b0381163314155b8015611f825750337307152bfde079b5319e5308c43fb1dbc9c76cb4f914155b15611fa057604051633e79b9f360e01b815260040160405180910390fd5b6113d26000836136b0565b606060018054610ccf9061505f565b611fc261371e565b6001600160a01b03166000908152601160205260409020805460ff19166001179055565b611fee61371e565b6015805460ff19166001179055565b6060620532a6821161203b57604051630143ac4d60e71b815260048101839052600080516020615bbe8339815191529063a1d626809060240161108d565b60006120468361266d565b90506000816020015161207c576040518060400160405280600b81526020016a1e39bb339f1e17b9bb339f60a91b815250612089565b61208982608001516122f6565b600e54608084015160009081526010602052604090209192506001600160a01b03169063ac499bbc90869085906120be61190a565b8660136040518763ffffffff1660e01b81526004016120e29695949392919061568d565b600060405180830381865afa1580156120ff573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526121279190810190615160565b949350505050565b336001600160a01b038316036121875760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610ded565b60405163423965e560e11b81523360048201526000907307152bfde079b5319e5308c43fb1dbc9c76cb4f990638472cbca90602401602060405180830381865afa1580156121d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121fd9190615730565b6040516331a9108f60e11b8152600481018290529091506000907307152bfde079b5319e5308c43fb1dbc9c76cb4f990636352211e90602401602060405180830381865afa158015612253573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122779190615749565b3360008181526016602090815260408083206001600160a01b03868116808652918452828520908b1680865290845293829020805460ff19168a15159081179091559151918252949550919392917fb35a3e86f41df9a708de503b8afccb6c992167394cba9e16a3b5c466d378d1aa910160405180910390a450505050565b6040516352f1489560e11b815260048101829052606090600080516020615bbe8339815191529063a5e2912a90602401600060405180830381865afa158015612343573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261236b9190810190615160565b9050600081511180156123cd57506040805180820190915260128152711e339034b21e912a3930b4ba111f1e17b39f60711b6020918201528151908201207fb6491396077b2019fd311df30a263f5b6f84c028f31b91717e9f140465bce24314155b156123d757919050565b600082815260106020526040812060030180546123f39061505f565b80601f016020809104026020016040519081016040528092919081815260200182805461241f9061505f565b801561246c5780601f106124415761010080835404028352916020019161246c565b820191906000526020600020905b81548152906001019060200180831161244f57829003601f168201915b5050600e54604051631349027b60e31b81529495506001600160a01b031693639a4813d893506124a2925085915060040161449f565b600060405180830381865afa1580156124bf573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526124e79190810190615160565b9392505050565b6060620532a6821161252c57604051632a12631d60e21b815260048101839052600080516020615bbe8339815191529063a8498c749060240161108d565b60006125378361266d565b6080810151600090815260106020526040902060040180549192509061255c9061505f565b80601f01602080910402602001604051908101604052809291908181526020018280546125889061505f565b80156125d55780601f106125aa576101008083540402835291602001916125d5565b820191906000526020600020905b8154815290600101906020018083116125b857829003601f168201915b5050505050915050919050565b600e54604051631b4751fb60e01b815260609182916001600160a01b0390911690631b4751fb9061261f908690309063e08714a6906004016152f9565b600060405180830381865afa15801561263c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261266491908101906153c3565b91509150915091565b6126a36040805160c08101825260008082526020820181905291810182905260608101829052608081018290529060a082015290565b620532a682116126df5760405163acb586df60e01b815260048101839052600080516020615bbe8339815191529063acb586df90602401610f89565b6000828152600b60209081526040808320815160c08101835281548152600182015460ff90811615159482019490945260028201549281019290925260038101546001600160a01b031660608301526004810154608083015260058101549192909160a084019116600781111561275857612758614539565b600781111561276957612769614539565b90525080516000908152600d602052604090205460608201519192506001600160801b0316906001600160a01b0381161580156127a857506040830151155b156127c95760405163082d451d60e01b815260048101869052602401610ded565b604051630ac7cb6f60e01b81526001600160a01b03821690630ac7cb6f906127f79086908690600401615766565b60c060405180830381865afa158015612814573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061283891906150a9565b95945050505050565b61284b3383613753565b6128675760405162461bcd60e51b8152600401610ded9061520b565b61287384848484613a3d565b50505050565b3360008181526011602052604090205460ff166128a95760405163090a0b6960e21b815260040160405180910390fd5b604051633a84e55160e21b815260048101849052600090600080516020615bbe8339815191529063ea13954490602401600060405180830381865afa1580156128f6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261291e919081019061578a565b60a08101519091506001600160a01b03161561294d5760405163cb391f5760e01b815260040160405180910390fd5b6000848152601060209081526040909120845181559084015184919060018201906129789082615910565b50604082015160028201805460ff1916600183600781111561299c5761299c614539565b0217905550606082015160038201906129b59082615910565b50608082015160048201906129ca9082615910565b5060a08201516005820180546001600160a01b039283166001600160a01b03199182161790915560c084015160068401805491909316911617905560e08201516007820190612a199082615910565b506101008201516008820190612a2f9082615910565b50505050505050565b60606000612a458361266d565b9050806020015115612a6557612a5e81608001516122f6565b915061174a565b505060408051808201909152600b81526a1e39bb339f1e17b9bb339f60a91b6020820152919050565b6000818152600260205260409020546060906001600160a01b0316612ac657604051632df0e24560e21b815260040160405180910390fd5b610cba82611ffd565b612ad7614357565b620532a68211612b535760405163d1bfc2af60e01b815260048101839052600080516020615bbe8339815191529063d1bfc2af90602401600060405180830381865afa158015612b2b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610cba919081019061578a565b6000612b5e8361266d565b905060106000826080015181526020019081526020016000206040518061012001604052908160008201548152602001600182018054612b9d9061505f565b80601f0160208091040260200160405190810160405280929190818152602001828054612bc99061505f565b8015612c165780601f10612beb57610100808354040283529160200191612c16565b820191906000526020600020905b815481529060010190602001808311612bf957829003601f168201915b5050509183525050600282015460209091019060ff166007811115612c3d57612c3d614539565b6007811115612c4e57612c4e614539565b8152602001600382018054612c629061505f565b80601f0160208091040260200160405190810160405280929190818152602001828054612c8e9061505f565b8015612cdb5780601f10612cb057610100808354040283529160200191612cdb565b820191906000526020600020905b815481529060010190602001808311612cbe57829003601f168201915b50505050508152602001600482018054612cf49061505f565b80601f0160208091040260200160405190810160405280929190818152602001828054612d209061505f565b8015612d6d5780601f10612d4257610100808354040283529160200191612d6d565b820191906000526020600020905b815481529060010190602001808311612d5057829003601f168201915b505050918352505060058201546001600160a01b0390811660208301526006830154166040820152600782018054606090920191612daa9061505f565b80601f0160208091040260200160405190810160405280929190818152602001828054612dd69061505f565b8015612e235780601f10612df857610100808354040283529160200191612e23565b820191906000526020600020905b815481529060010190602001808311612e0657829003601f168201915b50505050508152602001600882018054612e3c9061505f565b80601f0160208091040260200160405190810160405280929190818152602001828054612e689061505f565b8015612eb55780601f10612e8a57610100808354040283529160200191612eb5565b820191906000526020600020905b815481529060010190602001808311612e9857829003601f168201915b505050505081525050915050919050565b6060612ed182612acf565b6060015192915050565b606080620532a68511612f6b576040516370438a5360e11b8152600080516020615bbe8339815191529063e08714a690612f1d908890889088906004016159cf565b600060405180830381865afa158015612f3a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612f6291908101906153c3565b9150915061300e565b6000612f768661266d565b600e5460808201516000908152601060205260409081902090516354a2ab0560e01b81529293506001600160a01b03909116916354a2ab0591612fc3918a918a918a9188916004016159fa565b600060405180830381865afa158015612fe0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261300891908101906153c3565b92509250505b935093915050565b61301e61371e565b600e5460405163e1defed960e01b81526001600160a01b039091169063e1defed9906130509085908590600401614a33565b600060405180830381600087803b15801561306a57600080fd5b505af115801561307e573d6000803e3d6000fd5b505050505050565b6002600a54036130d85760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610ded565b6002600a553360009081526011602052604090205460ff1661310d57604051631758e6d560e11b815260040160405180910390fd5b60005b81518110156131435761313b82828151811061312e5761312e615586565b6020026020010151613958565b600101613110565b50506001600a55565b60405163423965e560e11b81526001600160a01b038316600482015260009081907307152bfde079b5319e5308c43fb1dbc9c76cb4f990638472cbca90602401602060405180830381865afa1580156131a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131cd9190615730565b6040516331a9108f60e11b8152600481018290529091506000907307152bfde079b5319e5308c43fb1dbc9c76cb4f990636352211e90602401602060405180830381865afa158015613223573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132479190615749565b6001600160a01b039586166000908152601660209081526040808320938916835292815282822096909716815294909552505050205460ff1690565b61328b614357565b604051633a84e55160e21b815260048101839052600090600080516020615bbe8339815191529063ea13954490602401600060405180830381865afa1580156132d8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613300919081019061578a565b60a08101519091506001600160a01b03161561331c5792915050565b601060008481526020019081526020016000206040518061012001604052908160008201548152602001600182018054612b9d9061505f565b61335d61371e565b63389a75e1600c52806000526020600c20805442111561338557636f5e88186000526004601cfd5b600090556112ab816139ff565b6060806060620532a68711613428576040516378938ca160e11b8152600080516020615bbe8339815191529063f1271942906133d8908a908a908a908a90600401615a4a565b600060405180830381865afa1580156133f5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261341d919081019061532b565b9250925092506134d0565b60006134338861266d565b600e546080820151600090815260106020526040908190209051631c1fc8db60e01b81529293506001600160a01b0390911691631c1fc8db91613483918c918c918c918c91899190600401615a94565b600060405180830381865afa1580156134a0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526134c8919081019061532b565b935093509350505b9450945094915050565b6134e261371e565b8060601b6134f857637448fbae6000526004601cfd5b6112ab816139ff565b6060806060620532a684116135935760405163f901667f60e01b8152600080516020615bbe8339815191529063f901667f9061354390889088906004016153a8565b600060405180830381865afa158015613560573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613588919081019061532b565b925092509250613625565b600e546080860151600090815260106020526040908190209051633c05553b60e01b81526001600160a01b0390921691633c05553b916135d9918991899160040161555f565b600060405180830381865afa1580156135f6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261361e919081019061532b565b9250925092505b9250925092565b60006001600160e01b0319821663780e9d6360e01b1480610cba5750610cba82613a70565b6000818152600260205260409020546001600160a01b03166112ab5760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b6044820152606401610ded565b600081815260046020526040902080546001600160a01b0319166001600160a01b03841690811790915581906136e582611981565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b638b78c6d819543314611a79576382b429006000526004601cfd5b6113d2828260405180602001604052806000815250613ac0565b60008061375f83611981565b9050806001600160a01b0316846001600160a01b031614806137865750613786818561314c565b806121275750836001600160a01b031661379f84610d52565b6001600160a01b031614949350505050565b826001600160a01b03166137c482611981565b6001600160a01b0316146138285760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201526437bbb732b960d91b6064820152608401610ded565b6001600160a01b03821661388a5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610ded565b613895838383613af3565b6138a06000826136b0565b6001600160a01b03831660009081526003602052604081208054600192906138c99084906151aa565b90915550506001600160a01b03821660009081526003602052604081208054600192906138f79084906151bd565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b600061396382611981565b905061397181600084613af3565b61397c6000836136b0565b6001600160a01b03811660009081526003602052604081208054600192906139a59084906151aa565b909155505060008281526002602052604080822080546001600160a01b0319169055518391906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b613a488484846137b1565b613a5484848484613c64565b6128735760405162461bcd60e51b8152600401610ded90615b05565b60006001600160e01b031982166380ac58cd60e01b1480613aa157506001600160e01b03198216635b5e139f60e01b145b80610cba57506301ffc9a760e01b6001600160e01b0319831614610cba565b613aca8383613d65565b613ad76000848484613c64565b610e8e5760405162461bcd60e51b8152600401610ded90615b05565b613afe838383613eb3565b6001600160a01b038316613b1157505050565b6001600160a01b03821615801590613ba1575060405162525fcd60e91b81526001600160a01b03831660048201527307152bfde079b5319e5308c43fb1dbc9c76cb4f99063a4bf9a0090602401602060405180830381865afa158015613b7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b9f9190615730565b155b15613bbf576040516326a45c0160e21b815260040160405180910390fd5b604051636670434960e01b8152600481018290526000907307152bfde079b5319e5308c43fb1dbc9c76cb4f990636670434990602401608060405180830381865afa158015613c12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c369190615638565b93505050508015613c5a5760405163290ea0ff60e01b815260040160405180910390fd5b6128738284613f6b565b60006001600160a01b0384163b15613d5a57604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290613ca8903390899088908890600401615b57565b6020604051808303816000875af1925050508015613ce3575060408051601f3d908101601f19168201909252613ce091810190615b8a565b60015b613d40573d808015613d11576040519150601f19603f3d011682016040523d82523d6000602084013e613d16565b606091505b508051600003613d385760405162461bcd60e51b8152600401610ded90615b05565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050612127565b506001949350505050565b6001600160a01b038216613dbb5760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610ded565b6000818152600260205260409020546001600160a01b031615613e205760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610ded565b613e2c60008383613af3565b6001600160a01b0382166000908152600360205260408120805460019290613e559084906151bd565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6001600160a01b038316613f0e57613f0981600880546000838152600960205260408120829055600182018355919091527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30155565b613f31565b816001600160a01b0316836001600160a01b031614613f3157613f31838261417e565b6001600160a01b038216613f4857610e8e8161421b565b826001600160a01b0316826001600160a01b031614610e8e57610e8e82826142ca565b6000613f7683611981565b60405162525fcd60e91b81526001600160a01b03821660048201529091506000907307152bfde079b5319e5308c43fb1dbc9c76cb4f99063a4bf9a0090602401602060405180830381865afa158015613fd3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ff79190615730565b600f5460405163125bc37160e11b8152600481018390529192506001600160a01b0316906324b786e290602401600060405180830381600087803b15801561403e57600080fd5b505af1158015614052573d6000803e3d6000fd5b5050600f5460405163236313ff60e11b8152600481018590526001600160a01b0390911692506346c627fe9150602401600060405180830381600087803b15801561409c57600080fd5b505af11580156140b0573d6000803e3d6000fd5b5050600f5460405163662c08cd60e01b8152600481018890526001600160a01b03909116925063662c08cd9150602401600060405180830381600087803b1580156140fa57600080fd5b505af115801561410e573d6000803e3d6000fd5b5050600f546040516302fbe78f60e41b8152600481018890526001600160a01b0387811660248301529091169250632fbe78f09150604401600060405180830381600087803b15801561416057600080fd5b505af1158015614174573d6000803e3d6000fd5b5050505050505050565b6000600161418b846119e1565b61419591906151aa565b6000838152600760205260409020549091508082146141e8576001600160a01b03841660009081526006602090815260408083208584528252808320548484528184208190558352600790915290208190555b5060009182526007602090815260408084208490556001600160a01b039094168352600681528383209183525290812055565b60085460009061422d906001906151aa565b6000838152600960205260408120546008805493945090928490811061425557614255615586565b90600052602060002001549050806008838154811061427657614276615586565b60009182526020808320909101929092558281526009909152604080822084905585825281205560088054806142ae576142ae615ba7565b6001900381819060005260206000200160009055905550505050565b60006142d5836119e1565b6001600160a01b039093166000908152600660209081526040808320868452825280832085905593825260079052919091209190915550565b8260028101928215614347579160200282015b8281111561434757825182906143379082615910565b5091602001919060010190614321565b506143539291506143b5565b5090565b604080516101208101825260008082526060602083015290918201908152602001606081526020016060815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160608152602001606081525090565b808211156143535760006143c982826143d2565b506001016143b5565b5080546143de9061505f565b6000825580601f106143ee575050565b601f0160209004906000526020600020908101906112ab91905b808211156143535760008155600101614408565b6001600160e01b0319811681146112ab57600080fd5b60006020828403121561444457600080fd5b81356124e78161441c565b60005b8381101561446a578181015183820152602001614452565b50506000910152565b6000815180845261448b81602086016020860161444f565b601f01601f19169290920160200192915050565b6020815260006124e76020830184614473565b6000602082840312156144c457600080fd5b5035919050565b6001600160a01b03811681146112ab57600080fd5b80356144eb816144cb565b919050565b6000806040838503121561450357600080fd5b823561450e816144cb565b946020939093013593505050565b60006020828403121561452e57600080fd5b81356124e7816144cb565b634e487b7160e01b600052602160045260246000fd5b6008811061456d57634e487b7160e01b600052602160045260246000fd5b9052565b805182526020810151151560208301526040810151604083015260018060a01b0360608201511660608301526080810151608083015260a0810151610e8e60a084018261454f565b60c08101610cba8284614571565b634e487b7160e01b600052604160045260246000fd5b60405161016081016001600160401b0381118282101715614600576146006145c7565b60405290565b604080519081016001600160401b0381118282101715614600576146006145c7565b60405160c081016001600160401b0381118282101715614600576146006145c7565b60405161012081016001600160401b0381118282101715614600576146006145c7565b604051601f8201601f191681016001600160401b0381118282101715614695576146956145c7565b604052919050565b60006001600160401b038211156146b6576146b66145c7565b50601f01601f191660200190565b600082601f8301126146d557600080fd5b81356146e86146e38261469d565b61466d565b8181528460208386010111156146fd57600080fd5b816020850160208301376000918101602001919091529392505050565b60006020828403121561472c57600080fd5b81356001600160401b0381111561474257600080fd5b612127848285016146c4565b60008060006060848603121561476357600080fd5b833561476e816144cb565b9250602084013561477e816144cb565b929592945050506040919091013590565b803560ff811681146144eb57600080fd5b80151581146112ab57600080fd5b80356144eb816147a0565b6000602082840312156147cb57600080fd5b81356001600160401b03808211156147e257600080fd5b9083019061016082860312156147f757600080fd5b6147ff6145dd565b823581526020830135602082015260408301356040820152606083013560608201526080830135608082015260a083013560a082015260c083013560c082015260e083013560e082015261010061485781850161478f565b90820152610120838101358381111561486f57600080fd5b61487b888287016146c4565b82840152505061014091506148918284016147ae565b91810191909152949350505050565b6060815260006148b36060830186614473565b82810360208401526148c58186614473565b905082810360408401526148d98185614473565b9695505050505050565b600060208083850312156148f657600080fd5b82356001600160401b038082111561490d57600080fd5b818501915085601f83011261492157600080fd5b614929614606565b80604084018881111561493b57600080fd5b845b8181101561496f578035858111156149555760008081fd5b6149618b8289016146c4565b85525092860192860161493d565b509098975050505050505050565b600881106112ab57600080fd5b80356144eb8161497d565b600060c082840312156149a757600080fd5b6149af614628565b90508135815260208201356149c3816147a0565b60208201526040828101359082015260608201356149e0816144cb565b60608201526080828101359082015260a08201356149fd8161497d565b60a082015292915050565b60008060e08385031215614a1b57600080fd5b614a258484614995565b9460c0939093013593505050565b604081526000614a466040830185614473565b82810360208401526128388185614473565b60008060e08385031215614a6b57600080fd5b82359150614a7c8460208501614995565b90509250929050565b6020808252825182820181905260009190848201906040850190845b81811015614abd57835183529284019291840191600101614aa1565b50909695505050505050565b60006101208b8352806020840152614ae38184018c614473565b9050614af2604084018b61454f565b8281036060840152614b04818a614473565b90508281036080840152614b188189614473565b6001600160a01b0388811660a0860152871660c085015283810360e08501529050614b438186614473565b9050828103610100840152614b588185614473565b9c9b505050505050505050505050565b60008060408385031215614b7b57600080fd5b8235614b86816144cb565b91506020830135614b96816147a0565b809150509250929050565b60008060008060808587031215614bb757600080fd5b8435614bc2816144cb565b93506020850135614bd2816144cb565b92506040850135915060608501356001600160401b03811115614bf457600080fd5b614c00878288016146c4565b91505092959194509250565b60008060408385031215614c1f57600080fd5b8235915060208301356001600160401b0380821115614c3d57600080fd5b908401906101208287031215614c5257600080fd5b614c5a61464a565b82358152602083013582811115614c7057600080fd5b614c7c888286016146c4565b602083015250614c8e6040840161498a565b6040820152606083013582811115614ca557600080fd5b614cb1888286016146c4565b606083015250608083013582811115614cc957600080fd5b614cd5888286016146c4565b608083015250614ce760a084016144e0565b60a0820152614cf860c084016144e0565b60c082015260e083013582811115614d0f57600080fd5b614d1b888286016146c4565b60e0830152506101008084013583811115614d3557600080fd5b614d41898287016146c4565b8284015250508093505050509250929050565b602081528151602082015260006020830151610120806040850152614d7d610140850183614473565b91506040850151614d91606086018261454f565b506060850151601f1980868503016080870152614dae8483614473565b935060808701519150808685030160a0870152614dcb8483614473565b935060a08701519150614de960c08701836001600160a01b03169052565b60c08701516001600160a01b03811660e0880152915060e08701519150610100818786030181880152614e1c8584614473565b9088015187820390920184880152935090506148d98382614473565b600080600060608486031215614e4d57600080fd5b8335925060208401356001600160401b0380821115614e6b57600080fd5b614e77878388016146c4565b93506040860135915080821115614e8d57600080fd5b50614e9a868287016146c4565b9150509250925092565b60008060408385031215614eb757600080fd5b82356001600160401b0380821115614ece57600080fd5b614eda868387016146c4565b93506020850135915080821115614ef057600080fd5b50614efd858286016146c4565b9150509250929050565b60006020808385031215614f1a57600080fd5b82356001600160401b0380821115614f3157600080fd5b818501915085601f830112614f4557600080fd5b813581811115614f5757614f576145c7565b8060051b9150614f6884830161466d565b8181529183018401918481019088841115614f8257600080fd5b938501935b83851015614fa057843582529385019390850190614f87565b98975050505050505050565b60008060408385031215614fbf57600080fd5b8235614fca816144cb565b91506020830135614b96816144cb565b60008060008060808587031215614ff057600080fd5b8435935060208501356001600160401b038082111561500e57600080fd5b61501a888389016146c4565b9450604087013591508082111561503057600080fd5b61503c888389016146c4565b9350606087013591508082111561505257600080fd5b50614c00878288016146c4565b600181811c9082168061507357607f821691505b60208210810361174a57634e487b7160e01b600052602260045260246000fd5b80516144eb816144cb565b80516144eb8161497d565b600060c082840312156150bb57600080fd5b6150c3614628565b8251815260208301516150d5816147a0565b60208201526040838101519082015260608301516150f2816144cb565b60608201526080838101519082015260a083015161510f8161497d565b60a08201529392505050565b600082601f83011261512c57600080fd5b815161513a6146e38261469d565b81815284602083860101111561514f57600080fd5b61212782602083016020870161444f565b60006020828403121561517257600080fd5b81516001600160401b0381111561518857600080fd5b6121278482850161511b565b634e487b7160e01b600052601160045260246000fd5b81810381811115610cba57610cba615194565b80820180821115610cba57610cba615194565b6000826151ed57634e487b7160e01b600052601260045260246000fd5b500690565b60006001820161520457615204615194565b5060010190565b6020808252602e908201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560408201526d1c881b9bdc88185c1c1c9bdd995960921b606082015260800190565b6000610160825184526020830151602085015260408301516040850152606083015160608501526080830151608085015260a083015160a085015260c083015160c085015260e083015160e0850152610100808401516152bd8287018260ff169052565b50506101208084015182828701526152d783870182614473565b92505050610140808401516152ef8287018215159052565b5090949350505050565b60408152600061530c6040830186615259565b905061212760208301848660201b63ffffffff919091161760401b9052565b60008060006060848603121561534057600080fd5b83516001600160401b038082111561535757600080fd5b6153638783880161511b565b9450602086015191508082111561537957600080fd5b6153858783880161511b565b9350604086015191508082111561539b57600080fd5b50614e9a8682870161511b565b60e081016153b68285614571565b8260c08301529392505050565b600080604083850312156153d657600080fd5b82516001600160401b03808211156153ed57600080fd5b6153f98683870161511b565b9350602085015191508082111561540f57600080fd5b50614efd8582860161511b565b600081546154298161505f565b80855260206001838116801561544657600181146154605761548e565b60ff1985168884015283151560051b88018301955061548e565b866000528260002060005b858110156154865781548a820186015290830190840161546b565b890184019650505b505050505092915050565b6000610120825484528060208501526154b78185016001850161541c565b90506154c7600284015460ff1690565b6154d4604086018261454f565b5083810360608501526154ea816003850161541c565b90508381036080850152615501816004850161541c565b905061551760058401546001600160a01b031690565b6001600160a01b0390811660a086015260068401541660c085015283810360e0850152615547816007850161541c565b9050838103610100850152612127816008850161541c565b600061010061556e8387614571565b8460c08401528060e08401526148d981840185615499565b634e487b7160e01b600052603260045260246000fd5b6000608082840312156155ae57600080fd5b604051608081016001600160401b0382821081831117156155d1576155d16145c7565b81604052845191506001600160801b03821682146155ee57600080fd5b908252602084015190808216821461560557600080fd5b5060208201526040830151615619816147a0565b6040820152606083015161562c816147a0565b60608201529392505050565b6000806000806080858703121561564e57600080fd5b8451615659816144cb565b602086015160408701519195509350615671816144cb565b6060860151909250615682816147a0565b939692955090935050565b600061016088835260206156a4602085018a614571565b8160e08501526156b682850189615499565b91508382036101008501526156cb8288614473565b91508382036101208501526156e08287614473565b848103610140860152915081604081018660005b600281101561571e57858303845261570c838361541c565b938501939250600191820191016156f4565b50909c9b505050505050505050505050565b60006020828403121561574257600080fd5b5051919050565b60006020828403121561575b57600080fd5b81516124e7816144cb565b60e081016157748285614571565b6001600160801b03831660c08301529392505050565b60006020828403121561579c57600080fd5b81516001600160401b03808211156157b357600080fd5b9083019061012082860312156157c857600080fd5b6157d061464a565b825181526020830151828111156157e657600080fd5b6157f28782860161511b565b6020830152506158046040840161509e565b604082015260608301518281111561581b57600080fd5b6158278782860161511b565b60608301525060808301518281111561583f57600080fd5b61584b8782860161511b565b60808301525061585d60a08401615093565b60a082015261586e60c08401615093565b60c082015260e08301518281111561588557600080fd5b6158918782860161511b565b60e08301525061010080840151838111156158ab57600080fd5b6158b78882870161511b565b918301919091525095945050505050565b601f821115610e8e576000816000526020600020601f850160051c810160208610156158f15750805b601f850160051c820191505b8181101561307e578281556001016158fd565b81516001600160401b03811115615929576159296145c7565b61593d81615937845461505f565b846158c8565b602080601f831160018114615972576000841561595a5750858301515b600019600386901b1c1916600185901b17855561307e565b600085815260208120601f198616915b828110156159a157888601518255948401946001909101908401615982565b50858210156159bf5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b8381526060602082015260006159e86060830185614473565b82810360408401526148d98185614473565b6000610140878352806020840152615a1481840188614473565b90508281036040840152615a288187614473565b9050615a376060840186614571565b828103610120840152614fa08185615499565b848152608060208201526000615a636080830186614473565b8281036040840152615a758186614473565b90508281036060840152615a898185614473565b979650505050505050565b6000610160888352806020840152615aae81840189614473565b90508281036040840152615ac28188614473565b90508281036060840152615ad68187614473565b9050615ae56080840186614571565b828103610140840152615af88185615499565b9998505050505050505050565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b6001600160a01b03858116825284166020820152604081018390526080606082018190526000906148d990830184614473565b600060208284031215615b9c57600080fd5b81516124e78161441c565b634e487b7160e01b600052603160045260246000fdfe0000000000000000000000006b8f34e0559aa9a5507e74ad93374d9745cdbf09a2646970667358221220f0fd37db28ac57615de11a9c1fadad4504b5186d6335bb14f2c99d22c56c297a64736f6c63430008160033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.