Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0xd5f9268e6B723c9e907C4D00e84652E9743fe1a8
Balance 0 ETH
Nonce 5
Code Size 8900 bytes
Proxy EIP-1967 Proxy Implementation: 0xfAf29F63...108A
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

8900 bytes
0x6080604052600436106100c1575f3560e01c80632c5f13e01161007e5780635c60da1b116100585780635c60da1b1461021f57806396a3233d1461024b578063acbcd03914610278578063c87b56dd14610297576100c1565b80632c5f13e0146101a357806342966c68146101cf57806354682d72146101ee576100c1565b806308f1ebdc146100cb578063115f4ed2146100ea578063150b7a02146101095780631ec82cb814610146578063289c1566146101655780632a6446ca14610184575b6100c96102b6565b005b3480156100d6575f80fd5b506100c96100e536600461195f565b6102f1565b3480156100f5575f80fd5b506100c96101043660046119d6565b610426565b348015610114575f80fd5b50610128610123366004611a1e565b6104d2565b6040516001600160e01b031990911681526020015b60405180910390f35b348015610151575f80fd5b506100c9610160366004611a8c565b610743565b348015610170575f80fd5b506100c961017f366004611aca565b610935565b34801561018f575f80fd5b506100c961019e3660046119d6565b610a18565b3480156101ae575f80fd5b506101c26101bd366004611af8565b610b17565b60405161013d9190611b5c565b3480156101da575f80fd5b506100c96101e9366004611af8565b610dce565b3480156101f9575f80fd5b5061020d610208366004611af8565b610e5e565b60405161013d96959493929190611b6e565b34801561022a575f80fd5b50610233610fc8565b6040516001600160a01b03909116815260200161013d565b348015610256575f80fd5b5061026a610265366004611bd2565b610fff565b60405190815260200161013d565b348015610283575f80fd5b506100c9610292366004611bfc565b611059565b3480156102a2575f80fd5b506101c26102b1366004611af8565b6112d4565b6102ef6102ea7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b61151e565b565b30604051632abb908560e11b81523360048201526001600160a01b039190911690635577210a906024015f6040518083038186803b158015610331575f80fd5b505afa158015610343573d5f803e3d5ffd5b50506040516bffffffffffffffffffffffff19606087901b166020820152603481018590525f9250605401905060408051601f1981840301815291815281516020928301205f818152600290935291205490915080158015906103be57505f818152600360205260409020600501546001600160a01b031615155b156103db576040516282b42960e81b815260040160405180910390fd5b5f8281526002602052604080822085905551849186916001600160a01b038916917f4f10d80ce1d90c74402bcbd4977224d006eb6dbb41b3b7e8e563b29a9f23a62491a45050505050565b30604051632abb908560e11b81523360048201526001600160a01b039190911690635577210a906024015f6040518083038186803b158015610466575f80fd5b505afa158015610478573d5f803e3d5ffd5b5050505f8481526003602052604090206002019050610498828483611d3a565b506040518381527ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce7906020015b60405180910390a1505050565b5f33308114610730576040516396a3233d60e01b81526001600160a01b0382166004820152602481018690525f9030906396a3233d90604401602060405180830381865afa158015610526573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061054a9190611df6565b9050805f036105c3576040516396a3233d60e01b81526001600160a01b03831660048201525f19602482015230906396a3233d90604401602060405180830381865afa15801561059c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105c09190611df6565b90505b805f036105e2576040516282b42960e81b815260040160405180910390fd5b6040805180820182526001600160a01b03848116825260208083018a81525f86815260039092529084902092516005840180546001600160a01b03191691909316179091555160069091015551631f44bb2560e01b81526004810182905230906323b872dd908290631f44bb25906024016020604051808303815f875af115801561066f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106939190611e0d565b6040516001600160e01b031960e084901b1681526001600160a01b039182166004820152908a166024820152604481018490526064015f604051808303815f87803b1580156106e0575f80fd5b505af11580156106f2573d5f803e3d5ffd5b505050508086836001600160a01b03167f9b4a3083ec74ccb05a120ced3652113e5285196623a497767dc78b88388b4b9660405160405180910390a4505b50630a85bd0160e11b9695505050505050565b30604051632abb908560e11b81523360048201526001600160a01b039190911690635577210a906024015f6040518083038186803b158015610783575f80fd5b505afa158015610795573d5f803e3d5ffd5b5030925050506001600160a01b038316819003610817576040516323b872dd60e01b81526001600160a01b0382811660048301819052908616602483015260448201849052906323b872dd906064015f604051808303815f87803b1580156107fb575f80fd5b505af115801561080d573d5f803e3d5ffd5b5050505050505050565b6040516396a3233d60e01b81526001600160a01b0384166004820152602481018390525f9030906396a3233d90604401602060405180830381865afa158015610862573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108869190611df6565b90508015806108ac57505f818152600360205260409020600501546001600160a01b0316155b1561091d576040516323b872dd60e01b81526001600160a01b0383811660048301528681166024830152604482018590528516906323b872dd906064015f604051808303815f87803b158015610900575f80fd5b505af1158015610912573d5f803e3d5ffd5b505050505050505050565b6040516282b42960e81b815260040160405180910390fd5b30604051632abb908560e11b81523360048201526001600160a01b039190911690635577210a906024015f6040518083038186803b158015610975575f80fd5b505afa158015610987573d5f803e3d5ffd5b5050505f8381526003602052604080822060040180546001600160a01b0319166001600160a01b038616908117909155905190925084917f47ee77ee77ab677c1ba0296743d87523dc3ffcf3212d24974f9d8bbda76844be91a36040518281527ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce79060200160405180910390a15050565b30604051632abb908560e11b81523360048201526001600160a01b039190911690635577210a906024015f6040518083038186803b158015610a58575f80fd5b505afa158015610a6a573d5f803e3d5ffd5b50505050825f03610af7576001610a82828483611d3a565b505f54600103610abd57604051600181527ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce7906020016104c5565b5f54604080516001815260208101929092527f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c91016104c5565b5f838152600360205260409020600101610498828483611d3a565b505050565b5f818152600360209081526040808320815160c08101909252805460ff16151582526001810180546060959484019190610b5090611cb5565b80601f0160208091040260200160405190810160405280929190818152602001828054610b7c90611cb5565b8015610bc75780601f10610b9e57610100808354040283529160200191610bc7565b820191905f5260205f20905b815481529060010190602001808311610baa57829003601f168201915b50505050508152602001600282018054610be090611cb5565b80601f0160208091040260200160405190810160405280929190818152602001828054610c0c90611cb5565b8015610c575780601f10610c2e57610100808354040283529160200191610c57565b820191905f5260205f20905b815481529060010190602001808311610c3a57829003601f168201915b505050918352505060038201546001600160a01b03908116602080840191909152600484015482166040808501919091528051808201909152600585015490921682526006909301549281019290925260600152600180549192505f91610cbd90611cb5565b80601f0160208091040260200160405190810160405280929190818152602001828054610ce990611cb5565b8015610d345780601f10610d0b57610100808354040283529160200191610d34565b820191905f5260205f20905b815481529060010190602001808311610d1757829003601f168201915b50505060a08501515192935050506001600160a01b031615610d955760a082015151610d5f9061153c565b610d708360a00151602001516115e3565b82604051602001610d8393929190611e43565b60405160208183030381529060405290505b60208201515115610dc757602080830151604051610db592849101611eb5565b60405160208183030381529060405290505b9392505050565b5f818152600360205260409020805460ff1916905530604051630d0873a760e21b81525f6004820152602481018390523360448201526001600160a01b039190911690633421ce9c906064016020604051808303815f875af1158015610e36573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e5a9190611e0d565b5050565b60036020525f90815260409020805460018201805460ff9092169291610e8390611cb5565b80601f0160208091040260200160405190810160405280929190818152602001828054610eaf90611cb5565b8015610efa5780601f10610ed157610100808354040283529160200191610efa565b820191905f5260205f20905b815481529060010190602001808311610edd57829003601f168201915b505050505090806002018054610f0f90611cb5565b80601f0160208091040260200160405190810160405280929190818152602001828054610f3b90611cb5565b8015610f865780601f10610f5d57610100808354040283529160200191610f86565b820191905f5260205f20905b815481529060010190602001808311610f6957829003601f168201915b50505050600383015460048401546040805180820190915260058601546001600160a01b03908116825260069096015460208201529394918216939116915086565b5f610ffa7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b6040516bffffffffffffffffffffffff19606084901b166020820152603481018290525f9060029082906054016040516020818303038152906040528051906020012081526020019081526020015f205490505b92915050565b30604051632abb908560e11b81523360048201526001600160a01b039190911690635577210a906024015f6040518083038186803b158015611099575f80fd5b505afa1580156110ab573d5f803e3d5ffd5b5050505060015f808282546110c09190611ef3565b925050819055506040518060c0016040528060011515815260200187878080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250505090825250604080516020601f8801819004810282018101909252868152918101919087908790819084018382808284375f920191909152505050908252506020018261115b575f611199565b61119984848080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061162592505050565b6001600160a01b031681525f60208083018290526040805180820182528381528083018490529381019390935281548252600381529190208251815460ff19169015151781559082015160018201906111f29082611f12565b50604082015160028201906112079082611f12565b5060608201516003820180546001600160a01b03199081166001600160a01b03938416179091556080840151600484018054831691841691909117905560a0909301518051600584018054909516921691909117909255602090910151600690910155305f54604051633dc8ded760e01b81526001600160a01b038a811660048301526024820192909252911690633dc8ded7906044015f604051808303815f87803b1580156112b5575f80fd5b505af11580156112c7573d5f803e3d5ffd5b5050505050505050505050565b5f818152600360209081526040808320815160c08101909252805460ff1615158252600181018054606095948401919061130d90611cb5565b80601f016020809104026020016040519081016040528092919081815260200182805461133990611cb5565b80156113845780601f1061135b57610100808354040283529160200191611384565b820191905f5260205f20905b81548152906001019060200180831161136757829003601f168201915b5050505050815260200160028201805461139d90611cb5565b80601f01602080910402602001604051908101604052809291908181526020018280546113c990611cb5565b80156114145780601f106113eb57610100808354040283529160200191611414565b820191905f5260205f20905b8154815290600101906020018083116113f757829003601f168201915b505050918352505060038201546001600160a01b03908116602080840191909152600484015482166040808501919091528051808201909152600585015490921682526006909301549281019290925260600152805190915061149257604051637e27328960e01b8152600481018490526024015b60405180910390fd5b60808101516001600160a01b03161561151457608081015160405163c87b56dd60e01b8152600481018590526001600160a01b039091169063c87b56dd906024015f60405180830381865afa1580156114ed573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610dc7919081019061205e565b610dc78382611666565b365f80375f80365f845af43d5f803e808015611538573d5ff35b3d5ffd5b60606115478261178c565b6028602282019081209192507f4040404040404040404040404040404040404040404040404040404040404040917f888888888888888888888888888888888888888888888888888888888888888016601160f31b5f5b82811a82028180015260018101906012190161159e57505050818151165f511660011c8151188152602081019050818151166020511660011c81511881525050919050565b60606080604051019050602081016040525f8152805f19835b928101926030600a8206018453600a9004806115fc575050819003601f19909101908152919050565b5f81518060401b6bfe61000180600a3d393df3000161fffe8211840152600b8101601584015ff09150816116605763301164255f526004601cfd5b90915290565b6060818101516001600160a01b031661169557604051637e27328960e01b815260048101849052602401611489565b5f805f6116a585606001516117b0565b8060200190518101906116b89190612098565b604051630162f89f60e51b8152600481018a905292955090935091505f9084903090632c5f13e0906024015f60405180830381865afa1580156116fd573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611724919081019061205e565b8461172e856117dc565b896040015160405160200161174795949392919061212d565b6040516020818303038152906040529050611761816117dc565b604051602001611771919061224a565b60405160208183030381529060405294505050505092915050565b6060611797826117e9565b8051613078825260020160011990910190815292915050565b6040515f19823b0164ffffffffff16602181015f601f8401853c80825260408201810160405250919050565b6060611053825f80611856565b60606040519050608081016040526f30313233343536373839616263646566600f5260028101905060288152602081015f60288201528260601b92505f5b808101820184821a600f81165160018301538060041c5182535050600181019060121901611827575050919050565b606083518015611940576003600282010460021b60405192507f4142434445464748494a4b4c4d4e4f505152535455565758595a616263646566601f526106708515027f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392d5f18603f526020830181810183886020010180515f82525b60038a0199508951603f8160121c16515f53603f81600c1c1651600153603f8160061c1651600253603f811651600353505f5184526004840193508284106118d1579052602001604052613d3d60f01b60038406600204808303919091525f8615159091029182900352900382525b509392505050565b6001600160a01b038116811461195c575f80fd5b50565b5f805f60608486031215611971575f80fd5b833561197c81611948565b95602085013595506040909401359392505050565b5f8083601f8401126119a1575f80fd5b50813567ffffffffffffffff8111156119b8575f80fd5b6020830191508360208285010111156119cf575f80fd5b9250929050565b5f805f604084860312156119e8575f80fd5b83359250602084013567ffffffffffffffff811115611a05575f80fd5b611a1186828701611991565b9497909650939450505050565b5f805f805f60808688031215611a32575f80fd5b8535611a3d81611948565b94506020860135611a4d81611948565b935060408601359250606086013567ffffffffffffffff811115611a6f575f80fd5b611a7b88828901611991565b969995985093965092949392505050565b5f805f60608486031215611a9e575f80fd5b8335611aa981611948565b92506020840135611ab981611948565b929592945050506040919091013590565b5f8060408385031215611adb575f80fd5b823591506020830135611aed81611948565b809150509250929050565b5f60208284031215611b08575f80fd5b5035919050565b5f5b83811015611b29578181015183820152602001611b11565b50505f910152565b5f8151808452611b48816020860160208601611b0f565b601f01601f19169290920160200192915050565b602081525f610dc76020830184611b31565b861515815260e060208201525f611b8860e0830188611b31565b8281036040840152611b9a8188611b31565b6001600160a01b03968716606085015294861660808401525050815190931660a08401526020015160c0909201919091529392505050565b5f8060408385031215611be3575f80fd5b8235611bee81611948565b946020939093013593505050565b5f805f805f805f6080888a031215611c12575f80fd5b8735611c1d81611948565b9650602088013567ffffffffffffffff80821115611c39575f80fd5b611c458b838c01611991565b909850965060408a0135915080821115611c5d575f80fd5b611c698b838c01611991565b909650945060608a0135915080821115611c81575f80fd5b50611c8e8a828b01611991565b989b979a50959850939692959293505050565b634e487b7160e01b5f52604160045260245ffd5b600181811c90821680611cc957607f821691505b602082108103611ce757634e487b7160e01b5f52602260045260245ffd5b50919050565b601f821115610b12575f81815260208120601f850160051c81016020861015611d135750805b601f850160051c820191505b81811015611d3257828155600101611d1f565b505050505050565b67ffffffffffffffff831115611d5257611d52611ca1565b611d6683611d608354611cb5565b83611ced565b5f601f841160018114611d97575f8515611d805750838201355b5f19600387901b1c1916600186901b178355611def565b5f83815260209020601f19861690835b82811015611dc75786850135825560209485019460019092019101611da7565b5086821015611de3575f1960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b5f60208284031215611e06575f80fd5b5051919050565b5f60208284031215611e1d575f80fd5b8151610dc781611948565b5f8151611e39818560208601611b0f565b9290920192915050565b64027b731b2960dd1b81525f8451611e62816005850160208901611b0f565b602f60f81b6005918401918201528451611e83816006840160208901611b0f565b632e372e3760e11b600692909101918201528351611ea881600a840160208801611b0f565b01600a0195945050505050565b5f8351611ec6818460208801611b0f565b632e372e3760e11b9083019081528351611ee7816004840160208801611b0f565b01600401949350505050565b8082018082111561105357634e487b7160e01b5f52601160045260245ffd5b815167ffffffffffffffff811115611f2c57611f2c611ca1565b611f4081611f3a8454611cb5565b84611ced565b602080601f831160018114611f73575f8415611f5c5750858301515b5f19600386901b1c1916600185901b178555611d32565b5f85815260208120601f198616915b82811015611fa157888601518255948401946001909101908401611f82565b5085821015611fbe57878501515f19600388901b60f8161c191681555b5050505050600190811b01905550565b5f67ffffffffffffffff80841115611fe857611fe8611ca1565b604051601f8501601f19908116603f0116810190828211818310171561201057612010611ca1565b81604052809350858152868686011115612028575f80fd5b612036866020830187611b0f565b5050509392505050565b5f82601f83011261204f575f80fd5b610dc783835160208501611fce565b5f6020828403121561206e575f80fd5b815167ffffffffffffffff811115612084575f80fd5b61209084828501612040565b949350505050565b5f805f606084860312156120aa575f80fd5b835167ffffffffffffffff808211156120c1575f80fd5b6120cd87838801612040565b945060208601519150808211156120e2575f80fd5b6120ee87838801612040565b93506040860151915080821115612103575f80fd5b508401601f81018613612114575f80fd5b61212386825160208401611fce565b9150509250925092565b683d913730b6b2911d1160b91b815285515f90612151816009850160208b01611b0f565b61088b60f21b600991840191820152720898dc99585d195917d89e488e888c1e11c88b606a1b600b8201526e113232b9b1b934b83a34b7b7111d1160891b601e82015286516121a781602d840160208b01611b0f565b016121b9602d820161088b60f21b9052565b6d1134b6b0b3b2911d113230ba309d60911b602f8201526121dd603d820187611e28565b670ed8985cd94d8d0b60c21b815290506121fa6008820186611e28565b61088b60f21b815290506f1132bc3a32b93730b62fbab936111d1160811b600282015261222a6012820185611e28565b601160f91b8152607d60f81b600182015260020198975050505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081525f825161228181601d850160208701611b0f565b91909101601d019291505056fea2646970667358221220245161f11daef9d482626d8702de5133f98cf0a8e3ff824b09bb4e17c2e86b9064736f6c63430008150033

Verified Source Code Full Match

Compiler: v0.8.21+commit.d9974bed EVM: shanghai Optimization: Yes (200 runs)
OnceaHuman.sol 355 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.21;

import {ERC721Baseline} from "erc721baseline/contracts/ERC721Baseline.sol";
import {IERC721Baseline} from "erc721baseline/contracts/IERC721Baseline.sol";
import {Base64} from "solady/src/utils/Base64.sol";
import {LibString} from "solady/src/utils/LibString.sol";
import {SSTORE2} from "solady/src/utils/SSTORE2.sol";

/**
 * @title  Once a Human
 * @author 0xG
 */
contract OnceaHuman is ERC721Baseline {
  uint256 private _nextTokenId;
  string private _description;

  struct Once {
    address token;
    uint256 id;
  }
  mapping(bytes32 => uint256) private onceMapping;

  struct Artwork {
    bool exists;
    string description;
    string externalUrl;
    address metadata;
    address renderer;
    Once once;
  }
  /**
   * @notice Retrieves the information for a given tokenId.
   */
  mapping(uint256 tokenId => Artwork artwork) public artwork;

  event MetadataUpdate(uint256 _tokenId);
  event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
  event SetRenderer(uint256 indexed tokenId, address indexed renderer);
  event RegisterExternalToken(address indexed externalToken, uint256 indexed externalTokenId, uint256 indexed tokenId);
  event OnceSwap(address indexed externalToken, uint256 indexed externalTokenId, uint256 indexed tokenId);

  error ERC721NonexistentToken(uint256 tokenId);

  constructor(address ERC721BaselineImplementation)
    ERC721Baseline(
      ERC721BaselineImplementation,
      "Once a Human",
      "ONCEAHUMAN"
    )
  { }

  /**
   * @notice Retrieves the token URI for a given tokenId.
   *
   * @param tokenId The ID of the token
   * @return uri The token URI
   */
  function tokenURI(uint256 tokenId) external view returns (string memory uri) {
    Artwork memory art = artwork[tokenId];

    if (!art.exists) revert ERC721NonexistentToken(tokenId);

    if (art.renderer != address(0)) {
      return IExternal(art.renderer).tokenURI(tokenId);
    }

    return _tokenURI(tokenId, art);
  }

  /**
   * @dev Default tokenURI renderer, retrieves metadata
   * reading from artwork[tokenId].metadata with SSTORE2.
   *
   * @param tokenId The ID of the token
   * @param art The Artwork instance for tokenId
   * @return uri The token URI
   */
  function _tokenURI(uint256 tokenId, Artwork memory art) internal view returns (string memory uri) {
    if (art.metadata == address(0)) revert ERC721NonexistentToken(tokenId);

    (
      string memory name,
      string memory mimeType,
      bytes memory image
    ) = abi.decode(SSTORE2.read(art.metadata), (string, string, bytes));

    string memory metadata = string.concat(
      '{"name":"', name, '",',
      '"created_by":"0xG",',
      '"description":"', this.description(tokenId), '",',
      '"image":"data:', mimeType, ';base64,', Base64.encode(image),'",',
      '"external_url":"',art.externalUrl,'"',
      '}'
    );

    return string.concat(
      'data:application/json;base64,',
      Base64.encode(bytes(metadata))
    );
  }

  /**
   * @notice The artwork description.
   * @dev This method constructs the description by assembling:
   * - token-specific description
   * - `Once {token_address}/{token_id}` if the token has been swapped
   * - collection-wide description
   *
   * @param tokenId The ID of the token to retrieve the description for
   * @return desc The token description
   */
  function description(uint256 tokenId) external view returns (string memory desc) {
    Artwork memory art = artwork[tokenId];

    string memory description = _description;

    if (art.once.token != address(0)) {
      description = string.concat(
        "Once ",
        LibString.toHexStringChecksummed(art.once.token),
        "/",
        LibString.toString(art.once.id),
        "\\n\\n",
        description
      );
    }

    if (bytes(art.description).length > 0) {
      description = string.concat(
        art.description,
        "\\n\\n",
        description
      );
    }

    return description;
  }

  /**
   * @notice Burns a token with the given tokenId.
   *
   * @param tokenId The ID of the token to burn
   */
  function burn(uint256 tokenId) external {
    artwork[tokenId].exists = false;
    baseline().__update(address(0), tokenId, msg.sender);
  }

  /**
   * @notice Retrieves the "Once a Human" token ID associated with an external artwork.
   *
   * @param externalToken The external artwork address
   * @param externalTokenId The external artwork token id
   * @return tokenId The Once a Human token ID associated with the external artwork
   */
  function tokenIdFromExternalToken(address externalToken, uint256 externalTokenId) external view returns (uint256 tokenId) {
    return onceMapping[keccak256(abi.encodePacked(externalToken, externalTokenId))];
  }

  /**
   * @notice Allows collectors who own external registered artworks to swap them for "Once a Human" tokens.
   * Collectors must send their external artwork token to this contract address using the external artwork's `safeTransferFrom` method.
   */
  function onERC721Received(
    address,
    address recipient,
    uint256 externalTokenId,
    bytes calldata
  ) external returns (bytes4) {
    address externalToken = msg.sender;

    if (externalToken != address(this)) {
      uint256 tokenId = this.tokenIdFromExternalToken(externalToken, externalTokenId);

      if (tokenId == 0) {
        tokenId = this.tokenIdFromExternalToken(externalToken, type(uint256).max);
      }

      if (tokenId == 0) revert IERC721Baseline.Unauthorized();

      artwork[tokenId].once = Once(
        externalToken,
        externalTokenId
      );

      IExternal(address(this)).transferFrom(baseline().__ownerOf(tokenId), recipient, tokenId);
      emit OnceSwap(externalToken, externalTokenId, tokenId);
    }

    return this.onERC721Received.selector;
  }


  /************************************************
   * Admin area
   ************************************************/

  /**
   * @notice Sets the renderer for a specific token.
   * @dev The renderer contract must implement the ERC721 `tokenURI` method.
   *
   * @param tokenId The ID of the token
   * @param renderer The renderer contract address
   */
  function setRenderer(uint256 tokenId, address renderer) external {
    baseline().requireAdmin(msg.sender);

    artwork[tokenId].renderer = renderer;

    emit SetRenderer(tokenId, renderer);
    emit MetadataUpdate(tokenId);
  }

  /**
   * @notice Sets the artwork description.
   * @dev When `tokenId` is `0` this method will update the shared collection description.
   *
   * @param tokenId The ID of the token to update
   * @param desc The new description
   */
  function setDescription(uint256 tokenId, string calldata desc) external {
    baseline().requireAdmin(msg.sender);

    if (tokenId == 0) {
      // Update shared description.
      _description = desc;

      if (_nextTokenId == 1) {
        emit MetadataUpdate(1);
      } else {
        emit BatchMetadataUpdate(1, _nextTokenId);
      }

      return;
    }

    artwork[tokenId].description = desc;

    emit MetadataUpdate(tokenId);
  }

  /**
   * @notice Updates the artwork external_url
   * which contains an off-chain upscaled version of the visual.
   *
   * @param tokenId The ID of the token to update
   * @param url The new external URL
   */
  function setExternalUrl(uint256 tokenId, string calldata url) external {
    baseline().requireAdmin(msg.sender);

    artwork[tokenId].externalUrl = url;

    emit MetadataUpdate(tokenId);
  }

  /**
   * @notice Establishes a link between an external and a "Once a Human" artwork.
   *
   * @param externalToken The external artwork address
   * @param externalTokenId The external artwork token id
   * @param tokenId The Once a Human token ID to associate with the external artwork
   */
  function registerExternalToken(address externalToken, uint256 externalTokenId, uint256 tokenId) external {
    baseline().requireAdmin(msg.sender);

    bytes32 hash = keccak256(abi.encodePacked(externalToken, externalTokenId));
    uint256 currentTokenId = onceMapping[hash];

    if (currentTokenId > 0 && artwork[currentTokenId].once.token != address(0)) {
      revert IERC721Baseline.Unauthorized();
    }

    onceMapping[hash] = tokenId;

    emit RegisterExternalToken(externalToken, externalTokenId, tokenId);
  }

  /**
   * @notice Mints a new artwork.
   * @dev metadata is:
   * ```
   * abi.encode(
   *  name,         // string
   *  externalUrl,  // string
   *  mimeType,     // string
   *  image         // bytes
   * )
   * ```
   *
   * @param to The token recipient
   * @param desc Optional description
   * @param metadata Abi encoded metadata
   */
  function mint(
    address to,
    string calldata desc,
    string calldata externalUrl,
    bytes calldata metadata
  ) external {
    baseline().requireAdmin(msg.sender);

    _nextTokenId += 1;

    artwork[_nextTokenId] = Artwork(
      true,
      desc,
      externalUrl,
      metadata.length > 0 ? SSTORE2.write(metadata) : address(0),
      address(0),
      Once(address(0), 0)
    );

    baseline().__mint(to, _nextTokenId);
  }

  /**
   * @notice Recovers tokens that are accidentally stuck in this contract.
   * @dev Once an external token has been legitimately swapped for a "Once a Human" token,
   * then the external token can't be recovered anymore.
   *
   * @param to The token recipient
   * @param token The token's contract address
   * @param id The token id to recover
   */
  function recover(address to, address token, uint256 id) external {
    baseline().requireAdmin(msg.sender);

    address thisContract = address(this);

    // 1. Once a Human token.
    if (token == thisContract) {
      IExternal(thisContract).transferFrom(thisContract, to, id);
      return;
    }

    uint256 tokenId = this.tokenIdFromExternalToken(token, id);

    // 2. External token with no "Once" association.
    // 3. External token with "Once" association but was sent with transferFrom instead of safeTransferFrom (onERC721Received was not invoked).
    if (tokenId == 0 || artwork[tokenId].once.token == address(0)) {
      IExternal(token).transferFrom(thisContract, to, id);
      return;
    }

    revert IERC721Baseline.Unauthorized();
  }
}

interface IExternal {
  function transferFrom(address from, address to, uint256 tokenId) external;
  function tokenURI(uint256 tokenId) external view returns (string memory);
}
SSTORE2.sol 258 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Read and write to persistent storage at a fraction of the cost.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SSTORE2.sol)
/// @author Saw-mon-and-Natalie (https://github.com/Saw-mon-and-Natalie)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol)
/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol)
/// @author Modified from SSTORE3 (https://github.com/Philogy/sstore3)
library SSTORE2 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The proxy initialization code.
    uint256 private constant _CREATE3_PROXY_INITCODE = 0x67363d3d37363d34f03d5260086018f3;

    /// @dev Hash of the `_CREATE3_PROXY_INITCODE`.
    /// Equivalent to `keccak256(abi.encodePacked(hex"67363d3d37363d34f03d5260086018f3"))`.
    bytes32 internal constant CREATE3_PROXY_INITCODE_HASH =
        0x21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CUSTOM ERRORS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Unable to deploy the storage contract.
    error DeploymentFailed();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         WRITE LOGIC                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Writes `data` into the bytecode of a storage contract and returns its address.
    function write(bytes memory data) internal returns (address pointer) {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(data) // Let `l` be `n + 1`. +1 as we prefix a STOP opcode.
            /**
             * ---------------------------------------------------+
             * Opcode | Mnemonic       | Stack     | Memory       |
             * ---------------------------------------------------|
             * 61 l   | PUSH2 l        | l         |              |
             * 80     | DUP1           | l l       |              |
             * 60 0xa | PUSH1 0xa      | 0xa l l   |              |
             * 3D     | RETURNDATASIZE | 0 0xa l l |              |
             * 39     | CODECOPY       | l         | [0..l): code |
             * 3D     | RETURNDATASIZE | 0 l       | [0..l): code |
             * F3     | RETURN         |           | [0..l): code |
             * 00     | STOP           |           |              |
             * ---------------------------------------------------+
             * @dev Prefix the bytecode with a STOP opcode to ensure it cannot be called.
             * Also PUSH2 is used since max contract size cap is 24,576 bytes which is less than 2 ** 16.
             */
            // Do a out-of-gas revert if `n + 1` is more than 2 bytes.
            mstore(add(data, gt(n, 0xfffe)), add(0xfe61000180600a3d393df300, shl(0x40, n)))
            // Deploy a new contract with the generated creation code.
            pointer := create(0, add(data, 0x15), add(n, 0xb))
            if iszero(pointer) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(data, n) // Restore the length of `data`.
        }
    }

    /// @dev Writes `data` into the bytecode of a storage contract with `salt`
    /// and returns its normal CREATE2 deterministic address.
    function writeCounterfactual(bytes memory data, bytes32 salt)
        internal
        returns (address pointer)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(data)
            // Do a out-of-gas revert if `n + 1` is more than 2 bytes.
            mstore(add(data, gt(n, 0xfffe)), add(0xfe61000180600a3d393df300, shl(0x40, n)))
            // Deploy a new contract with the generated creation code.
            pointer := create2(0, add(data, 0x15), add(n, 0xb), salt)
            if iszero(pointer) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(data, n) // Restore the length of `data`.
        }
    }

    /// @dev Writes `data` into the bytecode of a storage contract and returns its address.
    /// This uses the so-called "CREATE3" workflow,
    /// which means that `pointer` is agnostic to `data, and only depends on `salt`.
    function writeDeterministic(bytes memory data, bytes32 salt)
        internal
        returns (address pointer)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(data)
            mstore(0x00, _CREATE3_PROXY_INITCODE) // Store the `_PROXY_INITCODE`.
            let proxy := create2(0, 0x10, 0x10, salt)
            if iszero(proxy) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x14, proxy) // Store the proxy's address.
            // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01).
            // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex).
            mstore(0x00, 0xd694)
            mstore8(0x34, 0x01) // Nonce of the proxy contract (1).
            pointer := keccak256(0x1e, 0x17)

            // Do a out-of-gas revert if `n + 1` is more than 2 bytes.
            mstore(add(data, gt(n, 0xfffe)), add(0xfe61000180600a3d393df300, shl(0x40, n)))
            if iszero(
                mul( // The arguments of `mul` are evaluated last to first.
                    extcodesize(pointer),
                    call(gas(), proxy, 0, add(data, 0x15), add(n, 0xb), codesize(), 0x00)
                )
            ) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(data, n) // Restore the length of `data`.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                    ADDRESS CALCULATIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the initialization code hash of the storage contract for `data`.
    /// Used for mining vanity addresses with create2crunch.
    function initCodeHash(bytes memory data) internal pure returns (bytes32 hash) {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(data)
            // Do a out-of-gas revert if `n + 1` is more than 2 bytes.
            returndatacopy(returndatasize(), returndatasize(), gt(n, 0xfffe))
            mstore(data, add(0x61000180600a3d393df300, shl(0x40, n)))
            hash := keccak256(add(data, 0x15), add(n, 0xb))
            mstore(data, n) // Restore the length of `data`.
        }
    }

    /// @dev Equivalent to `predictCounterfactualAddress(data, salt, address(this))`
    function predictCounterfactualAddress(bytes memory data, bytes32 salt)
        internal
        view
        returns (address pointer)
    {
        pointer = predictCounterfactualAddress(data, salt, address(this));
    }

    /// @dev Returns the CREATE2 address of the storage contract for `data`
    /// deployed with `salt` by `deployer`.
    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
    function predictCounterfactualAddress(bytes memory data, bytes32 salt, address deployer)
        internal
        pure
        returns (address predicted)
    {
        bytes32 hash = initCodeHash(data);
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and store the bytecode hash.
            mstore8(0x00, 0xff) // Write the prefix.
            mstore(0x35, hash)
            mstore(0x01, shl(96, deployer))
            mstore(0x15, salt)
            predicted := keccak256(0x00, 0x55)
            // Restore the part of the free memory pointer that has been overwritten.
            mstore(0x35, 0)
        }
    }

    /// @dev Equivalent to `predictDeterministicAddress(salt, address(this))`.
    function predictDeterministicAddress(bytes32 salt) internal view returns (address pointer) {
        pointer = predictDeterministicAddress(salt, address(this));
    }

    /// @dev Returns the "CREATE3" deterministic address for `salt` with `deployer`.
    function predictDeterministicAddress(bytes32 salt, address deployer)
        internal
        pure
        returns (address pointer)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, deployer) // Store `deployer`.
            mstore8(0x0b, 0xff) // Store the prefix.
            mstore(0x20, salt) // Store the salt.
            mstore(0x40, CREATE3_PROXY_INITCODE_HASH) // Store the bytecode hash.

            mstore(0x14, keccak256(0x0b, 0x55)) // Store the proxy's address.
            mstore(0x40, m) // Restore the free memory pointer.
            // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01).
            // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex).
            mstore(0x00, 0xd694)
            mstore8(0x34, 0x01) // Nonce of the proxy contract (1).
            pointer := keccak256(0x1e, 0x17)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         READ LOGIC                         */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to `read(pointer, 0, 2 ** 256 - 1)`.
    function read(address pointer) internal view returns (bytes memory data) {
        /// @solidity memory-safe-assembly
        assembly {
            data := mload(0x40)
            let n := and(sub(extcodesize(pointer), 0x01), 0xffffffffff)
            extcodecopy(pointer, add(data, 0x1f), 0x00, add(n, 0x21))
            mstore(data, n) // Store the length.
            mstore(0x40, add(n, add(data, 0x40))) // Allocate memory.
        }
    }

    /// @dev Equivalent to `read(pointer, start, 2 ** 256 - 1)`.
    function read(address pointer, uint256 start) internal view returns (bytes memory data) {
        /// @solidity memory-safe-assembly
        assembly {
            data := mload(0x40)
            let n := and(sub(extcodesize(pointer), 0x01), 0xffffffffff)
            extcodecopy(pointer, add(data, 0x1f), start, add(n, 0x21))
            mstore(data, mul(sub(n, start), lt(start, n))) // Store the length.
            mstore(0x40, add(data, add(0x40, mload(data)))) // Allocate memory.
        }
    }

    /// @dev Returns a slice of the data on `pointer` from `start` to `end`.
    /// `start` and `end` will be clamped to the range `[0, args.length]`.
    /// The `pointer` MUST be deployed via the SSTORE2 write functions.
    /// Otherwise, the behavior is undefined.
    /// Out-of-gas reverts if `pointer` does not have any code.
    function read(address pointer, uint256 start, uint256 end)
        internal
        view
        returns (bytes memory data)
    {
        /// @solidity memory-safe-assembly
        assembly {
            data := mload(0x40)
            if iszero(lt(end, 0xffff)) { end := 0xffff }
            let d := mul(sub(end, start), lt(start, end))
            extcodecopy(pointer, add(data, 0x1f), start, add(d, 0x01))
            if iszero(and(0xff, mload(add(data, d)))) {
                let n := sub(extcodesize(pointer), 0x01)
                returndatacopy(returndatasize(), returndatasize(), shr(64, n))
                d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n))))
            }
            mstore(data, d) // Store the length.
            mstore(add(add(data, 0x20), d), 0) // Zeroize the slot after the bytes.
            mstore(0x40, add(add(data, 0x40), d)) // Allocate memory.
        }
    }
}
LibString.sol 1220 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Library for converting numbers into strings and other string operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
///
/// @dev Note:
/// For performance and bytecode compactness, most of the string operations are restricted to
/// byte strings (7-bit ASCII), except where otherwise specified.
/// Usage of byte string operations on charsets with runes spanning two or more bytes
/// can lead to undefined behavior.
library LibString {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CUSTOM ERRORS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The length of the output is too small to contain all the hex digits.
    error HexLengthInsufficient();

    /// @dev The length of the string is more than 32 bytes.
    error TooBigForSmallString();

    /// @dev The input string must be a 7-bit ASCII.
    error StringNot7BitASCII();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The constant returned when the `search` is not found in the string.
    uint256 internal constant NOT_FOUND = type(uint256).max;

    /// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
    uint128 internal constant ALPHANUMERIC_7_BIT_ASCII = 0x7fffffe07fffffe03ff000000000000;

    /// @dev Lookup for 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
    uint128 internal constant LETTERS_7_BIT_ASCII = 0x7fffffe07fffffe0000000000000000;

    /// @dev Lookup for 'abcdefghijklmnopqrstuvwxyz'.
    uint128 internal constant LOWERCASE_7_BIT_ASCII = 0x7fffffe000000000000000000000000;

    /// @dev Lookup for 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
    uint128 internal constant UPPERCASE_7_BIT_ASCII = 0x7fffffe0000000000000000;

    /// @dev Lookup for '0123456789'.
    uint128 internal constant DIGITS_7_BIT_ASCII = 0x3ff000000000000;

    /// @dev Lookup for '0123456789abcdefABCDEF'.
    uint128 internal constant HEXDIGITS_7_BIT_ASCII = 0x7e0000007e03ff000000000000;

    /// @dev Lookup for '01234567'.
    uint128 internal constant OCTDIGITS_7_BIT_ASCII = 0xff000000000000;

    /// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'.
    uint128 internal constant PRINTABLE_7_BIT_ASCII = 0x7fffffffffffffffffffffff00003e00;

    /// @dev Lookup for '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'.
    uint128 internal constant PUNCTUATION_7_BIT_ASCII = 0x78000001f8000001fc00fffe00000000;

    /// @dev Lookup for ' \t\n\r\x0b\x0c'.
    uint128 internal constant WHITESPACE_7_BIT_ASCII = 0x100003e00;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     DECIMAL OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the base 10 decimal representation of `value`.
    function toString(uint256 value) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            // The maximum value of a uint256 contains 78 digits (1 byte per digit), but
            // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
            // We will need 1 word for the trailing zeros padding, 1 word for the length,
            // and 3 words for a maximum of 78 digits.
            str := add(mload(0x40), 0x80)
            mstore(0x40, add(str, 0x20)) // Allocate the memory.
            mstore(str, 0) // Zeroize the slot after the string.

            let end := str // Cache the end of the memory to calculate the length later.
            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)`.
                // Store the character to the pointer.
                // The ASCII index of the '0' character is 48.
                mstore8(str, add(48, mod(temp, 10)))
                temp := div(temp, 10) // Keep dividing `temp` until zero.
                if iszero(temp) { break }
            }
            let length := sub(end, str)
            str := sub(str, 0x20) // Move the pointer 32 bytes back to make room for the length.
            mstore(str, length) // Store the length.
        }
    }

    /// @dev Returns the base 10 decimal representation of `value`.
    function toString(int256 value) internal pure returns (string memory str) {
        if (value >= 0) return toString(uint256(value));
        unchecked {
            str = toString(~uint256(value) + 1);
        }
        /// @solidity memory-safe-assembly
        assembly {
            // We still have some spare memory space on the left,
            // as we have allocated 3 words (96 bytes) for up to 78 digits.
            let length := mload(str) // Load the string length.
            mstore(str, 0x2d) // Store the '-' character.
            str := sub(str, 1) // Move back the string pointer by a byte.
            mstore(str, add(length, 1)) // Update the string length.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   HEXADECIMAL OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the hexadecimal representation of `value`,
    /// left-padded to an input length of `length` bytes.
    /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
    /// giving a total length of `length * 2 + 2` bytes.
    /// Reverts if `length` is too small for the output to contain all the digits.
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value, length);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Store the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Store the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`,
    /// left-padded to an input length of `length` bytes.
    /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
    /// giving a total length of `length * 2` bytes.
    /// Reverts if `length` is too small for the output to contain all the digits.
    function toHexStringNoPrefix(uint256 value, uint256 length)
        internal
        pure
        returns (string memory str)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes
            // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
            // We add 0x20 to the total and round down to a multiple of 0x20.
            // (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
            str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))
            mstore(0x40, add(str, 0x20)) // Allocate the memory.
            mstore(str, 0) // Zeroize the slot after the string.

            let end := str // Cache the end to calculate the length later.
            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            let start := sub(str, add(length, length))
            let w := not(1) // Tsk.
            let temp := value
            // 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 {} 1 {} {
                str := add(str, w) // `sub(str, 2)`.
                mstore8(add(str, 1), mload(and(temp, 15)))
                mstore8(str, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                if iszero(xor(str, start)) { break }
            }
            if temp {
                mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.
                revert(0x1c, 0x04)
            }
            let strLength := sub(end, str)
            str := sub(str, 0x20)
            mstore(str, strLength) // Store the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
    /// As address are 20 bytes long, the output will left-padded to have
    /// a length of `20 * 2 + 2` bytes.
    function toHexString(uint256 value) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Store the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Store the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x".
    /// The output excludes leading "0" from the `toHexString` output.
    /// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`.
    function toMinimalHexString(uint256 value) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(add(str, o), 0x3078) // Store the "0x" prefix, accounting for leading zero.
            str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero.
            mstore(str, sub(strLength, o)) // Store the length, accounting for leading zero.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output excludes leading "0" from the `toHexStringNoPrefix` output.
    /// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`.
    function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
            let strLength := mload(str) // Get the length.
            str := add(str, o) // Move the pointer, accounting for leading zero.
            mstore(str, sub(strLength, o)) // Store the length, accounting for leading zero.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is encoded using 2 hexadecimal digits per byte.
    /// As address are 20 bytes long, the output will left-padded to have
    /// a length of `20 * 2` bytes.
    function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
            // 0x02 bytes for the prefix, and 0x40 bytes for the digits.
            // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
            str := add(mload(0x40), 0x80)
            mstore(0x40, add(str, 0x20)) // Allocate the memory.
            mstore(str, 0) // Zeroize the slot after the string.

            let end := str // Cache the end to calculate the length later.
            mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.

            let w := not(1) // 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, 2)`.
                mstore8(add(str, 1), mload(and(temp, 15)))
                mstore8(str, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                if iszero(temp) { break }
            }
            let strLength := sub(end, str)
            str := sub(str, 0x20)
            mstore(str, strLength) // Store the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
    /// and the alphabets are capitalized conditionally according to
    /// https://eips.ethereum.org/EIPS/eip-55
    function toHexStringChecksummed(address value) internal pure returns (string memory str) {
        str = toHexString(value);
        /// @solidity memory-safe-assembly
        assembly {
            let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
            let o := add(str, 0x22)
            let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
            let t := shl(240, 136) // `0b10001000 << 240`
            for { let i := 0 } 1 {} {
                mstore(add(i, i), mul(t, byte(i, hashed)))
                i := add(i, 1)
                if eq(i, 20) { break }
            }
            mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
            o := add(o, 0x20)
            mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
    function toHexString(address value) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Store the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Store the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is encoded using 2 hexadecimal digits per byte.
    function toHexStringNoPrefix(address value) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            str := mload(0x40)
            // Allocate the memory.
            // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
            // 0x02 bytes for the prefix, and 0x28 bytes for the digits.
            // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
            mstore(0x40, add(str, 0x80))
            mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.

            str := add(str, 2)
            mstore(str, 40) // Store the length.
            let o := add(str, 0x20)
            mstore(add(o, 40), 0) // Zeroize the slot after the string.
            value := shl(96, value)
            // 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 i := 0 } 1 {} {
                let p := add(o, add(i, i))
                let temp := byte(i, value)
                mstore8(add(p, 1), mload(and(temp, 15)))
                mstore8(p, mload(shr(4, temp)))
                i := add(i, 1)
                if eq(i, 20) { break }
            }
        }
    }

    /// @dev Returns the hex encoded string from the raw bytes.
    /// The output is encoded using 2 hexadecimal digits per byte.
    function toHexString(bytes memory raw) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(raw);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Store the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Store the length.
        }
    }

    /// @dev Returns the hex encoded string from the raw bytes.
    /// The output is encoded using 2 hexadecimal digits per byte.
    function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            let length := mload(raw)
            str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
            mstore(str, add(length, length)) // Store the length of the output.

            mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
            let o := add(str, 0x20)
            let end := add(raw, length)
            for {} iszero(eq(raw, end)) {} {
                raw := add(raw, 1)
                mstore8(add(o, 1), mload(and(mload(raw), 15)))
                mstore8(o, mload(and(shr(4, mload(raw)), 15)))
                o := add(o, 2)
            }
            mstore(o, 0) // Zeroize the slot after the string.
            mstore(0x40, add(o, 0x20)) // Allocate the memory.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   RUNE STRING OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the number of UTF characters in the string.
    function runeCount(string memory s) internal pure returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            if mload(s) {
                mstore(0x00, div(not(0), 255))
                mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)
                let o := add(s, 0x20)
                let end := add(o, mload(s))
                for { result := 1 } 1 { result := add(result, 1) } {
                    o := add(o, byte(0, mload(shr(250, mload(o)))))
                    if iszero(lt(o, end)) { break }
                }
            }
        }
    }

    /// @dev Returns if this string is a 7-bit ASCII string.
    /// (i.e. all characters codes are in [0..127])
    function is7BitASCII(string memory s) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            let mask := shl(7, div(not(0), 255))
            result := 1
            let n := mload(s)
            if n {
                let o := add(s, 0x20)
                let end := add(o, n)
                let last := mload(end)
                mstore(end, 0)
                for {} 1 {} {
                    if and(mask, mload(o)) {
                        result := 0
                        break
                    }
                    o := add(o, 0x20)
                    if iszero(lt(o, end)) { break }
                }
                mstore(end, last)
            }
        }
    }

    /// @dev Returns if this string is a 7-bit ASCII string,
    /// AND all characters are in the `allowed` lookup.
    /// Note: If `s` is empty, returns true regardless of `allowed`.
    function is7BitASCII(string memory s, uint128 allowed) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := 1
            if mload(s) {
                let allowed_ := shr(128, shl(128, allowed))
                let o := add(s, 0x20)
                let end := add(o, mload(s))
                for {} 1 {} {
                    result := and(result, shr(byte(0, mload(o)), allowed_))
                    o := add(o, 1)
                    if iszero(and(result, lt(o, end))) { break }
                }
            }
        }
    }

    /// @dev Converts the bytes in the 7-bit ASCII string `s` to
    /// an allowed lookup for use in `is7BitASCII(s, allowed)`.
    /// To save runtime gas, you can cache the result in an immutable variable.
    function to7BitASCIIAllowedLookup(string memory s) internal pure returns (uint128 result) {
        /// @solidity memory-safe-assembly
        assembly {
            if mload(s) {
                let o := add(s, 0x20)
                let end := add(o, mload(s))
                for {} 1 {} {
                    result := or(result, shl(byte(0, mload(o)), 1))
                    o := add(o, 1)
                    if iszero(lt(o, end)) { break }
                }
                if shr(128, result) {
                    mstore(0x00, 0xc9807e0d) // `StringNot7BitASCII()`.
                    revert(0x1c, 0x04)
                }
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   BYTE STRING OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // For performance and bytecode compactness, byte string operations are restricted
    // to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.
    // Usage of byte string operations on charsets with runes spanning two or more bytes
    // can lead to undefined behavior.

    /// @dev Returns `subject` all occurrences of `search` replaced with `replacement`.
    function replace(string memory subject, string memory search, string memory replacement)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            let searchLength := mload(search)
            let replacementLength := mload(replacement)

            subject := add(subject, 0x20)
            search := add(search, 0x20)
            replacement := add(replacement, 0x20)
            result := add(mload(0x40), 0x20)

            let subjectEnd := add(subject, subjectLength)
            if iszero(gt(searchLength, subjectLength)) {
                let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)
                let h := 0
                if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
                let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
                let s := mload(search)
                for {} 1 {} {
                    let t := mload(subject)
                    // Whether the first `searchLength % 32` bytes of
                    // `subject` and `search` matches.
                    if iszero(shr(m, xor(t, s))) {
                        if h {
                            if iszero(eq(keccak256(subject, searchLength), h)) {
                                mstore(result, t)
                                result := add(result, 1)
                                subject := add(subject, 1)
                                if iszero(lt(subject, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        // Copy the `replacement` one word at a time.
                        for { let o := 0 } 1 {} {
                            mstore(add(result, o), mload(add(replacement, o)))
                            o := add(o, 0x20)
                            if iszero(lt(o, replacementLength)) { break }
                        }
                        result := add(result, replacementLength)
                        subject := add(subject, searchLength)
                        if searchLength {
                            if iszero(lt(subject, subjectSearchEnd)) { break }
                            continue
                        }
                    }
                    mstore(result, t)
                    result := add(result, 1)
                    subject := add(subject, 1)
                    if iszero(lt(subject, subjectSearchEnd)) { break }
                }
            }

            let resultRemainder := result
            result := add(mload(0x40), 0x20)
            let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))
            // Copy the rest of the string one word at a time.
            for {} lt(subject, subjectEnd) {} {
                mstore(resultRemainder, mload(subject))
                resultRemainder := add(resultRemainder, 0x20)
                subject := add(subject, 0x20)
            }
            result := sub(result, 0x20)
            let last := add(add(result, 0x20), k) // Zeroize the slot after the string.
            mstore(last, 0)
            mstore(0x40, add(last, 0x20)) // Allocate the memory.
            mstore(result, k) // Store the length.
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from left to right, starting from `from`.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function indexOf(string memory subject, string memory search, uint256 from)
        internal
        pure
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            for { let subjectLength := mload(subject) } 1 {} {
                if iszero(mload(search)) {
                    if iszero(gt(from, subjectLength)) {
                        result := from
                        break
                    }
                    result := subjectLength
                    break
                }
                let searchLength := mload(search)
                let subjectStart := add(subject, 0x20)

                result := not(0) // Initialize to `NOT_FOUND`.
                subject := add(subjectStart, from)
                let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)

                let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
                let s := mload(add(search, 0x20))

                if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }

                if iszero(lt(searchLength, 0x20)) {
                    for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
                        if iszero(shr(m, xor(mload(subject), s))) {
                            if eq(keccak256(subject, searchLength), h) {
                                result := sub(subject, subjectStart)
                                break
                            }
                        }
                        subject := add(subject, 1)
                        if iszero(lt(subject, end)) { break }
                    }
                    break
                }
                for {} 1 {} {
                    if iszero(shr(m, xor(mload(subject), s))) {
                        result := sub(subject, subjectStart)
                        break
                    }
                    subject := add(subject, 1)
                    if iszero(lt(subject, end)) { break }
                }
                break
            }
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from left to right.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function indexOf(string memory subject, string memory search)
        internal
        pure
        returns (uint256 result)
    {
        result = indexOf(subject, search, 0);
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from right to left, starting from `from`.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function lastIndexOf(string memory subject, string memory search, uint256 from)
        internal
        pure
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            for {} 1 {} {
                result := not(0) // Initialize to `NOT_FOUND`.
                let searchLength := mload(search)
                if gt(searchLength, mload(subject)) { break }
                let w := result

                let fromMax := sub(mload(subject), searchLength)
                if iszero(gt(fromMax, from)) { from := fromMax }

                let end := add(add(subject, 0x20), w)
                subject := add(add(subject, 0x20), from)
                if iszero(gt(subject, end)) { break }
                // As this function is not too often used,
                // we shall simply use keccak256 for smaller bytecode size.
                for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
                    if eq(keccak256(subject, searchLength), h) {
                        result := sub(subject, add(end, 1))
                        break
                    }
                    subject := add(subject, w) // `sub(subject, 1)`.
                    if iszero(gt(subject, end)) { break }
                }
                break
            }
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from right to left.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function lastIndexOf(string memory subject, string memory search)
        internal
        pure
        returns (uint256 result)
    {
        result = lastIndexOf(subject, search, uint256(int256(-1)));
    }

    /// @dev Returns true if `search` is found in `subject`, false otherwise.
    function contains(string memory subject, string memory search) internal pure returns (bool) {
        return indexOf(subject, search) != NOT_FOUND;
    }

    /// @dev Returns whether `subject` starts with `search`.
    function startsWith(string memory subject, string memory search)
        internal
        pure
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let searchLength := mload(search)
            // Just using keccak256 directly is actually cheaper.
            // forgefmt: disable-next-item
            result := and(
                iszero(gt(searchLength, mload(subject))),
                eq(
                    keccak256(add(subject, 0x20), searchLength),
                    keccak256(add(search, 0x20), searchLength)
                )
            )
        }
    }

    /// @dev Returns whether `subject` ends with `search`.
    function endsWith(string memory subject, string memory search)
        internal
        pure
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let searchLength := mload(search)
            let subjectLength := mload(subject)
            // Whether `search` is not longer than `subject`.
            let withinRange := iszero(gt(searchLength, subjectLength))
            // Just using keccak256 directly is actually cheaper.
            // forgefmt: disable-next-item
            result := and(
                withinRange,
                eq(
                    keccak256(
                        // `subject + 0x20 + max(subjectLength - searchLength, 0)`.
                        add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),
                        searchLength
                    ),
                    keccak256(add(search, 0x20), searchLength)
                )
            )
        }
    }

    /// @dev Returns `subject` repeated `times`.
    function repeat(string memory subject, uint256 times)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            if iszero(or(iszero(times), iszero(subjectLength))) {
                subject := add(subject, 0x20)
                result := mload(0x40)
                let output := add(result, 0x20)
                for {} 1 {} {
                    // Copy the `subject` one word at a time.
                    for { let o := 0 } 1 {} {
                        mstore(add(output, o), mload(add(subject, o)))
                        o := add(o, 0x20)
                        if iszero(lt(o, subjectLength)) { break }
                    }
                    output := add(output, subjectLength)
                    times := sub(times, 1)
                    if iszero(times) { break }
                }
                mstore(output, 0) // Zeroize the slot after the string.
                let resultLength := sub(output, add(result, 0x20))
                mstore(result, resultLength) // Store the length.
                mstore(0x40, add(result, add(resultLength, 0x40))) // Allocate the memory.
            }
        }
    }

    /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
    /// `start` and `end` are byte offsets.
    function slice(string memory subject, uint256 start, uint256 end)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            if iszero(gt(subjectLength, end)) { end := subjectLength }
            if iszero(gt(subjectLength, start)) { start := subjectLength }
            if lt(start, end) {
                result := mload(0x40)
                let resultLength := sub(end, start)
                mstore(result, resultLength)
                subject := add(subject, start)
                let w := not(0x1f)
                // Copy the `subject` one word at a time, backwards.
                for { let o := and(add(resultLength, 0x1f), w) } 1 {} {
                    mstore(add(result, o), mload(add(subject, o)))
                    o := add(o, w) // `sub(o, 0x20)`.
                    if iszero(o) { break }
                }
                // Zeroize the slot after the string.
                mstore(add(add(result, 0x20), resultLength), 0)
                mstore(0x40, add(result, add(resultLength, 0x40))) // Allocate the memory.
            }
        }
    }

    /// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
    /// `start` is a byte offset.
    function slice(string memory subject, uint256 start)
        internal
        pure
        returns (string memory result)
    {
        result = slice(subject, start, uint256(int256(-1)));
    }

    /// @dev Returns all the indices of `search` in `subject`.
    /// The indices are byte offsets.
    function indicesOf(string memory subject, string memory search)
        internal
        pure
        returns (uint256[] memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            let searchLength := mload(search)

            if iszero(gt(searchLength, subjectLength)) {
                subject := add(subject, 0x20)
                search := add(search, 0x20)
                result := add(mload(0x40), 0x20)

                let subjectStart := subject
                let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)
                let h := 0
                if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
                let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
                let s := mload(search)
                for {} 1 {} {
                    let t := mload(subject)
                    // Whether the first `searchLength % 32` bytes of
                    // `subject` and `search` matches.
                    if iszero(shr(m, xor(t, s))) {
                        if h {
                            if iszero(eq(keccak256(subject, searchLength), h)) {
                                subject := add(subject, 1)
                                if iszero(lt(subject, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        // Append to `result`.
                        mstore(result, sub(subject, subjectStart))
                        result := add(result, 0x20)
                        // Advance `subject` by `searchLength`.
                        subject := add(subject, searchLength)
                        if searchLength {
                            if iszero(lt(subject, subjectSearchEnd)) { break }
                            continue
                        }
                    }
                    subject := add(subject, 1)
                    if iszero(lt(subject, subjectSearchEnd)) { break }
                }
                let resultEnd := result
                // Assign `result` to the free memory pointer.
                result := mload(0x40)
                // Store the length of `result`.
                mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))
                // Allocate memory for result.
                // We allocate one more word, so this array can be recycled for {split}.
                mstore(0x40, add(resultEnd, 0x20))
            }
        }
    }

    /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.
    function split(string memory subject, string memory delimiter)
        internal
        pure
        returns (string[] memory result)
    {
        uint256[] memory indices = indicesOf(subject, delimiter);
        /// @solidity memory-safe-assembly
        assembly {
            let w := not(0x1f)
            let indexPtr := add(indices, 0x20)
            let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
            mstore(add(indicesEnd, w), mload(subject))
            mstore(indices, add(mload(indices), 1))
            let prevIndex := 0
            for {} 1 {} {
                let index := mload(indexPtr)
                mstore(indexPtr, 0x60)
                if iszero(eq(index, prevIndex)) {
                    let element := mload(0x40)
                    let elementLength := sub(index, prevIndex)
                    mstore(element, elementLength)
                    // Copy the `subject` one word at a time, backwards.
                    for { let o := and(add(elementLength, 0x1f), w) } 1 {} {
                        mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
                        o := add(o, w) // `sub(o, 0x20)`.
                        if iszero(o) { break }
                    }
                    // Zeroize the slot after the string.
                    mstore(add(add(element, 0x20), elementLength), 0)
                    // Allocate memory for the length and the bytes,
                    // rounded up to a multiple of 32.
                    mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))
                    // Store the `element` into the array.
                    mstore(indexPtr, element)
                }
                prevIndex := add(index, mload(delimiter))
                indexPtr := add(indexPtr, 0x20)
                if iszero(lt(indexPtr, indicesEnd)) { break }
            }
            result := indices
            if iszero(mload(delimiter)) {
                result := add(indices, 0x20)
                mstore(result, sub(mload(indices), 2))
            }
        }
    }

    /// @dev Returns a concatenated string of `a` and `b`.
    /// Cheaper than `string.concat()` and does not de-align the free memory pointer.
    function concat(string memory a, string memory b)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let w := not(0x1f)
            result := mload(0x40)
            let aLength := mload(a)
            // Copy `a` one word at a time, backwards.
            for { let o := and(add(aLength, 0x20), w) } 1 {} {
                mstore(add(result, o), mload(add(a, o)))
                o := add(o, w) // `sub(o, 0x20)`.
                if iszero(o) { break }
            }
            let bLength := mload(b)
            let output := add(result, aLength)
            // Copy `b` one word at a time, backwards.
            for { let o := and(add(bLength, 0x20), w) } 1 {} {
                mstore(add(output, o), mload(add(b, o)))
                o := add(o, w) // `sub(o, 0x20)`.
                if iszero(o) { break }
            }
            let totalLength := add(aLength, bLength)
            let last := add(add(result, 0x20), totalLength)
            mstore(last, 0) // Zeroize the slot after the string.
            mstore(result, totalLength) // Store the length.
            mstore(0x40, add(last, 0x20)) // Allocate the memory.
        }
    }

    /// @dev Returns a copy of the string in either lowercase or UPPERCASE.
    /// WARNING! This function is only compatible with 7-bit ASCII strings.
    function toCase(string memory subject, bool toUpper)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let length := mload(subject)
            if length {
                result := add(mload(0x40), 0x20)
                subject := add(subject, 1)
                let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)
                let w := not(0)
                for { let o := length } 1 {} {
                    o := add(o, w)
                    let b := and(0xff, mload(add(subject, o)))
                    mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))
                    if iszero(o) { break }
                }
                result := mload(0x40)
                mstore(result, length) // Store the length.
                let last := add(add(result, 0x20), length)
                mstore(last, 0) // Zeroize the slot after the string.
                mstore(0x40, add(last, 0x20)) // Allocate the memory.
            }
        }
    }

    /// @dev Returns a string from a small bytes32 string.
    /// `s` must be null-terminated, or behavior will be undefined.
    function fromSmallString(bytes32 s) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            let n := 0
            for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\0'.
            mstore(result, n) // Store the length.
            let o := add(result, 0x20)
            mstore(o, s) // Store the bytes of the string.
            mstore(add(o, n), 0) // Zeroize the slot after the string.
            mstore(0x40, add(result, 0x40)) // Allocate the memory.
        }
    }

    /// @dev Returns the small string, with all bytes after the first null byte zeroized.
    function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\0'.
            mstore(0x00, s)
            mstore(result, 0x00)
            result := mload(0x00)
        }
    }

    /// @dev Returns the string as a normalized null-terminated small string.
    function toSmallString(string memory s) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(s)
            if iszero(lt(result, 33)) {
                mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.
                revert(0x1c, 0x04)
            }
            result := shl(shl(3, sub(32, result)), mload(add(s, result)))
        }
    }

    /// @dev Returns a lowercased copy of the string.
    /// WARNING! This function is only compatible with 7-bit ASCII strings.
    function lower(string memory subject) internal pure returns (string memory result) {
        result = toCase(subject, false);
    }

    /// @dev Returns an UPPERCASED copy of the string.
    /// WARNING! This function is only compatible with 7-bit ASCII strings.
    function upper(string memory subject) internal pure returns (string memory result) {
        result = toCase(subject, true);
    }

    /// @dev Escapes the string to be used within HTML tags.
    function escapeHTML(string memory s) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            let end := add(s, mload(s))
            result := add(mload(0x40), 0x20)
            // Store the bytes of the packed offsets and strides into the scratch space.
            // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.
            mstore(0x1f, 0x900094)
            mstore(0x08, 0xc0000000a6ab)
            // Store "&quot;&amp;&#39;&lt;&gt;" into the scratch space.
            mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))
            for {} iszero(eq(s, end)) {} {
                s := add(s, 1)
                let c := and(mload(s), 0xff)
                // Not in `["\"","'","&","<",">"]`.
                if iszero(and(shl(c, 1), 0x500000c400000000)) {
                    mstore8(result, c)
                    result := add(result, 1)
                    continue
                }
                let t := shr(248, mload(c))
                mstore(result, mload(and(t, 0x1f)))
                result := add(result, shr(5, t))
            }
            let last := result
            mstore(last, 0) // Zeroize the slot after the string.
            result := mload(0x40)
            mstore(result, sub(last, add(result, 0x20))) // Store the length.
            mstore(0x40, add(last, 0x20)) // Allocate the memory.
        }
    }

    /// @dev Escapes the string to be used within double-quotes in a JSON.
    /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.
    function escapeJSON(string memory s, bool addDoubleQuotes)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let end := add(s, mload(s))
            result := add(mload(0x40), 0x20)
            if addDoubleQuotes {
                mstore8(result, 34)
                result := add(1, result)
            }
            // Store "\\u0000" in scratch space.
            // Store "0123456789abcdef" in scratch space.
            // Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`.
            // into the scratch space.
            mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)
            // Bitmask for detecting `["\"","\\"]`.
            let e := or(shl(0x22, 1), shl(0x5c, 1))
            for {} iszero(eq(s, end)) {} {
                s := add(s, 1)
                let c := and(mload(s), 0xff)
                if iszero(lt(c, 0x20)) {
                    if iszero(and(shl(c, 1), e)) {
                        // Not in `["\"","\\"]`.
                        mstore8(result, c)
                        result := add(result, 1)
                        continue
                    }
                    mstore8(result, 0x5c) // "\\".
                    mstore8(add(result, 1), c)
                    result := add(result, 2)
                    continue
                }
                if iszero(and(shl(c, 1), 0x3700)) {
                    // Not in `["\b","\t","\n","\f","\d"]`.
                    mstore8(0x1d, mload(shr(4, c))) // Hex value.
                    mstore8(0x1e, mload(and(c, 15))) // Hex value.
                    mstore(result, mload(0x19)) // "\\u00XX".
                    result := add(result, 6)
                    continue
                }
                mstore8(result, 0x5c) // "\\".
                mstore8(add(result, 1), mload(add(c, 8)))
                result := add(result, 2)
            }
            if addDoubleQuotes {
                mstore8(result, 34)
                result := add(1, result)
            }
            let last := result
            mstore(last, 0) // Zeroize the slot after the string.
            result := mload(0x40)
            mstore(result, sub(last, add(result, 0x20))) // Store the length.
            mstore(0x40, add(last, 0x20)) // Allocate the memory.
        }
    }

    /// @dev Escapes the string to be used within double-quotes in a JSON.
    function escapeJSON(string memory s) internal pure returns (string memory result) {
        result = escapeJSON(s, false);
    }

    /// @dev Returns whether `a` equals `b`.
    function eq(string memory a, string memory b) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
        }
    }

    /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.
    function eqs(string memory a, bytes32 b) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            // These should be evaluated on compile time, as far as possible.
            let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.
            let x := not(or(m, or(b, add(m, and(b, m)))))
            let r := shl(7, iszero(iszero(shr(128, x))))
            r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
    ...

// [truncated — 54644 bytes total]
Base64.sol 171 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Library to encode strings in Base64.
/// @author 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 - <[email protected]>.
library Base64 {
    /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
    /// See: https://datatracker.ietf.org/doc/html/rfc4648
    /// @param fileSafe  Whether to replace '+' with '-' and '/' with '_'.
    /// @param noPadding Whether to strip away the padding.
    function encode(bytes memory data, bool fileSafe, bool noPadding)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let dataLength := mload(data)

            if dataLength {
                // Multiply by 4/3 rounded up.
                // The `shl(2, ...)` is equivalent to multiplying by 4.
                let encodedLength := shl(2, div(add(dataLength, 2), 3))

                // Set `result` to point to the start of the free memory.
                result := mload(0x40)

                // Store the table into the scratch space.
                // Offsetted by -1 byte so that the `mload` will load the character.
                // We will rewrite the free memory pointer at `0x40` later with
                // the allocated size.
                // The magic constant 0x0670 will turn "-_" into "+/".
                mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
                mstore(0x3f, xor("ghijklmnopqrstuvwxyz0123456789-_", mul(iszero(fileSafe), 0x0670)))

                // Skip the first slot, which stores the length.
                let ptr := add(result, 0x20)
                let end := add(ptr, encodedLength)

                let dataEnd := add(add(0x20, data), dataLength)
                let dataEndValue := mload(dataEnd) // Cache the value at the `dataEnd` slot.
                mstore(dataEnd, 0x00) // Zeroize the `dataEnd` slot to clear dirty bits.

                // Run over the input, 3 bytes at a time.
                for {} 1 {} {
                    data := add(data, 3) // Advance 3 bytes.
                    let input := mload(data)

                    // Write 4 bytes. Optimized for fewer stack operations.
                    mstore8(0, mload(and(shr(18, input), 0x3F)))
                    mstore8(1, mload(and(shr(12, input), 0x3F)))
                    mstore8(2, mload(and(shr(6, input), 0x3F)))
                    mstore8(3, mload(and(input, 0x3F)))
                    mstore(ptr, mload(0x00))

                    ptr := add(ptr, 4) // Advance 4 bytes.
                    if iszero(lt(ptr, end)) { break }
                }
                mstore(dataEnd, dataEndValue) // Restore the cached value at `dataEnd`.
                mstore(0x40, add(end, 0x20)) // Allocate the memory.
                // Equivalent to `o = [0, 2, 1][dataLength % 3]`.
                let o := div(2, mod(dataLength, 3))
                // Offset `ptr` and pad with '='. We can simply write over the end.
                mstore(sub(ptr, o), shl(240, 0x3d3d))
                // Set `o` to zero if there is padding.
                o := mul(iszero(iszero(noPadding)), o)
                mstore(sub(ptr, o), 0) // Zeroize the slot after the string.
                mstore(result, sub(encodedLength, o)) // Store the length.
            }
        }
    }

    /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
    /// Equivalent to `encode(data, false, false)`.
    function encode(bytes memory data) internal pure returns (string memory result) {
        result = encode(data, false, false);
    }

    /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
    /// Equivalent to `encode(data, fileSafe, false)`.
    function encode(bytes memory data, bool fileSafe)
        internal
        pure
        returns (string memory result)
    {
        result = encode(data, fileSafe, false);
    }

    /// @dev Decodes base64 encoded `data`.
    ///
    /// Supports:
    /// - RFC 4648 (both standard and file-safe mode).
    /// - RFC 3501 (63: ',').
    ///
    /// Does not support:
    /// - Line breaks.
    ///
    /// Note: For performance reasons,
    /// this function will NOT revert on invalid `data` inputs.
    /// Outputs for invalid inputs will simply be undefined behaviour.
    /// It is the user's responsibility to ensure that the `data`
    /// is a valid base64 encoded string.
    function decode(string memory data) internal pure returns (bytes memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            let dataLength := mload(data)

            if dataLength {
                let decodedLength := mul(shr(2, dataLength), 3)

                for {} 1 {} {
                    // If padded.
                    if iszero(and(dataLength, 3)) {
                        let t := xor(mload(add(data, dataLength)), 0x3d3d)
                        // forgefmt: disable-next-item
                        decodedLength := sub(
                            decodedLength,
                            add(iszero(byte(30, t)), iszero(byte(31, t)))
                        )
                        break
                    }
                    // If non-padded.
                    decodedLength := add(decodedLength, sub(and(dataLength, 3), 1))
                    break
                }
                result := mload(0x40)

                // Write the length of the bytes.
                mstore(result, decodedLength)

                // Skip the first slot, which stores the length.
                let ptr := add(result, 0x20)
                let end := add(ptr, decodedLength)

                // Load the table into the scratch space.
                // Constants are optimized for smaller bytecode with zero gas overhead.
                // `m` also doubles as the mask of the upper 6 bits.
                let m := 0xfc000000fc00686c7074787c8084888c9094989ca0a4a8acb0b4b8bcc0c4c8cc
                mstore(0x5b, m)
                mstore(0x3b, 0x04080c1014181c2024282c3034383c4044484c5054585c6064)
                mstore(0x1a, 0xf8fcf800fcd0d4d8dce0e4e8ecf0f4)

                for {} 1 {} {
                    // Read 4 bytes.
                    data := add(data, 4)
                    let input := mload(data)

                    // Write 3 bytes.
                    // forgefmt: disable-next-item
                    mstore(ptr, or(
                        and(m, mload(byte(28, input))),
                        shr(6, or(
                            and(m, mload(byte(29, input))),
                            shr(6, or(
                                and(m, mload(byte(30, input))),
                                shr(6, mload(byte(31, input)))
                            ))
                        ))
                    ))
                    ptr := add(ptr, 3)
                    if iszero(lt(ptr, end)) { break }
                }
                mstore(0x40, add(end, 0x20)) // Allocate the memory.
                mstore(end, 0) // Zeroize the slot after the bytes.
                mstore(0x60, 0) // Restore the zero slot.
            }
        }
    }
}
IERC721Baseline.sol 401 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.21;

import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC2981} from "@openzeppelin/contracts/interfaces/IERC2981.sol";

/**
 * @title IERC721Baseline
 * @custom:version v0.1.0
 * @notice A baseline ERC721 contract implementation that exposes internal methods to a proxy instance.
 */
interface IERC721Baseline is IERC721, IERC2981 {

  /**
   * @notice The version of the implementation contract.
   * @return string the implementation version
   */
  function VERSION() external view returns (string memory);

  /**
   * @dev Indicates an unauthorized operation or unauthorized access attempt.
   */
  error Unauthorized();


  /************************************************
   * Initializer
   ************************************************/

  /**
   * @notice Initializes a proxy contract.
   * @dev This method MUST be called in the proxy constructor via delegatecall
   * to initialize the proxy with a name and symbol for the underlying ERC721.
   *
   * Additionally this method sets the deployer as owner and admin of the proxy.
   *
   * @param name contract name
   * @param symbol contract symbol
   */
  function initialize(string memory name, string memory symbol) external;


  /************************************************
   * Metadata
   ************************************************/

  /**
   * Metadata > ERC-4906 events
   */

  /**
   * @dev This event emits when the metadata of a token is changed.
   * So that the third-party platforms such as NFT market could
   * timely update the images and related attributes of the NFT.
   *
   * @param _tokenId the token ID being updated
   */
  event MetadataUpdate(uint256 _tokenId);

  /**
   * @dev This event emits when the metadata of a range of tokens is changed.
   * So that the third-party platforms such as NFT market could
   * timely update the images and related attributes of the NFTs.
   *
   * @param _fromTokenId the starting token ID
   * @param _toTokenId the ending token ID
   */
  event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);

  /**
   * @notice The total minted supply.
   * @dev The supply is decreased when a token is burned.
   * Generally it is recommended to use a separate counter variable to track the supply available for minting.
   *
   * @return uint256 the existing tokens' supply
   */
  function totalSupply() external view returns (uint256);

  /**
   * Token URI methods.
   *
   * The ERC721Baseline tokenURI implementation allows to define uris in the following order:
   *
   * 1. Token-specific URI by ID.
   * 2. Shared URI.
   * 3. Shared base URI + token ID.
   * 4. Empty string if none of the above was found.
   */

  /**
   * @notice Returns the token URI for a token ID.
   *
   * @param tokenId token ID
   * @return string the token URI for the token ID
   */
  function __tokenURI(uint256 tokenId) external view returns (string memory);

  /**
   * @notice Sets the token URI for a token ID.
   * @dev Emits EIP-4906's `MetadataUpdate` event with the `tokenId`.
   * This method is internal and only the proxy contract can call it.
   *
   * @param tokenId token ID
   * @param tokenURI URI pointing to the metadata
   */
  function __setTokenURI(uint256 tokenId, string calldata tokenURI) external;

  /**
   * @notice Returns the shared URI for the tokens.
   * @dev This method is internal and only the proxy contract can call it.
   *
   * @return string the shared URI
   */
  function __sharedURI() external view returns (string memory);

  /**
   * @notice Sets a shared URI for the tokens.
   * @dev This method doesn't emit the EIP-4906's `BatchMetadataUpdate` event
   * because ERC721Baseline allows to mint any token ID, starting at any index.
   * The proxy should emit `BatchMetadataUpdate`.
   *
   * This method is internal and only the proxy contract can call it.
   *
   * @param sharedURI shared URI for the tokens
   */
  function __setSharedURI(string calldata sharedURI) external;

  /**
   * @notice Returns the base URI for the tokens.
   * @dev When set this URI is prepended to the token ID.
   *
   * @return string the base URI
   */
  function __baseURI() external view returns (string memory);

  /**
   * @notice Sets a contract-wide base URI.
   * @dev This method doesn't emit the EIP-4906 `BatchMetadataUpdate` event
   * because ERC721Baseline allows to mint any token ID, starting at any index.
   * The proxy should emit `BatchMetadataUpdate`.
   *
   * This method is internal and only the proxy contract can call it.
   *
   * @param baseURI shared base URI for the tokens
   */
  function __setBaseURI(string calldata baseURI) external;


  /************************************************
   * Royalties
   ************************************************/

  /**
   * @notice The address of the royalties receiver.
   *
   * @return address the address of the royalties receiver
   */
  function royaltiesReceiver() external view returns (address);

  /**
   * @notice The royalties rate in basis points (100 bps = 1%).
   *
   * @return uint256 the royalties rate
   */
  function royaltiesBps() external view returns (uint256);

  /**
   * @notice Configures royalties receiver and bps for all the tokens.
   * @dev Bps stants for basis points where 100 bps = 1%.
   * The sender must be an admin.
   *
   * @param receiver address for the royalties receiver
   * @param bps (basis points) royalties rate
   */
  function configureRoyalties(address payable receiver, uint16 bps) external;

  /**
   * @notice Configures royalties receiver and bps for all the tokens.
   * @dev Bps stants for basis points where 100 bps = 1%.
   * This method is internal and only the proxy contract can call it.
   *
   * @param receiver address for the royalties receiver
   * @param bps (basis points) royalties rate
   */
  function __configureRoyalties(address payable receiver, uint16 bps) external;


  /************************************************
   * Internal ERC721 methods exposed to the proxy
   ************************************************/

  /**
   * @dev Indicates an invalid attempt to call a method from outside of the proxy.
   */
  error NotProxy();

  /**
   * @dev See {ERC721-_ownerOf}.
   */
  function __ownerOf(uint256 tokenId) external returns (address);

  /**
   * @dev See {ERC721-_update}.
   * This method is internal and only the proxy contract can call it.
   */
  function __update(address to, uint256 tokenId, address auth) external returns (address);

  /**
   * @dev See {ERC721-_mint}.
   * This method is internal and only the proxy contract can call it.
   */
  function __mint(address to, uint256 tokenId) external;

  /**
   * @dev Similar to {ERC721-_mint} but allows to set a dedicated tokenURI for the token.
   * This method is internal and only the proxy contract can call it.
   */
  function __mint(address to, uint256 tokenId, string calldata tokenURI) external;

  /**
   * @dev See {ERC721-_burn}.
   * This method is internal and only the proxy contract can call it.
   */
  function __burn(uint256 tokenId) external;

  /**
   * @dev See {ERC721-_transfer}.
   * This method is internal and only the proxy contract can call it.
   */
  function __transfer(address from, address to, uint256 tokenId) external;

  /**
   * @notice Allows to enable or disable a `_beforeTokenTransfer` hook method defined in the proxy contract.
   * @dev This method is internal and only the proxy contract can call it.
   *
   * When enabled, the proxy's `_beforeTokenTransfer` hook method is invoked prior to a transfer.
   *
   * The proxy's `_beforeTokenTransfer` method is called with the following params:
   *
   * - address the transaction's _msgSender()
   * - address from
   * - address to
   * - uint256 tokenId
   *
   * This method is internal and only the proxy contract can call it.
   */
  function __setBeforeTokenTransferHookEnabled(bool enabled) external;

  /**
   * @dev See {ERC721-_checkOnERC721Received}.
   *
   * NOTE: this method accepts an additional first parameter that is the original transaction's `msg.sender`.
   */
  function __checkOnERC721Received(address sender, address from, address to, uint256 tokenId, bytes memory data) external;

  /**
   * @dev See {ERC721-_isAuthorized}.
   */
  function __isAuthorized(address owner, address spender, uint256 tokenId) external view returns (bool);

  /**
   * @dev See {ERC721-_approve}.
   * This method is internal and only the proxy contract can call it.
   */
  function __approve(address to, uint256 tokenId, address auth, bool emitEvent) external;

  /**
   * @dev See {ERC721-_setApprovalForAll}.
   * This method is internal and only the proxy contract can call it.
   */
  function __setApprovalForAll(address owner, address operator, bool approved) external;


  /************************************************
   * Access control
   ************************************************/

  /**
   * Implements a multi-admin system and a minimal Ownable-compatible API.
   */

  /**
   * Access control > multi-admin system
   */

  /**
   * @notice Checks if an address is the contract owner or an admin.
   *
   * @param addr address to check
   * @return bool whether the address is an admin or not
   */
  function isAdmin(address addr) external view returns (bool);

  /**
   * @dev Emits when an admin is added or removed.
   *
   * @param addr address that is being added or removed as an admin
   * @param add boolean indicating whether the address was grented or revoked admin rights
   */
  event AdminSet(address indexed addr, bool indexed add);

  /**
   * @notice Allows to add or remove an admin.
   * Can only be called by an admin.
   *
   * @param addr address to add or remove
   * @param add boolean indicating whether the address should be granted or revoked rights
   */
  function setAdmin(address addr, bool add) external;

  /**
   * @notice Checks whether an address is an admin and reverts with an `Unauthorized` error if not.
   * @dev Call `requireAdmin` in proxies to implement admin-only public methods.
   *
   * @param addr the address to check
   */
  function requireAdmin(address addr) external view;

  /**
   * @notice Allows to add or remove an admin.
   * @dev This method is internal and only the proxy contract can call it.
   *
   * @param addr address to add or remove
   * @param add boolean indicating whether the address should be granted or revoked rights
   */
  function __setAdmin(address addr, bool add) external;

  /**
   * Access control > Ownable-compatible API.
   */

  /**
   * @notice Returns the address of the contract owner.
   *
   * @return address of the contract owner
   */
  function owner() external view returns (address);

  /**
   * @dev Emits when the contract ownership is transferred.
   *
   * @param previousOwner old owner address
   * @param newOwner new owner address
   */
  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

  /**
   * @notice Transfers ownership of the contract to a new account.
   * Can only be called by an admin.
   *
   * @param newOwner new owner address
   */
  function transferOwnership(address newOwner) external;

  /**
   * @notice Transfers ownership of the contract to a new account.
   * @dev This method is internal and only the proxy contract can call it.
   *
   * @param newOwner new owner address
   */
  function __transferOwnership(address newOwner) external;


  /************************************************
   * Utils
   ************************************************/

  /**
   * @dev Indicates an invalid signature.
   */
  error InvalidSignature();

  /**
   * @notice Recovers the signer's address from a message digest `hash`, and the `signature`.
   *
   * @param hash the message digest that was signed
   * @param signature the signature for hash
   * @return result address the recovered address
   */
  function recover(bytes32 hash, bytes memory signature) external view returns (address result);

  /**
   * @notice Recovers the signer's address from a message digest `hash`, and the `signature`.
   * @dev In this method the signature comes from calldata.
   *
   * @param hash the message digest that was signed
   * @param signature the signature for hash
   * @return result address the recovered address
   */
  function recoverCalldata(bytes32 hash, bytes calldata signature) external view returns (address result);

  /**
   * @notice Converts a uint256 to string
   *
   * @param value the uint256 to convert
   * @return string ASCII string decimal representation of `value`
   */
  function toString(uint256 value) external pure returns (string memory);

}
ERC721Baseline.sol 72 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.21;

import {Proxy} from "@openzeppelin/contracts/proxy/Proxy.sol";
import {IERC721Baseline} from "./IERC721Baseline.sol";

/**
 * @title ERC721Baseline
 * @custom:version v0.1.0
 * @notice A minimal proxy contract for ERC721BaselineImplementation.
 *
 * @dev ERC721BaselineImplementation uses ERC-7201 (Namespaced Storage Layout)
 * to prevent collisions with the proxies storage.
 * See https://eips.ethereum.org/EIPS/eip-7201.
 *
 * Proxies are encouraged, but not required, to use a similar pattern for storage.
 */
contract ERC721Baseline is Proxy {

  struct ImplementationSlot {
    address value;
  }

  /**
   * @dev Storage slot with the address of the current implementation.
   * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
   */
  bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

  constructor(
    address ERC721BaselineImplementation,
    string memory name,
    string memory symbol
  ) {
    ImplementationSlot storage implementation;
    assembly {
      implementation.slot := _IMPLEMENTATION_SLOT
    }
    implementation.value = ERC721BaselineImplementation;

    (bool success, bytes memory reason) = ERC721BaselineImplementation.delegatecall(
      abi.encodeCall(IERC721Baseline.initialize, (name, symbol))
    );

    if (success == false) {
      if (reason.length == 0) revert("Initialization Failed.");
      assembly {
        revert(add(32, reason), mload(reason))
      }
    }
  }

  function _implementation() internal view override returns (address) {
    ImplementationSlot storage implementation;
    assembly {
      implementation.slot := _IMPLEMENTATION_SLOT
    }
    return implementation.value;
  }

  function implementation() external view returns (address) {
    return _implementation();
  }

  /**
   * @notice Returns a reference to the ERC721BaselineImplementation contract.
   */
  function baseline() internal view returns (IERC721Baseline) {
    return IERC721Baseline(address(this));
  }
}
IERC2981.sol 23 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC2981.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../utils/introspection/IERC165.sol";

/**
 * @dev Interface for the NFT Royalty Standard.
 *
 * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
 * support for royalty payments across all NFT marketplaces and ecosystem participants.
 */
interface IERC2981 is IERC165 {
    /**
     * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
     * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
     */
    function royaltyInfo(
        uint256 tokenId,
        uint256 salePrice
    ) external view returns (address receiver, uint256 royaltyAmount);
}
IERC721.sol 135 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
     *   {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the address zero.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}
Proxy.sol 69 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/Proxy.sol)

pragma solidity ^0.8.20;

/**
 * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
 * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
 * be specified by overriding the virtual {_implementation} function.
 *
 * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
 * different contract through the {_delegate} function.
 *
 * The success and return data of the delegated call will be returned back to the caller of the proxy.
 */
abstract contract Proxy {
    /**
     * @dev Delegates the current call to `implementation`.
     *
     * This function does not return to its internal call site, it will return directly to the external caller.
     */
    function _delegate(address implementation) internal virtual {
        assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize())

            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)

            // Copy the returned data.
            returndatacopy(0, 0, returndatasize())

            switch result
            // delegatecall returns 0 on error.
            case 0 {
                revert(0, returndatasize())
            }
            default {
                return(0, returndatasize())
            }
        }
    }

    /**
     * @dev This is a virtual function that should be overridden so it returns the address to which the fallback
     * function and {_fallback} should delegate.
     */
    function _implementation() internal view virtual returns (address);

    /**
     * @dev Delegates the current call to the address returned by `_implementation()`.
     *
     * This function does not return to its internal call site, it will return directly to the external caller.
     */
    function _fallback() internal virtual {
        _delegate(_implementation());
    }

    /**
     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
     * function in the contract matches the call data.
     */
    fallback() external payable virtual {
        _fallback();
    }
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

Read Contract

artwork 0x54682d72 → bool, string, string, address, address, tuple
description 0x2c5f13e0 → string
implementation 0x5c60da1b → address
tokenIdFromExternalToken 0x96a3233d → uint256
tokenURI 0xc87b56dd → string

Write Contract 8 functions

These functions modify contract state and require a wallet transaction to execute.

burn 0x42966c68
uint256 tokenId
mint 0xacbcd039
address to
string desc
string externalUrl
bytes metadata
onERC721Received 0x150b7a02
address
address recipient
uint256 externalTokenId
bytes
returns: bytes4
recover 0x1ec82cb8
address to
address token
uint256 id
registerExternalToken 0x08f1ebdc
address externalToken
uint256 externalTokenId
uint256 tokenId
setDescription 0x2a6446ca
uint256 tokenId
string desc
setExternalUrl 0x115f4ed2
uint256 tokenId
string url
setRenderer 0x289c1566
uint256 tokenId
address renderer

Recent Transactions

No transactions found for this address