编译器
0.8.22+commit.4fc1097e
文件 1 的 40:Address.sol
pragma solidity ^0.8.1;
library Address {
function isContract(address account) internal view returns (bool) {
return account.code.length > 0;
}
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");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
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");
}
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);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
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);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
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);
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
文件 2 的 40:ChonkEquipHelper.sol
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);
}
contract ChonkEquipHelper {
IChonksMain public immutable chonksMain;
ChonkTraits public immutable chonkTraits;
error IncorrectTBAOwner();
error IncorrectTraitType();
constructor(address _chonksMain, address _chonkTraits) {
chonksMain = IChonksMain(_chonksMain);
chonkTraits = ChonkTraits(_chonkTraits);
}
function performValidations(
address _tbaForChonk,
uint256 _traitTokenId,
TraitCategory.Name _traitType
) view public {
_validateTBAOwnership(_tbaForChonk, _traitTokenId);
_validateTraitType(_traitTokenId, _traitType);
}
function equipValidation(
uint256 _chonkTokenId,
uint256 _traitTokenId
) view public 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();
}
}
文件 3 的 40:ChonkTraits.sol
pragma solidity ^0.8.22;
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";
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";
import { TraitRenderer } from "./renderers/TraitRenderer.sol";
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);
}
contract ChonkTraits is IERC165, ERC721Enumerable, ERC721Burnable, ITraitStorage, Ownable, IERC4906, ReentrancyGuard {
Traits public traitTokens;
TraitRenderer public traitRenderer;
ChonksMain public chonksMain;
ChonksMarket public marketplace;
mapping(uint256 => TraitMetadata) public traitIndexToMetadata;
mapping(uint256 traitId => address[] operators) public traitIdToApprovedOperators;
mapping (address => bool) public isMinter;
mapping (address => bool) public approvedInvalidators;
uint256 public nextTokenId;
uint256 internal _transientChonkId;
uint256 public initialMintStartTime;
string[2] descriptionParts;
error AddressCantBurn();
error CantTransfer();
error CantTransferDuringMint();
error CantTransferEquipped();
error MintStartTimeAlreadySet();
error NotATBA();
error NotAValidMinterContract();
error NotYourTrait();
error SetChonksMainAddress();
error SetMarketplaceAddress();
error TraitNotFound(uint256 _tokenId);
error TraitTokenDoesntExist();
modifier onlyMinter(address _address) {
if (!isMinter[_address]) revert NotAValidMinterContract();
_;
}
constructor() ERC721("Chonk Traits", "CHONK TRAITS") {
_initializeOwner(msg.sender);
traitRenderer = new TraitRenderer();
}
function getTraitIndexToMetadata(uint256 _traitIndex) public view returns (TraitMetadata memory) {
return traitIndexToMetadata[_traitIndex];
}
function setTraitForTokenId(uint256 _tokenId, ITraitStorage.StoredTrait memory _trait) public onlyMinter(msg.sender) {
traitTokens.all[_tokenId] = _trait;
}
function setTraitIndexToMetadata(uint256 _traitIndex, TraitMetadata memory _metadata) public onlyMinter(msg.sender) {
traitIndexToMetadata[_traitIndex] = _metadata;
}
function safeMint(address _to) public onlyMinter(msg.sender) returns (uint256) {
resolveEpochIfNecessary();
uint tokenId = ++nextTokenId;
_safeMint(_to, tokenId);
return tokenId;
}
function burn(uint256 _tokenId) public override {
if (!isMinter[msg.sender]) revert AddressCantBurn();
_burn(_tokenId);
}
function burnBatch(uint256[] memory tokenIds) public {
if (!isMinter[msg.sender]) revert AddressCantBurn();
for (uint256 i; i < tokenIds.length; ++i) {
_burn(tokenIds[i]);
}
}
function resolveEpochIfNecessary() public {
CommitReveal.Epoch storage currentEpoch = traitTokens.epochs[traitTokens.epoch];
if (
!currentEpoch.committed ||
(!currentEpoch.revealed && currentEpoch.revealBlock < block.number - 256)
) {
currentEpoch.revealBlock = uint64(block.number + 50);
currentEpoch.committed = true;
} else if (block.number > currentEpoch.revealBlock) {
currentEpoch.randomness = uint128(uint256(keccak256(
abi.encodePacked(
blockhash(currentEpoch.revealBlock),
block.prevrandao
))) % (2 ** 128 - 1)
);
currentEpoch.revealed = true;
emit CommitReveal.NewEpoch(traitTokens.epoch, currentEpoch.revealBlock);
emit BatchMetadataUpdate(0, type(uint256).max);
++traitTokens.epoch;
resolveEpochIfNecessary();
}
}
function getEpoch() view public returns(uint256) {
return traitTokens.epoch;
}
function getEpochData(uint256 index) view public returns(CommitReveal.Epoch memory) {
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) {
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);
}
function getTraitMetadata(uint256 _tokenId) public view returns (TraitMetadata memory) {
StoredTrait memory trait = getTrait(_tokenId);
return traitIndexToMetadata[trait.traitIndex];
}
function getStoredTraitForTokenId(uint256 _tokenId) public view returns (ITraitStorage.StoredTrait memory) {
return traitTokens.all[_tokenId];
}
function getCurrentEpoch() public view returns (uint256) {
return traitTokens.epoch;
}
function renderAsDataUri(uint256 _tokenId) public view returns (string memory) {
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) {
StoredTrait memory trait = getTrait(_tokenId);
return string(traitIndexToMetadata[trait.traitIndex].zMap);
}
function getTraitImageSvg(uint256 index) public view returns (string memory svg) {
return traitRenderer.getTraitImageSvg(traitIndexToMetadata[index].colorMap);
}
function getGhostSvg() public view returns (string memory svg) {
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 ) {
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 ) {
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) {
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) {
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;
}
function setChonksMain(address _ChonksMain) public onlyOwner {
chonksMain = ChonksMain(_ChonksMain);
}
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 addApprovedInvalidator(address _invalidator) public onlyOwner {
approvedInvalidators[_invalidator] = true;
}
function removeApprovedInvalidator(address _invalidator) public onlyOwner {
approvedInvalidators[_invalidator] = false;
}
function setMintStartTime(uint256 _initialMintStartTime) public onlyOwner {
if (initialMintStartTime != 0) revert MintStartTimeAlreadySet();
initialMintStartTime = _initialMintStartTime;
}
function setDescriptionParts(string[2] memory _descriptionParts) public onlyOwner {
descriptionParts = _descriptionParts;
}
function supportsInterface(bytes4 interfaceId) public view override(IERC165, ERC721Enumerable, ERC721) returns (bool) {
return super.supportsInterface(interfaceId);
}
function _cleanUpMarketplaceOffersAndBids(uint256 _tokenId, address _to) internal {
address tba = ownerOf(_tokenId);
uint256 chonkId = chonksMain.tbaAddressToTokenId(tba);
marketplace.removeChonkOfferOnTraitTransfer(chonkId);
marketplace.deleteTraitOffersBeforeTokenTransfer(_tokenId);
marketplace.deleteTraitBidsBeforeTokenTransfer(_tokenId, _to);
}
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal override(ERC721, ERC721Enumerable) {
if (from == address(0)) {
super._beforeTokenTransfer(from, to, tokenId);
return;
}
if (block.timestamp < initialMintStartTime + 24 hours) revert CantTransferDuringMint();
(,,, bool isEquipped) = chonksMain.getFullPictureForTrait(tokenId);
if (isEquipped) revert CantTransferEquipped();
(, address seller,,) = marketplace.traitOffers(tokenId);
if (seller != address(0)) {
if (msg.sender != address(marketplace)) revert CantTransfer();
}
if (to == address(0)) {
_cleanUpMarketplaceOffersAndBids(tokenId, to);
address tba = ownerOf(tokenId);
_transientChonkId = chonksMain.tbaAddressToTokenId(tba);
super._beforeTokenTransfer(from, to, tokenId);
return;
}
if (chonksMain.tbaAddressToTokenId(to) == 0) revert NotATBA();
_cleanUpMarketplaceOffersAndBids(tokenId, to);
super._beforeTokenTransfer(from, to, tokenId);
}
function _afterTokenTransfer(address _from , address _to, uint256 _traitTokenId) internal override(ERC721) {
if (address(chonksMain) == address(0)) revert SetChonksMainAddress();
if (address(marketplace) == address(0)) revert SetMarketplaceAddress();
if (_from == address(0)) return;
if (_to == address(0)) {
uint256 id = _transientChonkId;
_transientChonkId = 0;
marketplace.removeChonkOfferOnTraitTransfer(id);
return;
}
address tba = ownerOf(_traitTokenId);
uint256 chonkId = chonksMain.tbaAddressToTokenId(tba);
marketplace.removeChonkOfferOnTraitTransfer(chonkId);
emit BatchMetadataUpdate(0, type(uint256).max);
}
function approve(address _operator, uint256 _tokenId) public override(ERC721, IERC721) {
if (!_exists(_tokenId)) revert TraitTokenDoesntExist();
if (ownerOf(_tokenId) != msg.sender) revert NotYourTrait();
if (_operator == address(0)) {
address[] storage operators = traitIdToApprovedOperators[_tokenId];
for (uint256 i; i < operators.length; ++i) {
if (operators[i] == _operator) {
operators[i] = operators[operators.length - 1];
operators.pop();
break;
}
}
} else {
address[] storage operators = traitIdToApprovedOperators[_tokenId];
bool exists = false;
for (uint256 i; i < operators.length; ++i) {
if (operators[i] == _operator) {
exists = true;
break;
}
}
if (!exists) {
operators.push(_operator);
}
}
super.approve(_operator, _tokenId);
}
function setApprovalForAll(address _operator, bool _approved) public override(ERC721, IERC721) {
require(_operator != msg.sender, "ERC721: approve to caller");
uint256 balance = balanceOf(msg.sender);
for (uint256 i; i < balance; ++i) {
uint256 tokenId = tokenOfOwnerByIndex(msg.sender, i);
if (_approved) {
address[] storage operators = traitIdToApprovedOperators[tokenId];
bool exists = false;
for (uint256 j; j < operators.length; ++j) {
if (operators[j] == _operator) {
exists = true;
break;
}
}
if (!exists) {
operators.push(_operator);
}
} else {
address[] storage operators = traitIdToApprovedOperators[tokenId];
for (uint256 j; j < operators.length; ++j) {
if (operators[j] == _operator) {
operators[j] = operators[operators.length - 1];
operators.pop();
break;
}
}
}
}
super.setApprovalForAll(_operator, _approved);
}
function invalidateAllOperatorApprovals(uint256 _tokenId) public {
if (!_exists(_tokenId)) revert TraitTokenDoesntExist();
if (ownerOf(_tokenId) != msg.sender && msg.sender != address(chonksMain) && !approvedInvalidators[msg.sender])
revert NotYourTrait();
address[] memory operators = traitIdToApprovedOperators[_tokenId];
if (operators.length == 0) return;
_approve(address(0), _tokenId);
for (uint256 i; i < operators.length; ++i) {
_setApprovalForAll(ownerOf(_tokenId), operators[i], false);
}
delete traitIdToApprovedOperators[_tokenId];
emit ITraitStorage.AllOperatorApprovalsInvalidated(_tokenId);
}
function getApprovedOperators(uint256 traitId) public view returns (address[] memory) {
return traitIdToApprovedOperators[traitId];
}
}
文件 4 的 40:ChonksMain.sol
pragma solidity ^0.8.22;
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";
import { IAccountImplementation } from "./interfaces/TBABoilerplate/IAccountImplementation.sol";
import { IAccountProxy } from "./interfaces/TBABoilerplate/IAccountProxy.sol";
import { IRegistry } from "./interfaces/TBABoilerplate/IRegistry.sol";
import { MainRenderer2D } from "./renderers/MainRenderer2D.sol";
import { MainRenderer3D } from "./renderers/MainRenderer3D.sol";
import { ChonkTraits } from "./ChonkTraits.sol";
import { ChonkEquipHelper } from "./ChonkEquipHelper.sol";
import { IERC4906 } from "./interfaces/IERC4906.sol";
import { IChonkStorage } from "./interfaces/IChonkStorage.sol";
import { ITraitStorage } from "./interfaces/ITraitStorage.sol";
import { TraitCategory } from "./TraitCategory.sol";
import { ChonksMarket } from "./ChonksMarket.sol";
import { FirstReleaseDataMinter } from "./FirstReleaseDataMinter.sol";
contract ChonksMain is IChonkStorage, IERC165, ERC721Enumerable, Ownable, IERC4906, ReentrancyGuard {
IRegistry constant REGISTRY = IRegistry(0x000000006551c19487814612e58FE06813775758);
address constant ACCOUNT_PROXY = 0x55266d75D1a14E4572138116aF39863Ed6596E7F;
address constant ACCOUNT_IMPLEMENTATION = 0x41C8f39463A868d3A88af00cd0fe7102F30E44eC;
uint8 constant MAX_MINT_AMOUNT = 10;
mapping(uint256 => IChonkStorage.BodyMetadata) public bodyIndexToMetadata;
ChonkTraits public traitsContract;
ChonksMarket public marketplace;
ChonkEquipHelper public chonkEquipHelper;
FirstReleaseDataMinter public firstReleaseDataMinter;
MainRenderer2D public mainRenderer2D;
MainRenderer3D public mainRenderer3D;
uint256 public maxTraitsToOutput = 99;
uint256 public nextTokenId;
address public withdrawAddress;
uint256 public price;
uint256 public initialMintStartTime;
string[2] descriptionParts;
uint256 public immutable deploymentTime;
mapping(uint256 tokenID => StoredChonk chonk) public chonkTokens;
mapping(uint256 => address) public tokenIdToTBAAccountAddress;
mapping(address => uint256) public tbaAddressToTokenId;
mapping(uint256 chonkId => address[] operators) public chonkIdToApprovedOperators;
mapping(address => bool) public collectionsAddressDidUse;
mapping(address => bool) public friendsAddressDidUse;
mapping(address => bool) public creatorsAddressDidUse;
bytes32 public collectionsMerkle;
bytes32 public friendsMerkle;
bytes32 public creatorsMerkle;
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 onlyChonkOwner(uint256 _chonkId) {
if (msg.sender != ownerOf(_chonkId)) revert IncorrectChonkOwner();
_;
}
constructor() ERC721("Chonks", "CHONKS") {
_initializeOwner(msg.sender);
deploymentTime = block.timestamp;
}
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,
0,
8453,
address(this),
tokenId
);
tokenIdToTBAAccountAddress[tokenId] = tokenBoundAccountAddress;
tbaAddressToTokenId[tokenBoundAccountAddress] = tokenId;
IAccountProxy(payable(tokenBoundAccountAddress)).initialize(address(ACCOUNT_IMPLEMENTATION));
uint256[] memory traitsIds = firstReleaseDataMinter.safeMintMany(tokenBoundAccountAddress, _traitCount);
StoredChonk storage chonk = chonkTokens[tokenId];
chonk.tokenId = tokenId;
chonk.bodyIndex = uint8(uint256(keccak256(abi.encodePacked(tokenId))) % 5);
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];
}
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);
}
function tokenURI(uint256 _tokenId) public view override returns (string memory) {
if (!_exists(_tokenId)) revert ChonkDoesntExist();
return renderAsDataUri(_tokenId);
}
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)
);
}
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);
}
function getTBAAddressForChonkId(uint256 _chonkId) public view returns (address) {
return tokenIdToTBAAccountAddress[_chonkId];
}
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;
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);
}
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;
}
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;
}
function oneYearFromDeployment() public view returns (uint256) {
return deploymentTime + 365 days;
}
function isTimelocked() public view returns (bool) {
return block.timestamp > oneYearFromDeployment();
}
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;
}
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();
for (uint i; i < 6; i++) {
if (
!(colorBytes[i] >= 0x30 && colorBytes[i] <= 0x39) &&
!(colorBytes[i] >= 0x41 && colorBytes[i] <= 0x46) &&
!(colorBytes[i] >= 0x61 && colorBytes[i] <= 0x66)
) {
revert InvalidColor();
}
}
}
function setBackgroundColor(uint256 _chonkTokenId, string memory _color) public onlyChonkOwner(_chonkTokenId) {
validateColor(_color);
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);
if (_bodyIndex > 4) revert InvalidBodyIndex();
chonkTokens[_tokenId].backgroundColor = _color;
chonkTokens[_tokenId].bodyIndex = _bodyIndex;
chonkTokens[_tokenId].render3D = _render3D;
}
function supportsInterface(bytes4 interfaceId) public view override(IERC165, ERC721Enumerable) returns (bool) {
return super.supportsInterface(interfaceId);
}
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();
if (tbaAddressToTokenId[to] != 0) revert CantTransferToTBAs();
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];
marketplace.deleteTraitOffersBeforeTokenTransfer(traitTokenId);
marketplace.deleteTraitBidsBeforeTokenTransfer(traitTokenId, tbas);
traitsContract.invalidateAllOperatorApprovals(traitTokenId);
}
super._beforeTokenTransfer(from, to, tokenId);
}
function _afterTokenTransfer(address _from, address, uint256 _tokenId) internal virtual override {
_invalidateAllOperatorApprovals(_tokenId, _from);
}
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);
}
function setApprovalForAll(address _operator, bool _approved) public override(IERC721, ERC721) {
if (_approved) {
uint256[] memory chonkIds = walletOfOwner(msg.sender);
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);
}
function _invalidateAllOperatorApprovals(uint256 _chonkId, address _previousOwner) private {
address[] memory approvals = chonkIdToApprovedOperators[_chonkId];
address tbaForChonk = tokenIdToTBAAccountAddress[_chonkId];
for (uint256 i; i < approvals.length; ++i) {
_setApprovalForAll(_previousOwner, approvals[i], false);
_setApprovalForAll(tbaForChonk, approvals[i], false);
}
delete chonkIdToApprovedOperators[_chonkId];
}
function withdraw() public onlyOwner {
(bool success,) = payable(withdrawAddress).call{ value: address(this).balance }("");
if (!success) revert WithdrawFailed();
}
}
文件 5 的 40:ChonksMarket.sol
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 {
struct ChonkOffer {
uint256 priceInWei;
address seller;
address sellerTBA;
address onlySellTo;
uint256[] traitIds;
bytes encodedTraitIds;
}
struct TraitOffer {
uint256 priceInWei;
address seller;
address sellerTBA;
address onlySellTo;
}
struct ChonkBid {
address bidder;
uint256 amountInWei;
uint256[] traitIds;
bytes encodedTraitIds;
}
struct TraitBid {
address bidder;
address bidderTBA;
uint256 amountInWei;
}
ChonksMain public immutable CHONKS_MAIN;
ChonkTraits public immutable CHONK_TRAITS;
uint256 public royaltyPercentage;
address public teamWallet;
bool public paused = true;
bool public pausabilityRevoked;
mapping(uint256 chonkId => ChonkOffer chonkOffer) public chonkOffers;
mapping(uint256 traitId => TraitOffer traitOffer) public traitOffers;
mapping(uint256 chonkId => ChonkBid chonkBid) public chonkBids;
mapping(uint256 traitId => TraitBid traitBid) public traitBids;
mapping(address eoa => uint256 balance) public withdrawableFunds;
mapping(uint256 chonkId => address[] operators)
public chonkIdToApprovedOperators;
error ApproveTheMarketplace();
error BidderChanged();
error BidIsTooLow();
error CantAcceptYourOwnBid();
error CantBeZero();
error CantBidOnYourOwnChonk();
error CantBidOnYourOwnTrait();
error CantBuyYourOwnChonk();
error CantBuyYourOwnTrait();
error CMUnauthorized();
error NoBidToAccept();
error NoOfferToCancel();
error NotYourBid();
error NotYourChonk();
error NotYourOffer();
error NotYourTrait();
error OfferDoesNotExist();
error OnlySellToEOAs();
error OnlyTraitContract();
error Paused();
error PausabilityRevoked();
error TBANeedsToApproveMarketplace();
error TraitEquipped();
error TraitIdsChangedSinceBid();
error TraitIdsChangedSinceListingRelist();
error WithdrawFailed();
error WrongAmount();
error YouCantBuyThatChonk();
error YouCantBuyThatTrait();
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
);
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
);
modifier ensurePriceIsNotZero(uint256 _price) {
if (_price == 0) revert CantBeZero();
_;
}
modifier notPaused() {
if (paused) revert Paused();
_;
}
modifier onlyTraitContract() {
if (msg.sender != address(CHONK_TRAITS)) revert OnlyTraitContract();
_;
}
modifier onlyMainContract() {
if (msg.sender != address(CHONKS_MAIN)) revert CMUnauthorized();
_;
}
constructor(
address _chonksMain,
address _chonkTraits,
uint8 _royaltyPercentage,
address _teamWallet
) {
_initializeOwner(msg.sender);
CHONKS_MAIN = ChonksMain(_chonksMain);
CHONK_TRAITS = ChonkTraits(_chonkTraits);
royaltyPercentage = _royaltyPercentage;
teamWallet = _teamWallet;
}
function getChonkOffer(uint256 _chonkId) public view returns (
uint256 priceInWei,
address seller,
address sellerTBA,
address onlySellTo,
uint256[] memory traitIds,
bytes memory encodedTraitIds
) {
ChonkOffer memory offer = chonkOffers[_chonkId];
return (
offer.priceInWei,
offer.seller,
offer.sellerTBA,
offer.onlySellTo,
offer.traitIds,
offer.encodedTraitIds
);
}
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[] memory traitIds,
bytes memory encodedTraitIds
) {
ChonkBid memory bid = chonkBids[_chonkId];
return (
bid.bidder,
bid.amountInWei,
bid.traitIds,
bid.encodedTraitIds
);
}
function getTraitBid(uint256 _traitId) public view returns (
address bidder,
address bidderTBA,
uint256 amountInWei
) {
TraitBid memory bid = traitBids[_traitId];
return (
bid.bidder,
bid.bidderTBA,
bid.amountInWei
);
}
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) {
(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) {
(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 {
if (_seller != msg.sender) revert NotYourChonk();
(uint256[] memory traitIds , bytes memory encodedTraitIds) = getTraitIdsAndEncodingForChonk(_chonkId);
chonkOffers[_chonkId] = ChonkOffer({
priceInWei: _priceInWei,
seller: _seller,
sellerTBA: _sellerTBA,
onlySellTo: _onlySellTo,
traitIds: traitIds,
encodedTraitIds: encodedTraitIds
});
}
function buyChonk(uint256 _chonkId) public payable notPaused nonReentrant {
ChonkOffer memory offer = chonkOffers[_chonkId];
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();
if (offer.priceInWei != msg.value) revert WrongAmount();
if (!CHONKS_MAIN.isApprovedForAll(offer.seller, address(this)) && CHONKS_MAIN.getApproved(_chonkId) != address(this))
revert ApproveTheMarketplace();
(, bytes memory encodedTraitIds) = getTraitIdsAndEncodingForChonk(_chonkId);
if (keccak256(encodedTraitIds) != keccak256(offer.encodedTraitIds))
revert TraitIdsChangedSinceListingRelist();
delete chonkOffers[_chonkId];
uint256 refundAmount = 0;
ChonkBid memory existingBid = chonkBids[_chonkId];
if (existingBid.bidder == msg.sender) {
delete chonkBids[_chonkId];
refundAmount = existingBid.amountInWei;
}
if (refundAmount > 0)
_refundBid(existingBid.bidder, refundAmount);
CHONKS_MAIN.transferFrom(offer.seller, msg.sender, _chonkId);
_calculateRoyaltiesAndTransferFunds(msg.value, seller);
emit ChonkBought(_chonkId, msg.sender, msg.value, seller);
}
function withdrawBidOnChonk(uint256 _chonkId) public nonReentrant {
ChonkBid memory bid = chonkBids[_chonkId];
if (bid.bidder != msg.sender) revert NotYourBid();
delete chonkBids[_chonkId];
_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 (msg.value <= existingBid.amountInWei) revert BidIsTooLow();
(uint256[] memory traitIds , bytes memory encodedTraitIds) = getTraitIdsAndEncodingForChonk(_chonkId);
chonkBids[_chonkId] = ChonkBid(
msg.sender,
msg.value,
traitIds,
encodedTraitIds
);
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 {
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();
(, bytes memory encodedTraitIds) = getTraitIdsAndEncodingForChonk(_chonkId);
if (keccak256(encodedTraitIds) != keccak256(bid.encodedTraitIds))
revert TraitIdsChangedSinceListingRelist();
delete chonkBids[_chonkId];
_calculateRoyaltiesAndTransferFunds(bid.amountInWei, owner);
CHONKS_MAIN.transferFrom(msg.sender, bidder, _chonkId);
emit ChonkBidAccepted(_chonkId, bid.amountInWei, bidder, owner);
}
function cancelOfferTrait(uint256 _traitId, uint256 _chonkId) public {
if (!ensureTraitOwner(_traitId, _chonkId)) revert NotYourTrait();
address seller = traitOffers[_traitId].seller;
if (seller == address(0)) revert NoOfferToCancel();
if (seller != msg.sender) revert NotYourOffer();
delete traitOffers[_traitId];
emit TraitOfferCanceled(_traitId, msg.sender);
}
function offerTrait(
uint256 _traitId,
uint256 _chonkId,
uint256 _priceInWei
) public notPaused ensurePriceIsNotZero(_priceInWei) {
if (!ensureTraitOwner(_traitId, _chonkId)) revert NotYourTrait();
if (CHONKS_MAIN.checkIfTraitIsEquipped(_chonkId, _traitId))
revert TraitEquipped();
address tbaTraitOwner = CHONK_TRAITS.ownerOf(_traitId);
(address tokenOwner, ) = CHONKS_MAIN.getOwnerAndTBAAddressForChonkId(
_chonkId
);
traitOffers[_traitId] = TraitOffer(
_priceInWei,
tokenOwner,
tbaTraitOwner,
address(0)
);
emit TraitOffered(_traitId, _priceInWei, tokenOwner, tbaTraitOwner);
}
function offerTraitToAddress(
uint256 _traitId,
uint256 _chonkId,
uint256 _priceInWei,
address _onlySellTo
) public notPaused ensurePriceIsNotZero(_priceInWei) {
if (!ensureTraitOwner(_traitId, _chonkId)) revert NotYourTrait();
if (CHONKS_MAIN.tbaAddressToTokenId(_onlySellTo) != 0) revert OnlySellToEOAs();
if (CHONKS_MAIN.checkIfTraitIsEquipped(_chonkId, _traitId))
revert TraitEquipped();
address tbaTraitOwner = CHONK_TRAITS.ownerOf(_traitId);
(address tokenOwner, ) = CHONKS_MAIN.getOwnerAndTBAAddressForChonkId(_chonkId);
traitOffers[_traitId] = TraitOffer(
_priceInWei,
tokenOwner,
tbaTraitOwner,
_onlySellTo
);
emit TraitOfferedToAddress(_traitId, _priceInWei, tokenOwner, tbaTraitOwner, _onlySellTo);
}
function buyTrait(uint256 _traitId, uint256 _forChonkId) public payable notPaused nonReentrant {
address owner = CHONKS_MAIN.ownerOf(_forChonkId);
if (owner != msg.sender) revert NotYourChonk();
address tba = CHONKS_MAIN.tokenIdToTBAAccountAddress(_forChonkId);
address traitOwnerTBAAddress = CHONK_TRAITS.ownerOf(_traitId);
if (traitOwnerTBAAddress == tba) revert CantBuyYourOwnTrait();
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();
if (offer.priceInWei != msg.value) revert WrongAmount();
(,,, bool isEquipped) = CHONKS_MAIN.getFullPictureForTrait(_traitId);
if (isEquipped) revert TraitEquipped();
delete traitOffers[_traitId];
uint256 refundAmount = 0;
TraitBid memory existingBid = traitBids[_traitId];
if (existingBid.bidder == msg.sender) {
delete traitBids[_traitId];
refundAmount = existingBid.amountInWei;
}
if (refundAmount > 0)
_refundBid(existingBid.bidder, refundAmount);
CHONK_TRAITS.transferFrom(offer.sellerTBA, tba, _traitId);
_calculateRoyaltiesAndTransferFunds(msg.value, seller);
emit TraitBought(_traitId, tba, msg.value, msg.sender, seller);
}
function withdrawBidOnTrait(uint256 _traitId) public nonReentrant {
TraitBid memory bid = traitBids[_traitId];
if (bid.bidder != msg.sender) revert NotYourBid();
delete traitBids[_traitId];
_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);
if (chonkOwner != msg.sender) revert NotYourChonk();
(address traitOwnerTBA, , address traitChonkOwner, ) = CHONKS_MAIN.getFullPictureForTrait(_traitId);
if (traitChonkOwner == msg.sender || traitOwnerTBA == msg.sender) revert CantBidOnYourOwnTrait();
TraitBid memory existingBid = traitBids[_traitId];
if (msg.value <= existingBid.amountInWei) revert BidIsTooLow();
traitBids[_traitId] = TraitBid(msg.sender, tbaAddressOfBiddersChonk, msg.value);
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 {
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 traitOffers[_traitId];
delete traitBids[_traitId];
_calculateRoyaltiesAndTransferFunds(bid.amountInWei, seller);
CHONK_TRAITS.transferFrom(sellerTBA, bid.bidderTBA, _traitId);
emit TraitBidAccepted(_traitId, bid.amountInWei, bidder, seller);
}
function ensureTraitOwner(uint256 _traitId, uint256 _chonkId) public view returns (bool) {
address traitOwnerTBA = CHONK_TRAITS.ownerOf(_traitId);
(address chonkOwner, address tbaForChonkId) = CHONKS_MAIN.getOwnerAndTBAAddressForChonkId(_chonkId);
return (traitOwnerTBA == tbaForChonkId) && (chonkOwner == msg.sender);
}
function calculateRoyalty(uint256 _amount) public view returns (uint256) {
return (_amount * royaltyPercentage) / 10_000;
}
function getTraitIdsAndEncodingForChonk(uint256 _chonkId) public view returns (uint256[] memory traitIds, bytes memory encodedTraitIds) {
(, address tbaAddress) = CHONKS_MAIN.getOwnerAndTBAAddressForChonkId(_chonkId);
traitIds = CHONK_TRAITS.walletOfOwner(tbaAddress);
encodedTraitIds = abi.encode(traitIds);
}
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);
}
}
function deleteTraitOffersBeforeTokenTransfer(uint256 _traitId) public {
if (msg.sender != address(CHONKS_MAIN) && msg.sender != address(CHONK_TRAITS)) revert CMUnauthorized();
if (traitOffers[_traitId].seller != address(0)) delete traitOffers[_traitId];
}
function deleteTraitBidsBeforeTokenTransfer(uint256 _traitId, address[] memory _toTBAs) public {
if (msg.sender != address(CHONKS_MAIN)) revert CMUnauthorized();
TraitBid memory bid = traitBids[_traitId];
if (bid.bidder != address(0)) {
for (uint256 i; i < _toTBAs.length; ++i) {
address toTBA = _toTBAs[i];
if (bid.bidderTBA == toTBA) {
delete traitBids[_traitId];
_refundBid(bid.bidder, bid.amountInWei);
}
}
}
}
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 onlyTraitContract {
delete chonkOffers[_chonkId];
}
function withdrawFunds() public nonReentrant {
uint256 balance = withdrawableFunds[msg.sender];
withdrawableFunds[msg.sender] = 0;
(bool success, ) = msg.sender.call{value: balance, gas: 60_000}("");
if (!success) revert WithdrawFailed();
}
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: 60_000}("");
if (!success) withdrawableFunds[_to] += amountForSeller;
}
function _refundBid(address _to, uint256 _amount) private {
(bool success, ) = payable(_to).call{value: _amount, gas: 60_000}("");
if (!success) withdrawableFunds[_to] += _amount;
}
function setRoyaltyPercentage(uint256 _royaltyPercentage) public onlyOwner {
royaltyPercentage = _royaltyPercentage;
}
function setTeamWallet(address _teamWallet) public onlyOwner {
teamWallet = _teamWallet;
}
function pause(bool _value) public onlyOwner {
if (pausabilityRevoked) revert PausabilityRevoked();
paused = _value;
}
function revokePausability() public onlyOwner {
if (pausabilityRevoked) revert PausabilityRevoked();
pausabilityRevoked = true;
}
}
文件 6 的 40:CommitReveal.sol
pragma solidity ^0.8.17;
library CommitReveal {
event NewEpoch(uint256 indexed epoch, uint64 indexed revealBlock);
struct Epoch {
uint128 randomness;
uint64 revealBlock;
bool committed;
bool revealed;
}
}
文件 7 的 40:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 8 的 40:DynamicBuffer.sol
pragma solidity ^0.8.22;
library DynamicBuffer {
function allocate(uint256 capacity_)
internal
pure
returns (bytes memory buffer)
{
assembly {
let container := mload(0x40)
{
let size := add(capacity_, 0x60)
let newNextFree := add(container, size)
mstore(0x40, newNextFree)
}
{
let length := add(capacity_, 0x40)
mstore(container, length)
}
buffer := add(container, 0x20)
mstore(buffer, 0)
}
return buffer;
}
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)
} {
mstore(copyTo, mload(data))
}
mstore(buffer, add(mload(buffer), length))
}
}
function appendSafe(bytes memory buffer, bytes memory data) internal pure {
checkOverflow(buffer, data.length);
appendUnchecked(buffer, data);
}
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 {
encodedLength := shl(2, div(add(dataLength, 2), 3))
r := mod(dataLength, 3)
if noPadding {
encodedLength := sub(
encodedLength,
add(iszero(iszero(r)), eq(r, 1))
)
}
}
checkOverflow(buffer, encodedLength);
assembly {
let nextFree := mload(0x40)
mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
mstore(
0x3f,
sub(
"ghijklmnopqrstuvwxyz0123456789-_",
mul(iszero(fileSafe), 0x0230)
)
)
let ptr := add(add(buffer, 0x20), mload(buffer))
let end := add(data, dataLength)
for {} 1 {} {
data := add(data, 3)
let input := mload(data)
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)
if iszero(lt(data, end)) { break }
}
if iszero(noPadding) {
mstore8(sub(ptr, iszero(iszero(r))), 0x3d)
mstore8(sub(ptr, shl(1, eq(r, 1))), 0x3d)
}
mstore(buffer, add(mload(buffer), encodedLength))
mstore(0x40, nextFree)
}
}
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 {
encodedLength := shl(2, div(add(dataLength, 2), 3))
r := mod(dataLength, 3)
if noPadding {
encodedLength := sub(
encodedLength,
add(iszero(iszero(r)), eq(r, 1))
)
}
}
assembly {
let nextFree := mload(0x40)
mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
mstore(
0x3f,
sub(
"ghijklmnopqrstuvwxyz0123456789-_",
mul(iszero(fileSafe), 0x0230)
)
)
let ptr := add(add(buffer, 0x20), mload(buffer))
let end := add(data, dataLength)
for {} 1 {} {
data := add(data, 3)
let input := mload(data)
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)
if iszero(lt(data, end)) { break }
}
if iszero(noPadding) {
mstore8(sub(ptr, iszero(iszero(r))), 0x3d)
mstore8(sub(ptr, shl(1, eq(r, 1))), 0x3d)
}
mstore(buffer, add(mload(buffer), encodedLength))
mstore(0x40, nextFree)
}
}
function capacity(bytes memory buffer) internal pure returns (uint256) {
uint256 cap;
assembly {
cap := sub(mload(sub(buffer, 0x20)), 0x40)
}
return cap;
}
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.");
}
}
}
文件 9 的 40:ERC165.sol
pragma solidity ^0.8.0;
import "./IERC165.sol";
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
文件 10 的 40: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";
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
using Address for address;
using Strings for uint256;
string private _name;
string private _symbol;
mapping(uint256 => address) private _owners;
mapping(address => uint256) private _balances;
mapping(uint256 => address) private _tokenApprovals;
mapping(address => mapping(address => bool)) private _operatorApprovals;
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
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];
}
function ownerOf(uint256 tokenId) public view virtual override returns (address) {
address owner = _owners[tokenId];
require(owner != address(0), "ERC721: invalid token ID");
return owner;
}
function name() public view virtual override returns (string memory) {
return _name;
}
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
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())) : "";
}
function _baseURI() internal view virtual returns (string memory) {
return "";
}
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);
}
function getApproved(uint256 tokenId) public view virtual override returns (address) {
_requireMinted(tokenId);
return _tokenApprovals[tokenId];
}
function setApprovalForAll(address operator, bool approved) public virtual override {
_setApprovalForAll(_msgSender(), operator, approved);
}
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
return _operatorApprovals[owner][operator];
}
function transferFrom(
address from,
address to,
uint256 tokenId
) public virtual override {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner nor approved");
_transfer(from, to, tokenId);
}
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) public virtual override {
safeTransferFrom(from, to, tokenId, "");
}
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);
}
function _safeTransfer(
address from,
address to,
uint256 tokenId,
bytes memory data
) internal virtual {
_transfer(from, to, tokenId);
require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
}
function _exists(uint256 tokenId) internal view virtual returns (bool) {
return _owners[tokenId] != address(0);
}
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
address owner = ERC721.ownerOf(tokenId);
return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
}
function _safeMint(address to, uint256 tokenId) internal virtual {
_safeMint(to, tokenId, "");
}
function _safeMint(
address to,
uint256 tokenId,
bytes memory data
) internal virtual {
_mint(to, tokenId);
require(
_checkOnERC721Received(address(0), to, tokenId, data),
"ERC721: transfer to non ERC721Receiver implementer"
);
}
function _mint(address to, uint256 tokenId) internal virtual {
require(to != address(0), "ERC721: mint to the zero address");
require(!_exists(tokenId), "ERC721: token already minted");
_beforeTokenTransfer(address(0), to, tokenId);
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(address(0), to, tokenId);
_afterTokenTransfer(address(0), to, tokenId);
}
function _burn(uint256 tokenId) internal virtual {
address owner = ERC721.ownerOf(tokenId);
_beforeTokenTransfer(owner, address(0), tokenId);
_approve(address(0), tokenId);
_balances[owner] -= 1;
delete _owners[tokenId];
emit Transfer(owner, address(0), tokenId);
_afterTokenTransfer(owner, address(0), tokenId);
}
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);
_approve(address(0), tokenId);
_balances[from] -= 1;
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
_afterTokenTransfer(from, to, tokenId);
}
function _approve(address to, uint256 tokenId) internal virtual {
_tokenApprovals[tokenId] = to;
emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
}
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);
}
function _requireMinted(uint256 tokenId) internal view virtual {
require(_exists(tokenId), "ERC721: invalid token ID");
}
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 {
assembly {
revert(add(32, reason), mload(reason))
}
}
}
} else {
return true;
}
}
function _beforeTokenTransfer(
address from,
address to,
uint256 tokenId
) internal virtual {}
function _afterTokenTransfer(
address from,
address to,
uint256 tokenId
) internal virtual {}
}
文件 11 的 40:ERC721Burnable.sol
pragma solidity ^0.8.0;
import "../ERC721.sol";
import "../../../utils/Context.sol";
abstract contract ERC721Burnable is Context, ERC721 {
function burn(uint256 tokenId) public virtual {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner nor approved");
_burn(tokenId);
}
}
文件 12 的 40:ERC721Enumerable.sol
pragma solidity ^0.8.0;
import "../ERC721.sol";
import "./IERC721Enumerable.sol";
abstract contract ERC721Enumerable is ERC721, IERC721Enumerable {
mapping(address => mapping(uint256 => uint256)) private _ownedTokens;
mapping(uint256 => uint256) private _ownedTokensIndex;
uint256[] private _allTokens;
mapping(uint256 => uint256) private _allTokensIndex;
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) {
return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId);
}
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];
}
function totalSupply() public view virtual override returns (uint256) {
return _allTokens.length;
}
function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds");
return _allTokens[index];
}
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);
}
}
function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
uint256 length = ERC721.balanceOf(to);
_ownedTokens[to][length] = tokenId;
_ownedTokensIndex[tokenId] = length;
}
function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
_allTokensIndex[tokenId] = _allTokens.length;
_allTokens.push(tokenId);
}
function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
uint256 lastTokenIndex = ERC721.balanceOf(from) - 1;
uint256 tokenIndex = _ownedTokensIndex[tokenId];
if (tokenIndex != lastTokenIndex) {
uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
_ownedTokens[from][tokenIndex] = lastTokenId;
_ownedTokensIndex[lastTokenId] = tokenIndex;
}
delete _ownedTokensIndex[tokenId];
delete _ownedTokens[from][lastTokenIndex];
}
function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
uint256 lastTokenIndex = _allTokens.length - 1;
uint256 tokenIndex = _allTokensIndex[tokenId];
uint256 lastTokenId = _allTokens[lastTokenIndex];
_allTokens[tokenIndex] = lastTokenId;
_allTokensIndex[lastTokenId] = tokenIndex;
delete _allTokensIndex[tokenId];
_allTokens.pop();
}
}
文件 13 的 40:EncodeURI.sol
pragma solidity ^0.8.17;
contract EncodeURI {
bytes internal constant TABLE = "0123456789ABCDEF";
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);
}
}
文件 14 的 40:FirstReleaseDataMinter.sol
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];
address public chonksMain;
ChonkTraits public chonkTraits;
error OnlyChonksMain();
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) {
uint256 tokenId = chonkTraits.safeMint(_toTBA);
mintedIds[i] = tokenId;
ITraitStorage.StoredTrait memory trait = chonkTraits.getStoredTraitForTokenId(tokenId);
trait.epoch = chonkTraits.getCurrentEpoch();
trait.seed = tokenId;
trait.dataMinterContract = address(this);
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 {
trait.traitType = TraitCategory.Name.Accessory;
}
chonkTraits.setTraitForTokenId(tokenId, trait);
}
return mintedIds;
}
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);
}
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;
}
}
return 0;
}
function explainTrait(
ITraitStorage.StoredTrait memory storedTrait,
uint128 randomness
) view public returns (ITraitStorage.StoredTrait memory) {
uint256 n;
storedTrait.seed = uint256(keccak256(abi.encodePacked(randomness, storedTrait.seed))) % type(uint256).max;
storedTrait.isRevealed = randomness > 0;
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);
}
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 );
}
}
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 );
}
}
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;
}
}
文件 15 的 40:IAccountImplementation.sol
pragma solidity ^0.8.22;
interface IAccountImplementation {
function owner() external view returns (address);
}
文件 16 的 40:IAccountProxy.sol
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);
}
文件 17 的 40:IChonkStorage.sol
pragma solidity ^0.8.22;
interface IChonkStorage {
struct StoredChonk {
uint256 tokenId;
uint256 headId;
uint256 hairId;
uint256 faceId;
uint256 accessoryId;
uint256 topId;
uint256 bottomId;
uint256 shoesId;
uint8 bodyIndex;
string backgroundColor;
bool render3D;
}
struct BodyMetadata {
uint256 bodyIndex;
string bodyName;
bytes colorMap;
bytes zMap;
}
struct ChonkData {
string backgroundColor;
string bodyName;
uint256 numOfItemsInBackpack;
string[2] descriptionParts;
}
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);
}
文件 18 的 40:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 19 的 40:IERC4906.sol
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
interface IERC4906 is IERC165, IERC721 {
event MetadataUpdate(uint256 _tokenId);
event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
}
文件 20 的 40:IERC721.sol
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
interface IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
function approve(address to, uint256 tokenId) external;
function setApprovalForAll(address operator, bool _approved) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
文件 21 的 40:IERC721Enumerable.sol
pragma solidity ^0.8.0;
import "../IERC721.sol";
interface IERC721Enumerable is IERC721 {
function totalSupply() external view returns (uint256);
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
function tokenByIndex(uint256 index) external view returns (uint256);
}
文件 22 的 40:IERC721Metadata.sol
pragma solidity ^0.8.0;
import "../IERC721.sol";
interface IERC721Metadata is IERC721 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function tokenURI(uint256 tokenId) external view returns (string memory);
}
文件 23 的 40:IERC721Receiver.sol
pragma solidity ^0.8.0;
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
文件 24 的 40:IRegistry.sol
pragma solidity ^0.8.22;
interface IRegistry {
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);
}
文件 25 的 40:IScriptyBuilderV2.sol
pragma solidity ^0.8.22;
import "./IScriptyHTML.sol";
import "./IScriptyHTMLURLSafe.sol";
interface IScriptyBuilderV2 is IScriptyHTML, IScriptyHTMLURLSafe {}
文件 26 的 40:IScriptyContractStorage.sol
pragma solidity ^0.8.22;
interface IScriptyContractStorage {
function getContent(string calldata name, bytes memory data)
external
view
returns (bytes memory script);
}
文件 27 的 40:IScriptyHTML.sol
pragma solidity ^0.8.22;
import {HTMLRequest, HTMLTagType, HTMLTag} from "./../core/ScriptyCore.sol";
interface IScriptyHTML {
function getHTML(
HTMLRequest memory htmlRequest
) external view returns (bytes memory);
function getEncodedHTML(
HTMLRequest memory htmlRequest
) external view returns (bytes memory);
function getHTMLString(
HTMLRequest memory htmlRequest
) external view returns (string memory);
function getEncodedHTMLString(
HTMLRequest memory htmlRequest
) external view returns (string memory);
}
文件 28 的 40:IScriptyHTMLURLSafe.sol
pragma solidity ^0.8.22;
import {HTMLRequest, HTMLTagType, HTMLTag} from "./../core/ScriptyCore.sol";
interface IScriptyHTMLURLSafe {
function getHTMLURLSafe(
HTMLRequest memory htmlRequest
) external view returns (bytes memory);
function getHTMLURLSafeString(
HTMLRequest memory htmlRequest
) external view returns (string memory);
}
文件 29 的 40:ITraitStorage.sol
pragma solidity ^0.8.22;
import { CommitReveal } from "../common/CommitReveal.sol";
import { TraitCategory } from "../TraitCategory.sol";
interface ITraitStorage {
struct StoredTrait {
uint256 epoch;
bool isRevealed;
uint256 seed;
address dataMinterContract;
uint256 traitIndex;
TraitCategory.Name traitType;
}
struct Traits {
mapping(uint256 => StoredTrait) all;
uint256 epoch;
mapping(uint256 => CommitReveal.Epoch) epochs;
}
struct TraitMetadata {
uint256 traitIndex;
string traitName;
TraitCategory.Name traitType;
bytes colorMap;
bytes zMap;
address dataMinterContract;
address creatorAddress;
string creatorName;
string release;
}
event AllOperatorApprovalsInvalidated(uint256 indexed tokenId);
}
文件 30 的 40:MainRenderer2D.sol
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,
'}'
);
return string.concat("data:application/json;base64,", Utils.encode(bytes(json)));
}
function getBodyImageSvg(bytes memory _pixels) public pure returns (string memory) {
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);
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;
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,
'"}'
);
}
}
文件 31 的 40:MainRenderer3D.sol
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";
import { IScriptyBuilderV2, HTMLRequest, HTMLTagType, HTMLTag } from "../../lib/scripty/interfaces/IScriptyBuilderV2.sol";
contract MainRenderer3D is Ownable {
address immutable scriptyBuilderAddress = 0xD7587F110E08F4D120A231bA97d3B577A81Df022;
address immutable ethfsFileStorageAddress = 0x8FAA1AAb9DA8c75917C43Fb24fDdb513edDC3245;
EncodeURI public encodeURIContract;
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) {
if (bytes(_traitsAttributes).length > 0) {
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";
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;
}
}
文件 32 的 40:MerkleProofLib.sol
pragma solidity ^0.8.4;
library MerkleProofLib {
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf)
internal
pure
returns (bool isValid)
{
assembly {
if mload(proof) {
let offset := add(proof, 0x20)
let end := add(offset, shl(5, mload(proof)))
for {} 1 {} {
let scratch := shl(5, gt(leaf, mload(offset)))
mstore(scratch, leaf)
mstore(xor(scratch, 0x20), mload(offset))
leaf := keccak256(0x00, 0x40)
offset := add(offset, 0x20)
if iszero(lt(offset, end)) { break }
}
}
isValid := eq(leaf, root)
}
}
function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf)
internal
pure
returns (bool isValid)
{
assembly {
if proof.length {
let end := add(proof.offset, shl(5, proof.length))
let offset := proof.offset
for {} 1 {} {
let scratch := shl(5, gt(leaf, calldataload(offset)))
mstore(scratch, leaf)
mstore(xor(scratch, 0x20), calldataload(offset))
leaf := keccak256(0x00, 0x40)
offset := add(offset, 0x20)
if iszero(lt(offset, end)) { break }
}
}
isValid := eq(leaf, root)
}
}
function verifyMultiProof(
bytes32[] memory proof,
bytes32 root,
bytes32[] memory leaves,
bool[] memory flags
) internal pure returns (bool isValid) {
assembly {
let leavesLength := mload(leaves)
let proofLength := mload(proof)
let flagsLength := mload(flags)
leaves := add(0x20, leaves)
proof := add(0x20, proof)
flags := add(0x20, flags)
for {} eq(add(leavesLength, proofLength), add(flagsLength, 1)) {} {
if iszero(flagsLength) {
isValid := eq(mload(xor(leaves, mul(xor(proof, leaves), proofLength))), root)
break
}
let proofEnd := add(proof, shl(5, proofLength))
let hashesFront := mload(0x40)
leavesLength := shl(5, leavesLength)
for { let i := 0 } iszero(eq(i, leavesLength)) { i := add(i, 0x20) } {
mstore(add(hashesFront, i), mload(add(leaves, i)))
}
let hashesBack := add(hashesFront, leavesLength)
flagsLength := add(hashesBack, shl(5, flagsLength))
for {} 1 {} {
let a := mload(hashesFront)
let b := mload(add(hashesFront, 0x20))
hashesFront := add(hashesFront, 0x40)
if iszero(mload(flags)) {
b := mload(proof)
proof := add(proof, 0x20)
hashesFront := sub(hashesFront, 0x20)
}
flags := add(flags, 0x20)
let scratch := shl(5, gt(a, b))
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(
eq(mload(sub(hashesBack, 0x20)), root),
eq(proofEnd, proof)
)
break
}
}
}
function verifyMultiProofCalldata(
bytes32[] calldata proof,
bytes32 root,
bytes32[] calldata leaves,
bool[] calldata flags
) internal pure returns (bool isValid) {
assembly {
for {} eq(add(leaves.length, proof.length), add(flags.length, 1)) {} {
if iszero(flags.length) {
isValid := eq(
calldataload(
xor(leaves.offset, mul(xor(proof.offset, leaves.offset), proof.length))
),
root
)
break
}
let proofEnd := add(proof.offset, shl(5, proof.length))
let hashesFront := mload(0x40)
calldatacopy(hashesFront, leaves.offset, shl(5, leaves.length))
let hashesBack := add(hashesFront, shl(5, leaves.length))
flags.length := add(hashesBack, shl(5, flags.length))
for {} 1 {} {
let a := mload(hashesFront)
let b := mload(add(hashesFront, 0x20))
hashesFront := add(hashesFront, 0x40)
if iszero(calldataload(flags.offset)) {
b := calldataload(proof.offset)
proof.offset := add(proof.offset, 0x20)
hashesFront := sub(hashesFront, 0x20)
}
flags.offset := add(flags.offset, 0x20)
let scratch := shl(5, gt(a, b))
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(
eq(mload(sub(hashesBack, 0x20)), root),
eq(proofEnd, proof.offset)
)
break
}
}
}
function emptyProof() internal pure returns (bytes32[] calldata proof) {
assembly {
proof.length := 0
}
}
function emptyLeaves() internal pure returns (bytes32[] calldata leaves) {
assembly {
leaves.length := 0
}
}
function emptyFlags() internal pure returns (bool[] calldata flags) {
assembly {
flags.length := 0
}
}
}
文件 33 的 40:Ownable.sol
pragma solidity ^0.8.4;
abstract contract Ownable {
error Unauthorized();
error NewOwnerIsZeroAddress();
error NoHandoverRequest();
error AlreadyInitialized();
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
event OwnershipHandoverRequested(address indexed pendingOwner);
event OwnershipHandoverCanceled(address indexed pendingOwner);
uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
bytes32 internal constant _OWNER_SLOT =
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;
uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
function _guardInitializeOwner() internal pure virtual returns (bool guard) {}
function _initializeOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
assembly {
let ownerSlot := _OWNER_SLOT
if sload(ownerSlot) {
mstore(0x00, 0x0dc149f0)
revert(0x1c, 0x04)
}
newOwner := shr(96, shl(96, newOwner))
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
} else {
assembly {
newOwner := shr(96, shl(96, newOwner))
sstore(_OWNER_SLOT, newOwner)
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
}
}
function _setOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
assembly {
let ownerSlot := _OWNER_SLOT
newOwner := shr(96, shl(96, newOwner))
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
}
} else {
assembly {
let ownerSlot := _OWNER_SLOT
newOwner := shr(96, shl(96, newOwner))
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
sstore(ownerSlot, newOwner)
}
}
}
function _checkOwner() internal view virtual {
assembly {
if iszero(eq(caller(), sload(_OWNER_SLOT))) {
mstore(0x00, 0x82b42900)
revert(0x1c, 0x04)
}
}
}
function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
return 48 * 3600;
}
function transferOwnership(address newOwner) public payable virtual onlyOwner {
assembly {
if iszero(shl(96, newOwner)) {
mstore(0x00, 0x7448fbae)
revert(0x1c, 0x04)
}
}
_setOwner(newOwner);
}
function renounceOwnership() public payable virtual onlyOwner {
_setOwner(address(0));
}
function requestOwnershipHandover() public payable virtual {
unchecked {
uint256 expires = block.timestamp + _ownershipHandoverValidFor();
assembly {
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), expires)
log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
}
}
}
function cancelOwnershipHandover() public payable virtual {
assembly {
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), 0)
log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
}
}
function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
assembly {
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
let handoverSlot := keccak256(0x0c, 0x20)
if gt(timestamp(), sload(handoverSlot)) {
mstore(0x00, 0x6f5e8818)
revert(0x1c, 0x04)
}
sstore(handoverSlot, 0)
}
_setOwner(pendingOwner);
}
function owner() public view virtual returns (address result) {
assembly {
result := sload(_OWNER_SLOT)
}
}
function ownershipHandoverExpiresAt(address pendingOwner)
public
view
virtual
returns (uint256 result)
{
assembly {
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
result := sload(keccak256(0x0c, 0x20))
}
}
modifier onlyOwner() virtual {
_checkOwner();
_;
}
}
文件 34 的 40:ReentrancyGuard.sol
pragma solidity ^0.8.0;
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
}
文件 35 的 40:ScriptyCore.sol
pragma solidity ^0.8.17;
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;
bytes public constant DATA_HTML_BASE64_URI_RAW = "data:text/html;base64,";
bytes public constant DATA_HTML_URL_SAFE = "data%3Atext%2Fhtml%2C";
bytes public constant HTML_OPEN_RAW = "<html>";
bytes public constant HTML_OPEN_URL_SAFE = "%3Chtml%3E";
bytes public constant HEAD_OPEN_RAW = "<head>";
bytes public constant HEAD_OPEN_URL_SAFE = "%3Chead%3E";
bytes public constant HEAD_CLOSE_RAW = "</head>";
bytes public constant HEAD_CLOSE_URL_SAFE = "%3C%2Fhead%3E";
bytes public constant BODY_OPEN_RAW = "<body>";
bytes public constant BODY_OPEN_URL_SAFE = "%3Cbody%3E";
bytes public constant HTML_BODY_CLOSED_RAW = "</body></html>";
bytes public constant HTML_BODY_CLOSED_URL_SAFE =
"%3C%2Fbody%3E%3C%2Fhtml%3E";
uint256 public constant URLS_RAW_BYTES = 39;
uint256 public constant URLS_SAFE_BYTES = 90;
uint256 public constant HTML_RAW_BYTES = 13;
uint256 public constant HEAD_RAW_BYTES = 13;
uint256 public constant BODY_RAW_BYTES = 13;
uint256 public constant RAW_BYTES = 39;
uint256 public constant HTML_URL_SAFE_BYTES = 23;
uint256 public constant HEAD_URL_SAFE_BYTES = 23;
uint256 public constant BODY_SAFE_BYTES = 23;
uint256 public constant URL_SAFE_BYTES = 69;
uint256 public constant HTML_BASE64_DATA_URI_BYTES = 22;
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);
}
function tagOpenCloseForHTMLTagURLSafe(
HTMLTag memory htmlTag
) public pure returns (bytes memory, bytes memory) {
if (
htmlTag.tagType == HTMLTagType.script ||
htmlTag.tagType == HTMLTagType.scriptBase64DataURI
) {
return (
"%253Cscript%2520src%253D%2522data%253Atext%252Fjavascript%253Bbase64%252C",
"%2522%253E%253C%252Fscript%253E"
);
} else if (htmlTag.tagType == HTMLTagType.scriptGZIPBase64DataURI) {
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) {
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);
}
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;
}
function sizeForBase64Encoding(
uint256 value
) public pure returns (uint256) {
unchecked {
return 4 * ((value + 2) / 3);
}
}
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;
}
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);
}
}
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);
}
}
文件 36 的 40:ScriptyStructs.sol
pragma solidity ^0.8.17;
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;
}
文件 37 的 40:Strings.sol
pragma solidity ^0.8.0;
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
function toString(uint256 value) internal pure returns (string memory) {
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);
}
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);
}
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);
}
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
}
文件 38 的 40:TraitCategory.sol
pragma solidity ^0.8.22;
library TraitCategory {
enum Name {
None,
Head,
Hair,
Face,
Accessory,
Top,
Bottom,
Shoes
}
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 "";
}
}
文件 39 的 40:TraitRenderer.sol
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)));
}
function getSvgStart() public pure returns (string memory) {
return SVG_START;
}
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;
bytes memory pixels = new bytes(30 * 30 * 5);
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;
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,
'"}'
);
}
}
文件 40 的 40:Utils.sol
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);
}
function random(uint256 input, uint256 _max) internal pure returns (uint256) {
return (uint256(keccak256(abi.encodePacked(input))) % _max);
}
function random(uint256 input, string memory salt, uint256 _max) internal pure returns (uint256) {
return (uint256(keccak256(abi.encodePacked(input, salt))) % _max);
}
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);
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);
}
function toString(uint256 value) internal pure returns (string memory str) {
assembly {
str := add(mload(0x40), 0x80)
mstore(0x40, add(str, 0x20))
mstore(str, 0)
let end := str
let w := not(0)
for { let temp := value } 1 {} {
str := add(str, w)
mstore8(str, add(48, mod(temp, 10)))
temp := div(temp, 10)
if iszero(temp) { break }
}
let length := sub(end, str)
str := sub(str, 0x20)
mstore(str, length)
}
}
function toString(int256 value) internal pure returns (string memory str) {
if (value >= 0) {
return toString(uint256(value));
}
unchecked {
str = toString(uint256(-value));
}
assembly {
let length := mload(str)
mstore(str, 0x2d)
str := sub(str, 1)
mstore(str, add(length, 1))
}
}
function encode(bytes memory data, bool fileSafe, bool noPadding) internal pure returns (string memory result) {
assembly {
let dataLength := mload(data)
if dataLength {
let encodedLength := shl(2, div(add(dataLength, 2), 3))
result := mload(0x40)
mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
mstore(0x3f, xor("ghijklmnopqrstuvwxyz0123456789-_", mul(iszero(fileSafe), 0x0670)))
let ptr := add(result, 0x20)
let end := add(ptr, encodedLength)
for {} 1 {} {
data := add(data, 3)
let input := mload(data)
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)
if iszero(lt(ptr, end)) { break }
}
mstore(0x40, add(end, 0x20))
let o := div(2, mod(dataLength, 3))
mstore(sub(ptr, o), shl(240, 0x3d3d))
o := mul(iszero(iszero(noPadding)), o)
mstore(sub(ptr, o), 0)
mstore(result, sub(encodedLength, o))
}
}
}
function encode(bytes memory data) internal pure returns (string memory result) {
result = encode(data, false, false);
}
function encode(bytes memory data, bool fileSafe) internal pure returns (string memory result) {
result = encode(data, fileSafe, 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;
}
}
{
"compilationTarget": {
"src/ChonksMain.sol": "ChonksMain"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@openzeppelin/=lib/openzeppelin-contracts/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":forge-std/=lib/forge-std/src/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/",
":scripty/=lib/scripty/",
":solady/=lib/solady/src/"
]
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"CanOnlyReserveFirstTwo","type":"error"},{"inputs":[],"name":"CantTransferDuringMint","type":"error"},{"inputs":[],"name":"CantTransferToTBAs","type":"error"},{"inputs":[],"name":"ChonkDoesntExist","type":"error"},{"inputs":[],"name":"FirstReleaseDataMinterNotSet","type":"error"},{"inputs":[],"name":"IncorrectChonkOwner","type":"error"},{"inputs":[],"name":"IncorrectTBAOwner","type":"error"},{"inputs":[],"name":"IncorrectTraitType","type":"error"},{"inputs":[],"name":"InsufficientFunds","type":"error"},{"inputs":[],"name":"InvalidBodyIndex","type":"error"},{"inputs":[],"name":"InvalidColor","type":"error"},{"inputs":[],"name":"InvalidMintAmount","type":"error"},{"inputs":[],"name":"InvalidTraitCategory","type":"error"},{"inputs":[],"name":"InvalidTraitCount","type":"error"},{"inputs":[],"name":"MintEnded","type":"error"},{"inputs":[],"name":"MintNotStarted","type":"error"},{"inputs":[],"name":"MintStartTimeAlreadySet","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"Timelocked","type":"error"},{"inputs":[],"name":"TraitLengthsMustMatch","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"UseUnequip","type":"error"},{"inputs":[],"name":"WithdrawFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"string","name":"color","type":"string"}],"name":"BackgroundColor","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":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"_bodyIndex","type":"uint8"}],"name":"BodyIndex","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"traitTokenId","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"traitCategory","type":"uint8"}],"name":"Equip","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"EquipAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"MetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Mint","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":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"renderZ","type":"bool"}],"name":"Render3D","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"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"traitCategory","type":"uint8"}],"name":"Unequip","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"UnequipAll","type":"event"},{"inputs":[{"internalType":"uint256","name":"_bodyIndex","type":"uint256"},{"internalType":"string","name":"_bodyName","type":"string"},{"internalType":"bytes","name":"_colorMap","type":"bytes"},{"internalType":"bytes","name":"_zMap","type":"bytes"}],"name":"addNewBody","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"uint256","name":"_chonkId","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":"","type":"uint256"}],"name":"bodyIndexToMetadata","outputs":[{"internalType":"uint256","name":"bodyIndex","type":"uint256"},{"internalType":"string","name":"bodyName","type":"string"},{"internalType":"bytes","name":"colorMap","type":"bytes"},{"internalType":"bytes","name":"zMap","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chonkId","type":"uint256"},{"internalType":"uint256","name":"_traitId","type":"uint256"}],"name":"checkIfTraitIsEquipped","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"chonkEquipHelper","outputs":[{"internalType":"contract ChonkEquipHelper","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"chonkId","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"chonkIdToApprovedOperators","outputs":[{"internalType":"address","name":"operators","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chonkTokenId","type":"uint256"},{"internalType":"uint256[]","name":"_traitTokenIds","type":"uint256[]"},{"internalType":"uint8[]","name":"_traitCategories","type":"uint8[]"},{"internalType":"uint8","name":"_bodyIndex","type":"uint8"},{"internalType":"string","name":"_backgroundColor","type":"string"},{"internalType":"bool","name":"_render3D","type":"bool"}],"name":"chonkMakeover","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenID","type":"uint256"}],"name":"chonkTokens","outputs":[{"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"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"collectionsAddressDidUse","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collectionsMerkle","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"creatorsAddressDidUse","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"creatorsMerkle","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deploymentTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chonkTokenId","type":"uint256"},{"internalType":"uint256","name":"_traitTokenId","type":"uint256"}],"name":"equip","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chonkTokenId","type":"uint256"},{"internalType":"uint256[]","name":"_traitTokenIds","type":"uint256[]"},{"internalType":"uint8[]","name":"_traitCategories","type":"uint8[]"}],"name":"equipMany","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"firstReleaseDataMinter","outputs":[{"internalType":"contract FirstReleaseDataMinter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"friendsAddressDidUse","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"friendsMerkle","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"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":"getBackpackSVGs","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"getBodyImageSvg","outputs":[{"internalType":"string","name":"","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":"getBodySVGZmapsAndMetadata","outputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bytes","name":"","type":"bytes"},{"internalType":"string","name":"","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":"getBodySvgAndMetadata","outputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getBodyZMap","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getChonk","outputs":[{"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":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tbaAddress","type":"address"}],"name":"getChonkIdForTBAAddress","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chonkId","type":"uint256"}],"name":"getChonkIdToApprovedOperators","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getChonkZMap","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chonkTraitTokenId","type":"uint256"}],"name":"getFullPictureForTrait","outputs":[{"internalType":"address","name":"traitOwnerTBA","type":"address"},{"internalType":"uint256","name":"chonkTokenId","type":"uint256"},{"internalType":"address","name":"chonkOwner","type":"address"},{"internalType":"bool","name":"isEquipped","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chonkId","type":"uint256"}],"name":"getOwnerAndTBAAddressForChonkId","outputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"tbaAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chonkId","type":"uint256"}],"name":"getTBAAddressForChonkId","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chonkId","type":"uint256"}],"name":"getTraitsForChonkId","outputs":[{"internalType":"uint256[]","name":"traitTokens","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialMintStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isTimelocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mainRenderer2D","outputs":[{"internalType":"contract MainRenderer2D","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mainRenderer3D","outputs":[{"internalType":"contract MainRenderer3D","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"marketplace","outputs":[{"internalType":"contract ChonksMarket","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxTraitsToOutput","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes32[]","name":"_merkleProof","type":"bytes32[]"}],"name":"mint","outputs":[],"stateMutability":"payable","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":"oneYearFromDeployment","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":[],"name":"price","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"renderAsDataUri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"renderAsDataUri2D","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"renderAsDataUri3D","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"bool","name":"_approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chonkId","type":"uint256"},{"internalType":"address","name":"_operator","type":"address"},{"internalType":"bool","name":"_approved","type":"bool"}],"name":"setApprovalForAllChonksMarketplace","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chonkTokenId","type":"uint256"},{"internalType":"string","name":"_color","type":"string"}],"name":"setBackgroundColor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chonkTokenId","type":"uint256"},{"internalType":"uint8","name":"_bodyIndex","type":"uint8"}],"name":"setBodyIndex","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"string","name":"_color","type":"string"},{"internalType":"uint8","name":"_bodyIndex","type":"uint8"},{"internalType":"bool","name":"_render3D","type":"bool"}],"name":"setChonkAttributes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_chonkEquipHelper","type":"address"}],"name":"setChonkEquipHelper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"}],"name":"setCollectionsMerkle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"}],"name":"setCreatorsMerkle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string[2]","name":"_descriptionParts","type":"string[2]"}],"name":"setDescriptionParts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_dataContract","type":"address"}],"name":"setFirstReleaseDataMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"}],"name":"setFriendsMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_mainRenderer2D","type":"address"}],"name":"setMainRenderer2D","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_mainRenderer3D","type":"address"}],"name":"setMainRenderer3D","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_marketplace","type":"address"}],"name":"setMarketplace","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxTraitsToOutput","type":"uint256"}],"name":"setMaxTraitsToOutput","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_initialMintStartTime","type":"uint256"}],"name":"setMintStartTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_priceInWei","type":"uint256"}],"name":"setPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"bool","name":"_render3D","type":"bool"}],"name":"setTokenRender3D","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ChonkTraits","name":"_address","type":"address"}],"name":"setTraitsContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_withdrawAddress","type":"address"}],"name":"setWithdrawAddress","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":"address","name":"","type":"address"}],"name":"tbaAddressToTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint8","name":"_traitCount","type":"uint8"}],"name":"teamMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"teamReserve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokenIdToTBAAccountAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"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":[],"name":"traitsContract","outputs":[{"internalType":"contract ChonkTraits","name":"","type":"address"}],"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":[{"internalType":"uint256","name":"_chonkTokenId","type":"uint256"},{"internalType":"enum TraitCategory.Name","name":"traitType","type":"uint8"}],"name":"unequip","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chonkTokenId","type":"uint256"}],"name":"unequipAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"walletOfOwner","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]