// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)pragmasolidity ^0.8.1;/**
* @dev Collection of functions related to the address type
*/libraryAddress{
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/functionisContract(address account) internalviewreturns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0// for contracts in construction, since the code is only stored at the end// of the constructor execution.return account.code.length>0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/functionsendValue(addresspayable recipient, uint256 amount) internal{
require(address(this).balance>= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/functionfunctionCall(address target, bytesmemory data) internalreturns (bytesmemory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/functionfunctionCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/functionfunctionCallWithValue(address target,
bytesmemory data,
uint256 value
) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/functionfunctionCallWithValue(address target,
bytesmemory data,
uint256 value,
stringmemory errorMessage
) internalreturns (bytesmemory) {
require(address(this).balance>= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytesmemory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/functionfunctionStaticCall(address target, bytesmemory data) internalviewreturns (bytesmemory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/functionfunctionStaticCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalviewreturns (bytesmemory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytesmemory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/functionfunctionDelegateCall(address target, bytesmemory data) internalreturns (bytesmemory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/functionfunctionDelegateCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalreturns (bytesmemory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytesmemory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/functionverifyCallResult(bool success,
bytesmemory returndata,
stringmemory errorMessage
) internalpurereturns (bytesmemory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if presentif (returndata.length>0) {
// The easiest way to bubble the revert reason is using memory via assembly/// @solidity memory-safe-assemblyassembly {
let returndata_size :=mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.22;// OpenZeppelin Importsimport { ERC721 } from"@openzeppelin/contracts/token/ERC721/ERC721.sol";
import { ERC721Burnable } from"@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
import { ERC721Enumerable } from"@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import { IERC165 } from"@openzeppelin/contracts/utils/introspection/IERC165.sol";
import { IERC721 } from"@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { Ownable } from"solady/auth/Ownable.sol";
import { ReentrancyGuard } from"@openzeppelin/contracts/security/ReentrancyGuard.sol";
// Associated Interfaces and Librariesimport { 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";
// Rendererimport { TraitRenderer } from"./renderers/TraitRenderer.sol";
// Other Chonks Associated Contractsimport { ChonksMain } from"./ChonksMain.sol";
import { ChonksMarket } from"./ChonksMarket.sol";
interfaceIRenderMinterV1{
functionexplainTrait(
ITraitStorage.StoredTrait calldata storedTrait,
uint128 randomness
) externalviewreturns (ITraitStorage.StoredTrait memory);
}
/*
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:cllllllllllllllllllllllllllllllllc:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:okOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOko:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:clllxOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOdlllc:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:okOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOko:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ccldOOOOOOO0KKKxllldOOOOOOOOOOOO0KKKxllldOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ldxxkOOOOOOOXWMNl ;kOOOOOOOOOOOXWMWl ;kOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ldxxkOOOOOOOXMMWl ;kkkkkkkkkkkOXMMWl ;kOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ldxxkOOOOOOOXMMWl ,dxxxxxxxxxxxKWMWl ;kOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:coooxOOOOOOOKNNXd'.'cxkkkxxkkkkkk0XNXd'.'lkOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:;:oOOOOOOOOOOOOOkkOOOOOOOOOOOOOOOOOOkkOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:lddxkOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOkxxdl:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::oOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOo::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:okkkkkkkkkkkkkkkkkkkkkkkkkkkkxxddl:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:lxxxxxxxxxxxxxxxxxxxxxxxxxxxxl::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:cllldxkxxxxkkkkkkkkkkkkkkkkkkkkkxdlllc:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:okOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOko:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;clllxOOOOOOOOkkkOOOOOOOOOOOOOOkkkOOOOOOOOdlllc:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:okOOOOOOOOOOkxxxkOOOOOOOOOOOOkxxxkOOOOOOOOOOko:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOkkkOOOOkkkkkOOOOOOOOOOOOkkxkkOOOOkkkOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOkxxxkOOOOOOOOOOOOOOOOOOOOOOOOOOOOkxxxkOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOkxxxkOOOOOOOOOOOOOOOOOOOOOOOOOOOOkxxxkOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOkxxxkOOOOOOOOOOOOOOOOOOOOOOOOOOOOkxxxkOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ldxddoooxOOOOOOOOOOOOOOOOOOOOOOOOOOOOxoooddxdl:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::::::oOOOOOOOOOOOOOOOOOOOOOOOOOOOOo::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOOOOOkxdxkOOOOOOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOOOOOo:::okOOOOOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOkxddl:;:lddxxkOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOo::::;;;:::::oOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ldddddxdl:;;;;;;;;;;:ldxxxxxdl:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::::::::;;;;;;;;;;;;:::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
*/contractChonkTraitsisIERC165, ERC721Enumerable, ERC721Burnable, ITraitStorage, Ownable, IERC4906, ReentrancyGuard{
// We use this database for persistent storage
Traits public traitTokens;
// The renderer contract for the Traits
TraitRenderer public traitRenderer;
// The ChonksMain contract
ChonksMain public chonksMain;
// The ChonksMarket contract
ChonksMarket public marketplace;
// Metadata for each Trait by indexmapping(uint256=> TraitMetadata) public traitIndexToMetadata;
// Approved operators for each Traitmapping(uint256 traitId =>address[] operators) public traitIdToApprovedOperators;
// Contract addresses that are approved to create Traitsmapping (address=>bool) public isMinter;
// These are Chonks-related contracts that are approved to invalidate operator approvalsmapping (address=>bool) public approvedInvalidators;
// The next token ID to be minteduint256public nextTokenId;
// The transient Chonk ID, used in _beforeTokenTransfer and _afterTokenTransferuint256internal _transientChonkId;
// When the initial mint starteduint256public initialMintStartTime;
// The description partsstring[2] descriptionParts;
/// ErrorserrorAddressCantBurn();
errorCantTransfer();
errorCantTransferDuringMint();
errorCantTransferEquipped();
errorMintStartTimeAlreadySet();
errorNotATBA();
errorNotAValidMinterContract();
errorNotYourTrait();
errorSetChonksMainAddress();
errorSetMarketplaceAddress();
errorTraitNotFound(uint256 _tokenId);
errorTraitTokenDoesntExist();
/// ModifiersmodifieronlyMinter(address _address) {
// Add DataMinter contract first via `AddMinter`.if (!isMinter[_address]) revert NotAValidMinterContract();
_;
}
constructor() ERC721("Chonk Traits", "CHONK TRAITS") {
_initializeOwner(msg.sender);
traitRenderer =new TraitRenderer();
}
functiongetTraitIndexToMetadata(uint256 _traitIndex) publicviewreturns (TraitMetadata memory) {
return traitIndexToMetadata[_traitIndex];
}
// Called by DataMinter contracts to set the trait for a tokenIdfunctionsetTraitForTokenId(uint256 _tokenId, ITraitStorage.StoredTrait memory _trait) publiconlyMinter(msg.sender) {
traitTokens.all[_tokenId] = _trait;
}
/// @dev Called in DataMinter contracts to add TraitsfunctionsetTraitIndexToMetadata(uint256 _traitIndex, TraitMetadata memory _metadata) publiconlyMinter(msg.sender) {
traitIndexToMetadata[_traitIndex] = _metadata;
}
/// @dev NOTE: Mints to a smart contract address that implements onERC721ReceivedfunctionsafeMint(address _to) publiconlyMinter(msg.sender) returns (uint256) {
resolveEpochIfNecessary();
uint tokenId =++nextTokenId;
_safeMint(_to, tokenId);
return tokenId;
}
functionburn(uint256 _tokenId) publicoverride{
if (!isMinter[msg.sender]) revert AddressCantBurn();
_burn(_tokenId);
}
functionburnBatch(uint256[] memory tokenIds) public{
if (!isMinter[msg.sender]) revert AddressCantBurn();
for (uint256 i; i < tokenIds.length; ++i) {
_burn(tokenIds[i]);
}
}
/// @notice Initializes and closes epochs. Thank you Jalil & MouseDev./// @dev Based on the commit-reveal scheme proposed by MouseDev in Checks.functionresolveEpochIfNecessary() public{
CommitReveal.Epoch storage currentEpoch = traitTokens.epochs[traitTokens.epoch];
if (
// If epoch has not been committed,!currentEpoch.committed ||// Or the reveal commitment timed out.
(!currentEpoch.revealed && currentEpoch.revealBlock <block.number-256)
) {
// This means the epoch has not been committed, OR the epoch was committed but has expired.// Set committed to true, and record the reveal block:// this was 50 x 12, 600 seconds - 10 mins on L1// but on L2 it's more like 50 x 2, 100 seconds - 1.6 mins on L2
currentEpoch.revealBlock =uint64(block.number+50);
currentEpoch.committed =true;
} elseif (block.number> currentEpoch.revealBlock) {
// Epoch has been committed and is within range to be revealed.// Set its randomness to the target block hash.
currentEpoch.randomness =uint128(uint256(keccak256(
abi.encodePacked(
blockhash(currentEpoch.revealBlock),
block.prevrandao
))) % (2**128-1)
);
currentEpoch.revealed =true;
// Notify dApps about the new epoch.emit CommitReveal.NewEpoch(traitTokens.epoch, currentEpoch.revealBlock);
// Notify OS to update all tokensemit BatchMetadataUpdate(0, type(uint256).max);
// Initialize the next epoch++traitTokens.epoch;
resolveEpochIfNecessary();
}
}
/// @notice The identifier of the current epochfunctiongetEpoch() viewpublicreturns(uint256) {
return traitTokens.epoch;
}
/// @notice Get the data for a given epoch/// @param index The identifier of the epoch to fetchfunctiongetEpochData(uint256 index) viewpublicreturns(CommitReveal.Epoch memory) {
return traitTokens.epochs[index];
}
functiontokenURI(uint256 _tokenId) publicviewoverridereturns (stringmemory) {
if (!_exists(_tokenId)) revert TraitTokenDoesntExist();
return renderAsDataUri(_tokenId);
}
functiongetTrait(uint256 _tokenId) publicviewreturns (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);
}
/// @notice Lets you easily go from the Trait token id to the Trait Metadata, as explained by the DataMinter contract the Trait was minted withfunctiongetTraitMetadata(uint256 _tokenId) publicviewreturns (TraitMetadata memory) {
StoredTrait memory trait = getTrait(_tokenId);
return traitIndexToMetadata[trait.traitIndex];
}
functiongetStoredTraitForTokenId(uint256 _tokenId) publicviewreturns (ITraitStorage.StoredTrait memory) {
return traitTokens.all[_tokenId];
}
functiongetCurrentEpoch() publicviewreturns (uint256) {
return traitTokens.epoch;
}
functionrenderAsDataUri(uint256 _tokenId) publicviewreturns (stringmemory) {
StoredTrait memory trait = getTrait(_tokenId);
stringmemory traitSvg = trait.isRevealed ? getTraitImageSvg(trait.traitIndex) : '<svg></svg>';
return traitRenderer.renderAsDataUri(
_tokenId,
trait,
traitIndexToMetadata[trait.traitIndex],
getGhostSvg(),
traitSvg,
descriptionParts
);
}
functiongetSvgForTokenId(uint256 _tokenId) publicviewreturns (stringmemory traitSvg) {
// don't get the ghost here for now
StoredTrait memory trait = getTrait(_tokenId);
if (trait.isRevealed) {
traitSvg = getTraitImageSvg(trait.traitIndex);
} else {
traitSvg ='<svg></svg>';
}
}
functiongetZMapForTokenId(uint256 _tokenId) publicviewreturns (stringmemory) {
StoredTrait memory trait = getTrait(_tokenId);
returnstring(traitIndexToMetadata[trait.traitIndex].zMap);
}
functiongetTraitImageSvg(uint256 index) publicviewreturns (stringmemory svg) {
return traitRenderer.getTraitImageSvg(traitIndexToMetadata[index].colorMap);
}
functiongetGhostSvg() publicviewreturns (stringmemory svg) {
return traitRenderer.getGhostSvg();
}
functioncreateSvgFromPixels(bytesmemory _pixels) publicviewreturns (bytesmemory svgParts) {
return traitRenderer.createSvgFromPixels(_pixels);
}
functiongetSvgAndMetadataTrait(StoredTrait memory trait, uint256 traitId) publicviewreturns (stringmemory traitSvg, stringmemory traitAttributes ) {
return traitRenderer.getSvgAndMetadataTrait(
trait,
traitId,
traitIndexToMetadata[trait.traitIndex]
);
}
functiongetSVGZmapAndMetadataTrait(StoredTrait memory trait, uint256 traitId) publicviewreturns(stringmemory traitSvg, bytesmemory traitZmap, stringmemory traitAttributes ) {
return traitRenderer.getSVGZmapAndMetadataTrait(
trait,
traitId,
traitIndexToMetadata[trait.traitIndex]
);
}
functiongetSvgAndMetadata(IChonkStorage.StoredChonk memory storedChonk) publicviewreturns (stringmemory traitsSvg, stringmemory traitsAttributes) {
return traitRenderer.getSvgAndMetadata(storedChonk, this.callGetSvgAndMetadataTrait);
}
functiongetSvgZmapsAndMetadata(IChonkStorage.StoredChonk memory storedChonk) publicviewreturns (stringmemory traitsSvg, bytesmemory traitZMaps, stringmemory traitsAttributes) {
return traitRenderer.getSvgZmapsAndMetadata(storedChonk, this.callGetSVGZmapAndMetadataTrait);
}
functioncallGetSvgAndMetadataTrait(uint256 _traitId, stringmemory _traitsSvg, stringmemory _traitsAttributes ) publicviewreturns (stringmemory traitsSvg, stringmemory traitsAttributes) {
StoredTrait memory storedTrait = getTrait(_traitId);
return traitRenderer.callGetSvgAndMetadataTrait(
_traitId,
_traitsSvg,
_traitsAttributes,
storedTrait,
traitIndexToMetadata[storedTrait.traitIndex]
);
}
functioncallGetSVGZmapAndMetadataTrait(uint256 _traitId, stringmemory _traitsSvg, stringmemory _traitsAttributes, bytesmemory _traitZMaps) publicviewreturns (stringmemory traitsSvg, stringmemory traitsAttributes, bytesmemory traitZMaps) {
StoredTrait memory storedTrait = getTrait(_traitId);
return traitRenderer.callGetSVGZmapAndMetadataTrait(
_traitId,
_traitsSvg,
_traitsAttributes,
_traitZMaps,
storedTrait,
traitIndexToMetadata[storedTrait.traitIndex]
);
}
functionwalletOfOwner(address _owner) publicviewreturns(uint256[] memory) {
uint256 tokenCount = balanceOf(_owner);
uint256[] memory tokensId =newuint256[](tokenCount);
for (uint256 i; i < tokenCount; ++i){
tokensId[i] = tokenOfOwnerByIndex(_owner, i);
}
return tokensId;
}
/// Setters/OnlyOwnerfunctionsetChonksMain(address _ChonksMain) publiconlyOwner{
chonksMain = ChonksMain(_ChonksMain);
}
functionsetMarketplace(address _marketplace) publiconlyOwner{
marketplace = ChonksMarket(_marketplace);
}
functionaddMinter(address _minter) publiconlyOwner{
isMinter[_minter] =true;
}
functionremoveMinter(address _minter) publiconlyOwner{
isMinter[_minter] =false;
}
functionsetTraitRenderer(address _traitRenderer) publiconlyOwner{
traitRenderer = TraitRenderer(_traitRenderer);
}
functionsetGhostMaps(bytesmemory _colorMap, bytesmemory _zMap) publiconlyOwner{
// ghost.colorMap = _colorMap;// ghost.zMap = _zMap;
traitRenderer.setGhostMaps(_colorMap, _zMap);
}
functionaddApprovedInvalidator(address _invalidator) publiconlyOwner{
approvedInvalidators[_invalidator] =true;
}
functionremoveApprovedInvalidator(address _invalidator) publiconlyOwner{
approvedInvalidators[_invalidator] =false;
}
functionsetMintStartTime(uint256 _initialMintStartTime) publiconlyOwner{
if (initialMintStartTime !=0) revert MintStartTimeAlreadySet();
initialMintStartTime = _initialMintStartTime;
}
functionsetDescriptionParts(string[2] memory _descriptionParts) publiconlyOwner{
descriptionParts = _descriptionParts;
}
/// BoilerplatefunctionsupportsInterface(bytes4 interfaceId) publicviewoverride(IERC165, ERC721Enumerable, ERC721) returns (bool) {
returnsuper.supportsInterface(interfaceId);
}
function_cleanUpMarketplaceOffersAndBids(uint256 _tokenId, address _to) internal{
// Delete the Offer on Chonk ID before the transferaddress tba = ownerOf(_tokenId);
uint256 chonkId = chonksMain.tbaAddressToTokenId(tba);
marketplace.removeChonkOfferOnTraitTransfer(chonkId);
marketplace.deleteTraitOffersBeforeTokenTransfer(_tokenId);
marketplace.deleteTraitBidsBeforeTokenTransfer(_tokenId, _to);
}
// Override functions for marketplace compatibilityfunction_beforeTokenTransfer(addressfrom, address to, uint256 tokenId) internaloverride(ERC721, ERC721Enumerable) {
if (from==address(0)) {
super._beforeTokenTransfer(from, to, tokenId);
return;
}
if (block.timestamp< initialMintStartTime +24hours) revert CantTransferDuringMint();
// Check if the Trait is equipped on the Chonk, revert if so
(,,, bool isEquipped) = chonksMain.getFullPictureForTrait(tokenId);
if (isEquipped) revert CantTransferEquipped();
(, address seller,,) = marketplace.traitOffers(tokenId);
// If there's an Offer on the Trait, seller is not 0if (seller !=address(0)) {
if (msg.sender!=address(marketplace)) revert CantTransfer();
}
// If burningif (to ==address(0)) {
_cleanUpMarketplaceOffersAndBids(tokenId, to);
// If burning, store the owning Chonk ID for Marketplace cleanup lateraddress tba = ownerOf(tokenId);
_transientChonkId = chonksMain.tbaAddressToTokenId(tba);
super._beforeTokenTransfer(from, to, tokenId);
return;
}
// Ensure the `to` address is a TBAif (chonksMain.tbaAddressToTokenId(to) ==0) revert NotATBA();
_cleanUpMarketplaceOffersAndBids(tokenId, to);
super._beforeTokenTransfer(from, to, tokenId);
}
// Remove an active ChonkOffer because owned Traits changedfunction_afterTokenTransfer(address _from , address _to, uint256 _traitTokenId) internaloverride(ERC721) {
if (address(chonksMain) ==address(0)) revert SetChonksMainAddress();
if (address(marketplace) ==address(0)) revert SetMarketplaceAddress();
// Ignore if mintingif (_from ==address(0)) return;
// If burningif (_to ==address(0)) {
uint256 id = _transientChonkId;
_transientChonkId =0;
marketplace.removeChonkOfferOnTraitTransfer(id);
return;
}
// Delete the Offer on Chonk ID after the transferaddress tba = ownerOf(_traitTokenId);
uint256 chonkId = chonksMain.tbaAddressToTokenId(tba);
marketplace.removeChonkOfferOnTraitTransfer(chonkId);
emit BatchMetadataUpdate(0, type(uint256).max);
}
// Approvals/// @notice Override approve to track individual token approvalsfunctionapprove(address _operator, uint256 _tokenId) publicoverride(ERC721, IERC721) {
if (!_exists(_tokenId)) revert TraitTokenDoesntExist();
if (ownerOf(_tokenId) !=msg.sender) revert NotYourTrait();
// if removing approvalif (_operator ==address(0)) {
// Remove the operator from the arrayaddress[] storage operators = traitIdToApprovedOperators[_tokenId];
for (uint256 i; i < operators.length; ++i) {
if (operators[i] == _operator) {
// Replace with last element and pop
operators[i] = operators[operators.length-1];
operators.pop();
break;
}
}
} else {
// Add operator if not already presentaddress[] 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);
}
/// @notice Override setApprovalForAll to track operator approvalsfunctionsetApprovalForAll(address _operator, bool _approved) publicoverride(ERC721, IERC721) {
// Cannot approve self as operatorrequire(_operator !=msg.sender, "ERC721: approve to caller");
// For setApprovalForAll, we need to update approvals for all tokens owned by msg.senderuint256 balance = balanceOf(msg.sender);
for (uint256 i; i < balance; ++i) {
uint256 tokenId = tokenOfOwnerByIndex(msg.sender, i);
if (_approved) {
// Add operator if not already presentaddress[] 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 {
// Remove the operatoraddress[] storage operators = traitIdToApprovedOperators[tokenId];
for (uint256 j; j < operators.length; ++j) {
if (operators[j] == _operator) {
// Replace with last element and pop
operators[j] = operators[operators.length-1];
operators.pop();
break;
}
}
}
}
super.setApprovalForAll(_operator, _approved);
}
/// @notice Invalidates all operator approvals for a specific tokenfunctioninvalidateAllOperatorApprovals(uint256 _tokenId) public{
if (!_exists(_tokenId)) revert TraitTokenDoesntExist();
// We allow ChonksMain to invalidate all operator approvals for a tokenif (ownerOf(_tokenId) !=msg.sender&&msg.sender!=address(chonksMain) &&!approvedInvalidators[msg.sender])
revert NotYourTrait();
address[] memory operators = traitIdToApprovedOperators[_tokenId];
if (operators.length==0) return;
// Remove individual token approval
_approve(address(0), _tokenId);
// Remove all operator approvals for this tokenfor (uint256 i; i < operators.length; ++i) {
_setApprovalForAll(ownerOf(_tokenId), operators[i], false);
}
// Clear tracking arraydelete traitIdToApprovedOperators[_tokenId];
emit ITraitStorage.AllOperatorApprovalsInvalidated(_tokenId);
}
/// Approval Getters// Function to get the entire array of approved operators for a traitIdfunctiongetApprovedOperators(uint256 traitId) publicviewreturns (address[] memory) {
return traitIdToApprovedOperators[traitId];
}
}
Contract Source Code
File 4 of 40: ChonksMain.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.22;// OpenZeppelin/Solady Importsimport { ERC721 } from"@openzeppelin/contracts/token/ERC721/ERC721.sol";
import { ERC721Enumerable } from"@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import { IERC165 } from"@openzeppelin/contracts/utils/introspection/IERC165.sol";
import { IERC721 } from"@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { MerkleProofLib } from"solady/utils/MerkleProofLib.sol";
import { Ownable } from"solady/auth/Ownable.sol";
import { ReentrancyGuard } from"@openzeppelin/contracts/security/ReentrancyGuard.sol";
// ERC-6551 Importsimport { IAccountImplementation } from"./interfaces/TBABoilerplate/IAccountImplementation.sol";
import { IAccountProxy } from"./interfaces/TBABoilerplate/IAccountProxy.sol";
import { IRegistry } from"./interfaces/TBABoilerplate/IRegistry.sol";
// Renderersimport { MainRenderer2D } from"./renderers/MainRenderer2D.sol";
import { MainRenderer3D } from"./renderers/MainRenderer3D.sol";
// The Traits ERC-721 Contractimport { ChonkTraits } from"./ChonkTraits.sol";
import { ChonkEquipHelper } from"./ChonkEquipHelper.sol";
// Associated Interfaces and Librariesimport { IERC4906 } from"./interfaces/IERC4906.sol";
import { IChonkStorage } from"./interfaces/IChonkStorage.sol";
import { ITraitStorage } from"./interfaces/ITraitStorage.sol";
import { TraitCategory } from"./TraitCategory.sol";
// Other Chonks Associated Contractsimport { ChonksMarket } from"./ChonksMarket.sol";
import { FirstReleaseDataMinter } from"./FirstReleaseDataMinter.sol";
/*
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:cllllllllllllllllllllllllllllllllc:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:okOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOko:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:clllxOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOdlllc:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:okOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOko:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ccldOOOOOOO0KKKxllldOOOOOOOOOOOO0KKKxllldOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ldxxkOOOOOOOXWMNl ;kOOOOOOOOOOOXWMWl ;kOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ldxxkOOOOOOOXMMWl ;kkkkkkkkkkkOXMMWl ;kOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ldxxkOOOOOOOXMMWl ,dxxxxxxxxxxxKWMWl ;kOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:coooxOOOOOOOKNNXd'.'cxkkkxxkkkkkk0XNXd'.'lkOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:;:oOOOOOOOOOOOOOkkOOOOOOOOOOOOOOOOOOkkOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:lddxkOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOkxxdl:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::oOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOo::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:okkkkkkkkkkkkkkkkkkkkkkkkkkkkxxddl:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:lxxxxxxxxxxxxxxxxxxxxxxxxxxxxl::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:cllldxkxxxxkkkkkkkkkkkkkkkkkkkkkxdlllc:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:okOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOko:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;clllxOOOOOOOOkkkOOOOOOOOOOOOOOkkkOOOOOOOOdlllc:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:okOOOOOOOOOOkxxxkOOOOOOOOOOOOkxxxkOOOOOOOOOOko:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOkkkOOOOkkkkkOOOOOOOOOOOOkkxkkOOOOkkkOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOkxxxkOOOOOOOOOOOOOOOOOOOOOOOOOOOOkxxxkOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOkxxxkOOOOOOOOOOOOOOOOOOOOOOOOOOOOkxxxkOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOkxxxkOOOOOOOOOOOOOOOOOOOOOOOOOOOOkxxxkOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ldxddoooxOOOOOOOOOOOOOOOOOOOOOOOOOOOOxoooddxdl:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::::::oOOOOOOOOOOOOOOOOOOOOOOOOOOOOo::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOOOOOkxdxkOOOOOOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOOOOOo:::okOOOOOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOkxddl:;:lddxxkOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:oOOOOOOOo::::;;;:::::oOOOOOOOo:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:ldddddxdl:;;;;;;;;;;:ldxxxxxdl:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::::::::;;;;;;;;;;;;:::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
*/contractChonksMainisIChonkStorage, IERC165, ERC721Enumerable, Ownable, IERC4906, ReentrancyGuard{
// ERC-6551 Boilerplate addresses
IRegistry constant REGISTRY = IRegistry(0x000000006551c19487814612e58FE06813775758);
addressconstant ACCOUNT_PROXY =0x55266d75D1a14E4572138116aF39863Ed6596E7F;
addressconstant ACCOUNT_IMPLEMENTATION =0x41C8f39463A868d3A88af00cd0fe7102F30E44eC;
// constantsuint8constant MAX_MINT_AMOUNT =10;
// Storage for Body metadatamapping(uint256=> IChonkStorage.BodyMetadata) public bodyIndexToMetadata;
/// The address of the ERC-721 Traits contract
ChonkTraits public traitsContract;
// The address of the ChonksMarket contract
ChonksMarket public marketplace;
// The address of the ChonkEquipHelper helper contract
ChonkEquipHelper public chonkEquipHelper;
// The contract that handles rendering and minting the first release of traits
FirstReleaseDataMinter public firstReleaseDataMinter;
// The render contract that handles SVG generation
MainRenderer2D public mainRenderer2D;
// The render contract that handles 3d generation
MainRenderer3D public mainRenderer3D;
uint256public maxTraitsToOutput =99;
uint256public nextTokenId;
addresspublic withdrawAddress;
uint256public price;
uint256public initialMintStartTime;
string[2] descriptionParts;
// The date when contract was deployed, a year after which, certain functions can't be called by the owneruint256publicimmutable deploymentTime;
mapping(uint256 tokenID => StoredChonk chonk) public chonkTokens;
// Mapping of tokenID to the TBA account addressmapping(uint256=>address) public tokenIdToTBAAccountAddress;
// Mapping of the TBA account address to its tokenId. Great for getting from Trait Token ID to Chonk Token ID or Ownermapping(address=>uint256) public tbaAddressToTokenId;
// Chonk ID to approved addressesmapping(uint256 chonkId =>address[] operators) public chonkIdToApprovedOperators;
// Mappings for Merklesmapping(address=>bool) public collectionsAddressDidUse;
mapping(address=>bool) public friendsAddressDidUse;
mapping(address=>bool) public creatorsAddressDidUse;
/// Merkle Rootsbytes32public collectionsMerkle;
bytes32public friendsMerkle;
bytes32public creatorsMerkle;
/// ErrorserrorCanOnlyReserveFirstTwo();
errorCantTransferDuringMint();
errorCantTransferToTBAs();
errorChonkDoesntExist();
errorFirstReleaseDataMinterNotSet();
errorIncorrectChonkOwner();
errorIncorrectTBAOwner();
errorIncorrectTraitType();
errorInsufficientFunds();
errorInvalidBodyIndex();
errorInvalidColor();
errorInvalidTraitCount();
errorInvalidTraitCategory();
errorInvalidMintAmount();
errorMintEnded();
errorMintNotStarted();
errorMintStartTimeAlreadySet();
errorTimelocked();
errorTraitLengthsMustMatch();
errorUseUnequip();
errorWithdrawFailed();
/// ModifiermodifieronlyChonkOwner(uint256 _chonkId) {
if (msg.sender!= ownerOf(_chonkId)) revert IncorrectChonkOwner();
_;
}
/// Constructorconstructor() ERC721("Chonks", "CHONKS") {
_initializeOwner(msg.sender);
deploymentTime =block.timestamp;
}
functionteamReserve() publiconlyOwner{
if (totalSupply() >2) revert CanOnlyReserveFirstTwo();
_mintInternal(msg.sender, 2, 7);
}
functionteamMint(address _to, uint256 _amount, uint8 _traitCount) publiconlyOwner{
if (_traitCount <4|| _traitCount >7) revert InvalidTraitCount();
if (initialMintStartTime ==0||block.timestamp< initialMintStartTime) revert MintNotStarted();
if (block.timestamp> initialMintStartTime +26hours) revert MintEnded();
_mintInternal(_to, _amount, _traitCount);
}
functionmint(uint256 _amount, bytes32[] memory _merkleProof) publicpayable{
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 +24hours) 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;
}
} elseif (MerkleProofLib.verify(_merkleProof, friendsMerkle, leaf)) {
if (!friendsAddressDidUse[msg.sender]) {
traitCount =6;
friendsAddressDidUse[msg.sender] =true;
}
} elseif (MerkleProofLib.verify(_merkleProof, creatorsMerkle, leaf)) {
if (!creatorsAddressDidUse[msg.sender]) {
traitCount =7;
creatorsAddressDidUse[msg.sender] =true;
}
}
_mintInternal(msg.sender, _amount, traitCount);
}
function_mintInternal(address _to, uint256 _amount, uint8 _traitCount) internal{
for (uint i; i < _amount; ++i) {
uint256 tokenId =++nextTokenId;
_mint(_to, tokenId);
address tokenBoundAccountAddress = REGISTRY.createAccount(
ACCOUNT_PROXY, // implementation address0, // salt8453, // chainIdaddress(this), // tokenContract
tokenId // tokenId
);
// Set the cross-reference between tokenId and TBA account address
tokenIdToTBAAccountAddress[tokenId] = tokenBoundAccountAddress;
tbaAddressToTokenId[tokenBoundAccountAddress] = tokenId;
// Initialize the TBA
IAccountProxy(payable(tokenBoundAccountAddress)).initialize(address(ACCOUNT_IMPLEMENTATION));
// Mint Traits to equip belowuint256[] memory traitsIds = firstReleaseDataMinter.safeMintMany(tokenBoundAccountAddress, _traitCount);
// Initialize the Chonk
StoredChonk storage chonk = chonkTokens[tokenId];
chonk.tokenId = tokenId;
// This randomly picks your Chonk skin color but you can change it any time.
chonk.bodyIndex =uint8(uint256(keccak256(abi.encodePacked(tokenId))) %5); // even chance for 5 different bodies// Set the default background color
chonk.backgroundColor ="0D6E9D";
chonk.shoesId = traitsIds[0];
chonk.bottomId = traitsIds[1];
chonk.topId = traitsIds[2];
chonk.hairId = traitsIds[3];
}
}
functiongetOwnerAndTBAAddressForChonkId(uint256 _chonkId) publicviewreturns (address owner, address tbaAddress) {
owner = ownerOf(_chonkId);
tbaAddress = tokenIdToTBAAccountAddress[_chonkId];
}
/// Equip/Unequip Traitsfunctionequip(uint256 _chonkTokenId, uint256 _traitTokenId) publiconlyChonkOwner(_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));
}
functionunequip(uint256 _chonkTokenId, TraitCategory.Name traitType) publiconlyChonkOwner(_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;
elseif (traitType == TraitCategory.Name.Hair) chonkTokens[_chonkTokenId].hairId = _traitTokenId;
elseif (traitType == TraitCategory.Name.Face) chonkTokens[_chonkTokenId].faceId = _traitTokenId;
elseif (traitType == TraitCategory.Name.Accessory) chonkTokens[_chonkTokenId].accessoryId = _traitTokenId;
elseif (traitType == TraitCategory.Name.Top) chonkTokens[_chonkTokenId].topId = _traitTokenId;
elseif (traitType == TraitCategory.Name.Bottom) chonkTokens[_chonkTokenId].bottomId = _traitTokenId;
elseif (traitType == TraitCategory.Name.Shoes) chonkTokens[_chonkTokenId].shoesId = _traitTokenId;
}
functionunequipAll(uint256 _chonkTokenId) publiconlyChonkOwner(_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);
}
functionequipMany(uint256 _chonkTokenId,
uint256[] calldata _traitTokenIds,
uint8[] calldata _traitCategories
) publiconlyChonkOwner(_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;
} elseif (_traitCategory ==uint8(TraitCategory.Name.Hair)) {
chonk.hairId = _traitTokenId;
} elseif (_traitCategory ==uint8(TraitCategory.Name.Face)) {
chonk.faceId = _traitTokenId;
} elseif (_traitCategory ==uint8(TraitCategory.Name.Accessory)) {
chonk.accessoryId = _traitTokenId;
} elseif (_traitCategory ==uint8(TraitCategory.Name.Top)) {
chonk.topId = _traitTokenId;
} elseif (_traitCategory ==uint8(TraitCategory.Name.Bottom)) {
chonk.bottomId = _traitTokenId;
} elseif (_traitCategory ==uint8(TraitCategory.Name.Shoes)) {
chonk.shoesId = _traitTokenId;
}
emit Equip(owner, _chonkTokenId, _traitTokenId, _traitCategory);
}
emit EquipAll(owner, _chonkTokenId);
}
/// tokenURI/RenderingfunctiontokenURI(uint256 _tokenId) publicviewoverridereturns (stringmemory) {
if (!_exists(_tokenId)) revert ChonkDoesntExist();
return renderAsDataUri(_tokenId);
}
/// @param _index The index of the body to get the SVG for/// @return svg The SVG for the bodyfunctiongetBodyImageSvg(uint256 _index) publicviewreturns (stringmemory) {
return mainRenderer2D.colorMapToSVG(bodyIndexToMetadata[_index].colorMap);
}
functiongetBodySVGZmapsAndMetadata(IChonkStorage.StoredChonk memory storedChonk) publicviewreturns (stringmemory, bytesmemory , stringmemory) {
return (
getBodyImageSvg(storedChonk.bodyIndex),
bodyIndexToMetadata[storedChonk.bodyIndex].zMap,
mainRenderer2D.stringTrait('Body Type', bodyIndexToMetadata[storedChonk.bodyIndex].bodyName)
);
}
functiongetBodySvgAndMetadata(IChonkStorage.StoredChonk memory storedChonk) publicviewreturns (stringmemory, stringmemory) {
return (
getBodyImageSvg(storedChonk.bodyIndex),
mainRenderer2D.stringTrait('Body Type', bodyIndexToMetadata[storedChonk.bodyIndex].bodyName)
);
}
// Returns all necessary ownership info for a TraitfunctiongetFullPictureForTrait(uint256 _chonkTraitTokenId) publicviewreturns (address traitOwnerTBA,
uint256 chonkTokenId,
address chonkOwner,
bool isEquipped
) {
traitOwnerTBA = traitsContract.ownerOf(_chonkTraitTokenId);
chonkTokenId = tbaAddressToTokenId[traitOwnerTBA];
chonkOwner = ownerOf(chonkTokenId);
isEquipped = checkIfTraitIsEquipped(chonkTokenId, _chonkTraitTokenId);
}
/// @notice Returns the TBA address for a ChonkfunctiongetTBAAddressForChonkId(uint256 _chonkId) publicviewreturns (address) {
return tokenIdToTBAAccountAddress[_chonkId];
}
/// @notice Returns the ChonkId for a TBAfunctiongetChonkIdForTBAAddress(address _tbaAddress) publicviewreturns (uint256) {
return tbaAddressToTokenId[_tbaAddress];
}
functiongetTraitsForChonkId(uint256 _chonkId) publicviewreturns (uint256[] memory traitTokens) {
address tbaAddress = getTBAAddressForChonkId(_chonkId);
traitTokens = traitsContract.walletOfOwner(tbaAddress);
}
functiongetBackpackSVGs(uint256 _tokenId) publicviewreturns (stringmemory) {
return mainRenderer2D.getBackpackSVGs(
address(traitsContract),
getTBAAddressForChonkId(_tokenId),
maxTraitsToOutput
);
}
function_gatherData(uint256 _tokenId) internalviewreturns (stringmemory bodySvg,
bytesmemory bodyZmap,
stringmemory traitsSvg,
bytesmemory traitZmaps,
stringmemory traitsAttributes,
stringmemory backpackSVGs,
bytesmemory fullZmap,
ChonkData memory chonkdata
) {
StoredChonk memory storedChonk = getChonk(_tokenId);
(bodySvg, bodyZmap,) = getBodySVGZmapsAndMetadata(storedChonk);
(traitsSvg, traitZmaps, traitsAttributes) = traitsContract.getSvgZmapsAndMetadata(storedChonk);
backpackSVGs = getBackpackSVGs(_tokenId);
chonkdata.backgroundColor = storedChonk.backgroundColor;
chonkdata.numOfItemsInBackpack = getTraitsForChonkId(_tokenId).length;
chonkdata.bodyName = bodyIndexToMetadata[storedChonk.bodyIndex].bodyName;
chonkdata.descriptionParts = descriptionParts; // stuffing descriptionParts in here to avoid stack too deep
fullZmap =bytes.concat(bodyZmap, traitZmaps);
}
functionrenderAsDataUri(uint256 _tokenId) publicviewreturns (stringmemory) {
return (getChonk(_tokenId).render3D) ? renderAsDataUri3D(_tokenId) : renderAsDataUri2D(_tokenId);
}
functionrenderAsDataUri2D(uint256 _tokenId) publicviewreturns (stringmemory) {
(
stringmemory bodySvg,,
stringmemory traitsSvg,,
stringmemory traitsAttributes,
stringmemory backpackSVGs,,
ChonkData memory chonkdata
) = _gatherData(_tokenId);
return mainRenderer2D.renderAsDataUri(
_tokenId,
bodySvg,
traitsSvg,
traitsAttributes,
backpackSVGs,
chonkdata
);
}
functionrenderAsDataUri3D(uint256 _tokenId) publicviewreturns (stringmemory) {
(
stringmemory bodySvg,,
stringmemory traitsSvg,,
stringmemory traitsAttributes,,
bytesmemory fullZmap,
ChonkData memory chonkdata
) = _gatherData(_tokenId);
return mainRenderer3D.renderAsDataUri(
_tokenId,
bodySvg,
traitsSvg,
traitsAttributes,
fullZmap,
chonkdata
);
}
functionchonkMakeover(uint256 _chonkTokenId,
uint256[] calldata _traitTokenIds,
uint8[] calldata _traitCategories,
uint8 _bodyIndex,
stringmemory _backgroundColor,
bool _render3D
) publiconlyChonkOwner(_chonkTokenId) {
equipMany(_chonkTokenId, _traitTokenIds, _traitCategories);
setBodyIndex(_chonkTokenId, _bodyIndex);
setBackgroundColor(_chonkTokenId, _backgroundColor);
setTokenRender3D(_chonkTokenId, _render3D);
}
/// Getters// Gets complete zMap for a Chonk, body and traitsfunctiongetChonkZMap(uint256 _tokenId) publicviewreturns (stringmemory) {
bytesmemory traitZmaps;
(, traitZmaps,) = traitsContract.getSvgZmapsAndMetadata(getChonk(_tokenId));
returnstring.concat(
getBodyZMap(_tokenId),
string(traitZmaps)
);
}
functiongetBodyZMap(uint256 _tokenId) publicviewreturns (stringmemory) {
bytesmemory bodyZmap;
(, bodyZmap,) = getBodySVGZmapsAndMetadata(getChonk(_tokenId));
returnstring(bodyZmap);
}
functiongetChonk(uint256 _tokenId) publicviewreturns (IChonkStorage.StoredChonk memory) {
return chonkTokens[_tokenId];
}
functioncheckIfTraitIsEquipped(uint256 _chonkId, uint256 _traitId) publicviewreturns (bool) {
IChonkStorage.StoredChonk memory storedChonk = getChonk(_chonkId);
return storedChonk.headId == _traitId ||
storedChonk.hairId == _traitId ||
storedChonk.faceId == _traitId ||
storedChonk.accessoryId == _traitId ||
storedChonk.topId == _traitId ||
storedChonk.bottomId == _traitId ||
storedChonk.shoesId == _traitId;
}
/// @dev Returns the token ids the end user's wallet ownsfunctionwalletOfOwner(address _owner) publicviewreturns (uint256[] memory) {
uint256 tokenCount = balanceOf(_owner);
uint256[] memory tokensId =newuint256[](tokenCount);
for (uint256 i; i < tokenCount; ++i){
tokensId[i] = tokenOfOwnerByIndex(_owner, i);
}
return tokensId;
}
/// @notice Returns the timestamp one year after contract deploymentfunctiononeYearFromDeployment() publicviewreturns (uint256) {
return deploymentTime +365days;
}
functionisTimelocked() publicviewreturns (bool) {
returnblock.timestamp> oneYearFromDeployment();
}
/// Ownable FunctionsfunctionaddNewBody(uint256 _bodyIndex, stringmemory _bodyName, bytesmemory _colorMap, bytesmemory _zMap) publiconlyOwner{
if (isTimelocked()) revert Timelocked();
BodyMetadata storage metadata = bodyIndexToMetadata[_bodyIndex];
metadata.bodyIndex = _bodyIndex;
metadata.bodyName = _bodyName;
metadata.colorMap = _colorMap;
metadata.zMap = _zMap;
}
functionsetTraitsContract(ChonkTraits _address) publiconlyOwner{
if (isTimelocked()) revert Timelocked();
traitsContract = _address;
}
functionsetFirstReleaseDataMinter(address _dataContract) publiconlyOwner{
if (isTimelocked()) revert Timelocked();
firstReleaseDataMinter = FirstReleaseDataMinter(_dataContract);
}
functionsetMainRenderer2D(address _mainRenderer2D) publiconlyOwner{
if (isTimelocked()) revert Timelocked();
mainRenderer2D = MainRenderer2D(_mainRenderer2D);
}
functionsetMainRenderer3D(address _mainRenderer3D) publiconlyOwner{
if (isTimelocked()) revert Timelocked();
mainRenderer3D = MainRenderer3D(_mainRenderer3D);
}
functionsetMarketplace(address _marketplace) publiconlyOwner{
if (isTimelocked()) revert Timelocked();
marketplace = ChonksMarket(_marketplace);
}
functionsetChonkEquipHelper(address _chonkEquipHelper) publiconlyOwner{
if (isTimelocked()) revert Timelocked();
chonkEquipHelper = ChonkEquipHelper(_chonkEquipHelper);
}
functionsetMaxTraitsToOutput(uint256 _maxTraitsToOutput) publiconlyOwner{
maxTraitsToOutput = _maxTraitsToOutput;
}
functionsetMintStartTime(uint256 _initialMintStartTime) publiconlyOwner{
if (initialMintStartTime !=0) revert MintStartTimeAlreadySet();
initialMintStartTime = _initialMintStartTime;
}
functionsetWithdrawAddress(address _withdrawAddress) publiconlyOwner{
withdrawAddress = _withdrawAddress;
}
functionsetPrice(uint256 _priceInWei) publiconlyOwner{
price = _priceInWei;
}
functionsetFriendsMerkleRoot(bytes32 _merkleRoot) publiconlyOwner{
friendsMerkle = _merkleRoot;
}
functionsetCollectionsMerkle(bytes32 _merkleRoot) publiconlyOwner{
collectionsMerkle = _merkleRoot;
}
functionsetCreatorsMerkle(bytes32 _merkleRoot) publiconlyOwner{
creatorsMerkle = _merkleRoot;
}
functionsetDescriptionParts(string[2] memory _descriptionParts) publiconlyOwner{
descriptionParts = _descriptionParts;
}
/// Public SettersfunctionsetBodyIndex(uint256 _chonkTokenId, uint8 _bodyIndex) publiconlyChonkOwner(_chonkTokenId) {
if (_bodyIndex >4) revert InvalidBodyIndex();
chonkTokens[_chonkTokenId].bodyIndex = _bodyIndex;
emit BodyIndex(ownerOf(_chonkTokenId), _chonkTokenId, _bodyIndex );
}
functionsetTokenRender3D(uint256 _tokenId, bool _render3D) publiconlyChonkOwner(_tokenId) {
chonkTokens[_tokenId].render3D = _render3D;
emit Render3D(ownerOf(_tokenId), _tokenId, _render3D);
}
functionvalidateColor(stringmemory _color) internalpure{
bytesmemory colorBytes =bytes(_color);
if (colorBytes.length!=6) revert InvalidColor();
// Ensure all characters are valid hex characters (0-9, a-f, A-F)for (uint i; i <6; i++) {
if (
!(colorBytes[i] >=0x30&& colorBytes[i] <=0x39) &&// 0-9!(colorBytes[i] >=0x41&& colorBytes[i] <=0x46) &&// A-F!(colorBytes[i] >=0x61&& colorBytes[i] <=0x66) // a-f
) {
revert InvalidColor(); // Invalid character found
}
}
}
functionsetBackgroundColor(uint256 _chonkTokenId, stringmemory _color) publiconlyChonkOwner(_chonkTokenId) {
validateColor(_color); // Call the helper function
chonkTokens[_chonkTokenId].backgroundColor = _color;
emit BackgroundColor(ownerOf(_chonkTokenId), _chonkTokenId, _color );
}
functionsetChonkAttributes(uint256 _tokenId, stringmemory _color, uint8 _bodyIndex, bool _render3D) publiconlyChonkOwner(_tokenId) {
validateColor(_color); // Call the helper functionif (_bodyIndex >4) revert InvalidBodyIndex();
chonkTokens[_tokenId].backgroundColor = _color;
chonkTokens[_tokenId].bodyIndex = _bodyIndex;
chonkTokens[_tokenId].render3D = _render3D;
}
// BoilerplatefunctionsupportsInterface(bytes4 interfaceId) publicviewoverride(IERC165, ERC721Enumerable) returns (bool) {
returnsuper.supportsInterface(interfaceId);
}
// Override functions for marketplace compatibilityfunction_beforeTokenTransfer(addressfrom, address to, uint256 tokenId) internaloverride{
if (from==address(0)) {
super._beforeTokenTransfer(from, to, tokenId);
return;
}
if (block.timestamp< initialMintStartTime +24hours) revert CantTransferDuringMint();
// Ensure you can't transfer a Chonk to a TBA (Chonks can't hold Chonks)if (tbaAddressToTokenId[to] !=0) revert CantTransferToTBAs();
// Cache TBA address and trait tokens to minimize external callsaddress tbaAddress = tokenIdToTBAAccountAddress[tokenId];
uint256[] memory chonkIds = walletOfOwner(to);
address[] memory tbas =newaddress[](chonkIds.length);
for (uint256 j; j < chonkIds.length; ++j) {
tbas[j] = tokenIdToTBAAccountAddress[chonkIds[j]];
}
marketplace.deleteChonkOfferBeforeTokenTransfer(tokenId);
marketplace.deleteChonkBidsBeforeTokenTransfer(tokenId, to);
uint256[] memory traitTokenIds = traitsContract.walletOfOwner(tbaAddress);
for (uint256 i; i < traitTokenIds.length; ++i) {
uint256 traitTokenId = traitTokenIds[i];
// Clean up marketplace offers/bids
marketplace.deleteTraitOffersBeforeTokenTransfer(traitTokenId);
marketplace.deleteTraitBidsBeforeTokenTransfer(traitTokenId, tbas);
// Clean up past approvals for new TBA owner
traitsContract.invalidateAllOperatorApprovals(traitTokenId);
}
super._beforeTokenTransfer(from, to, tokenId);
}
function_afterTokenTransfer(address _from, address, uint256 _tokenId) internalvirtualoverride{
_invalidateAllOperatorApprovals(_tokenId, _from);
}
/// ApprovalsfunctiongetChonkIdToApprovedOperators(uint256 _chonkId) publicviewreturns (address[] memory) {
return chonkIdToApprovedOperators[_chonkId];
}
functionapprove(address _operator, uint256 _chonkId) publicoverride(IERC721, ERC721) {
if (msg.sender!= ownerOf(_chonkId)) revert Unauthorized();
_incrementApprovals(_chonkId, _operator);
_approve(_operator, _chonkId);
}
functionsetApprovalForAllChonksMarketplace(uint256 _chonkId, address _operator, bool _approved) public{
address owner = ownerOf(_chonkId);
if (owner !=msg.sender) revert Unauthorized();
if (_approved) _incrementApprovals(_chonkId, _operator);
_setApprovalForAll(owner, _operator, _approved);
}
// Please use the function above as it's more appropriate. Traditional marketplaces will use thisfunctionsetApprovalForAll(address _operator, bool _approved) publicoverride(IERC721, ERC721) {
if (_approved) {
uint256[] memory chonkIds = walletOfOwner(msg.sender);
// Don't approve if the user doesn't own any Chonksif (chonkIds.length==0) revert Unauthorized();
for (uint i; i < chonkIds.length; ++i) {
_incrementApprovals(chonkIds[i], _operator);
}
}
_setApprovalForAll(msg.sender, _operator, _approved);
}
function_incrementApprovals(uint256 _chonkId, address _operator) private{
address[] storage operators = chonkIdToApprovedOperators[_chonkId];
operators.push(_operator);
}
/// @dev – Called on _afterTokenTransfer/// Prevents subsequent owners from using the previous owner's approvalsfunction_invalidateAllOperatorApprovals(uint256 _chonkId, address _previousOwner) private{
address[] memory approvals = chonkIdToApprovedOperators[_chonkId];
address tbaForChonk = tokenIdToTBAAccountAddress[_chonkId];
// may need to use tbaAddressToTokenId w/ msg.sender value and check that?// Invalidate all other approvals including the ChonksMarketfor (uint256 i; i < approvals.length; ++i) {
_setApprovalForAll(_previousOwner, approvals[i], false);
_setApprovalForAll(tbaForChonk, approvals[i], false);
}
delete chonkIdToApprovedOperators[_chonkId];
}
/// Withdrawfunctionwithdraw() publiconlyOwner{
(bool success,) =payable(withdrawAddress).call{ value: address(this).balance }("");
if (!success) revert WithdrawFailed();
}
}
Contract Source Code
File 5 of 40: ChonksMarket.sol
// SPDX-License-Identifier: MITpragmasolidity ^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";
contractChonksMarketisOwnable, ReentrancyGuard{
// Structs// If a Chonk offer, sells the Chonk and all of its Traits, else just sells the TraitstructChonkOffer {
// How much for the Chonkuint256 priceInWei;
// Who is selling (the end user wallet)address seller;
// The TBA of the Chonk IDaddress sellerTBA;
// An optional address to restrict the buyer toaddress onlySellTo;
// Accompanying Trait IDsuint256[] traitIds;
// An abi.encoded version of the traitIdsbytes encodedTraitIds;
}
structTraitOffer {
// How much for the Chonkuint256 priceInWei;
// Who is selling (the end user wallet)address seller;
// The TBA of the Chonk IDaddress sellerTBA;
// An optional address to restrict the buyer toaddress onlySellTo;
}
structChonkBid {
// The address of the bidderaddress bidder;
// The amount in Weiuint256 amountInWei;
// Accompanying Trait IDsuint256[] traitIds;
// An abi.encoded version of the traitIdsbytes encodedTraitIds;
}
structTraitBid {
// The address of the bidderaddress bidder;
// Chonk TBAaddress bidderTBA;
// The amount in Weiuint256 amountInWei;
}
// Storage
ChonksMain publicimmutable CHONKS_MAIN;
ChonkTraits publicimmutable CHONK_TRAITS;
uint256public royaltyPercentage; // starts at 250 (for 2.5%)addresspublic teamWallet;
boolpublic paused =true;
boolpublic pausabilityRevoked;
// Offersmapping(uint256 chonkId => ChonkOffer chonkOffer) public chonkOffers;
mapping(uint256 traitId => TraitOffer traitOffer) public traitOffers;
// Bidsmapping(uint256 chonkId => ChonkBid chonkBid) public chonkBids;
mapping(uint256 traitId => TraitBid traitBid) public traitBids;
// Fundsmapping(address eoa =>uint256 balance) public withdrawableFunds;
// Approvals for TBAmapping(uint256 chonkId =>address[] operators)
public chonkIdToApprovedOperators;
/// ErrorserrorApproveTheMarketplace();
errorBidderChanged();
errorBidIsTooLow();
errorCantAcceptYourOwnBid();
errorCantBeZero();
errorCantBidOnYourOwnChonk();
errorCantBidOnYourOwnTrait();
errorCantBuyYourOwnChonk();
errorCantBuyYourOwnTrait();
errorCMUnauthorized();
errorNoBidToAccept();
errorNoOfferToCancel();
errorNotYourBid();
errorNotYourChonk();
errorNotYourOffer();
errorNotYourTrait();
errorOfferDoesNotExist();
errorOnlySellToEOAs();
errorOnlyTraitContract();
errorPaused();
errorPausabilityRevoked();
errorTBANeedsToApproveMarketplace();
errorTraitEquipped();
errorTraitIdsChangedSinceBid();
errorTraitIdsChangedSinceListingRelist();
errorWithdrawFailed();
errorWrongAmount();
errorYouCantBuyThatChonk();
errorYouCantBuyThatTrait();
/// Events (These map to the order of the functions below)// Chonk EventseventChonkOfferCanceled(uint256indexed chonkId,
addressindexed seller
);
eventChonkOffered(uint256indexed chonkId,
uint256indexed price,
addressindexed seller,
address sellerTBA
);
eventChonkOfferedToAddress(uint256indexed chonkId,
uint256indexed price,
addressindexed seller,
address sellerTBA,
address onlySellTo
);
eventChonkBought(uint256indexed chonkId,
addressindexed buyer,
uint256indexed amountInWei,
address seller
);
eventChonkBidWithdrawn(uint256indexed chonkId,
addressindexed bidder,
uint256indexed amountInWei
);
eventChonkBidEntered(uint256indexed chonkId,
addressindexed bidder,
uint256indexed amountInWei
);
eventChonkBidAccepted(uint256indexed chonkId,
uint256indexed amountInWei,
addressindexed buyer,
address seller
);
// Trait EventseventTraitOfferCanceled(uint256indexed traitId,
addressindexed seller
);
eventTraitOffered(uint256indexed traitId,
uint256indexed price,
addressindexed seller,
address sellerTBA
);
eventTraitOfferedToAddress(uint256indexed traitId,
uint256indexed price,
addressindexed seller,
address sellerTBA,
address onlySellTo
);
eventTraitBought(uint256indexed traitId,
addressindexed buyerTBA,
uint256indexed amountInWei,
address buyer,
address seller
);
eventTraitBidWithdrawn(uint256indexed traitId,
addressindexed bidder,
uint256indexed amountInWei
);
eventTraitBidEntered(uint256indexed traitId,
addressindexed bidder,
uint256indexed amountInWei
);
eventTraitBidAccepted(uint256indexed traitId,
uint256indexed amountInWei,
addressindexed buyer,
address seller
);
/// ModifiersmodifierensurePriceIsNotZero(uint256 _price) {
if (_price ==0) revert CantBeZero();
_;
}
modifiernotPaused() {
if (paused) revert Paused();
_;
}
modifieronlyTraitContract() {
if (msg.sender!=address(CHONK_TRAITS)) revert OnlyTraitContract();
_;
}
modifieronlyMainContract() {
if (msg.sender!=address(CHONKS_MAIN)) revert CMUnauthorized();
_;
}
/// Constructorconstructor(address _chonksMain,
address _chonkTraits,
uint8 _royaltyPercentage,
address _teamWallet
) {
_initializeOwner(msg.sender);
CHONKS_MAIN = ChonksMain(_chonksMain);
CHONK_TRAITS = ChonkTraits(_chonkTraits);
royaltyPercentage = _royaltyPercentage;
teamWallet = _teamWallet;
}
// GETTERS// Add a custom getter functionfunctiongetChonkOffer(uint256 _chonkId) publicviewreturns (uint256 priceInWei,
address seller,
address sellerTBA,
address onlySellTo,
uint256[] memory traitIds,
bytesmemory encodedTraitIds
) {
ChonkOffer memory offer = chonkOffers[_chonkId];
return (
offer.priceInWei,
offer.seller,
offer.sellerTBA,
offer.onlySellTo,
offer.traitIds,
offer.encodedTraitIds
);
}
functiongetTraitOffer(uint256 _traitId) publicviewreturns (uint256 priceInWei,
address seller,
address sellerTBA,
address onlySellTo
) {
TraitOffer memory offer = traitOffers[_traitId];
return (
offer.priceInWei,
offer.seller,
offer.sellerTBA,
offer.onlySellTo
);
}
functiongetChonkBid(uint256 _chonkId) publicviewreturns (address bidder,
uint256 amountInWei,
uint256[] memory traitIds,
bytesmemory encodedTraitIds
) {
ChonkBid memory bid = chonkBids[_chonkId];
return (
bid.bidder,
bid.amountInWei,
bid.traitIds,
bid.encodedTraitIds
);
}
functiongetTraitBid(uint256 _traitId) publicviewreturns (address bidder,
address bidderTBA,
uint256 amountInWei
) {
TraitBid memory bid = traitBids[_traitId];
return (
bid.bidder,
bid.bidderTBA,
bid.amountInWei
);
}
/*
Chonk
Cancel, Offer, Buy
Withdraw Bid, Bid, Accept Bid
*/functioncancelOfferChonk(uint256 _chonkId) public{
if (chonkOffers[_chonkId].seller !=msg.sender) revert NotYourOffer();
delete chonkOffers[_chonkId];
emit ChonkOfferCanceled(_chonkId, msg.sender);
}
functionofferChonk(uint256 _chonkId, uint256 _priceInWei) publicnotPausedensurePriceIsNotZero(_priceInWei) {
(address owner, address tbaAddress) = CHONKS_MAIN.getOwnerAndTBAAddressForChonkId(_chonkId);
_offerChonk(_chonkId, _priceInWei, address(0), owner, tbaAddress);
emit ChonkOffered(_chonkId, _priceInWei, owner, tbaAddress);
}
functionofferChonkToAddress(uint256 _chonkId,
uint256 _priceInWei,
address _onlySellTo
) publicnotPausedensurePriceIsNotZero(_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 , bytesmemory encodedTraitIds) = getTraitIdsAndEncodingForChonk(_chonkId);
chonkOffers[_chonkId] = ChonkOffer({
priceInWei: _priceInWei,
seller: _seller,
sellerTBA: _sellerTBA,
onlySellTo: _onlySellTo,
traitIds: traitIds,
encodedTraitIds: encodedTraitIds
});
}
functionbuyChonk(uint256 _chonkId) publicpayablenotPausednonReentrant{
ChonkOffer memory offer = chonkOffers[_chonkId];
// Ensure Offeraddress seller = offer.seller;
if (seller ==address(0)) revert OfferDoesNotExist();
if (seller ==msg.sender) revert CantBuyYourOwnChonk();
if (offer.onlySellTo !=address(0) && offer.onlySellTo !=msg.sender)
revert YouCantBuyThatChonk();
// Ensure correct priceif (offer.priceInWei !=msg.value) revert WrongAmount();
if (!CHONKS_MAIN.isApprovedForAll(offer.seller, address(this)) && CHONKS_MAIN.getApproved(_chonkId) !=address(this))
revert ApproveTheMarketplace();
// Compare current traits owned by the Chonk's TBA with traits at time of listing. Prevents front running attack in the same block
(, bytesmemory encodedTraitIds) = getTraitIdsAndEncodingForChonk(_chonkId);
if (keccak256(encodedTraitIds) !=keccak256(offer.encodedTraitIds))
revert TraitIdsChangedSinceListingRelist();
// Delete the Offerdelete chonkOffers[_chonkId];
// Refund and clear existing Bid if from buyeruint256 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);
// Transfer Chonk (Don't need to transfer Traits because they come with the Chonk)
CHONKS_MAIN.transferFrom(offer.seller, msg.sender, _chonkId);
// Pay Royalties and Seller
_calculateRoyaltiesAndTransferFunds(msg.value, seller);
emit ChonkBought(_chonkId, msg.sender, msg.value, seller);
}
///////////////////////////////////////////////////////////////////////functionwithdrawBidOnChonk(uint256 _chonkId) publicnonReentrant{
// Ensure bid and that it's yours
ChonkBid memory bid = chonkBids[_chonkId];
if (bid.bidder !=msg.sender) revert NotYourBid();
// Delete from mappingdelete chonkBids[_chonkId];
// Refund your bid
_refundBid(msg.sender, bid.amountInWei);
emit ChonkBidWithdrawn(_chonkId, msg.sender, bid.amountInWei);
}
functionbidOnChonk(uint256 _chonkId) publicpayableensurePriceIsNotZero(msg.value) notPausednonReentrant{
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 , bytesmemory 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);
}
functionacceptBidForChonk(uint256 _chonkId, address _bidder) publicnotPausednonReentrant{
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();
// Since they bid, your Chonk-owned traits changed. They need to re-bid.
(, bytesmemory 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);
}
/*
Trait
Cancel, Offer, Buy
Withdraw Bid, Bid, Accept Bid
*/functioncancelOfferTrait(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);
}
/// Note: Needs to be called by the EOA that owns the ChonkfunctionofferTrait(uint256 _traitId,
uint256 _chonkId,
uint256 _priceInWei
) publicnotPausedensurePriceIsNotZero(_priceInWei) {
if (!ensureTraitOwner(_traitId, _chonkId)) revert NotYourTrait();
// Please unequip the trait if you want to sell itif (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);
}
/// @notice This should be called by the EOA that owns the Chonk, not the TBA/// @param _traitId The ID of the Trait you're selling/// @param _chonkId The ID of the Chonk you're selling the Trait for. This Chonk ID must own the `_traitId`/// @param _priceInWei The price of the Trait you're selling, in Wei/// @param _onlySellTo should be the EOA that will be buying the Trait for their ChonkfunctionofferTraitToAddress(uint256 _traitId,
uint256 _chonkId,
uint256 _priceInWei,
address _onlySellTo
) publicnotPausedensurePriceIsNotZero(_priceInWei) {
if (!ensureTraitOwner(_traitId, _chonkId)) revert NotYourTrait();
if (CHONKS_MAIN.tbaAddressToTokenId(_onlySellTo) !=0) revert OnlySellToEOAs();
// Please unequip the trait if you want to sell itif (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);
}
/// @notice This should be called by the EOA that owns the Chonk/// @param _traitId The ID of the Trait you're buying/// @param _forChonkId should be your Chonk you're buying the Trait forfunctionbuyTrait(uint256 _traitId, uint256 _forChonkId) publicpayablenotPausednonReentrant{
// Ensure msg.sender owns the Chonk token of the TBAaddress owner = CHONKS_MAIN.ownerOf(_forChonkId);
if (owner !=msg.sender) revert NotYourChonk();
// Ensure you don't own the Traitaddress tba = CHONKS_MAIN.tokenIdToTBAAccountAddress(_forChonkId);
address traitOwnerTBAAddress = CHONK_TRAITS.ownerOf(_traitId);
if (traitOwnerTBAAddress == tba) revert CantBuyYourOwnTrait();
// Ensure Offer
TraitOffer memory offer = traitOffers[_traitId];
if (!CHONK_TRAITS.isApprovedForAll(offer.sellerTBA, address(this)) && CHONK_TRAITS.getApproved(_traitId) !=address(this))
revert TBANeedsToApproveMarketplace();
address seller = offer.seller;
if (seller ==address(0)) revert OfferDoesNotExist();
if (seller ==msg.sender) revert CantBuyYourOwnTrait();
if (offer.onlySellTo !=address(0) && offer.onlySellTo !=msg.sender)
revert YouCantBuyThatTrait();
// Ensure correct priceif (offer.priceInWei !=msg.value) revert WrongAmount();
(,,, bool isEquipped) = CHONKS_MAIN.getFullPictureForTrait(_traitId);
if (isEquipped) revert TraitEquipped();
// Delete the Offerdelete traitOffers[_traitId];
// Clear existing Bid if it existsuint256 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);
}
///////////////////////////////////////////////////////////////////////functionwithdrawBidOnTrait(uint256 _traitId) publicnonReentrant{
// Ensure bid and that it's yours
TraitBid memory bid = traitBids[_traitId];
if (bid.bidder !=msg.sender) revert NotYourBid();
// Delete from mappingdelete traitBids[_traitId];
// Refund your bid
_refundBid(msg.sender, bid.amountInWei);
emit TraitBidWithdrawn(_traitId, msg.sender, bid.amountInWei);
}
functionbidOnTrait(uint256 _traitId, uint256 _yourChonkId) publicpayableensurePriceIsNotZero(msg.value) notPausednonReentrant{
(address chonkOwner, address tbaAddressOfBiddersChonk) = CHONKS_MAIN.getOwnerAndTBAAddressForChonkId(_yourChonkId);
// Ensure msg.sender owns the Chonk trait will go toif (chonkOwner !=msg.sender) revert NotYourChonk();
// Ensure msg.sender does own Chonk or Trait
(address traitOwnerTBA, , address traitChonkOwner, ) = CHONKS_MAIN.getFullPictureForTrait(_traitId);
if (traitChonkOwner ==msg.sender|| traitOwnerTBA ==msg.sender) revert CantBidOnYourOwnTrait();
TraitBid memory existingBid = traitBids[_traitId];
if (msg.value<= existingBid.amountInWei) revert BidIsTooLow();
// address bidderTBA = CHONKS_MAIN.tokenIdToTBAAccountAddress(_yourChonkId);
traitBids[_traitId] = TraitBid(msg.sender, tbaAddressOfBiddersChonk, msg.value);
if (existingBid.amountInWei >0) {
_refundBid(existingBid.bidder, existingBid.amountInWei);
}
emit TraitBidEntered(_traitId, msg.sender, msg.value);
}
functionacceptBidForTrait(uint256 _traitId, address _bidder) publicnotPausednonReentrant{
// Ensure Bid
TraitBid memory bid = traitBids[_traitId];
address bidder = bid.bidder;
if (bidder ==address(0)) revert NoBidToAccept();
if (bidder ==msg.sender) revert CantAcceptYourOwnBid();
if (bidder != _bidder) revert BidderChanged();
(address sellerTBA, , address seller, bool isEquipped) = CHONKS_MAIN.getFullPictureForTrait(_traitId);
if (seller !=msg.sender) revert NotYourTrait();
if (isEquipped) revert TraitEquipped();
// Delete Offer for trait ID if present, delete Bid you're acceptingdelete traitOffers[_traitId];
delete traitBids[_traitId];
_calculateRoyaltiesAndTransferFunds(bid.amountInWei, seller);
CHONK_TRAITS.transferFrom(sellerTBA, bid.bidderTBA, _traitId);
emit TraitBidAccepted(_traitId, bid.amountInWei, bidder, seller);
}
/// Helper Functions// Ensures that the msg.sender owns the Chonk which owns the TBA that owns the TraitfunctionensureTraitOwner(uint256 _traitId, uint256 _chonkId) publicviewreturns (bool) {
address traitOwnerTBA = CHONK_TRAITS.ownerOf(_traitId);
(address chonkOwner, address tbaForChonkId) = CHONKS_MAIN.getOwnerAndTBAAddressForChonkId(_chonkId);
return (traitOwnerTBA == tbaForChonkId) && (chonkOwner ==msg.sender);
}
functioncalculateRoyalty(uint256 _amount) publicviewreturns (uint256) {
return (_amount * royaltyPercentage) /10_000;
}
functiongetTraitIdsAndEncodingForChonk(uint256 _chonkId) publicviewreturns (uint256[] memory traitIds, bytesmemory encodedTraitIds) {
(, address tbaAddress) = CHONKS_MAIN.getOwnerAndTBAAddressForChonkId(_chonkId);
traitIds = CHONK_TRAITS.walletOfOwner(tbaAddress);
encodedTraitIds =abi.encode(traitIds);
}
/// Before Token TransferfunctiondeleteChonkOfferBeforeTokenTransfer(uint256 _chonkId) publiconlyMainContract{
ChonkOffer memory offer = chonkOffers[_chonkId];
if (offer.seller !=address(0)) delete chonkOffers[_chonkId];
}
functiondeleteChonkBidsBeforeTokenTransfer(uint256 _chonkId, address _toEOA) publiconlyMainContract{
ChonkBid memory bid = chonkBids[_chonkId];
if (bid.bidder == _toEOA) {
delete chonkBids[_chonkId];
_refundBid(bid.bidder, bid.amountInWei);
}
}
functiondeleteTraitOffersBeforeTokenTransfer(uint256 _traitId) public{
if (msg.sender!=address(CHONKS_MAIN) &&msg.sender!=address(CHONK_TRAITS)) revert CMUnauthorized();
// Delete the Trait Offerif (traitOffers[_traitId].seller !=address(0)) delete traitOffers[_traitId];
}
/// @dev Loops through all of the TBAs associated with the _toEOA address to see if they bid on the Trait. If so, delete and refund the bidderfunctiondeleteTraitBidsBeforeTokenTransfer(uint256 _traitId, address[] memory _toTBAs) public{
if (msg.sender!=address(CHONKS_MAIN)) revert CMUnauthorized();
// This handles the case where the bid.bidder owns multiple Chonks// since each Chonk has its own TBA and when you bid, we record the TBA// the transfer would happen to, we need to check the bid's bidderTBA against// the TBA that will receive the trait and then refund the *bidder* if necessary
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);
}
}
}
}
functiondeleteTraitBidsBeforeTokenTransfer(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);
}
}
}
functionremoveChonkOfferOnTraitTransfer(uint256 _chonkId) publiconlyTraitContract{
delete chonkOffers[_chonkId];
}
/// WithdrawfunctionwithdrawFunds() publicnonReentrant{
uint256 balance = withdrawableFunds[msg.sender];
withdrawableFunds[msg.sender] =0;
(bool success, ) =msg.sender.call{value: balance, gas: 60_000}("");
if (!success) revert WithdrawFailed();
}
/// Privatefunction_calculateRoyaltiesAndTransferFunds(uint256 _amount, address _to) privatereturns (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;
}
/// Only Owner// Set the royalty percentagefunctionsetRoyaltyPercentage(uint256 _royaltyPercentage) publiconlyOwner{
royaltyPercentage = _royaltyPercentage;
}
// Set the wallet to receive royaltiesfunctionsetTeamWallet(address _teamWallet) publiconlyOwner{
teamWallet = _teamWallet;
}
// Allows us to pause the marketfunctionpause(bool _value) publiconlyOwner{
if (pausabilityRevoked) revert PausabilityRevoked();
paused = _value;
}
// Allows us to revoke the pausability. Will happen after enough time has passed and we feel confident in the market// CAUTION: This is irreversible. Ensure `paused` is `false` before revokingfunctionrevokePausability() publiconlyOwner{
if (pausabilityRevoked) revert PausabilityRevoked();
pausabilityRevoked =true;
}
}
Contract Source Code
File 6 of 40: CommitReveal.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.17;libraryCommitReveal{
eventNewEpoch(uint256indexed epoch, uint64indexed revealBlock);
// copy pasta from the legendary Checks contract by jalil + mouseDevstructEpoch {
// The source of randomness for tokens from this epochuint128 randomness;
// The block at which this epoch was / is revealeduint64 revealBlock;
// Whether the epoch has been instantiatedbool committed;
// Whether the epoch has been revealedbool revealed;
}
}
Contract Source Code
File 7 of 40: Context.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)pragmasolidity ^0.8.0;/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
}
Contract Source Code
File 8 of 40: DynamicBuffer.sol
// SPDX-License-Identifier: MIT// Copyright (c) 2021 the ethier authors (github.com/divergencetech/ethier)pragmasolidity ^0.8.22;/// @title DynamicBuffer/// @author David Huber (@cxkoda) and Simon Fremaux (@dievardump). See also/// https://raw.githubusercontent.com/dievardump/solidity-dynamic-buffer/// @notice This library is used to allocate a big amount of container memory// which will be subsequently filled without needing to reallocate/// memory./// @dev First, allocate memory./// Then use `buffer.appendUnchecked(theBytes)` or `appendSafe()` if/// bounds checking is required.libraryDynamicBuffer{
/// @notice Allocates container space for the DynamicBuffer/// @param capacity_ The intended max amount of bytes in the buffer/// @return buffer The memory location of the buffer/// @dev Allocates `capacity_ + 0x60` bytes of space/// The buffer array starts at the first container data position,/// (i.e. `buffer = container + 0x20`)functionallocate(uint256 capacity_)
internalpurereturns (bytesmemory buffer)
{
assembly {
// Get next-free memory addresslet container :=mload(0x40)
// Allocate memory by setting a new next-free address
{
// Add 2 x 32 bytes in size for the two length fields// Add 32 bytes safety space for 32B chunked copylet size :=add(capacity_, 0x60)
let newNextFree :=add(container, size)
mstore(0x40, newNextFree)
}
// Set the correct container length
{
let length :=add(capacity_, 0x40)
mstore(container, length)
}
// The buffer starts at idx 1 in the container (0 is length)
buffer :=add(container, 0x20)
// Init content with length 0mstore(buffer, 0)
}
return buffer;
}
/// @notice Appends data to buffer, and update buffer length/// @param buffer the buffer to append the data to/// @param data the data to append/// @dev Does not perform out-of-bound checks (container capacity)/// for efficiency.functionappendUnchecked(bytesmemory buffer, bytesmemory data)
internalpure{
assembly {
let length :=mload(data)
for {
data :=add(data, 0x20)
let dataEnd :=add(data, length)
let copyTo :=add(buffer, add(mload(buffer), 0x20))
} lt(data, dataEnd) {
data :=add(data, 0x20)
copyTo :=add(copyTo, 0x20)
} {
// Copy 32B chunks from data to buffer.// This may read over data array boundaries and copy invalid// bytes, which doesn't matter in the end since we will// later set the correct buffer length, and have allocated an// additional word to avoid buffer overflow.mstore(copyTo, mload(data))
}
// Update buffer lengthmstore(buffer, add(mload(buffer), length))
}
}
/// @notice Appends data to buffer, and update buffer length/// @param buffer the buffer to append the data to/// @param data the data to append/// @dev Performs out-of-bound checks and calls `appendUnchecked`.functionappendSafe(bytesmemory buffer, bytesmemory data) internalpure{
checkOverflow(buffer, data.length);
appendUnchecked(buffer, data);
}
/// @notice Appends data encoded as Base64 to buffer./// @param fileSafe Whether to replace '+' with '-' and '/' with '_'./// @param noPadding Whether to strip away the padding./// @dev Encodes `data` using the base64 encoding described in RFC 4648./// See: https://datatracker.ietf.org/doc/html/rfc4648/// Author: Modified from Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol)/// Author: Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Base64.sol)/// Author: Modified from (https://github.com/Brechtpd/base64/blob/main/base64.sol) by Brecht Devos.functionappendSafeBase64(bytesmemory buffer,
bytesmemory data,
bool fileSafe,
bool noPadding
) internalpure{
uint256 dataLength = data.length;
if (data.length==0) {
return;
}
uint256 encodedLength;
uint256 r;
assembly {
// For each 3 bytes block, we will have 4 bytes in the base64// encoding: `encodedLength = 4 * divCeil(dataLength, 3)`.// The `shl(2, ...)` is equivalent to multiplying by 4.
encodedLength :=shl(2, div(add(dataLength, 2), 3))
r :=mod(dataLength, 3)
if noPadding {
// if r == 0 => no modification// if r == 1 => encodedLength -= 2// if r == 2 => encodedLength -= 1
encodedLength :=sub(
encodedLength,
add(iszero(iszero(r)), eq(r, 1))
)
}
}
checkOverflow(buffer, encodedLength);
assembly {
let nextFree :=mload(0x40)
// Store the table into the scratch space.// Offsetted by -1 byte so that the `mload` will load the character.// We will rewrite the free memory pointer at `0x40` later with// the allocated size.mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
mstore(
0x3f,
sub(
"ghijklmnopqrstuvwxyz0123456789-_",
// The magic constant 0x0230 will translate "-_" + "+/".mul(iszero(fileSafe), 0x0230)
)
)
// Skip the first slot, which stores the length.let ptr :=add(add(buffer, 0x20), mload(buffer))
let end :=add(data, dataLength)
// Run over the input, 3 bytes at a time.// prettier-ignore// solhint-disable-next-line no-empty-blocksfor {} 1 {} {
data :=add(data, 3) // Advance 3 bytes.let input :=mload(data)
// Write 4 bytes. Optimized for fewer stack operations.mstore8( ptr , mload(and(shr(18, input), 0x3F)))
mstore8(add(ptr, 1), mload(and(shr(12, input), 0x3F)))
mstore8(add(ptr, 2), mload(and(shr( 6, input), 0x3F)))
mstore8(add(ptr, 3), mload(and( input , 0x3F)))
ptr :=add(ptr, 4) // Advance 4 bytes.// prettier-ignoreifiszero(lt(data, end)) { break }
}
ifiszero(noPadding) {
// Offset `ptr` and pad with '='. We can simply write over the end.mstore8(sub(ptr, iszero(iszero(r))), 0x3d) // Pad at `ptr - 1` if `r > 0`.mstore8(sub(ptr, shl(1, eq(r, 1))), 0x3d) // Pad at `ptr - 2` if `r == 1`.
}
mstore(buffer, add(mload(buffer), encodedLength))
mstore(0x40, nextFree)
}
}
/// @notice Appends data encoded as Base64 to buffer./// @param fileSafe Whether to replace '+' with '-' and '/' with '_'./// @param noPadding Whether to strip away the padding./// @dev Encodes `data` using the base64 encoding described in RFC 4648./// See: https://datatracker.ietf.org/doc/html/rfc4648/// Author: Modified from Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol)/// Author: Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Base64.sol)/// Author: Modified from (https://github.com/Brechtpd/base64/blob/main/base64.sol) by Brecht Devos.functionappendUncheckedBase64(bytesmemory buffer,
bytesmemory data,
bool fileSafe,
bool noPadding
) internalpure{
uint256 dataLength = data.length;
if (data.length==0) {
return;
}
uint256 encodedLength;
uint256 r;
assembly {
// For each 3 bytes block, we will have 4 bytes in the base64// encoding: `encodedLength = 4 * divCeil(dataLength, 3)`.// The `shl(2, ...)` is equivalent to multiplying by 4.
encodedLength :=shl(2, div(add(dataLength, 2), 3))
r :=mod(dataLength, 3)
if noPadding {
// if r == 0 => no modification// if r == 1 => encodedLength -= 2// if r == 2 => encodedLength -= 1
encodedLength :=sub(
encodedLength,
add(iszero(iszero(r)), eq(r, 1))
)
}
}
assembly {
let nextFree :=mload(0x40)
// Store the table into the scratch space.// Offsetted by -1 byte so that the `mload` will load the character.// We will rewrite the free memory pointer at `0x40` later with// the allocated size.mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
mstore(
0x3f,
sub(
"ghijklmnopqrstuvwxyz0123456789-_",
// The magic constant 0x0230 will translate "-_" + "+/".mul(iszero(fileSafe), 0x0230)
)
)
// Skip the first slot, which stores the length.let ptr :=add(add(buffer, 0x20), mload(buffer))
let end :=add(data, dataLength)
// Run over the input, 3 bytes at a time.// prettier-ignore// solhint-disable-next-line no-empty-blocksfor {} 1 {} {
data :=add(data, 3) // Advance 3 bytes.let input :=mload(data)
// Write 4 bytes. Optimized for fewer stack operations.mstore8( ptr , mload(and(shr(18, input), 0x3F)))
mstore8(add(ptr, 1), mload(and(shr(12, input), 0x3F)))
mstore8(add(ptr, 2), mload(and(shr( 6, input), 0x3F)))
mstore8(add(ptr, 3), mload(and( input , 0x3F)))
ptr :=add(ptr, 4) // Advance 4 bytes.// prettier-ignoreifiszero(lt(data, end)) { break }
}
ifiszero(noPadding) {
// Offset `ptr` and pad with '='. We can simply write over the end.mstore8(sub(ptr, iszero(iszero(r))), 0x3d) // Pad at `ptr - 1` if `r > 0`.mstore8(sub(ptr, shl(1, eq(r, 1))), 0x3d) // Pad at `ptr - 2` if `r == 1`.
}
mstore(buffer, add(mload(buffer), encodedLength))
mstore(0x40, nextFree)
}
}
/// @notice Returns the capacity of a given buffer.functioncapacity(bytesmemory buffer) internalpurereturns (uint256) {
uint256 cap;
assembly {
cap :=sub(mload(sub(buffer, 0x20)), 0x40)
}
return cap;
}
/// @notice Reverts if the buffer will overflow after appending a given/// number of bytes.functioncheckOverflow(bytesmemory buffer, uint256 addedLength)
internalpure{
uint256 cap = capacity(buffer);
uint256 newLength = buffer.length+ addedLength;
if (cap < newLength) {
revert("DynamicBuffer: Appending out of bounds.");
}
}
}
Contract Source Code
File 9 of 40: ERC165.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)pragmasolidity ^0.8.0;import"./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/abstractcontractERC165isIERC165{
/**
* @dev See {IERC165-supportsInterface}.
*/functionsupportsInterface(bytes4 interfaceId) publicviewvirtualoverridereturns (bool) {
return interfaceId ==type(IERC165).interfaceId;
}
}
Contract Source Code
File 10 of 40: ERC721.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/ERC721.sol)pragmasolidity ^0.8.0;import"./IERC721.sol";
import"./IERC721Receiver.sol";
import"./extensions/IERC721Metadata.sol";
import"../../utils/Address.sol";
import"../../utils/Context.sol";
import"../../utils/Strings.sol";
import"../../utils/introspection/ERC165.sol";
/**
* @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
* the Metadata extension, but not including the Enumerable extension, which is available separately as
* {ERC721Enumerable}.
*/contractERC721isContext, ERC165, IERC721, IERC721Metadata{
usingAddressforaddress;
usingStringsforuint256;
// Token namestringprivate _name;
// Token symbolstringprivate _symbol;
// Mapping from token ID to owner addressmapping(uint256=>address) private _owners;
// Mapping owner address to token countmapping(address=>uint256) private _balances;
// Mapping from token ID to approved addressmapping(uint256=>address) private _tokenApprovals;
// Mapping from owner to operator approvalsmapping(address=>mapping(address=>bool)) private _operatorApprovals;
/**
* @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
*/constructor(stringmemory name_, stringmemory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/functionsupportsInterface(bytes4 interfaceId) publicviewvirtualoverride(ERC165, IERC165) returns (bool) {
return
interfaceId ==type(IERC721).interfaceId||
interfaceId ==type(IERC721Metadata).interfaceId||super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC721-balanceOf}.
*/functionbalanceOf(address owner) publicviewvirtualoverridereturns (uint256) {
require(owner !=address(0), "ERC721: address zero is not a valid owner");
return _balances[owner];
}
/**
* @dev See {IERC721-ownerOf}.
*/functionownerOf(uint256 tokenId) publicviewvirtualoverridereturns (address) {
address owner = _owners[tokenId];
require(owner !=address(0), "ERC721: invalid token ID");
return owner;
}
/**
* @dev See {IERC721Metadata-name}.
*/functionname() publicviewvirtualoverridereturns (stringmemory) {
return _name;
}
/**
* @dev See {IERC721Metadata-symbol}.
*/functionsymbol() publicviewvirtualoverridereturns (stringmemory) {
return _symbol;
}
/**
* @dev See {IERC721Metadata-tokenURI}.
*/functiontokenURI(uint256 tokenId) publicviewvirtualoverridereturns (stringmemory) {
_requireMinted(tokenId);
stringmemory baseURI = _baseURI();
returnbytes(baseURI).length>0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
}
/**
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, can be overridden in child contracts.
*/function_baseURI() internalviewvirtualreturns (stringmemory) {
return"";
}
/**
* @dev See {IERC721-approve}.
*/functionapprove(address to, uint256 tokenId) publicvirtualoverride{
address owner = ERC721.ownerOf(tokenId);
require(to != owner, "ERC721: approval to current owner");
require(
_msgSender() == owner || isApprovedForAll(owner, _msgSender()),
"ERC721: approve caller is not token owner nor approved for all"
);
_approve(to, tokenId);
}
/**
* @dev See {IERC721-getApproved}.
*/functiongetApproved(uint256 tokenId) publicviewvirtualoverridereturns (address) {
_requireMinted(tokenId);
return _tokenApprovals[tokenId];
}
/**
* @dev See {IERC721-setApprovalForAll}.
*/functionsetApprovalForAll(address operator, bool approved) publicvirtualoverride{
_setApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC721-isApprovedForAll}.
*/functionisApprovedForAll(address owner, address operator) publicviewvirtualoverridereturns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev See {IERC721-transferFrom}.
*/functiontransferFrom(addressfrom,
address to,
uint256 tokenId
) publicvirtualoverride{
//solhint-disable-next-line max-line-lengthrequire(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner nor approved");
_transfer(from, to, tokenId);
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/functionsafeTransferFrom(addressfrom,
address to,
uint256 tokenId
) publicvirtualoverride{
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/functionsafeTransferFrom(addressfrom,
address to,
uint256 tokenId,
bytesmemory data
) publicvirtualoverride{
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner nor approved");
_safeTransfer(from, to, tokenId, data);
}
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* `data` is additional data, it has no specified format and it is sent in call to `to`.
*
* This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
* implement alternative mechanisms to perform token transfer, such as signature-based.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/function_safeTransfer(addressfrom,
address to,
uint256 tokenId,
bytesmemory data
) internalvirtual{
_transfer(from, to, tokenId);
require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
}
/**
* @dev Returns whether `tokenId` exists.
*
* Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
*
* Tokens start existing when they are minted (`_mint`),
* and stop existing when they are burned (`_burn`).
*/function_exists(uint256 tokenId) internalviewvirtualreturns (bool) {
return _owners[tokenId] !=address(0);
}
/**
* @dev Returns whether `spender` is allowed to manage `tokenId`.
*
* Requirements:
*
* - `tokenId` must exist.
*/function_isApprovedOrOwner(address spender, uint256 tokenId) internalviewvirtualreturns (bool) {
address owner = ERC721.ownerOf(tokenId);
return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
}
/**
* @dev Safely mints `tokenId` and transfers it to `to`.
*
* Requirements:
*
* - `tokenId` must not exist.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/function_safeMint(address to, uint256 tokenId) internalvirtual{
_safeMint(to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/function_safeMint(address to,
uint256 tokenId,
bytesmemory data
) internalvirtual{
_mint(to, tokenId);
require(
_checkOnERC721Received(address(0), to, tokenId, data),
"ERC721: transfer to non ERC721Receiver implementer"
);
}
/**
* @dev Mints `tokenId` and transfers it to `to`.
*
* WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
*
* Requirements:
*
* - `tokenId` must not exist.
* - `to` cannot be the zero address.
*
* Emits a {Transfer} event.
*/function_mint(address to, uint256 tokenId) internalvirtual{
require(to !=address(0), "ERC721: mint to the zero address");
require(!_exists(tokenId), "ERC721: token already minted");
_beforeTokenTransfer(address(0), to, tokenId);
_balances[to] +=1;
_owners[tokenId] = to;
emit Transfer(address(0), to, tokenId);
_afterTokenTransfer(address(0), to, tokenId);
}
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/function_burn(uint256 tokenId) internalvirtual{
address owner = ERC721.ownerOf(tokenId);
_beforeTokenTransfer(owner, address(0), tokenId);
// Clear approvals
_approve(address(0), tokenId);
_balances[owner] -=1;
delete _owners[tokenId];
emit Transfer(owner, address(0), tokenId);
_afterTokenTransfer(owner, address(0), tokenId);
}
/**
* @dev Transfers `tokenId` from `from` to `to`.
* As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
*
* Emits a {Transfer} event.
*/function_transfer(addressfrom,
address to,
uint256 tokenId
) internalvirtual{
require(ERC721.ownerOf(tokenId) ==from, "ERC721: transfer from incorrect owner");
require(to !=address(0), "ERC721: transfer to the zero address");
_beforeTokenTransfer(from, to, tokenId);
// Clear approvals from the previous owner
_approve(address(0), tokenId);
_balances[from] -=1;
_balances[to] +=1;
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
_afterTokenTransfer(from, to, tokenId);
}
/**
* @dev Approve `to` to operate on `tokenId`
*
* Emits an {Approval} event.
*/function_approve(address to, uint256 tokenId) internalvirtual{
_tokenApprovals[tokenId] = to;
emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Emits an {ApprovalForAll} event.
*/function_setApprovalForAll(address owner,
address operator,
bool approved
) internalvirtual{
require(owner != operator, "ERC721: approve to caller");
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
/**
* @dev Reverts if the `tokenId` has not been minted yet.
*/function_requireMinted(uint256 tokenId) internalviewvirtual{
require(_exists(tokenId), "ERC721: invalid token ID");
}
/**
* @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
* The call is not executed if the target address is not a contract.
*
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param data bytes optional data to send along with the call
* @return bool whether the call correctly returned the expected magic value
*/function_checkOnERC721Received(addressfrom,
address to,
uint256 tokenId,
bytesmemory data
) privatereturns (bool) {
if (to.isContract()) {
try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
return retval == IERC721Receiver.onERC721Received.selector;
} catch (bytesmemory reason) {
if (reason.length==0) {
revert("ERC721: transfer to non ERC721Receiver implementer");
} else {
/// @solidity memory-safe-assemblyassembly {
revert(add(32, reason), mload(reason))
}
}
}
} else {
returntrue;
}
}
/**
* @dev Hook that is called before any token transfer. This includes minting
* and burning.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
* transferred to `to`.
* - When `from` is zero, `tokenId` will be minted for `to`.
* - When `to` is zero, ``from``'s `tokenId` will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/function_beforeTokenTransfer(addressfrom,
address to,
uint256 tokenId
) internalvirtual{}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/function_afterTokenTransfer(addressfrom,
address to,
uint256 tokenId
) internalvirtual{}
}
Contract Source Code
File 11 of 40: ERC721Burnable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/extensions/ERC721Burnable.sol)pragmasolidity ^0.8.0;import"../ERC721.sol";
import"../../../utils/Context.sol";
/**
* @title ERC721 Burnable Token
* @dev ERC721 Token that can be burned (destroyed).
*/abstractcontractERC721BurnableisContext, ERC721{
/**
* @dev Burns `tokenId`. See {ERC721-_burn}.
*
* Requirements:
*
* - The caller must own `tokenId` or be an approved operator.
*/functionburn(uint256 tokenId) publicvirtual{
//solhint-disable-next-line max-line-lengthrequire(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner nor approved");
_burn(tokenId);
}
}
Contract Source Code
File 12 of 40: ERC721Enumerable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/ERC721Enumerable.sol)pragmasolidity ^0.8.0;import"../ERC721.sol";
import"./IERC721Enumerable.sol";
/**
* @dev This implements an optional extension of {ERC721} defined in the EIP that adds
* enumerability of all the token ids in the contract as well as all token ids owned by each
* account.
*/abstractcontractERC721EnumerableisERC721, IERC721Enumerable{
// Mapping from owner to list of owned token IDsmapping(address=>mapping(uint256=>uint256)) private _ownedTokens;
// Mapping from token ID to index of the owner tokens listmapping(uint256=>uint256) private _ownedTokensIndex;
// Array with all token ids, used for enumerationuint256[] private _allTokens;
// Mapping from token id to position in the allTokens arraymapping(uint256=>uint256) private _allTokensIndex;
/**
* @dev See {IERC165-supportsInterface}.
*/functionsupportsInterface(bytes4 interfaceId) publicviewvirtualoverride(IERC165, ERC721) returns (bool) {
return interfaceId ==type(IERC721Enumerable).interfaceId||super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
*/functiontokenOfOwnerByIndex(address owner, uint256 index) publicviewvirtualoverridereturns (uint256) {
require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
return _ownedTokens[owner][index];
}
/**
* @dev See {IERC721Enumerable-totalSupply}.
*/functiontotalSupply() publicviewvirtualoverridereturns (uint256) {
return _allTokens.length;
}
/**
* @dev See {IERC721Enumerable-tokenByIndex}.
*/functiontokenByIndex(uint256 index) publicviewvirtualoverridereturns (uint256) {
require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds");
return _allTokens[index];
}
/**
* @dev Hook that is called before any token transfer. This includes minting
* and burning.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
* transferred to `to`.
* - When `from` is zero, `tokenId` will be minted for `to`.
* - When `to` is zero, ``from``'s `tokenId` will be burned.
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/function_beforeTokenTransfer(addressfrom,
address to,
uint256 tokenId
) internalvirtualoverride{
super._beforeTokenTransfer(from, to, tokenId);
if (from==address(0)) {
_addTokenToAllTokensEnumeration(tokenId);
} elseif (from!= to) {
_removeTokenFromOwnerEnumeration(from, tokenId);
}
if (to ==address(0)) {
_removeTokenFromAllTokensEnumeration(tokenId);
} elseif (to !=from) {
_addTokenToOwnerEnumeration(to, tokenId);
}
}
/**
* @dev Private function to add a token to this extension's ownership-tracking data structures.
* @param to address representing the new owner of the given token ID
* @param tokenId uint256 ID of the token to be added to the tokens list of the given address
*/function_addTokenToOwnerEnumeration(address to, uint256 tokenId) private{
uint256 length = ERC721.balanceOf(to);
_ownedTokens[to][length] = tokenId;
_ownedTokensIndex[tokenId] = length;
}
/**
* @dev Private function to add a token to this extension's token tracking data structures.
* @param tokenId uint256 ID of the token to be added to the tokens list
*/function_addTokenToAllTokensEnumeration(uint256 tokenId) private{
_allTokensIndex[tokenId] = _allTokens.length;
_allTokens.push(tokenId);
}
/**
* @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
* while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
* gas optimizations e.g. when performing a transfer operation (avoiding double writes).
* This has O(1) time complexity, but alters the order of the _ownedTokens array.
* @param from address representing the previous owner of the given token ID
* @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
*/function_removeTokenFromOwnerEnumeration(addressfrom, uint256 tokenId) private{
// To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and// then delete the last slot (swap and pop).uint256 lastTokenIndex = ERC721.balanceOf(from) -1;
uint256 tokenIndex = _ownedTokensIndex[tokenId];
// When the token to delete is the last token, the swap operation is unnecessaryif (tokenIndex != lastTokenIndex) {
uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
_ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
_ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
}
// This also deletes the contents at the last position of the arraydelete _ownedTokensIndex[tokenId];
delete _ownedTokens[from][lastTokenIndex];
}
/**
* @dev Private function to remove a token from this extension's token tracking data structures.
* This has O(1) time complexity, but alters the order of the _allTokens array.
* @param tokenId uint256 ID of the token to be removed from the tokens list
*/function_removeTokenFromAllTokensEnumeration(uint256 tokenId) private{
// To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and// then delete the last slot (swap and pop).uint256 lastTokenIndex = _allTokens.length-1;
uint256 tokenIndex = _allTokensIndex[tokenId];
// When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so// rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding// an 'if' statement (like in _removeTokenFromOwnerEnumeration)uint256 lastTokenId = _allTokens[lastTokenIndex];
_allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
_allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index// This also deletes the contents at the last position of the arraydelete _allTokensIndex[tokenId];
_allTokens.pop();
}
}
Contract Source Code
File 13 of 40: EncodeURI.sol
// SPDX-License-Identifier: MIT// via @iamwhitelightspragmasolidity ^0.8.17;contractEncodeURI{
/**
* @dev URI Encoding/Decoding Hex Table
*/bytesinternalconstant TABLE ="0123456789ABCDEF";
/**
* @dev URI encodes the provided string. Gas optimized like crazy.
*/functionencodeURI(stringmemory str_) publicpurereturns (stringmemory) {
bytesmemory 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;
}
}
bytesmemory output =newbytes(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;
}
}
returnstring(output);
}
}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.22;/// A shared interface for data storage of the ChonksinterfaceIChonkStorage{
structStoredChonk {
// The token id of the Chonkuint256 tokenId;
// The token id of the head, 0 if unequippeduint256 headId;
// The token id of the hair, 0 if unequippeduint256 hairId;
// The token id of the face, 0 if unequippeduint256 faceId;
// The token id of the accessory, 0 if unequippeduint256 accessoryId;
// The token id of the top, 0 if unequippeduint256 topId;
// The token id of the bottom, 0 if unequippeduint256 bottomId;
// The token id of the shoes, 0 if unequippeduint256 shoesId;
// Randomly set in ChonksMain.mint() but can be updated by holder at any timeuint8 bodyIndex;
// RRGGBB colour of the background, default blue #0D6E9D set in ChonksMain.sol mint(), and setBackgroundColor()string backgroundColor;
// Bool to determine whether to render in 3D or notbool render3D;
}
structBodyMetadata {
// Refers to the number used in ChonksMain.addNewBody; Not token iduint256 bodyIndex;
// e.g. 'Skin Tone 1'string bodyName;
// bytes memory colorMap = new bytes(2700); 30x30 grid by 3 bytes (rgb, each colour is a byte, or 2 hex digits);bytes colorMap;
// The map of possible 3D traitsbytes zMap;
}
structChonkData {
string backgroundColor;
string bodyName;
// string rendererSet;uint256 numOfItemsInBackpack;
string[2] descriptionParts;
}
/// EventseventMint(addressindexed owner, uint256indexed tokenId);
eventEquip(addressindexed owner, uint256indexed tokenId, uint256indexed traitTokenId, uint8 traitCategory);
eventUnequip(addressindexed owner, uint256indexed tokenId, uint8 traitCategory);
eventEquipAll(addressindexed owner, uint256indexed tokenId);
eventUnequipAll(addressindexed owner, uint256indexed tokenId);
eventBackgroundColor(addressindexed owner, uint256indexed tokenId, string color);
eventBodyIndex(addressindexed owner, uint256indexed tokenId, uint8 _bodyIndex);
eventRender3D(addressindexed owner, uint256indexed tokenId, bool renderZ);
}
Contract Source Code
File 18 of 40: IERC165.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/interfaceIERC165{
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/functionsupportsInterface(bytes4 interfaceId) externalviewreturns (bool);
}
Contract Source Code
File 19 of 40: IERC4906.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;import"@openzeppelin/contracts/token/ERC721/IERC721.sol";
import"@openzeppelin/contracts/utils/introspection/IERC165.sol";
/// @title EIP-721 Metadata Update ExtensioninterfaceIERC4906isIERC165, IERC721{
/// @dev This event emits when the metadata of a token is changed./// Third-party platforms such as NFT marketplaces can listen to/// the event and auto-update the tokens in their apps.eventMetadataUpdate(uint256 _tokenId);
eventBatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
}
Contract Source Code
File 20 of 40: IERC721.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol)pragmasolidity ^0.8.0;import"../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/interfaceIERC721isIERC165{
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/eventTransfer(addressindexedfrom, addressindexed to, uint256indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/eventApproval(addressindexed owner, addressindexed approved, uint256indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/eventApprovalForAll(addressindexed owner, addressindexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/functionbalanceOf(address owner) externalviewreturns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/functionownerOf(uint256 tokenId) externalviewreturns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/functionsafeTransferFrom(addressfrom,
address to,
uint256 tokenId,
bytescalldata data
) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/functionsafeTransferFrom(addressfrom,
address to,
uint256 tokenId
) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/functiontransferFrom(addressfrom,
address to,
uint256 tokenId
) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/functionapprove(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/functionsetApprovalForAll(address operator, bool _approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/functiongetApproved(uint256 tokenId) externalviewreturns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/functionisApprovedForAll(address owner, address operator) externalviewreturns (bool);
}
Contract Source Code
File 21 of 40: IERC721Enumerable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)pragmasolidity ^0.8.0;import"../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/interfaceIERC721EnumerableisIERC721{
/**
* @dev Returns the total amount of tokens stored by the contract.
*/functiontotalSupply() externalviewreturns (uint256);
/**
* @dev Returns a token ID owned by `owner` at a given `index` of its token list.
* Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
*/functiontokenOfOwnerByIndex(address owner, uint256 index) externalviewreturns (uint256);
/**
* @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
* Use along with {totalSupply} to enumerate all tokens.
*/functiontokenByIndex(uint256 index) externalviewreturns (uint256);
}
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)pragmasolidity ^0.8.0;/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/interfaceIERC721Receiver{
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/functiononERC721Received(address operator,
addressfrom,
uint256 tokenId,
bytescalldata data
) externalreturns (bytes4);
}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.22;///////////////////////////////////////////////////////////// ░██████╗░█████╗░██████╗░██╗██████╗░████████╗██╗░░░██╗ //// ██╔════╝██╔══██╗██╔══██╗██║██╔══██╗╚══██╔══╝╚██╗░██╔╝ //// ╚█████╗░██║░░╚═╝██████╔╝██║██████╔╝░░░██║░░░░╚████╔╝░ //// ░╚═══██╗██║░░██╗██╔══██╗██║██╔═══╝░░░░██║░░░░░╚██╔╝░░ //// ██████╔╝╚█████╔╝██║░░██║██║██║░░░░░░░░██║░░░░░░██║░░░ //// ╚═════╝░░╚════╝░╚═╝░░╚═╝╚═╝╚═╝░░░░░░░░╚═╝░░░░░░╚═╝░░░ //////////////////////////////////////////////////////////////**
@title A generic HTML builder that fetches and assembles given JS requests.
@author @0xthedude
@author @xtremetom
Special thanks to @cxkoda and @frolic
*/import"./IScriptyHTML.sol";
import"./IScriptyHTMLURLSafe.sol";
interfaceIScriptyBuilderV2isIScriptyHTML, IScriptyHTMLURLSafe{}
Contract Source Code
File 26 of 40: IScriptyContractStorage.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.22;///////////////////////////////////////////////////////////// ░██████╗░█████╗░██████╗░██╗██████╗░████████╗██╗░░░██╗ //// ██╔════╝██╔══██╗██╔══██╗██║██╔══██╗╚══██╔══╝╚██╗░██╔╝ //// ╚█████╗░██║░░╚═╝██████╔╝██║██████╔╝░░░██║░░░░╚████╔╝░ //// ░╚═══██╗██║░░██╗██╔══██╗██║██╔═══╝░░░░██║░░░░░╚██╔╝░░ //// ██████╔╝╚█████╔╝██║░░██║██║██║░░░░░░░░██║░░░░░░██║░░░ //// ╚═════╝░░╚════╝░╚═╝░░╚═╝╚═╝╚═╝░░░░░░░░╚═╝░░░░░░╚═╝░░░ /////////////////////////////////////////////////////////////interfaceIScriptyContractStorage{
// =============================================================// GETTERS// =============================================================/**
* @notice Get the full content
* @param name - Name given to the script. Eg: threejs.min.js_r148
* @param data - Arbitrary data to be passed to storage
* @return script - Full script from merged chunks
*/functiongetContent(stringcalldata name, bytesmemory data)
externalviewreturns (bytesmemory script);
}
Contract Source Code
File 27 of 40: IScriptyHTML.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.22;///////////////////////////////////////////////////////////// ░██████╗░█████╗░██████╗░██╗██████╗░████████╗██╗░░░██╗ //// ██╔════╝██╔══██╗██╔══██╗██║██╔══██╗╚══██╔══╝╚██╗░██╔╝ //// ╚█████╗░██║░░╚═╝██████╔╝██║██████╔╝░░░██║░░░░╚████╔╝░ //// ░╚═══██╗██║░░██╗██╔══██╗██║██╔═══╝░░░░██║░░░░░╚██╔╝░░ //// ██████╔╝╚█████╔╝██║░░██║██║██║░░░░░░░░██║░░░░░░██║░░░ //// ╚═════╝░░╚════╝░╚═╝░░╚═╝╚═╝╚═╝░░░░░░░░╚═╝░░░░░░╚═╝░░░ /////////////////////////////////////////////////////////////import {HTMLRequest, HTMLTagType, HTMLTag} from"./../core/ScriptyCore.sol";
interfaceIScriptyHTML{
// =============================================================// RAW HTML GETTERS// =============================================================/**
* @notice Get HTML with requested head tags and body tags
* @dev Your HTML is returned in the following format:
* <html>
* <head>
* [tagOpen[0]][contractRequest[0] | tagContent[0]][tagClose[0]]
* [tagOpen[1]][contractRequest[0] | tagContent[1]][tagClose[1]]
* ...
* [tagOpen[n]][contractRequest[0] | tagContent[n]][tagClose[n]]
* </head>
* <body>
* [tagOpen[0]][contractRequest[0] | tagContent[0]][tagClose[0]]
* [tagOpen[1]][contractRequest[0] | tagContent[1]][tagClose[1]]
* ...
* [tagOpen[n]][contractRequest[0] | tagContent[n]][tagClose[n]]
* </body>
* </html>
* @param htmlRequest - HTMLRequest
* @return Full HTML with head and body tags
*/functiongetHTML(
HTMLRequest memory htmlRequest
) externalviewreturns (bytesmemory);
// =============================================================// ENCODED HTML GETTERS// =============================================================/**
* @notice Get {getHTML} and base64 encode it
* @param htmlRequest - HTMLRequest
* @return Full HTML with head and script tags, base64 encoded
*/functiongetEncodedHTML(
HTMLRequest memory htmlRequest
) externalviewreturns (bytesmemory);
// =============================================================// STRING UTILITIES// =============================================================/**
* @notice Convert {getHTML} output to a string
* @param htmlRequest - HTMLRequest
* @return {getHTMLWrapped} as a string
*/functiongetHTMLString(
HTMLRequest memory htmlRequest
) externalviewreturns (stringmemory);
/**
* @notice Convert {getEncodedHTML} output to a string
* @param htmlRequest - HTMLRequest
* @return {getEncodedHTML} as a string
*/functiongetEncodedHTMLString(
HTMLRequest memory htmlRequest
) externalviewreturns (stringmemory);
}
Contract Source Code
File 28 of 40: IScriptyHTMLURLSafe.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.22;///////////////////////////////////////////////////////////// ░██████╗░█████╗░██████╗░██╗██████╗░████████╗██╗░░░██╗ //// ██╔════╝██╔══██╗██╔══██╗██║██╔══██╗╚══██╔══╝╚██╗░██╔╝ //// ╚█████╗░██║░░╚═╝██████╔╝██║██████╔╝░░░██║░░░░╚████╔╝░ //// ░╚═══██╗██║░░██╗██╔══██╗██║██╔═══╝░░░░██║░░░░░╚██╔╝░░ //// ██████╔╝╚█████╔╝██║░░██║██║██║░░░░░░░░██║░░░░░░██║░░░ //// ╚═════╝░░╚════╝░╚═╝░░╚═╝╚═╝╚═╝░░░░░░░░╚═╝░░░░░░╚═╝░░░ /////////////////////////////////////////////////////////////import {HTMLRequest, HTMLTagType, HTMLTag} from"./../core/ScriptyCore.sol";
interfaceIScriptyHTMLURLSafe{
// =============================================================// RAW HTML GETTERS// =============================================================/**
* @notice Get URL safe HTML with requested head tags and body tags
* @dev Any tags with tagType = 1/script are converted to base64 and wrapped
* with <script src="data:text/javascript;base64,[SCRIPT]"></script>
*
* [WARNING]: Large non-base64 libraries that need base64 encoding
* carry a high risk of causing a gas out. Highly advised the use
* of base64 encoded scripts where possible
*
* Your HTML is returned in the following format:
*
* <html>
* <head>
* [tagOpen[0]][contractRequest[0] | tagContent[0]][tagClose[0]]
* [tagOpen[1]][contractRequest[0] | tagContent[1]][tagClose[1]]
* ...
* [tagOpen[n]][contractRequest[0] | tagContent[n]][tagClose[n]]
* </head>
* <body>
* [tagOpen[0]][contractRequest[0] | tagContent[0]][tagClose[0]]
* [tagOpen[1]][contractRequest[0] | tagContent[1]][tagClose[1]]
* ...
* [tagOpen[n]][contractRequest[0] | tagContent[n]][tagClose[n]]
* </body>
* </html>
* @param htmlRequest - HTMLRequest
* @return Full HTML with head and body tags
*/functiongetHTMLURLSafe(
HTMLRequest memory htmlRequest
) externalviewreturns (bytesmemory);
// =============================================================// STRING UTILITIES// =============================================================/**
* @notice Convert {getHTMLURLSafe} output to a string
* @param htmlRequest - HTMLRequest
* @return {getHTMLURLSafe} as a string
*/functiongetHTMLURLSafeString(
HTMLRequest memory htmlRequest
) externalviewreturns (stringmemory);
}
Contract Source Code
File 29 of 40: ITraitStorage.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.22;import { CommitReveal } from"../common/CommitReveal.sol";
import { TraitCategory } from"../TraitCategory.sol";
interfaceITraitStorage{
structStoredTrait {
// The epoch when it was minteduint256 epoch;
// If the trait has been revealed or notbool isRevealed;
// Data used to calculate the commit-revealuint256 seed;
// The RenderMinter contract that can `explain` the traitaddress dataMinterContract;
// A sequential numbering of the traits that exist in the collectionuint256 traitIndex;
// e.g. Head, Top, Shoes, etc.
TraitCategory.Name traitType;
}
structTraits {
// A mapping of each token ID to what it actually is (the StoredTrait)mapping(uint256=> StoredTrait) all;
// Collection-wide epoch; The current epoch index of the mapping belowuint256 epoch;
// A mapping of the above epoch (or past epochs) to the commit-reveal scheme. The epoch in StoredTrait is the epoch when that trait was *minted*mapping(uint256=> CommitReveal.Epoch) epochs;
}
// With Bodies, we just hardcode 5 Bodies in contracts// But with Traits, we want to be able to add them, hence this structstructTraitMetadata {
// Refers to the number used in ChonkTraits.addNewTrait; not a token IDuint256 traitIndex;
// e.g. 'Blue top'string traitName;
// e.g. TraitCategory.Name.Top
TraitCategory.Name traitType;
// The row-major byte array of the 2d version of a Traitbytes colorMap;
// The row-major byte array of the 3d version of a Traitbytes zMap;
// The DataMinter contract responsible for this trait/// @dev Cast as not an addressaddress dataMinterContract;
// Address of creatoraddress creatorAddress;
// Name of creatorstring creatorName;
// Which Release the Trait was instring release;
}
// Event for when all approvals are invalidatedeventAllOperatorApprovalsInvalidated(uint256indexed tokenId);
}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.4;/// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree./// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol)/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol)/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol)libraryMerkleProofLib{
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* MERKLE PROOF VERIFICATION OPERATIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.functionverify(bytes32[] memory proof, bytes32 root, bytes32 leaf)
internalpurereturns (bool isValid)
{
/// @solidity memory-safe-assemblyassembly {
ifmload(proof) {
// Initialize `offset` to the offset of `proof` elements in memory.let offset :=add(proof, 0x20)
// Left shift by 5 is equivalent to multiplying by 0x20.let end :=add(offset, shl(5, mload(proof)))
// Iterate over proof elements to compute root hash.for {} 1 {} {
// Slot of `leaf` in scratch space.// If the condition is true: 0x20, otherwise: 0x00.let scratch :=shl(5, gt(leaf, mload(offset)))
// Store elements to hash contiguously in scratch space.// Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.mstore(scratch, leaf)
mstore(xor(scratch, 0x20), mload(offset))
// Reuse `leaf` to store the hash to reduce stack operations.
leaf :=keccak256(0x00, 0x40)
offset :=add(offset, 0x20)
ifiszero(lt(offset, end)) { break }
}
}
isValid :=eq(leaf, root)
}
}
/// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.functionverifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf)
internalpurereturns (bool isValid)
{
/// @solidity memory-safe-assemblyassembly {
if proof.length {
// Left shift by 5 is equivalent to multiplying by 0x20.let end :=add(proof.offset, shl(5, proof.length))
// Initialize `offset` to the offset of `proof` in the calldata.let offset := proof.offset// Iterate over proof elements to compute root hash.for {} 1 {} {
// Slot of `leaf` in scratch space.// If the condition is true: 0x20, otherwise: 0x00.let scratch :=shl(5, gt(leaf, calldataload(offset)))
// Store elements to hash contiguously in scratch space.// Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.mstore(scratch, leaf)
mstore(xor(scratch, 0x20), calldataload(offset))
// Reuse `leaf` to store the hash to reduce stack operations.
leaf :=keccak256(0x00, 0x40)
offset :=add(offset, 0x20)
ifiszero(lt(offset, end)) { break }
}
}
isValid :=eq(leaf, root)
}
}
/// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,/// given `proof` and `flags`.////// Note:/// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`/// will always return false./// - The sum of the lengths of `proof` and `leaves` must never overflow./// - Any non-zero word in the `flags` array is treated as true./// - The memory offset of `proof` must be non-zero/// (i.e. `proof` is not pointing to the scratch space).functionverifyMultiProof(bytes32[] memory proof,
bytes32 root,
bytes32[] memory leaves,
bool[] memory flags
) internalpurereturns (bool isValid) {
// Rebuilds the root by consuming and producing values on a queue.// The queue starts with the `leaves` array, and goes into a `hashes` array.// After the process, the last element on the queue is verified// to be equal to the `root`.//// The `flags` array denotes whether the sibling// should be popped from the queue (`flag == true`), or// should be popped from the `proof` (`flag == false`)./// @solidity memory-safe-assemblyassembly {
// Cache the lengths of the arrays.let leavesLength :=mload(leaves)
let proofLength :=mload(proof)
let flagsLength :=mload(flags)
// Advance the pointers of the arrays to point to the data.
leaves :=add(0x20, leaves)
proof :=add(0x20, proof)
flags :=add(0x20, flags)
// If the number of flags is correct.for {} eq(add(leavesLength, proofLength), add(flagsLength, 1)) {} {
// For the case where `proof.length + leaves.length == 1`.ifiszero(flagsLength) {
// `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
isValid :=eq(mload(xor(leaves, mul(xor(proof, leaves), proofLength))), root)
break
}
// The required final proof offset if `flagsLength` is not zero, otherwise zero.let proofEnd :=add(proof, shl(5, proofLength))
// We can use the free memory space for the queue.// We don't need to allocate, since the queue is temporary.let hashesFront :=mload(0x40)
// Copy the leaves into the hashes.// Sometimes, a little memory expansion costs less than branching.// Should cost less, even with a high free memory offset of 0x7d00.
leavesLength :=shl(5, leavesLength)
for { let i :=0 } iszero(eq(i, leavesLength)) { i :=add(i, 0x20) } {
mstore(add(hashesFront, i), mload(add(leaves, i)))
}
// Compute the back of the hashes.let hashesBack :=add(hashesFront, leavesLength)
// This is the end of the memory for the queue.// We recycle `flagsLength` to save on stack variables (sometimes save gas).
flagsLength :=add(hashesBack, shl(5, flagsLength))
for {} 1 {} {
// Pop from `hashes`.let a :=mload(hashesFront)
// Pop from `hashes`.let b :=mload(add(hashesFront, 0x20))
hashesFront :=add(hashesFront, 0x40)
// If the flag is false, load the next proof,// else, pops from the queue.ifiszero(mload(flags)) {
// Loads the next proof.
b :=mload(proof)
proof :=add(proof, 0x20)
// Unpop from `hashes`.
hashesFront :=sub(hashesFront, 0x20)
}
// Advance to the next flag.
flags :=add(flags, 0x20)
// Slot of `a` in scratch space.// If the condition is true: 0x20, otherwise: 0x00.let scratch :=shl(5, gt(a, b))
// Hash the scratch space and push the result onto the queue.mstore(scratch, a)
mstore(xor(scratch, 0x20), b)
mstore(hashesBack, keccak256(0x00, 0x40))
hashesBack :=add(hashesBack, 0x20)
ifiszero(lt(hashesBack, flagsLength)) { break }
}
isValid :=and(
// Checks if the last value in the queue is same as the root.eq(mload(sub(hashesBack, 0x20)), root),
// And whether all the proofs are used, if required.eq(proofEnd, proof)
)
break
}
}
}
/// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,/// given `proof` and `flags`.////// Note:/// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`/// will always return false./// - Any non-zero word in the `flags` array is treated as true./// - The calldata offset of `proof` must be non-zero/// (i.e. `proof` is from a regular Solidity function with a 4-byte selector).functionverifyMultiProofCalldata(bytes32[] calldata proof,
bytes32 root,
bytes32[] calldata leaves,
bool[] calldata flags
) internalpurereturns (bool isValid) {
// Rebuilds the root by consuming and producing values on a queue.// The queue starts with the `leaves` array, and goes into a `hashes` array.// After the process, the last element on the queue is verified// to be equal to the `root`.//// The `flags` array denotes whether the sibling// should be popped from the queue (`flag == true`), or// should be popped from the `proof` (`flag == false`)./// @solidity memory-safe-assemblyassembly {
// If the number of flags is correct.for {} eq(add(leaves.length, proof.length), add(flags.length, 1)) {} {
// For the case where `proof.length + leaves.length == 1`.ifiszero(flags.length) {
// `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.// forgefmt: disable-next-item
isValid :=eq(
calldataload(
xor(leaves.offset, mul(xor(proof.offset, leaves.offset), proof.length))
),
root
)
break
}
// The required final proof offset if `flagsLength` is not zero, otherwise zero.let proofEnd :=add(proof.offset, shl(5, proof.length))
// We can use the free memory space for the queue.// We don't need to allocate, since the queue is temporary.let hashesFront :=mload(0x40)
// Copy the leaves into the hashes.// Sometimes, a little memory expansion costs less than branching.// Should cost less, even with a high free memory offset of 0x7d00.calldatacopy(hashesFront, leaves.offset, shl(5, leaves.length))
// Compute the back of the hashes.let hashesBack :=add(hashesFront, shl(5, leaves.length))
// This is the end of the memory for the queue.// We recycle `flagsLength` to save on stack variables (sometimes save gas).
flags.length:=add(hashesBack, shl(5, flags.length))
// We don't need to make a copy of `proof.offset` or `flags.offset`,// as they are pass-by-value (this trick may not always save gas).for {} 1 {} {
// Pop from `hashes`.let a :=mload(hashesFront)
// Pop from `hashes`.let b :=mload(add(hashesFront, 0x20))
hashesFront :=add(hashesFront, 0x40)
// If the flag is false, load the next proof,// else, pops from the queue.ifiszero(calldataload(flags.offset)) {
// Loads the next proof.
b :=calldataload(proof.offset)
proof.offset:=add(proof.offset, 0x20)
// Unpop from `hashes`.
hashesFront :=sub(hashesFront, 0x20)
}
// Advance to the next flag offset.
flags.offset:=add(flags.offset, 0x20)
// Slot of `a` in scratch space.// If the condition is true: 0x20, otherwise: 0x00.let scratch :=shl(5, gt(a, b))
// Hash the scratch space and push the result onto the queue.mstore(scratch, a)
mstore(xor(scratch, 0x20), b)
mstore(hashesBack, keccak256(0x00, 0x40))
hashesBack :=add(hashesBack, 0x20)
ifiszero(lt(hashesBack, flags.length)) { break }
}
isValid :=and(
// Checks if the last value in the queue is same as the root.eq(mload(sub(hashesBack, 0x20)), root),
// And whether all the proofs are used, if required.eq(proofEnd, proof.offset)
)
break
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* EMPTY CALLDATA HELPERS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Returns an empty calldata bytes32 array.functionemptyProof() internalpurereturns (bytes32[] calldata proof) {
/// @solidity memory-safe-assemblyassembly {
proof.length:=0
}
}
/// @dev Returns an empty calldata bytes32 array.functionemptyLeaves() internalpurereturns (bytes32[] calldata leaves) {
/// @solidity memory-safe-assemblyassembly {
leaves.length:=0
}
}
/// @dev Returns an empty calldata bool array.functionemptyFlags() internalpurereturns (bool[] calldata flags) {
/// @solidity memory-safe-assemblyassembly {
flags.length:=0
}
}
}
Contract Source Code
File 33 of 40: Ownable.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.4;/// @notice Simple single owner authorization mixin./// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)////// @dev Note:/// This implementation does NOT auto-initialize the owner to `msg.sender`./// You MUST call the `_initializeOwner` in the constructor / initializer.////// While the ownable portion follows/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,/// the nomenclature for the 2-step ownership handover may be unique to this codebase.abstractcontractOwnable{
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* CUSTOM ERRORS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev The caller is not authorized to call the function.errorUnauthorized();
/// @dev The `newOwner` cannot be the zero address.errorNewOwnerIsZeroAddress();
/// @dev The `pendingOwner` does not have a valid handover request.errorNoHandoverRequest();
/// @dev Cannot double-initialize.errorAlreadyInitialized();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* EVENTS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev The ownership is transferred from `oldOwner` to `newOwner`./// This event is intentionally kept the same as OpenZeppelin's Ownable to be/// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),/// despite it not being as lightweight as a single argument event.eventOwnershipTransferred(addressindexed oldOwner, addressindexed newOwner);
/// @dev An ownership handover to `pendingOwner` has been requested.eventOwnershipHandoverRequested(addressindexed pendingOwner);
/// @dev The ownership handover to `pendingOwner` has been canceled.eventOwnershipHandoverCanceled(addressindexed pendingOwner);
/// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.uint256privateconstant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
/// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.uint256privateconstant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
/// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.uint256privateconstant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* STORAGE *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev The owner slot is given by:/// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`./// It is intentionally chosen to be a high value/// to avoid collision with lower slots./// The choice of manual storage layout is to enable compatibility/// with both regular and upgradeable contracts.bytes32internalconstant _OWNER_SLOT =0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;
/// The ownership handover slot of `newOwner` is given by:/// ```/// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))/// let handoverSlot := keccak256(0x00, 0x20)/// ```/// It stores the expiry timestamp of the two-step ownership handover.uint256privateconstant _HANDOVER_SLOT_SEED =0x389a75e1;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* INTERNAL FUNCTIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Override to return true to make `_initializeOwner` prevent double-initialization.function_guardInitializeOwner() internalpurevirtualreturns (bool guard) {}
/// @dev Initializes the owner directly without authorization guard./// This function must be called upon initialization,/// regardless of whether the contract is upgradeable or not./// This is to enable generalization to both regular and upgradeable contracts,/// and to save gas in case the initial owner is not the caller./// For performance reasons, this function will not check if there/// is an existing owner.function_initializeOwner(address newOwner) internalvirtual{
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assemblyassembly {
let ownerSlot := _OWNER_SLOT
ifsload(ownerSlot) {
mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.revert(0x1c, 0x04)
}
// Clean the upper 96 bits.
newOwner :=shr(96, shl(96, newOwner))
// Store the new value.sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
// Emit the {OwnershipTransferred} event.log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
} else {
/// @solidity memory-safe-assemblyassembly {
// Clean the upper 96 bits.
newOwner :=shr(96, shl(96, newOwner))
// Store the new value.sstore(_OWNER_SLOT, newOwner)
// Emit the {OwnershipTransferred} event.log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
}
}
/// @dev Sets the owner directly without authorization guard.function_setOwner(address newOwner) internalvirtual{
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assemblyassembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner :=shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
}
} else {
/// @solidity memory-safe-assemblyassembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner :=shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.sstore(ownerSlot, newOwner)
}
}
}
/// @dev Throws if the sender is not the owner.function_checkOwner() internalviewvirtual{
/// @solidity memory-safe-assemblyassembly {
// If the caller is not the stored owner, revert.ifiszero(eq(caller(), sload(_OWNER_SLOT))) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.revert(0x1c, 0x04)
}
}
}
/// @dev Returns how long a two-step ownership handover is valid for in seconds./// Override to return a different value if needed./// Made internal to conserve bytecode. Wrap it in a public function if needed.function_ownershipHandoverValidFor() internalviewvirtualreturns (uint64) {
return48*3600;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* PUBLIC UPDATE FUNCTIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Allows the owner to transfer the ownership to `newOwner`.functiontransferOwnership(address newOwner) publicpayablevirtualonlyOwner{
/// @solidity memory-safe-assemblyassembly {
ifiszero(shl(96, newOwner)) {
mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.revert(0x1c, 0x04)
}
}
_setOwner(newOwner);
}
/// @dev Allows the owner to renounce their ownership.functionrenounceOwnership() publicpayablevirtualonlyOwner{
_setOwner(address(0));
}
/// @dev Request a two-step ownership handover to the caller./// The request will automatically expire in 48 hours (172800 seconds) by default.functionrequestOwnershipHandover() publicpayablevirtual{
unchecked {
uint256 expires =block.timestamp+ _ownershipHandoverValidFor();
/// @solidity memory-safe-assemblyassembly {
// Compute and set the handover slot to `expires`.mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), expires)
// Emit the {OwnershipHandoverRequested} event.log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
}
}
}
/// @dev Cancels the two-step ownership handover to the caller, if any.functioncancelOwnershipHandover() publicpayablevirtual{
/// @solidity memory-safe-assemblyassembly {
// Compute and set the handover slot to 0.mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), 0)
// Emit the {OwnershipHandoverCanceled} event.log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
}
}
/// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`./// Reverts if there is no existing ownership handover requested by `pendingOwner`.functioncompleteOwnershipHandover(address pendingOwner) publicpayablevirtualonlyOwner{
/// @solidity memory-safe-assemblyassembly {
// Compute and set the handover slot to 0.mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
let handoverSlot :=keccak256(0x0c, 0x20)
// If the handover does not exist, or has expired.ifgt(timestamp(), sload(handoverSlot)) {
mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.revert(0x1c, 0x04)
}
// Set the handover slot to 0.sstore(handoverSlot, 0)
}
_setOwner(pendingOwner);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* PUBLIC READ FUNCTIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Returns the owner of the contract.functionowner() publicviewvirtualreturns (address result) {
/// @solidity memory-safe-assemblyassembly {
result :=sload(_OWNER_SLOT)
}
}
/// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.functionownershipHandoverExpiresAt(address pendingOwner)
publicviewvirtualreturns (uint256 result)
{
/// @solidity memory-safe-assemblyassembly {
// Compute the handover slot.mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
// Load the handover slot.
result :=sload(keccak256(0x0c, 0x20))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* MODIFIERS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Marks a function as only callable by the owner.modifieronlyOwner() virtual{
_checkOwner();
_;
}
}
Contract Source Code
File 34 of 40: ReentrancyGuard.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)pragmasolidity ^0.8.0;/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/abstractcontractReentrancyGuard{
// Booleans are more expensive than uint256 or any type that takes up a full// word because each write operation emits an extra SLOAD to first read the// slot's contents, replace the bits taken up by the boolean, and then write// back. This is the compiler's defense against contract upgrades and// pointer aliasing, and it cannot be disabled.// The values being non-zero value makes deployment a bit more expensive,// but in exchange the refund on every call to nonReentrant will be lower in// amount. Since refunds are capped to a percentage of the total// transaction's gas, it is best to keep them low in cases like this one, to// increase the likelihood of the full refund coming into effect.uint256privateconstant _NOT_ENTERED =1;
uint256privateconstant _ENTERED =2;
uint256private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/modifiernonReentrant() {
// On the first call to nonReentrant, _notEntered will be truerequire(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
Contract Source Code
File 35 of 40: ScriptyCore.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.17;///////////////////////////////////////////////////////////// ░██████╗░█████╗░██████╗░██╗██████╗░████████╗██╗░░░██╗ //// ██╔════╝██╔══██╗██╔══██╗██║██╔══██╗╚══██╔══╝╚██╗░██╔╝ //// ╚█████╗░██║░░╚═╝██████╔╝██║██████╔╝░░░██║░░░░╚████╔╝░ //// ░╚═══██╗██║░░██╗██╔══██╗██║██╔═══╝░░░░██║░░░░░╚██╔╝░░ //// ██████╔╝╚█████╔╝██║░░██║██║██║░░░░░░░░██║░░░░░░██║░░░ //// ╚═════╝░░╚════╝░╚═╝░░╚═╝╚═╝╚═╝░░░░░░░░╚═╝░░░░░░╚═╝░░░ ///////////////////////////////////////////////////////////////░░░░░░░░░░░░░░░░░░░░░░ CORE ░░░░░░░░░░░░░░░░░░░░░/////////////////////////////////////////////////////////////import {HTMLRequest, HTMLTagType, HTMLTag} from"./ScriptyStructs.sol";
import {DynamicBuffer} from"./../utils/DynamicBuffer.sol";
import {IScriptyContractStorage} from"./../interfaces/IScriptyContractStorage.sol";
contractScriptyCore{
usingDynamicBufferforbytes;
// =============================================================// TAG CONSTANTS// =============================================================// data:text/html;base64,// raw// 22 bytesbytespublicconstant DATA_HTML_BASE64_URI_RAW ="data:text/html;base64,";
// url encoded// 21 bytesbytespublicconstant DATA_HTML_URL_SAFE ="data%3Atext%2Fhtml%2C";
// <html>,// raw// 6 bytesbytespublicconstant HTML_OPEN_RAW ="<html>";
// url encoded// 10 bytesbytespublicconstant HTML_OPEN_URL_SAFE ="%3Chtml%3E";
// <head>,// raw// 6 bytesbytespublicconstant HEAD_OPEN_RAW ="<head>";
// url encoded// 10 bytesbytespublicconstant HEAD_OPEN_URL_SAFE ="%3Chead%3E";
// </head>,// raw// 7 bytesbytespublicconstant HEAD_CLOSE_RAW ="</head>";
// url encoded// 13 bytesbytespublicconstant HEAD_CLOSE_URL_SAFE ="%3C%2Fhead%3E";
// <body>// 6 bytesbytespublicconstant BODY_OPEN_RAW ="<body>";
// url encoded// 10 bytesbytespublicconstant BODY_OPEN_URL_SAFE ="%3Cbody%3E";
// </body></html>// 14 bytesbytespublicconstant HTML_BODY_CLOSED_RAW ="</body></html>";
// 26 bytesbytespublicconstant HTML_BODY_CLOSED_URL_SAFE ="%3C%2Fbody%3E%3C%2Fhtml%3E";
// [RAW]// HTML_OPEN + HEAD_OPEN + HEAD_CLOSE + BODY_OPEN + HTML_BODY_CLOSEDuint256publicconstant URLS_RAW_BYTES =39;
// [URL_SAFE]// DATA_HTML_URL_SAFE + HTML_OPEN + HEAD_OPEN + HEAD_CLOSE + BODY_OPEN + HTML_BODY_CLOSEDuint256publicconstant URLS_SAFE_BYTES =90;
// [RAW]// HTML_OPEN + HTML_CLOSEuint256publicconstant HTML_RAW_BYTES =13;
// [RAW]// HEAD_OPEN + HEAD_CLOSEuint256publicconstant HEAD_RAW_BYTES =13;
// [RAW]// BODY_OPEN + BODY_CLOSEuint256publicconstant BODY_RAW_BYTES =13;
// All raw// HTML_RAW_BYTES + HEAD_RAW_BYTES + BODY_RAW_BYTESuint256publicconstant RAW_BYTES =39;
// [URL_SAFE]// HTML_OPEN + HTML_CLOSEuint256publicconstant HTML_URL_SAFE_BYTES =23;
// [URL_SAFE]// HEAD_OPEN + HEAD_CLOSEuint256publicconstant HEAD_URL_SAFE_BYTES =23;
// [URL_SAFE]// BODY_OPEN + BODY_CLOSEuint256publicconstant BODY_SAFE_BYTES =23;
// All url safe// HTML_URL_SAFE_BYTES + HEAD_URL_SAFE_BYTES + BODY_URL_SAFE_BYTES// %3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody%3E%3C%2Fbody%3E%3C%2Fhtml%3Euint256publicconstant URL_SAFE_BYTES =69;
// data:text/html;base64,uint256publicconstant HTML_BASE64_DATA_URI_BYTES =22;
// =============================================================// TAG OPEN CLOSE TEMPLATES// =============================================================/**
* @notice Grab tag open and close depending on tag type
* @dev
* tagType: 0/HTMLTagType.useTagOpenAndClose or any other:
* [tagOpen][CONTENT][tagClose]
*
* tagType: 1/HTMLTagType.script:
* <script>[SCRIPT]</script>
*
* tagType: 2/HTMLTagType.scriptBase64DataURI:
* <script src="data:text/javascript;base64,[SCRIPT]"></script>
*
* tagType: 3/HTMLTagType.scriptGZIPBase64DataURI:
* <script type="text/javascript+gzip" src="data:text/javascript;base64,[SCRIPT]"></script>
*
* tagType: 4/HTMLTagType.scriptPNGBase64DataURI
* <script type="text/javascript+png" name="[NAME]" src="data:text/javascript;base64,[SCRIPT]"></script>
*
* [IMPORTANT NOTE]: The tags `text/javascript+gzip` and `text/javascript+png` are used to identify scripts
* during decompression
*
* @param htmlTag - HTMLTag data for code
* @return (tagOpen, tagClose) - Tag open and close as a tuple
*/functiontagOpenCloseForHTMLTag(
HTMLTag memory htmlTag
) publicpurereturns (bytesmemory, bytesmemory) {
if (htmlTag.tagType == HTMLTagType.script) {
return ("<script>", "</script>");
} elseif (htmlTag.tagType == HTMLTagType.scriptBase64DataURI) {
return ('<script src="data:text/javascript;base64,', '"></script>');
} elseif (htmlTag.tagType == HTMLTagType.scriptGZIPBase64DataURI) {
return (
'<script type="text/javascript+gzip" src="data:text/javascript;base64,',
'"></script>'
);
} elseif (htmlTag.tagType == HTMLTagType.scriptPNGBase64DataURI) {
return (
'<script type="text/javascript+png" src="data:text/javascript;base64,',
'"></script>'
);
}
return (htmlTag.tagOpen, htmlTag.tagClose);
}
/**
* @notice Grab URL safe tag open and close depending on tag type
* @dev
* tagType: 0/HTMLTagType.useTagOpenAndClose or any other:
* [tagOpen][scriptContent or scriptFromContract][tagClose]
*
* tagType: 1/HTMLTagType.script:
* tagType: 2/HTMLTagType.scriptBase64DataURI:
* <script src="data:text/javascript;base64,[SCRIPT]"></script>
*
* tagType: 3/HTMLTagType.scriptGZIPBase64DataURI:
* <script type="text/javascript+gzip" src="data:text/javascript;base64,[SCRIPT]"></script>
*
* tagType: 4/HTMLTagType.scriptPNGBase64DataURI
* <script type="text/javascript+png" name="[NAME]" src="data:text/javascript;base64,[SCRIPT]"></script>
*
* [IMPORTANT NOTE]: The tags `text/javascript+gzip` and `text/javascript+png` are used to identify scripts
* during decompression
*
* @param htmlTag - HTMLTag data for code
* @return (tagOpen, tagClose) - Tag open and close as a tuple
*/functiontagOpenCloseForHTMLTagURLSafe(
HTMLTag memory htmlTag
) publicpurereturns (bytesmemory, bytesmemory) {
if (
htmlTag.tagType == HTMLTagType.script ||
htmlTag.tagType == HTMLTagType.scriptBase64DataURI
) {
// <script src="data:text/javascript;base64,// "></script>return (
"%253Cscript%2520src%253D%2522data%253Atext%252Fjavascript%253Bbase64%252C",
"%2522%253E%253C%252Fscript%253E"
);
} elseif (htmlTag.tagType == HTMLTagType.scriptGZIPBase64DataURI) {
// <script type="text/javascript+gzip" src="data:text/javascript;base64,// "></script>return (
"%253Cscript%2520type%253D%2522text%252Fjavascript%252Bgzip%2522%2520src%253D%2522data%253Atext%252Fjavascript%253Bbase64%252C",
"%2522%253E%253C%252Fscript%253E"
);
} elseif (htmlTag.tagType == HTMLTagType.scriptPNGBase64DataURI) {
// <script type="text/javascript+png" src="data:text/javascript;base64,// "></script>return (
"%253Cscript%2520type%253D%2522text%252Fjavascript%252Bpng%2522%2520src%253D%2522data%253Atext%252Fjavascript%253Bbase64%252C",
"%2522%253E%253C%252Fscript%253E"
);
}
return (htmlTag.tagOpen, htmlTag.tagClose);
}
// =============================================================// TAG CONTENT FETCHER// =============================================================/**
* @notice Grabs requested tag content from storage
* @dev
* If given HTMLTag contains non empty contractAddress
* this method will fetch the content from given storage
* contract. Otherwise, it will return the tagContent
* from the given htmlTag.
*
* @param htmlTag - HTMLTag
*/functionfetchTagContent(
HTMLTag memory htmlTag
) publicviewreturns (bytesmemory) {
if (htmlTag.contractAddress !=address(0)) {
return
IScriptyContractStorage(htmlTag.contractAddress).getContent(
htmlTag.name,
htmlTag.contractData
);
}
return htmlTag.tagContent;
}
// =============================================================// SIZE OPERATIONS// =============================================================/**
* @notice Calculate the buffer size post base64 encoding
* @param value - Starting buffer size
* @return Final buffer size as uint256
*/functionsizeForBase64Encoding(uint256 value
) publicpurereturns (uint256) {
unchecked {
return4* ((value +2) /3);
}
}
/**
* @notice Adds the required tag open/close and calculates buffer size of tags
* @dev Effectively multiple functions bundled into one as this saves gas
* @param htmlTags - Array of HTMLTag
* @param isURLSafe - Bool to handle tag content/open/close encoding
* @return Total buffersize of updated HTMLTags
*/function_enrichHTMLTags(
HTMLTag[] memory htmlTags,
bool isURLSafe
) internalviewreturns (uint256) {
if (htmlTags.length==0) {
return0;
}
bytesmemory tagOpen;
bytesmemory tagClose;
bytesmemory tagContent;
uint256 totalSize;
uint256 length = htmlTags.length;
uint256 i;
unchecked {
do {
tagContent = fetchTagContent(htmlTags[i]);
htmlTags[i].tagContent = tagContent;
if (isURLSafe && htmlTags[i].tagType == HTMLTagType.script) {
totalSize += sizeForBase64Encoding(tagContent.length);
} else {
totalSize += tagContent.length;
}
if (isURLSafe) {
(tagOpen, tagClose) = tagOpenCloseForHTMLTagURLSafe(
htmlTags[i]
);
} else {
(tagOpen, tagClose) = tagOpenCloseForHTMLTag(htmlTags[i]);
}
htmlTags[i].tagOpen = tagOpen;
htmlTags[i].tagClose = tagClose;
totalSize += tagOpen.length;
totalSize += tagClose.length;
} while (++i < length);
}
return totalSize;
}
// =============================================================// HTML CONCATENATION// =============================================================/**
* @notice Append tags to the html buffer for tags
* @param htmlFile - bytes buffer
* @param htmlTags - Tags being added to buffer
* @param base64EncodeTagContent - Bool to handle tag content encoding
*/function_appendHTMLTags(bytesmemory htmlFile,
HTMLTag[] memory htmlTags,
bool base64EncodeTagContent
) internalpure{
uint256 i;
unchecked {
do {
_appendHTMLTag(htmlFile, htmlTags[i], base64EncodeTagContent);
} while (++i < htmlTags.length);
}
}
/**
* @notice Append tag to the html buffer
* @param htmlFile - bytes buffer
* @param htmlTag - Request being added to buffer
* @param base64EncodeTagContent - Bool to handle tag content encoding
*/function_appendHTMLTag(bytesmemory htmlFile,
HTMLTag memory htmlTag,
bool base64EncodeTagContent
) internalpure{
htmlFile.appendSafe(htmlTag.tagOpen);
if (base64EncodeTagContent) {
htmlFile.appendSafeBase64(htmlTag.tagContent, false, false);
} else {
htmlFile.appendSafe(htmlTag.tagContent);
}
htmlFile.appendSafe(htmlTag.tagClose);
}
}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.22;libraryUtils{
uint256internalconstant MULTIPLIER =100;
uint256internalconstant GOLDEN_RATIO =161803;
functiontoHexString(bytesmemory data) internalpurereturns (stringmemory) {
bytesmemory alphabet ="0123456789abcdef";
bytesmemory str =newbytes(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)];
}
returnstring(str);
}
/// @dev Zero-index based pseudorandom number based on one input and max bound - thank you Checksfunctionrandom(uint256 input, uint256 _max) internalpurereturns (uint256) {
return (uint256(keccak256(abi.encodePacked(input))) % _max);
}
/// @dev Zero-index based salted pseudorandom number based on two inputs and max bound - thank you Checksfunctionrandom(uint256 input, stringmemory salt, uint256 _max) internalpurereturns (uint256) {
return (uint256(keccak256(abi.encodePacked(input, salt))) % _max);
}
/**
* Compute the largest integer smaller than or equal to the square root of `n`
*/// function floorSqrt(uint256 n) internal pure returns (uint256) { unchecked {// if (n > 0) {// uint256 x = n / 2 + 1;// uint256 y = (x + n / x) / 2;// while (x > y) {// x = y;// y = (x + n / x) / 2;// }// return x;// }// return 0;// }}/**
* Compute the smallest integer larger than or equal to the square root of `n`
*/// function ceilSqrt(uint256 n) internal pure returns (uint256) { unchecked {// uint256 x = floorSqrt(n);// return x ** 2 == n ? x : x + 1;// }}// function lerp(int256 targetFrom, int256 targetTo, int256 currentFrom, int256 currentTo, int current) internal pure returns (int256) { unchecked {// int256 t = 0;a// int256 divisor = currentTo - currentFrom - 1;// if (divisor > 0) {// t = (current - currentFrom) * int256(MULTIPLIER) / (divisor);// }// return targetFrom * int256(MULTIPLIER) + t * (targetTo - targetFrom);// }}functiontoByteArray(bytes32 _bytes32) internalpurereturns (bytesmemory result) {
uint8 i =0;
while(i <32&& _bytes32[i] !=0) {
i++;
}
bytesmemory bytesArray =newbytes(i);
for (i =0; i <32&& _bytes32[i] !=0; i++) {
bytesArray[i] = _bytes32[i];
}
return bytesArray;
}
functiontoString(bytes32 _bytes32) internalpurereturns (stringmemory result) {
returnstring(toByteArray(_bytes32));
}
functiontoStringBytes3(bytes3 _bytes) publicpurereturns (stringmemory) {
bytesmemory hexChars ="0123456789abcdef";
bytesmemory hexString =newbytes(6); // Since bytes3 contains 3 bytes, resulting in 6 hex charactersfor (uint i =0; i <3; i++) {
hexString[i *2] = hexChars[uint8(_bytes[i] >>4)];
hexString[1+ i *2] = hexChars[uint8(_bytes[i] &0x0f)];
}
returnstring(hexString);
}
/*
Gas Efficient uint/int to string functions
Copied from: https://github.com/Vectorized/solady/blob/main/src/utils/LibString.sol
*//// @dev Returns the base 10 decimal representation of `value`.functiontoString(uint256 value) internalpurereturns (stringmemory str) {
/// @solidity memory-safe-assemblyassembly {
// The maximum value of a uint256 contains 78 digits (1 byte per digit), but// we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.// We will need 1 word for the trailing zeros padding, 1 word for the length,// and 3 words for a maximum of 78 digits.
str :=add(mload(0x40), 0x80)
// Update the free memory pointer to allocate.mstore(0x40, add(str, 0x20))
// Zeroize the slot after the string.mstore(str, 0)
// Cache the end of the memory to calculate the length later.let end := str
let w :=not(0) // Tsk.// We write the string from rightmost digit to leftmost digit.// The following is essentially a do-while loop that also handles the zero case.for { let temp := value } 1 {} {
str :=add(str, w) // `sub(str, 1)`.// Write the character to the pointer.// The ASCII index of the '0' character is 48.mstore8(str, add(48, mod(temp, 10)))
// Keep dividing `temp` until zero.
temp :=div(temp, 10)
ifiszero(temp) { break }
}
let length :=sub(end, str)
// Move the pointer 32 bytes leftwards to make room for the length.
str :=sub(str, 0x20)
// Store the length.mstore(str, length)
}
}
/// @dev Returns the base 10 decimal representation of `value`.functiontoString(int256 value) internalpurereturns (stringmemory str) {
if (value >=0) {
return toString(uint256(value));
}
unchecked {
str = toString(uint256(-value));
}
/// @solidity memory-safe-assemblyassembly {
// We still have some spare memory space on the left,// as we have allocated 3 words (96 bytes) for up to 78 digits.let length :=mload(str) // Load the string length.mstore(str, 0x2d) // Store the '-' character.
str :=sub(str, 1) // Move back the string pointer by a byte.mstore(str, add(length, 1)) // Update the string length.
}
}
/// @dev Encodes `data` using the base64 encoding described in RFC 4648./// See: https://datatracker.ietf.org/doc/html/rfc4648/// @param fileSafe Whether to replace '+' with '-' and '/' with '_'./// @param noPadding Whether to strip away the padding.functionencode(bytesmemory data, bool fileSafe, bool noPadding) internalpurereturns (stringmemory result) {
/// @solidity memory-safe-assemblyassembly {
let dataLength :=mload(data)
if dataLength {
// Multiply by 4/3 rounded up.// The `shl(2, ...)` is equivalent to multiplying by 4.let encodedLength :=shl(2, div(add(dataLength, 2), 3))
// Set `result` to point to the start of the free memory.
result :=mload(0x40)
// Store the table into the scratch space.// Offsetted by -1 byte so that the `mload` will load the character.// We will rewrite the free memory pointer at `0x40` later with// the allocated size.// The magic constant 0x0670 will turn "-_" into "+/".mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
mstore(0x3f, xor("ghijklmnopqrstuvwxyz0123456789-_", mul(iszero(fileSafe), 0x0670)))
// Skip the first slot, which stores the length.let ptr :=add(result, 0x20)
let end :=add(ptr, encodedLength)
// Run over the input, 3 bytes at a time.for {} 1 {} {
data :=add(data, 3) // Advance 3 bytes.let input :=mload(data)
// Write 4 bytes. Optimized for fewer stack operations.mstore8(0, mload(and(shr(18, input), 0x3F)))
mstore8(1, mload(and(shr(12, input), 0x3F)))
mstore8(2, mload(and(shr(6, input), 0x3F)))
mstore8(3, mload(and(input, 0x3F)))
mstore(ptr, mload(0x00))
ptr :=add(ptr, 4) // Advance 4 bytes.ifiszero(lt(ptr, end)) { break }
}
mstore(0x40, add(end, 0x20)) // Allocate the memory.// Equivalent to `o = [0, 2, 1][dataLength % 3]`.let o :=div(2, mod(dataLength, 3))
// Offset `ptr` and pad with '='. We can simply write over the end.mstore(sub(ptr, o), shl(240, 0x3d3d))
// Set `o` to zero if there is padding.
o :=mul(iszero(iszero(noPadding)), o)
mstore(sub(ptr, o), 0) // Zeroize the slot after the string.mstore(result, sub(encodedLength, o)) // Store the length.
}
}
}
/// @dev Encodes `data` using the base64 encoding described in RFC 4648./// Equivalent to `encode(data, false, false)`.functionencode(bytesmemory data) internalpurereturns (stringmemory result) {
result = encode(data, false, false);
}
/// @dev Encodes `data` using the base64 encoding described in RFC 4648./// Equivalent to `encode(data, fileSafe, false)`.functionencode(bytesmemory data, bool fileSafe) internalpurereturns (stringmemory result) {
result = encode(data, fileSafe, false);
}
// function contains(string memory _str, string memory _searchStr) internal pure returns (bool) {// bytes memory str = bytes(_str);// bytes memory searchStr = bytes(_searchStr);// for (uint i = 0; i <= str.length - searchStr.length; i++) {// bool found = true;// for (uint j = 0; j < searchStr.length; j++) {// if (str[i + j] != searchStr[j]) {// found = false;// break;// }// }// if (found) return true;// }// return false;// }functionstartsWith(stringmemory _str, stringmemory _prefix) internalpurereturns (bool) {
bytesmemory str =bytes(_str);
bytesmemory prefix =bytes(_prefix);
if (str.length< prefix.length) returnfalse;
for (uint i =0; i < prefix.length; i++) {
if (str[i] != prefix[i]) returnfalse;
}
returntrue;
}
}