Source Code
More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 25 from a total of 381 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Set Approval For... | 4754117 | 593 days ago | IN | 0 ETH | 0.00000118 | ||||
| Set Approval For... | 4753992 | 593 days ago | IN | 0 ETH | 0.00000126 | ||||
| Set Approval For... | 4038817 | 610 days ago | IN | 0 ETH | 0.00000004 | ||||
| Set Approval For... | 4038815 | 610 days ago | IN | 0 ETH | 0.00000004 | ||||
| Set Approval For... | 3777953 | 616 days ago | IN | 0 ETH | 0.00001077 | ||||
| Set Approval For... | 3252134 | 628 days ago | IN | 0 ETH | 0.00000584 | ||||
| Set Approval For... | 2989209 | 634 days ago | IN | 0 ETH | 0.00000989 | ||||
| Safe Transfer Fr... | 2951764 | 635 days ago | IN | 0 ETH | 0.0000114 | ||||
| Claim | 2908887 | 636 days ago | IN | 0 ETH | 0.00000663 | ||||
| Set Approval For... | 2857065 | 637 days ago | IN | 0 ETH | 0.0000134 | ||||
| Set Approval For... | 2857063 | 637 days ago | IN | 0 ETH | 0.00001358 | ||||
| Set Approval For... | 2854250 | 637 days ago | IN | 0 ETH | 0.00001128 | ||||
| Set Approval For... | 2854024 | 637 days ago | IN | 0 ETH | 0.00001171 | ||||
| Set Approval For... | 2853885 | 637 days ago | IN | 0 ETH | 0.00001233 | ||||
| Set Approval For... | 2853884 | 637 days ago | IN | 0 ETH | 0.00001269 | ||||
| Withdraw Ether | 2662226 | 642 days ago | IN | 0 ETH | 0.00001279 | ||||
| Claim | 2648298 | 642 days ago | IN | 0 ETH | 0.00000718 | ||||
| Reserve Mint | 2648248 | 642 days ago | IN | 0 ETH | 0.00000874 | ||||
| Claim | 2605905 | 643 days ago | IN | 0 ETH | 0.00000849 | ||||
| Reserve Mint | 2605839 | 643 days ago | IN | 0 ETH | 0.0000093 | ||||
| Safe Transfer Fr... | 2482417 | 646 days ago | IN | 0 ETH | 0.00001755 | ||||
| Claim | 2390232 | 648 days ago | IN | 0 ETH | 0.00001008 | ||||
| Reserve Mint | 2390183 | 648 days ago | IN | 0 ETH | 0.00000979 | ||||
| Claim | 2303000 | 650 days ago | IN | 0 ETH | 0.00001079 | ||||
| Claim | 2302978 | 650 days ago | IN | 0 ETH | 0.00001125 |
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 2662226 | 642 days ago | 0.053 ETH |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
CryptoThrashers
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
No with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {MerkleProofLib} from "solady/src/utils/MerkleProofLib.sol";
import {Ownable} from "solady/src/auth/Ownable.sol";
import {ReentrancyGuard} from "solady/src/utils/ReentrancyGuard.sol";
import {LibString} from "solady/src/utils/LibString.sol";
import {ERC721R} from "./ERC721R.sol";
import {IBlast} from "./IBlast.sol";
import {IBlastPoints} from "./IBlastPoints.sol";
contract CryptoThrashers is ERC721R, Ownable, ReentrancyGuard {
uint256 public _claimSize = 1;
uint256 public _reserved = 551;
mapping(address => bool) public AreYouThrasher;
mapping(address => bool) public LocalMint;
bool public _mintIsActive = false;
bytes32 public _merkleRoot;
string private _baseTokenURI;
constructor(
string memory _initBaseURI
) ERC721R("CryptoThrashers", "CTS", 3000) {
_initializeOwner(msg.sender);
IBlast(0x4300000000000000000000000000000000000002).configureClaimableGas();
IBlastPoints(0x2536FE9ab3F511540F2f9e2eC2A805005C3Dd800).configurePointsOperator(0x2e696Fe1bf4Dba76E4a093ce3E603db8F01dC8E8);
_baseTokenURI = _initBaseURI;
}
function _baseURI() internal view virtual returns (string memory) {
return _baseTokenURI;
}
function claim() public nonReentrant {
require(_mintIsActive, "Mint is not active.");
require(_claimSize > 0, "There are no free thrashers left.");
require(totalSupply() + _claimSize + _reserved < maxSupply, "There aren't this many thrashers left.");
require(!AreYouThrasher[msg.sender], "Alredy claimed.");
AreYouThrasher[msg.sender] = true;
_mintRandom(msg.sender, _claimSize);
}
function mint(uint256 _numToMint) public payable nonReentrant {
require(_mintIsActive, "Mint is not active.");
require(totalSupply() + _numToMint + _reserved < maxSupply, "There aren't this many thrashers left.");
uint256 costForMintingThrashers = getCostForMintingThrashers(_numToMint);
require(msg.value >= costForMintingThrashers, "Not enough eth.");
if (msg.value > costForMintingThrashers) {
payable(msg.sender).transfer(msg.value - costForMintingThrashers);
}
_mintRandom(msg.sender, _numToMint);
}
function getCostForMintingThrashers(uint256 _numToMint) public view returns (uint256) {
require(totalSupply() + _numToMint + _reserved < maxSupply, "There aren't this many thrashers left.");
if (_numToMint == 1) {
return 0.002 ether;
} else if (_numToMint == 3) {
return 0.005 ether;
} else if (_numToMint == 5) {
return 0.007 ether;
} else if (_numToMint == 10) {
return 0.01 ether;
} else if (_numToMint == 30) {
return 0.027 ether;
} else {
revert("Unsupported mint amount");
}
}
function localMint(uint256 _numToMint, bytes32[] memory proof) public nonReentrant() {
require(_mintIsActive, "Mint is not active.");
require(totalSupply() + _numToMint < maxSupply, "There aren't this many thrashers left.");
require(!LocalMint[msg.sender], "Alredy claimed.");
bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(msg.sender, _numToMint))));
require(MerkleProofLib.verify(proof, _merkleRoot, leaf), "You are not local.");
LocalMint[msg.sender] = true;
_reserved -= _numToMint;
_mintRandom(msg.sender, _numToMint);
}
function reserveMint(uint256 _numToMint) external onlyOwner {
require(totalSupply() + _numToMint < maxSupply, "There aren't this many thrashers left.");
_mintRandom(msg.sender, _numToMint);
}
function tokenURI(uint256 _tokenId) public view override returns (string memory) {
string memory base = _baseURI();
string memory _tokenURI = LibString.toString(_tokenId);
if (bytes(base).length == 0) {
return _tokenURI;
}
return string(abi.encodePacked(base, _tokenURI));
}
function toggleMint() external onlyOwner {
_mintIsActive = !_mintIsActive;
}
function setBaseURI(string calldata baseURI) external onlyOwner {
_baseTokenURI = baseURI;
}
function setMerkleRoot(bytes32 merkleRoot) external onlyOwner {
_merkleRoot = merkleRoot;
}
function setReserved(uint256 reserved) external onlyOwner {
_reserved = reserved;
}
function setClaimSize(uint256 claimSize) external onlyOwner {
_claimSize = claimSize;
}
function withdrawEther() external onlyOwner {
uint256 balance = address(this).balance;
payable(msg.sender).transfer(balance);
}
function claimContractsGas() external onlyOwner {
IBlast(0x4300000000000000000000000000000000000002).claimAllGas(address(this), msg.sender);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {ERC721} from "solady/src/tokens/ERC721.sol";
import {LibPRNG} from "solady/src/utils/LibPRNG.sol";
import {LibString} from "solady/src/utils/LibString.sol";
abstract contract ERC721R is ERC721 {
using LibPRNG for LibPRNG.PRNG;
using LibString for uint256;
error ContractsCannotMint();
error MustMintAtLeastOneToken();
error NotEnoughAvailableTokens();
string private _name;
string private _symbol;
mapping(uint256 => uint256) private _availableTokens;
uint256 public remainingSupply;
uint256 public immutable maxSupply;
constructor(string memory name_, string memory symbol_, uint256 maxSupply_) {
_name = name_;
_symbol = symbol_;
maxSupply = maxSupply_;
remainingSupply = maxSupply_;
}
function totalSupply() public view virtual returns (uint256) {
return maxSupply - remainingSupply;
}
function name() public view virtual override returns (string memory) {
return _name;
}
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
function numberMinted(address minter) public view virtual returns (uint32) {
return uint32(ERC721._getAux(minter) >> 192);
}
function _mintRandom(address to, uint256 _numToMint) internal virtual {
if (msg.sender != tx.origin) revert ContractsCannotMint();
if (_numToMint == 0) revert MustMintAtLeastOneToken();
if (remainingSupply < _numToMint) revert NotEnoughAvailableTokens();
LibPRNG.PRNG memory prng = LibPRNG.PRNG(uint256(keccak256(abi.encodePacked(
block.timestamp, block.prevrandao
))));
uint256 updatedRemainingSupply = remainingSupply;
for (uint256 i; i < _numToMint; ) {
uint256 randomIndex = prng.uniform(updatedRemainingSupply);
uint256 tokenId = getAvailableTokenAtIndex(randomIndex, updatedRemainingSupply);
_mint(to, tokenId);
--updatedRemainingSupply;
unchecked {++i;}
}
_incrementAmountMinted(to, uint32(_numToMint));
remainingSupply = updatedRemainingSupply;
}
function getAvailableTokenAtIndex(uint256 indexToUse, uint256 updatedNumAvailableTokens)
private
returns (uint256 result)
{
uint256 valAtIndex = _availableTokens[indexToUse];
uint256 lastIndex = updatedNumAvailableTokens - 1;
uint256 lastValInArray = _availableTokens[lastIndex];
result = valAtIndex == 0 ? indexToUse : valAtIndex;
if (indexToUse != lastIndex) {
_availableTokens[indexToUse] = lastValInArray == 0 ? lastIndex : lastValInArray;
}
if (lastValInArray != 0) {
delete _availableTokens[lastIndex];
}
}
function _setExtraAddressData(address minter, uint192 extraData) internal virtual {
uint32 numMinted = numberMinted(minter);
ERC721._setAux(
minter,
uint224((uint256(numMinted) << 192)) | uint224(extraData)
);
}
function _getAddressExtraData(address minter) internal view virtual returns (uint192) {
return uint192(_getAux(minter));
}
function _incrementAmountMinted(address minter, uint32 newMints) private {
uint32 numMinted = numberMinted(minter);
uint32 newMintNumMinted = numMinted + uint32(newMints);
uint224 auxData = ERC721._getAux(minter);
ERC721._setAux(
minter,
uint224(uint256(newMintNumMinted) << 192) | uint224(uint192(auxData))
);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
enum YieldMode {
AUTOMATIC,
VOID,
CLAIMABLE
}
enum GasMode {
VOID,
CLAIMABLE
}
interface IBlast {
// configure
function configureContract(address contractAddress, YieldMode _yield, GasMode gasMode, address governor) external;
function configure(YieldMode _yield, GasMode gasMode, address governor) external;
// base configuration options
function configureClaimableYield() external;
function configureClaimableYieldOnBehalf(address contractAddress) external;
function configureAutomaticYield() external;
function configureAutomaticYieldOnBehalf(address contractAddress) external;
function configureVoidYield() external;
function configureVoidYieldOnBehalf(address contractAddress) external;
function configureClaimableGas() external;
function configureClaimableGasOnBehalf(address contractAddress) external;
function configureVoidGas() external;
function configureVoidGasOnBehalf(address contractAddress) external;
function configureGovernor(address _governor) external;
function configureGovernorOnBehalf(address _newGovernor, address contractAddress) external;
// claim yield
function claimYield(address contractAddress, address recipientOfYield, uint256 amount) external returns (uint256);
function claimAllYield(address contractAddress, address recipientOfYield) external returns (uint256);
// claim gas
function claimAllGas(address contractAddress, address recipientOfGas) external returns (uint256);
function claimGasAtMinClaimRate(address contractAddress, address recipientOfGas, uint256 minClaimRateBips) external returns (uint256);
function claimMaxGas(address contractAddress, address recipientOfGas) external returns (uint256);
function claimGas(address contractAddress, address recipientOfGas, uint256 gasToClaim, uint256 gasSecondsToConsume) external returns (uint256);
// read functions
function readClaimableYield(address contractAddress) external view returns (uint256);
function readYieldConfiguration(address contractAddress) external view returns (uint8);
function readGasParams(address contractAddress) external view returns (uint256 etherSeconds, uint256 etherBalance, uint256 lastUpdated, GasMode);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IBlastPoints {
function configurePointsOperator(address operator) external;
function configurePointsOperatorOnBehalf(address contractAddress, address operator) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The caller is not authorized to call the function.
error Unauthorized();
/// @dev The `newOwner` cannot be the zero address.
error NewOwnerIsZeroAddress();
/// @dev The `pendingOwner` does not have a valid handover request.
error NoHandoverRequest();
/// @dev Cannot double-initialize.
error AlreadyInitialized();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ownership is transferred from `oldOwner` to `newOwner`.
/// This event is intentionally kept the same as OpenZeppelin's Ownable to be
/// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
/// despite it not being as lightweight as a single argument event.
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
/// @dev An ownership handover to `pendingOwner` has been requested.
event OwnershipHandoverRequested(address indexed pendingOwner);
/// @dev The ownership handover to `pendingOwner` has been canceled.
event OwnershipHandoverCanceled(address indexed pendingOwner);
/// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
/// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
/// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The owner slot is given by:
/// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
/// It is intentionally chosen to be a high value
/// to avoid collision with lower slots.
/// The choice of manual storage layout is to enable compatibility
/// with both regular and upgradeable contracts.
bytes32 internal constant _OWNER_SLOT =
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;
/// The ownership handover slot of `newOwner` is given by:
/// ```
/// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
/// let handoverSlot := keccak256(0x00, 0x20)
/// ```
/// It stores the expiry timestamp of the two-step ownership handover.
uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
function _guardInitializeOwner() internal pure virtual returns (bool guard) {}
/// @dev Initializes the owner directly without authorization guard.
/// This function must be called upon initialization,
/// regardless of whether the contract is upgradeable or not.
/// This is to enable generalization to both regular and upgradeable contracts,
/// and to save gas in case the initial owner is not the caller.
/// For performance reasons, this function will not check if there
/// is an existing owner.
function _initializeOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
if sload(ownerSlot) {
mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
revert(0x1c, 0x04)
}
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
} else {
/// @solidity memory-safe-assembly
assembly {
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(_OWNER_SLOT, newOwner)
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
}
}
/// @dev Sets the owner directly without authorization guard.
function _setOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
}
} else {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, newOwner)
}
}
}
/// @dev Throws if the sender is not the owner.
function _checkOwner() internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// If the caller is not the stored owner, revert.
if iszero(eq(caller(), sload(_OWNER_SLOT))) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Returns how long a two-step ownership handover is valid for in seconds.
/// Override to return a different value if needed.
/// Made internal to conserve bytecode. Wrap it in a public function if needed.
function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
return 48 * 3600;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC UPDATE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Allows the owner to transfer the ownership to `newOwner`.
function transferOwnership(address newOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
if iszero(shl(96, newOwner)) {
mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
revert(0x1c, 0x04)
}
}
_setOwner(newOwner);
}
/// @dev Allows the owner to renounce their ownership.
function renounceOwnership() public payable virtual onlyOwner {
_setOwner(address(0));
}
/// @dev Request a two-step ownership handover to the caller.
/// The request will automatically expire in 48 hours (172800 seconds) by default.
function requestOwnershipHandover() public payable virtual {
unchecked {
uint256 expires = block.timestamp + _ownershipHandoverValidFor();
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to `expires`.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), expires)
// Emit the {OwnershipHandoverRequested} event.
log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
}
}
}
/// @dev Cancels the two-step ownership handover to the caller, if any.
function cancelOwnershipHandover() public payable virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), 0)
// Emit the {OwnershipHandoverCanceled} event.
log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
}
}
/// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
/// Reverts if there is no existing ownership handover requested by `pendingOwner`.
function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
let handoverSlot := keccak256(0x0c, 0x20)
// If the handover does not exist, or has expired.
if gt(timestamp(), sload(handoverSlot)) {
mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
revert(0x1c, 0x04)
}
// Set the handover slot to 0.
sstore(handoverSlot, 0)
}
_setOwner(pendingOwner);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC READ FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the owner of the contract.
function owner() public view virtual returns (address result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(_OWNER_SLOT)
}
}
/// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
function ownershipHandoverExpiresAt(address pendingOwner)
public
view
virtual
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
// Compute the handover slot.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
// Load the handover slot.
result := sload(keccak256(0x0c, 0x20))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Marks a function as only callable by the owner.
modifier onlyOwner() virtual {
_checkOwner();
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple ERC721 implementation with storage hitchhiking.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC721.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC721/ERC721.sol)
///
/// @dev Note:
/// - The ERC721 standard allows for self-approvals.
/// For performance, this implementation WILL NOT revert for such actions.
/// Please add any checks with overrides if desired.
/// - For performance, methods are made payable where permitted by the ERC721 standard.
/// - The `safeTransfer` functions use the identity precompile (0x4)
/// to copy memory internally.
///
/// If you are overriding:
/// - NEVER violate the ERC721 invariant:
/// the balance of an owner MUST always be equal to their number of ownership slots.
/// The transfer functions do not have an underflow guard for user token balances.
/// - Make sure all variables written to storage are properly cleaned
// (e.g. the bool value for `isApprovedForAll` MUST be either 1 or 0 under the hood).
/// - Check that the overridden function is actually used in the function you want to
/// change the behavior of. Much of the code has been manually inlined for performance.
abstract contract ERC721 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev An account can hold up to 4294967295 tokens.
uint256 internal constant _MAX_ACCOUNT_BALANCE = 0xffffffff;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Only the token owner or an approved account can manage the token.
error NotOwnerNorApproved();
/// @dev The token does not exist.
error TokenDoesNotExist();
/// @dev The token already exists.
error TokenAlreadyExists();
/// @dev Cannot query the balance for the zero address.
error BalanceQueryForZeroAddress();
/// @dev Cannot mint or transfer to the zero address.
error TransferToZeroAddress();
/// @dev The token must be owned by `from`.
error TransferFromIncorrectOwner();
/// @dev The recipient's balance has overflowed.
error AccountBalanceOverflow();
/// @dev Cannot safely transfer to a contract that does not implement
/// the ERC721Receiver interface.
error TransferToNonERC721ReceiverImplementer();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Emitted when token `id` is transferred from `from` to `to`.
event Transfer(address indexed from, address indexed to, uint256 indexed id);
/// @dev Emitted when `owner` enables `account` to manage the `id` token.
event Approval(address indexed owner, address indexed account, uint256 indexed id);
/// @dev Emitted when `owner` enables or disables `operator` to manage all of their tokens.
event ApprovalForAll(address indexed owner, address indexed operator, bool isApproved);
/// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
uint256 private constant _TRANSFER_EVENT_SIGNATURE =
0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
/// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
uint256 private constant _APPROVAL_EVENT_SIGNATURE =
0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;
/// @dev `keccak256(bytes("ApprovalForAll(address,address,bool)"))`.
uint256 private constant _APPROVAL_FOR_ALL_EVENT_SIGNATURE =
0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ownership data slot of `id` is given by:
/// ```
/// mstore(0x00, id)
/// mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
/// let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
/// ```
/// Bits Layout:
/// - [0..159] `addr`
/// - [160..255] `extraData`
///
/// The approved address slot is given by: `add(1, ownershipSlot)`.
///
/// See: https://notes.ethereum.org/%40vbuterin/verkle_tree_eip
///
/// The balance slot of `owner` is given by:
/// ```
/// mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
/// mstore(0x00, owner)
/// let balanceSlot := keccak256(0x0c, 0x1c)
/// ```
/// Bits Layout:
/// - [0..31] `balance`
/// - [32..255] `aux`
///
/// The `operator` approval slot of `owner` is given by:
/// ```
/// mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, operator))
/// mstore(0x00, owner)
/// let operatorApprovalSlot := keccak256(0x0c, 0x30)
/// ```
uint256 private constant _ERC721_MASTER_SLOT_SEED = 0x7d8825530a5a2e7a << 192;
/// @dev Pre-shifted and pre-masked constant.
uint256 private constant _ERC721_MASTER_SLOT_SEED_MASKED = 0x0a5a2e7a00000000;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC721 METADATA */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the token collection name.
function name() public view virtual returns (string memory);
/// @dev Returns the token collection symbol.
function symbol() public view virtual returns (string memory);
/// @dev Returns the Uniform Resource Identifier (URI) for token `id`.
function tokenURI(uint256 id) public view virtual returns (string memory);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC721 */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the owner of token `id`.
///
/// Requirements:
/// - Token `id` must exist.
function ownerOf(uint256 id) public view virtual returns (address result) {
result = _ownerOf(id);
/// @solidity memory-safe-assembly
assembly {
if iszero(result) {
mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Returns the number of tokens owned by `owner`.
///
/// Requirements:
/// - `owner` must not be the zero address.
function balanceOf(address owner) public view virtual returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
// Revert if the `owner` is the zero address.
if iszero(owner) {
mstore(0x00, 0x8f4eb604) // `BalanceQueryForZeroAddress()`.
revert(0x1c, 0x04)
}
mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
mstore(0x00, owner)
result := and(sload(keccak256(0x0c, 0x1c)), _MAX_ACCOUNT_BALANCE)
}
}
/// @dev Returns the account approved to manage token `id`.
///
/// Requirements:
/// - Token `id` must exist.
function getApproved(uint256 id) public view virtual returns (address result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, id)
mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
if iszero(shl(96, sload(ownershipSlot))) {
mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
revert(0x1c, 0x04)
}
result := sload(add(1, ownershipSlot))
}
}
/// @dev Sets `account` as the approved account to manage token `id`.
///
/// Requirements:
/// - Token `id` must exist.
/// - The caller must be the owner of the token,
/// or an approved operator for the token owner.
///
/// Emits an {Approval} event.
function approve(address account, uint256 id) public payable virtual {
_approve(msg.sender, account, id);
}
/// @dev Returns whether `operator` is approved to manage the tokens of `owner`.
function isApprovedForAll(address owner, address operator)
public
view
virtual
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
mstore(0x1c, operator)
mstore(0x08, _ERC721_MASTER_SLOT_SEED_MASKED)
mstore(0x00, owner)
result := sload(keccak256(0x0c, 0x30))
}
}
/// @dev Sets whether `operator` is approved to manage the tokens of the caller.
///
/// Emits an {ApprovalForAll} event.
function setApprovalForAll(address operator, bool isApproved) public virtual {
/// @solidity memory-safe-assembly
assembly {
// Convert to 0 or 1.
isApproved := iszero(iszero(isApproved))
// Update the `isApproved` for (`msg.sender`, `operator`).
mstore(0x1c, operator)
mstore(0x08, _ERC721_MASTER_SLOT_SEED_MASKED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x30), isApproved)
// Emit the {ApprovalForAll} event.
mstore(0x00, isApproved)
// forgefmt: disable-next-item
log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, caller(), shr(96, shl(96, operator)))
}
}
/// @dev Transfers token `id` from `from` to `to`.
///
/// Requirements:
///
/// - Token `id` must exist.
/// - `from` must be the owner of the token.
/// - `to` cannot be the zero address.
/// - The caller must be the owner of the token, or be approved to manage the token.
///
/// Emits a {Transfer} event.
function transferFrom(address from, address to, uint256 id) public payable virtual {
_beforeTokenTransfer(from, to, id);
/// @solidity memory-safe-assembly
assembly {
// Clear the upper 96 bits.
let bitmaskAddress := shr(96, not(0))
from := and(bitmaskAddress, from)
to := and(bitmaskAddress, to)
// Load the ownership data.
mstore(0x00, id)
mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, caller()))
let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
let ownershipPacked := sload(ownershipSlot)
let owner := and(bitmaskAddress, ownershipPacked)
// Revert if the token does not exist, or if `from` is not the owner.
if iszero(mul(owner, eq(owner, from))) {
// `TokenDoesNotExist()`, `TransferFromIncorrectOwner()`.
mstore(shl(2, iszero(owner)), 0xceea21b6a1148100)
revert(0x1c, 0x04)
}
// Load, check, and update the token approval.
{
mstore(0x00, from)
let approvedAddress := sload(add(1, ownershipSlot))
// Revert if the caller is not the owner, nor approved.
if iszero(or(eq(caller(), from), eq(caller(), approvedAddress))) {
if iszero(sload(keccak256(0x0c, 0x30))) {
mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
revert(0x1c, 0x04)
}
}
// Delete the approved address if any.
if approvedAddress { sstore(add(1, ownershipSlot), 0) }
}
// Update with the new owner.
sstore(ownershipSlot, xor(ownershipPacked, xor(from, to)))
// Decrement the balance of `from`.
{
let fromBalanceSlot := keccak256(0x0c, 0x1c)
sstore(fromBalanceSlot, sub(sload(fromBalanceSlot), 1))
}
// Increment the balance of `to`.
{
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x1c)
let toBalanceSlotPacked := add(sload(toBalanceSlot), 1)
// Revert if `to` is the zero address, or if the account balance overflows.
if iszero(mul(to, and(toBalanceSlotPacked, _MAX_ACCOUNT_BALANCE))) {
// `TransferToZeroAddress()`, `AccountBalanceOverflow()`.
mstore(shl(2, iszero(to)), 0xea553b3401336cea)
revert(0x1c, 0x04)
}
sstore(toBalanceSlot, toBalanceSlotPacked)
}
// Emit the {Transfer} event.
log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id)
}
_afterTokenTransfer(from, to, id);
}
/// @dev Equivalent to `safeTransferFrom(from, to, id, "")`.
function safeTransferFrom(address from, address to, uint256 id) public payable virtual {
transferFrom(from, to, id);
if (_hasCode(to)) _checkOnERC721Received(from, to, id, "");
}
/// @dev Transfers token `id` from `from` to `to`.
///
/// Requirements:
///
/// - Token `id` must exist.
/// - `from` must be the owner of the token.
/// - `to` cannot be the zero address.
/// - The caller must be the owner of the token, or be approved to manage the token.
/// - 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 id, bytes calldata data)
public
payable
virtual
{
transferFrom(from, to, id);
if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);
}
/// @dev Returns true if this contract implements the interface defined by `interfaceId`.
/// See: https://eips.ethereum.org/EIPS/eip-165
/// This function call must use less than 30000 gas.
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
let s := shr(224, interfaceId)
// ERC165: 0x01ffc9a7, ERC721: 0x80ac58cd, ERC721Metadata: 0x5b5e139f.
result := or(or(eq(s, 0x01ffc9a7), eq(s, 0x80ac58cd)), eq(s, 0x5b5e139f))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL QUERY FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns if token `id` exists.
function _exists(uint256 id) internal view virtual returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, id)
mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
result := iszero(iszero(shl(96, sload(add(id, add(id, keccak256(0x00, 0x20)))))))
}
}
/// @dev Returns the owner of token `id`.
/// Returns the zero address instead of reverting if the token does not exist.
function _ownerOf(uint256 id) internal view virtual returns (address result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, id)
mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
result := shr(96, shl(96, sload(add(id, add(id, keccak256(0x00, 0x20))))))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL DATA HITCHHIKING FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// For performance, no events are emitted for the hitchhiking setters.
// Please emit your own events if required.
/// @dev Returns the auxiliary data for `owner`.
/// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data.
/// Auxiliary data can be set for any address, even if it does not have any tokens.
function _getAux(address owner) internal view virtual returns (uint224 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
mstore(0x00, owner)
result := shr(32, sload(keccak256(0x0c, 0x1c)))
}
}
/// @dev Set the auxiliary data for `owner` to `value`.
/// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data.
/// Auxiliary data can be set for any address, even if it does not have any tokens.
function _setAux(address owner, uint224 value) internal virtual {
/// @solidity memory-safe-assembly
assembly {
mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
mstore(0x00, owner)
let balanceSlot := keccak256(0x0c, 0x1c)
let packed := sload(balanceSlot)
sstore(balanceSlot, xor(packed, shl(32, xor(value, shr(32, packed)))))
}
}
/// @dev Returns the extra data for token `id`.
/// Minting, transferring, burning a token will not change the extra data.
/// The extra data can be set on a non-existent token.
function _getExtraData(uint256 id) internal view virtual returns (uint96 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, id)
mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
result := shr(160, sload(add(id, add(id, keccak256(0x00, 0x20)))))
}
}
/// @dev Sets the extra data for token `id` to `value`.
/// Minting, transferring, burning a token will not change the extra data.
/// The extra data can be set on a non-existent token.
function _setExtraData(uint256 id, uint96 value) internal virtual {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, id)
mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
let packed := sload(ownershipSlot)
sstore(ownershipSlot, xor(packed, shl(160, xor(value, shr(160, packed)))))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL MINT FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Mints token `id` to `to`.
///
/// Requirements:
///
/// - Token `id` must not exist.
/// - `to` cannot be the zero address.
///
/// Emits a {Transfer} event.
function _mint(address to, uint256 id) internal virtual {
_beforeTokenTransfer(address(0), to, id);
/// @solidity memory-safe-assembly
assembly {
// Clear the upper 96 bits.
to := shr(96, shl(96, to))
// Load the ownership data.
mstore(0x00, id)
mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
let ownershipPacked := sload(ownershipSlot)
// Revert if the token already exists.
if shl(96, ownershipPacked) {
mstore(0x00, 0xc991cbb1) // `TokenAlreadyExists()`.
revert(0x1c, 0x04)
}
// Update with the owner.
sstore(ownershipSlot, or(ownershipPacked, to))
// Increment the balance of the owner.
{
mstore(0x00, to)
let balanceSlot := keccak256(0x0c, 0x1c)
let balanceSlotPacked := add(sload(balanceSlot), 1)
// Revert if `to` is the zero address, or if the account balance overflows.
if iszero(mul(to, and(balanceSlotPacked, _MAX_ACCOUNT_BALANCE))) {
// `TransferToZeroAddress()`, `AccountBalanceOverflow()`.
mstore(shl(2, iszero(to)), 0xea553b3401336cea)
revert(0x1c, 0x04)
}
sstore(balanceSlot, balanceSlotPacked)
}
// Emit the {Transfer} event.
log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, 0, to, id)
}
_afterTokenTransfer(address(0), to, id);
}
/// @dev Mints token `id` to `to`, and updates the extra data for token `id` to `value`.
/// Does NOT check if token `id` already exists (assumes `id` is auto-incrementing).
///
/// Requirements:
///
/// - `to` cannot be the zero address.
///
/// Emits a {Transfer} event.
function _mintAndSetExtraDataUnchecked(address to, uint256 id, uint96 value) internal virtual {
_beforeTokenTransfer(address(0), to, id);
/// @solidity memory-safe-assembly
assembly {
// Clear the upper 96 bits.
to := shr(96, shl(96, to))
// Update with the owner and extra data.
mstore(0x00, id)
mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
sstore(add(id, add(id, keccak256(0x00, 0x20))), or(shl(160, value), to))
// Increment the balance of the owner.
{
mstore(0x00, to)
let balanceSlot := keccak256(0x0c, 0x1c)
let balanceSlotPacked := add(sload(balanceSlot), 1)
// Revert if `to` is the zero address, or if the account balance overflows.
if iszero(mul(to, and(balanceSlotPacked, _MAX_ACCOUNT_BALANCE))) {
// `TransferToZeroAddress()`, `AccountBalanceOverflow()`.
mstore(shl(2, iszero(to)), 0xea553b3401336cea)
revert(0x1c, 0x04)
}
sstore(balanceSlot, balanceSlotPacked)
}
// Emit the {Transfer} event.
log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, 0, to, id)
}
_afterTokenTransfer(address(0), to, id);
}
/// @dev Equivalent to `_safeMint(to, id, "")`.
function _safeMint(address to, uint256 id) internal virtual {
_safeMint(to, id, "");
}
/// @dev Mints token `id` to `to`.
///
/// Requirements:
///
/// - Token `id` must not exist.
/// - `to` cannot be the zero address.
/// - If `to` refers to a smart contract, it must implement
/// {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
///
/// Emits a {Transfer} event.
function _safeMint(address to, uint256 id, bytes memory data) internal virtual {
_mint(to, id);
if (_hasCode(to)) _checkOnERC721Received(address(0), to, id, data);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL BURN FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Equivalent to `_burn(address(0), id)`.
function _burn(uint256 id) internal virtual {
_burn(address(0), id);
}
/// @dev Destroys token `id`, using `by`.
///
/// Requirements:
///
/// - Token `id` must exist.
/// - If `by` is not the zero address,
/// it must be the owner of the token, or be approved to manage the token.
///
/// Emits a {Transfer} event.
function _burn(address by, uint256 id) internal virtual {
address owner = ownerOf(id);
_beforeTokenTransfer(owner, address(0), id);
/// @solidity memory-safe-assembly
assembly {
// Clear the upper 96 bits.
by := shr(96, shl(96, by))
// Load the ownership data.
mstore(0x00, id)
mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by))
let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
let ownershipPacked := sload(ownershipSlot)
// Reload the owner in case it is changed in `_beforeTokenTransfer`.
owner := shr(96, shl(96, ownershipPacked))
// Revert if the token does not exist.
if iszero(owner) {
mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
revert(0x1c, 0x04)
}
// Load and check the token approval.
{
mstore(0x00, owner)
let approvedAddress := sload(add(1, ownershipSlot))
// If `by` is not the zero address, do the authorization check.
// Revert if the `by` is not the owner, nor approved.
if iszero(or(iszero(by), or(eq(by, owner), eq(by, approvedAddress)))) {
if iszero(sload(keccak256(0x0c, 0x30))) {
mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
revert(0x1c, 0x04)
}
}
// Delete the approved address if any.
if approvedAddress { sstore(add(1, ownershipSlot), 0) }
}
// Clear the owner.
sstore(ownershipSlot, xor(ownershipPacked, owner))
// Decrement the balance of `owner`.
{
let balanceSlot := keccak256(0x0c, 0x1c)
sstore(balanceSlot, sub(sload(balanceSlot), 1))
}
// Emit the {Transfer} event.
log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, owner, 0, id)
}
_afterTokenTransfer(owner, address(0), id);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL APPROVAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns whether `account` is the owner of token `id`, or is approved to manage it.
///
/// Requirements:
/// - Token `id` must exist.
function _isApprovedOrOwner(address account, uint256 id)
internal
view
virtual
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
result := 1
// Clear the upper 96 bits.
account := shr(96, shl(96, account))
// Load the ownership data.
mstore(0x00, id)
mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, account))
let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
let owner := shr(96, shl(96, sload(ownershipSlot)))
// Revert if the token does not exist.
if iszero(owner) {
mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
revert(0x1c, 0x04)
}
// Check if `account` is the `owner`.
if iszero(eq(account, owner)) {
mstore(0x00, owner)
// Check if `account` is approved to manage the token.
if iszero(sload(keccak256(0x0c, 0x30))) {
result := eq(account, sload(add(1, ownershipSlot)))
}
}
}
}
/// @dev Returns the account approved to manage token `id`.
/// Returns the zero address instead of reverting if the token does not exist.
function _getApproved(uint256 id) internal view virtual returns (address result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, id)
mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
result := sload(add(1, add(id, add(id, keccak256(0x00, 0x20)))))
}
}
/// @dev Equivalent to `_approve(address(0), account, id)`.
function _approve(address account, uint256 id) internal virtual {
_approve(address(0), account, id);
}
/// @dev Sets `account` as the approved account to manage token `id`, using `by`.
///
/// Requirements:
/// - Token `id` must exist.
/// - If `by` is not the zero address, `by` must be the owner
/// or an approved operator for the token owner.
///
/// Emits a {Transfer} event.
function _approve(address by, address account, uint256 id) internal virtual {
assembly {
// Clear the upper 96 bits.
let bitmaskAddress := shr(96, not(0))
account := and(bitmaskAddress, account)
by := and(bitmaskAddress, by)
// Load the owner of the token.
mstore(0x00, id)
mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by))
let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
let owner := and(bitmaskAddress, sload(ownershipSlot))
// Revert if the token does not exist.
if iszero(owner) {
mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
revert(0x1c, 0x04)
}
// If `by` is not the zero address, do the authorization check.
// Revert if `by` is not the owner, nor approved.
if iszero(or(iszero(by), eq(by, owner))) {
mstore(0x00, owner)
if iszero(sload(keccak256(0x0c, 0x30))) {
mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
revert(0x1c, 0x04)
}
}
// Sets `account` as the approved account to manage `id`.
sstore(add(1, ownershipSlot), account)
// Emit the {Approval} event.
log4(codesize(), 0x00, _APPROVAL_EVENT_SIGNATURE, owner, account, id)
}
}
/// @dev Approve or remove the `operator` as an operator for `by`,
/// without authorization checks.
///
/// Emits an {ApprovalForAll} event.
function _setApprovalForAll(address by, address operator, bool isApproved) internal virtual {
/// @solidity memory-safe-assembly
assembly {
// Clear the upper 96 bits.
by := shr(96, shl(96, by))
operator := shr(96, shl(96, operator))
// Convert to 0 or 1.
isApproved := iszero(iszero(isApproved))
// Update the `isApproved` for (`by`, `operator`).
mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, operator))
mstore(0x00, by)
sstore(keccak256(0x0c, 0x30), isApproved)
// Emit the {ApprovalForAll} event.
mstore(0x00, isApproved)
log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, by, operator)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL TRANSFER FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Equivalent to `_transfer(address(0), from, to, id)`.
function _transfer(address from, address to, uint256 id) internal virtual {
_transfer(address(0), from, to, id);
}
/// @dev Transfers token `id` from `from` to `to`.
///
/// Requirements:
///
/// - Token `id` must exist.
/// - `from` must be the owner of the token.
/// - `to` cannot be the zero address.
/// - If `by` is not the zero address,
/// it must be the owner of the token, or be approved to manage the token.
///
/// Emits a {Transfer} event.
function _transfer(address by, address from, address to, uint256 id) internal virtual {
_beforeTokenTransfer(from, to, id);
/// @solidity memory-safe-assembly
assembly {
// Clear the upper 96 bits.
let bitmaskAddress := shr(96, not(0))
from := and(bitmaskAddress, from)
to := and(bitmaskAddress, to)
by := and(bitmaskAddress, by)
// Load the ownership data.
mstore(0x00, id)
mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by))
let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
let ownershipPacked := sload(ownershipSlot)
let owner := and(bitmaskAddress, ownershipPacked)
// Revert if the token does not exist, or if `from` is not the owner.
if iszero(mul(owner, eq(owner, from))) {
// `TokenDoesNotExist()`, `TransferFromIncorrectOwner()`.
mstore(shl(2, iszero(owner)), 0xceea21b6a1148100)
revert(0x1c, 0x04)
}
// Load, check, and update the token approval.
{
mstore(0x00, from)
let approvedAddress := sload(add(1, ownershipSlot))
// If `by` is not the zero address, do the authorization check.
// Revert if the `by` is not the owner, nor approved.
if iszero(or(iszero(by), or(eq(by, from), eq(by, approvedAddress)))) {
if iszero(sload(keccak256(0x0c, 0x30))) {
mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
revert(0x1c, 0x04)
}
}
// Delete the approved address if any.
if approvedAddress { sstore(add(1, ownershipSlot), 0) }
}
// Update with the new owner.
sstore(ownershipSlot, xor(ownershipPacked, xor(from, to)))
// Decrement the balance of `from`.
{
let fromBalanceSlot := keccak256(0x0c, 0x1c)
sstore(fromBalanceSlot, sub(sload(fromBalanceSlot), 1))
}
// Increment the balance of `to`.
{
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x1c)
let toBalanceSlotPacked := add(sload(toBalanceSlot), 1)
// Revert if `to` is the zero address, or if the account balance overflows.
if iszero(mul(to, and(toBalanceSlotPacked, _MAX_ACCOUNT_BALANCE))) {
// `TransferToZeroAddress()`, `AccountBalanceOverflow()`.
mstore(shl(2, iszero(to)), 0xea553b3401336cea)
revert(0x1c, 0x04)
}
sstore(toBalanceSlot, toBalanceSlotPacked)
}
// Emit the {Transfer} event.
log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id)
}
_afterTokenTransfer(from, to, id);
}
/// @dev Equivalent to `_safeTransfer(from, to, id, "")`.
function _safeTransfer(address from, address to, uint256 id) internal virtual {
_safeTransfer(from, to, id, "");
}
/// @dev Transfers token `id` from `from` to `to`.
///
/// Requirements:
///
/// - Token `id` must exist.
/// - `from` must be the owner of the token.
/// - `to` cannot be the zero address.
/// - The caller must be the owner of the token, or be approved to manage the token.
/// - If `to` refers to a smart contract, it must implement
/// {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
///
/// Emits a {Transfer} event.
function _safeTransfer(address from, address to, uint256 id, bytes memory data)
internal
virtual
{
_transfer(address(0), from, to, id);
if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);
}
/// @dev Equivalent to `_safeTransfer(by, from, to, id, "")`.
function _safeTransfer(address by, address from, address to, uint256 id) internal virtual {
_safeTransfer(by, from, to, id, "");
}
/// @dev Transfers token `id` from `from` to `to`.
///
/// Requirements:
///
/// - Token `id` must exist.
/// - `from` must be the owner of the token.
/// - `to` cannot be the zero address.
/// - If `by` is not the zero address,
/// it must be the owner of the token, or be approved to manage the token.
/// - If `to` refers to a smart contract, it must implement
/// {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
///
/// Emits a {Transfer} event.
function _safeTransfer(address by, address from, address to, uint256 id, bytes memory data)
internal
virtual
{
_transfer(by, from, to, id);
if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HOOKS FOR OVERRIDING */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Hook that is called before any token transfers, including minting and burning.
function _beforeTokenTransfer(address from, address to, uint256 id) internal virtual {}
/// @dev Hook that is called after any token transfers, including minting and burning.
function _afterTokenTransfer(address from, address to, uint256 id) internal virtual {}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns if `a` has bytecode of non-zero length.
function _hasCode(address a) private view returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := extcodesize(a) // Can handle dirty upper bits.
}
}
/// @dev Perform a call to invoke {IERC721Receiver-onERC721Received} on `to`.
/// Reverts if the target does not support the function correctly.
function _checkOnERC721Received(address from, address to, uint256 id, bytes memory data)
private
{
/// @solidity memory-safe-assembly
assembly {
// Prepare the calldata.
let m := mload(0x40)
let onERC721ReceivedSelector := 0x150b7a02
mstore(m, onERC721ReceivedSelector)
mstore(add(m, 0x20), caller()) // The `operator`, which is always `msg.sender`.
mstore(add(m, 0x40), shr(96, shl(96, from)))
mstore(add(m, 0x60), id)
mstore(add(m, 0x80), 0x80)
let n := mload(data)
mstore(add(m, 0xa0), n)
if n { pop(staticcall(gas(), 4, add(data, 0x20), n, add(m, 0xc0), n)) }
// Revert if the call reverts.
if iszero(call(gas(), to, 0, add(m, 0x1c), add(n, 0xa4), m, 0x20)) {
if returndatasize() {
// Bubble up the revert if the call reverts.
returndatacopy(m, 0x00, returndatasize())
revert(m, returndatasize())
}
}
// Load the returndata and compare it.
if iszero(eq(mload(m), shl(224, onERC721ReceivedSelector))) {
mstore(0x00, 0xd1a57ed6) // `TransferToNonERC721ReceiverImplementer()`.
revert(0x1c, 0x04)
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for generating pseudorandom numbers.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibPRNG.sol)
library LibPRNG {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRUCTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev A pseudorandom number state in memory.
struct PRNG {
uint256 state;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Seeds the `prng` with `state`.
function seed(PRNG memory prng, uint256 state) internal pure {
/// @solidity memory-safe-assembly
assembly {
mstore(prng, state)
}
}
/// @dev Returns the next pseudorandom uint256.
/// All bits of the returned uint256 pass the NIST Statistical Test Suite.
function next(PRNG memory prng) internal pure returns (uint256 result) {
// We simply use `keccak256` for a great balance between
// runtime gas costs, bytecode size, and statistical properties.
//
// A high-quality LCG with a 32-byte state
// is only about 30% more gas efficient during runtime,
// but requires a 32-byte multiplier, which can cause bytecode bloat
// when this function is inlined.
//
// Using this method is about 2x more efficient than
// `nextRandomness = uint256(keccak256(abi.encode(randomness)))`.
/// @solidity memory-safe-assembly
assembly {
result := keccak256(prng, 0x20)
mstore(prng, result)
}
}
/// @dev Returns a pseudorandom uint256, uniformly distributed
/// between 0 (inclusive) and `upper` (exclusive).
/// If your modulus is big, this method is recommended
/// for uniform sampling to avoid modulo bias.
/// For uniform sampling across all uint256 values,
/// or for small enough moduli such that the bias is neligible,
/// use {next} instead.
function uniform(PRNG memory prng, uint256 upper) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
for {} 1 {} {
result := keccak256(prng, 0x20)
mstore(prng, result)
if iszero(lt(result, mod(sub(0, upper), upper))) { break }
}
result := mod(result, upper)
}
}
/// @dev Shuffles the array in-place with Fisher-Yates shuffle.
function shuffle(PRNG memory prng, uint256[] memory a) internal pure {
/// @solidity memory-safe-assembly
assembly {
let n := mload(a)
let w := not(0)
let mask := shr(128, w)
if n {
for { a := add(a, 0x20) } 1 {} {
// We can just directly use `keccak256`, cuz
// the other approaches don't save much.
let r := keccak256(prng, 0x20)
mstore(prng, r)
// Note that there will be a very tiny modulo bias
// if the length of the array is not a power of 2.
// For all practical purposes, it is negligible
// and will not be a fairness or security concern.
{
let j := add(a, shl(5, mod(shr(128, r), n)))
n := add(n, w) // `sub(n, 1)`.
if iszero(n) { break }
let i := add(a, shl(5, n))
let t := mload(i)
mstore(i, mload(j))
mstore(j, t)
}
{
let j := add(a, shl(5, mod(and(r, mask), n)))
n := add(n, w) // `sub(n, 1)`.
if iszero(n) { break }
let i := add(a, shl(5, n))
let t := mload(i)
mstore(i, mload(j))
mstore(j, t)
}
}
}
}
}
/// @dev Shuffles the bytes in-place with Fisher-Yates shuffle.
function shuffle(PRNG memory prng, bytes memory a) internal pure {
/// @solidity memory-safe-assembly
assembly {
let n := mload(a)
let w := not(0)
let mask := shr(128, w)
if n {
let b := add(a, 0x01)
for { a := add(a, 0x20) } 1 {} {
// We can just directly use `keccak256`, cuz
// the other approaches don't save much.
let r := keccak256(prng, 0x20)
mstore(prng, r)
// Note that there will be a very tiny modulo bias
// if the length of the array is not a power of 2.
// For all practical purposes, it is negligible
// and will not be a fairness or security concern.
{
let o := mod(shr(128, r), n)
n := add(n, w) // `sub(n, 1)`.
if iszero(n) { break }
let t := mload(add(b, n))
mstore8(add(a, n), mload(add(b, o)))
mstore8(add(a, o), t)
}
{
let o := mod(and(r, mask), n)
n := add(n, w) // `sub(n, 1)`.
if iszero(n) { break }
let t := mload(add(b, n))
mstore8(add(a, n), mload(add(b, o)))
mstore8(add(a, o), t)
}
}
}
}
}
/// @dev Returns a sample from the standard normal distribution denominated in `WAD`.
function standardNormalWad(PRNG memory prng) internal pure returns (int256 result) {
/// @solidity memory-safe-assembly
assembly {
// Technically, this is the Irwin-Hall distribution with 20 samples.
// The chance of drawing a sample outside 10 σ from the standard normal distribution
// is ≈ 0.000000000000000000000015, which is insignificant for most practical purposes.
// Passes the Kolmogorov-Smirnov test for 200k samples. Uses about 322 gas.
result := keccak256(prng, 0x20)
mstore(prng, result)
let n := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff43 // Prime.
let a := 0x100000000000000000000000000000051 // Prime and a primitive root of `n`.
let m := 0x1fffffffffffffff1fffffffffffffff1fffffffffffffff1fffffffffffffff
let s := 0x1000000000000000100000000000000010000000000000001
let r1 := mulmod(result, a, n)
let r2 := mulmod(r1, a, n)
let r3 := mulmod(r2, a, n)
// forgefmt: disable-next-item
result := sub(sar(96, mul(26614938895861601847173011183,
add(add(shr(192, mul(s, add(and(m, result), and(m, r1)))),
shr(192, mul(s, add(and(m, r2), and(m, r3))))),
shr(192, mul(s, and(m, mulmod(r3, a, n))))))), 7745966692414833770)
}
}
/// @dev Returns a sample from the unit exponential distribution denominated in `WAD`.
function exponentialWad(PRNG memory prng) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
// Passes the Kolmogorov-Smirnov test for 200k samples.
// Gas usage varies, starting from about 172+ gas.
let r := keccak256(prng, 0x20)
mstore(prng, r)
let p := shl(129, r)
let w := shl(1, r)
if iszero(gt(w, p)) {
let n := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff43 // Prime.
let a := 0x100000000000000000000000000000051 // Prime and a primitive root of `n`.
for {} 1 {} {
r := mulmod(r, a, n)
if iszero(lt(shl(129, r), w)) {
r := mulmod(r, a, n)
result := add(1000000000000000000, result)
w := shl(1, r)
p := shl(129, r)
if iszero(lt(w, p)) { break }
continue
}
w := shl(1, r)
if iszero(lt(w, shl(129, r))) { break }
}
}
result := add(div(p, shl(129, 170141183460469231732)), result)
}
}
}// 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();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The constant returned when the `search` is not found in the string.
uint256 internal constant NOT_FOUND = type(uint256).max;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* 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)
// Update the free memory pointer to allocate.
mstore(0x40, add(str, 0x20))
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end of the memory to calculate the length later.
let end := str
let w := not(0) // Tsk.
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let temp := value } 1 {} {
str := add(str, w) // `sub(str, 1)`.
// Write the character to the pointer.
// The ASCII index of the '0' character is 48.
mstore8(str, add(48, mod(temp, 10)))
// Keep dividing `temp` until zero.
temp := div(temp, 10)
if iszero(temp) { break }
}
let length := sub(end, str)
// Move the pointer 32 bytes leftwards to make room for the length.
str := sub(str, 0x20)
// Store the length.
mstore(str, length)
}
}
/// @dev Returns the base 10 decimal representation of `value`.
function toString(int256 value) internal pure returns (string memory str) {
if (value >= 0) {
return toString(uint256(value));
}
unchecked {
str = toString(~uint256(value) + 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) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write 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)))
// Allocate the memory.
mstore(0x40, add(str, 0x20))
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end to calculate the length later.
let end := str
// 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)
}
// Compute the string's length.
let strLength := sub(end, str)
// Move the pointer and write the length.
str := sub(str, 0x20)
mstore(str, strLength)
}
}
/// @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) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write 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) // Write 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)) // Write 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)) // Write 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)
// Allocate the memory.
mstore(0x40, add(str, 0x20))
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end to calculate the length later.
let end := str
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
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 }
}
// Compute the string's length.
let strLength := sub(end, str)
// Move the pointer and write the length.
str := sub(str, 0x20)
mstore(str, strLength)
}
}
/// @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) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write 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))
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
str := add(str, 2)
mstore(str, 40)
let o := add(str, 0x20)
mstore(add(o, 40), 0)
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) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write 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.
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
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)
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* 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.
// Allocate the memory.
mstore(0x40, add(result, add(resultLength, 0x20)))
}
}
}
/// @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)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, add(result, and(add(resultLength, 0x3f), w)))
}
}
}
/// @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)
// Zeroize the slot after the string.
mstore(last, 0)
// Stores the length.
mstore(result, totalLength)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, and(add(last, 0x1f), w))
}
}
/// @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)
let o := add(result, 0x20)
mstore(o, s)
mstore(add(o, n), 0)
mstore(0x40, add(result, 0x40))
}
}
/// @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 ""&'<>" 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))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),
xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))
}
}
/// @dev Packs a single string with its length into a single word.
/// Returns `bytes32(0)` if the length is zero or greater than 31.
function packOne(string memory a) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
// We don't need to zero right pad the string,
// since this is our own custom non-standard packing scheme.
result :=
mul(
// Load the length and the bytes.
mload(add(a, 0x1f)),
// `length != 0 && length < 32`. Abuses underflow.
// Assumes that the length is valid and within the block gas limit.
lt(sub(mload(a), 1), 0x1f)
)
}
}
/// @dev Unpacks a string packed using {packOne}.
/// Returns the empty string if `packed` is `bytes32(0)`.
/// If `packed` is not an output of {packOne}, the output behavior is undefined.
function unpackOne(bytes32 packed) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
// Grab the free memory pointer.
result := mload(0x40)
// Allocate 2 words (1 for the length, 1 for the bytes).
mstore(0x40, add(result, 0x40))
// Zeroize the length slot.
mstore(result, 0)
// Store the length and bytes.
mstore(add(result, 0x1f), packed)
// Right pad with zeroes.
mstore(add(add(result, 0x20), mload(result)), 0)
}
}
/// @dev Packs two strings with their lengths into a single word.
/// Returns `bytes32(0)` if combined length is zero or greater than 30.
function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let aLength := mload(a)
// We don't need to zero right pad the strings,
// since this is our own custom non-standard packing scheme.
result :=
mul(
// Load the length and the bytes of `a` and `b`.
or(
shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),
mload(sub(add(b, 0x1e), aLength))
),
// `totalLength != 0 && totalLength < 31`. Abuses underflow.
// Assumes that the lengths are valid and within the block gas limit.
lt(sub(add(aLength, mload(b)), 1), 0x1e)
)
}
}
/// @dev Unpacks strings packed using {packTwo}.
/// Returns the empty strings if `packed` is `bytes32(0)`.
/// If `packed` is not an output of {packTwo}, the output behavior is undefined.
function unpackTwo(bytes32 packed)
internal
pure
returns (string memory resultA, string memory resultB)
{
/// @solidity memory-safe-assembly
assembly {
// Grab the free memory pointer.
resultA := mload(0x40)
resultB := add(resultA, 0x40)
// Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.
mstore(0x40, add(resultB, 0x40))
// Zeroize the length slots.
mstore(resultA, 0)
mstore(resultB, 0)
// Store the lengths and bytes.
mstore(add(resultA, 0x1f), packed)
mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
// Right pad with zeroes.
mstore(add(add(resultA, 0x20), mload(resultA)), 0)
mstore(add(add(resultB, 0x20), mload(resultB)), 0)
}
}
/// @dev Directly returns `a` without copying.
function directReturn(string memory a) internal pure {
assembly {
// Assumes that the string does not start from the scratch space.
let retStart := sub(a, 0x20)
let retSize := add(mload(a), 0x40)
// Right pad with zeroes. Just in case the string is produced
// by a method that doesn't zero right pad.
mstore(add(retStart, retSize), 0)
// Store the return offset.
mstore(retStart, 0x20)
// End the transaction, returning the string.
return(retStart, retSize)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol)
library MerkleProofLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MERKLE PROOF VERIFICATION OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf)
internal
pure
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
if mload(proof) {
// Initialize `offset` to the offset of `proof` elements in memory.
let offset := add(proof, 0x20)
// Left shift by 5 is equivalent to multiplying by 0x20.
let end := add(offset, shl(5, mload(proof)))
// Iterate over proof elements to compute root hash.
for {} 1 {} {
// Slot of `leaf` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(leaf, mload(offset)))
// Store elements to hash contiguously in scratch space.
// Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
mstore(scratch, leaf)
mstore(xor(scratch, 0x20), mload(offset))
// Reuse `leaf` to store the hash to reduce stack operations.
leaf := keccak256(0x00, 0x40)
offset := add(offset, 0x20)
if iszero(lt(offset, end)) { break }
}
}
isValid := eq(leaf, root)
}
}
/// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf)
internal
pure
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
if proof.length {
// Left shift by 5 is equivalent to multiplying by 0x20.
let end := add(proof.offset, shl(5, proof.length))
// Initialize `offset` to the offset of `proof` in the calldata.
let offset := proof.offset
// Iterate over proof elements to compute root hash.
for {} 1 {} {
// Slot of `leaf` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(leaf, calldataload(offset)))
// Store elements to hash contiguously in scratch space.
// Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
mstore(scratch, leaf)
mstore(xor(scratch, 0x20), calldataload(offset))
// Reuse `leaf` to store the hash to reduce stack operations.
leaf := keccak256(0x00, 0x40)
offset := add(offset, 0x20)
if iszero(lt(offset, end)) { break }
}
}
isValid := eq(leaf, root)
}
}
/// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
/// given `proof` and `flags`.
///
/// Note:
/// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
/// will always return false.
/// - The sum of the lengths of `proof` and `leaves` must never overflow.
/// - Any non-zero word in the `flags` array is treated as true.
/// - The memory offset of `proof` must be non-zero
/// (i.e. `proof` is not pointing to the scratch space).
function verifyMultiProof(
bytes32[] memory proof,
bytes32 root,
bytes32[] memory leaves,
bool[] memory flags
) internal pure returns (bool isValid) {
// Rebuilds the root by consuming and producing values on a queue.
// The queue starts with the `leaves` array, and goes into a `hashes` array.
// After the process, the last element on the queue is verified
// to be equal to the `root`.
//
// The `flags` array denotes whether the sibling
// should be popped from the queue (`flag == true`), or
// should be popped from the `proof` (`flag == false`).
/// @solidity memory-safe-assembly
assembly {
// Cache the lengths of the arrays.
let leavesLength := mload(leaves)
let proofLength := mload(proof)
let flagsLength := mload(flags)
// Advance the pointers of the arrays to point to the data.
leaves := add(0x20, leaves)
proof := add(0x20, proof)
flags := add(0x20, flags)
// If the number of flags is correct.
for {} eq(add(leavesLength, proofLength), add(flagsLength, 1)) {} {
// For the case where `proof.length + leaves.length == 1`.
if iszero(flagsLength) {
// `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
isValid := eq(mload(xor(leaves, mul(xor(proof, leaves), proofLength))), root)
break
}
// The required final proof offset if `flagsLength` is not zero, otherwise zero.
let proofEnd := add(proof, shl(5, proofLength))
// We can use the free memory space for the queue.
// We don't need to allocate, since the queue is temporary.
let hashesFront := mload(0x40)
// Copy the leaves into the hashes.
// Sometimes, a little memory expansion costs less than branching.
// Should cost less, even with a high free memory offset of 0x7d00.
leavesLength := shl(5, leavesLength)
for { let i := 0 } iszero(eq(i, leavesLength)) { i := add(i, 0x20) } {
mstore(add(hashesFront, i), mload(add(leaves, i)))
}
// Compute the back of the hashes.
let hashesBack := add(hashesFront, leavesLength)
// This is the end of the memory for the queue.
// We recycle `flagsLength` to save on stack variables (sometimes save gas).
flagsLength := add(hashesBack, shl(5, flagsLength))
for {} 1 {} {
// Pop from `hashes`.
let a := mload(hashesFront)
// Pop from `hashes`.
let b := mload(add(hashesFront, 0x20))
hashesFront := add(hashesFront, 0x40)
// If the flag is false, load the next proof,
// else, pops from the queue.
if iszero(mload(flags)) {
// Loads the next proof.
b := mload(proof)
proof := add(proof, 0x20)
// Unpop from `hashes`.
hashesFront := sub(hashesFront, 0x20)
}
// Advance to the next flag.
flags := add(flags, 0x20)
// Slot of `a` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(a, b))
// Hash the scratch space and push the result onto the queue.
mstore(scratch, a)
mstore(xor(scratch, 0x20), b)
mstore(hashesBack, keccak256(0x00, 0x40))
hashesBack := add(hashesBack, 0x20)
if iszero(lt(hashesBack, flagsLength)) { break }
}
isValid :=
and(
// Checks if the last value in the queue is same as the root.
eq(mload(sub(hashesBack, 0x20)), root),
// And whether all the proofs are used, if required.
eq(proofEnd, proof)
)
break
}
}
}
/// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
/// given `proof` and `flags`.
///
/// Note:
/// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
/// will always return false.
/// - Any non-zero word in the `flags` array is treated as true.
/// - The calldata offset of `proof` must be non-zero
/// (i.e. `proof` is from a regular Solidity function with a 4-byte selector).
function verifyMultiProofCalldata(
bytes32[] calldata proof,
bytes32 root,
bytes32[] calldata leaves,
bool[] calldata flags
) internal pure returns (bool isValid) {
// Rebuilds the root by consuming and producing values on a queue.
// The queue starts with the `leaves` array, and goes into a `hashes` array.
// After the process, the last element on the queue is verified
// to be equal to the `root`.
//
// The `flags` array denotes whether the sibling
// should be popped from the queue (`flag == true`), or
// should be popped from the `proof` (`flag == false`).
/// @solidity memory-safe-assembly
assembly {
// If the number of flags is correct.
for {} eq(add(leaves.length, proof.length), add(flags.length, 1)) {} {
// For the case where `proof.length + leaves.length == 1`.
if iszero(flags.length) {
// `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
// forgefmt: disable-next-item
isValid := eq(
calldataload(
xor(leaves.offset, mul(xor(proof.offset, leaves.offset), proof.length))
),
root
)
break
}
// The required final proof offset if `flagsLength` is not zero, otherwise zero.
let proofEnd := add(proof.offset, shl(5, proof.length))
// We can use the free memory space for the queue.
// We don't need to allocate, since the queue is temporary.
let hashesFront := mload(0x40)
// Copy the leaves into the hashes.
// Sometimes, a little memory expansion costs less than branching.
// Should cost less, even with a high free memory offset of 0x7d00.
calldatacopy(hashesFront, leaves.offset, shl(5, leaves.length))
// Compute the back of the hashes.
let hashesBack := add(hashesFront, shl(5, leaves.length))
// This is the end of the memory for the queue.
// We recycle `flagsLength` to save on stack variables (sometimes save gas).
flags.length := add(hashesBack, shl(5, flags.length))
// We don't need to make a copy of `proof.offset` or `flags.offset`,
// as they are pass-by-value (this trick may not always save gas).
for {} 1 {} {
// Pop from `hashes`.
let a := mload(hashesFront)
// Pop from `hashes`.
let b := mload(add(hashesFront, 0x20))
hashesFront := add(hashesFront, 0x40)
// If the flag is false, load the next proof,
// else, pops from the queue.
if iszero(calldataload(flags.offset)) {
// Loads the next proof.
b := calldataload(proof.offset)
proof.offset := add(proof.offset, 0x20)
// Unpop from `hashes`.
hashesFront := sub(hashesFront, 0x20)
}
// Advance to the next flag offset.
flags.offset := add(flags.offset, 0x20)
// Slot of `a` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(a, b))
// Hash the scratch space and push the result onto the queue.
mstore(scratch, a)
mstore(xor(scratch, 0x20), b)
mstore(hashesBack, keccak256(0x00, 0x40))
hashesBack := add(hashesBack, 0x20)
if iszero(lt(hashesBack, flags.length)) { break }
}
isValid :=
and(
// Checks if the last value in the queue is same as the root.
eq(mload(sub(hashesBack, 0x20)), root),
// And whether all the proofs are used, if required.
eq(proofEnd, proof.offset)
)
break
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EMPTY CALLDATA HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns an empty calldata bytes32 array.
function emptyProof() internal pure returns (bytes32[] calldata proof) {
/// @solidity memory-safe-assembly
assembly {
proof.length := 0
}
}
/// @dev Returns an empty calldata bytes32 array.
function emptyLeaves() internal pure returns (bytes32[] calldata leaves) {
/// @solidity memory-safe-assembly
assembly {
leaves.length := 0
}
}
/// @dev Returns an empty calldata bool array.
function emptyFlags() internal pure returns (bool[] calldata flags) {
/// @solidity memory-safe-assembly
assembly {
flags.length := 0
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Reentrancy guard mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Unauthorized reentrant call.
error Reentrancy();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Equivalent to: `uint72(bytes9(keccak256("_REENTRANCY_GUARD_SLOT")))`.
/// 9 bytes is large enough to avoid collisions with lower slots,
/// but not too large to result in excessive bytecode bloat.
uint256 private constant _REENTRANCY_GUARD_SLOT = 0x929eee149b4bd21268;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* REENTRANCY GUARD */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Guards a function from reentrancy.
modifier nonReentrant() virtual {
/// @solidity memory-safe-assembly
assembly {
if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
mstore(0x00, 0xab143c06) // `Reentrancy()`.
revert(0x1c, 0x04)
}
sstore(_REENTRANCY_GUARD_SLOT, address())
}
_;
/// @solidity memory-safe-assembly
assembly {
sstore(_REENTRANCY_GUARD_SLOT, codesize())
}
}
/// @dev Guards a view function from read-only reentrancy.
modifier nonReadReentrant() virtual {
/// @solidity memory-safe-assembly
assembly {
if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
mstore(0x00, 0xab143c06) // `Reentrancy()`.
revert(0x1c, 0x04)
}
}
_;
}
}{
"evmVersion": "paris",
"optimizer": {
"enabled": false,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"string","name":"_initBaseURI","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccountBalanceOverflow","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"ContractsCannotMint","type":"error"},{"inputs":[],"name":"MustMintAtLeastOneToken","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"NotEnoughAvailableTokens","type":"error"},{"inputs":[],"name":"NotOwnerNorApproved","type":"error"},{"inputs":[],"name":"Reentrancy","type":"error"},{"inputs":[],"name":"TokenAlreadyExists","type":"error"},{"inputs":[],"name":"TokenDoesNotExist","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"isApproved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"AreYouThrasher","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"LocalMint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_claimSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_merkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_mintIsActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_reserved","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimContractsGas","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_numToMint","type":"uint256"}],"name":"getCostForMintingThrashers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_numToMint","type":"uint256"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"name":"localMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_numToMint","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter","type":"address"}],"name":"numberMinted","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"remainingSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_numToMint","type":"uint256"}],"name":"reserveMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"isApproved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"baseURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"claimSize","type":"uint256"}],"name":"setClaimSize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"}],"name":"setMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserved","type":"uint256"}],"name":"setReserved","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"toggleMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"withdrawEther","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60a060405260016004556102276005556000600860006101000a81548160ff0219169083151502179055503480156200003757600080fd5b5060405162003ba538038062003ba583398181016040528101906200005d9190620004b7565b6040518060400160405280600f81526020017f43727970746f54687261736865727300000000000000000000000000000000008152506040518060400160405280600381526020017f4354530000000000000000000000000000000000000000000000000000000000815250610bb88260009081620000dd919062000753565b508160019081620000ef919062000753565b5080608081815250508060038190555050505062000113336200023960201b60201c565b73430000000000000000000000000000000000000273ffffffffffffffffffffffffffffffffffffffff16634e606c476040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156200017057600080fd5b505af115801562000185573d6000803e3d6000fd5b50505050732536fe9ab3f511540f2f9e2ec2a805005c3dd80073ffffffffffffffffffffffffffffffffffffffff166336b91f2b732e696fe1bf4dba76e4a093ce3e603db8f01dc8e86040518263ffffffff1660e01b8152600401620001ec91906200087f565b600060405180830381600087803b1580156200020757600080fd5b505af11580156200021c573d6000803e3d6000fd5b5050505080600a908162000231919062000753565b50506200089c565b620002496200031f60201b60201c565b15620002c7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278054156200028657630dc149f06000526004601cfd5b8160601b60601c9150811560ff1b821781558160007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3506200031c565b8060601b60601c9050807fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927558060007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a35b50565b600090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6200038d8262000342565b810181811067ffffffffffffffff82111715620003af57620003ae62000353565b5b80604052505050565b6000620003c462000324565b9050620003d2828262000382565b919050565b600067ffffffffffffffff821115620003f557620003f462000353565b5b620004008262000342565b9050602081019050919050565b60005b838110156200042d57808201518184015260208101905062000410565b60008484015250505050565b6000620004506200044a84620003d7565b620003b8565b9050828152602081018484840111156200046f576200046e6200033d565b5b6200047c8482856200040d565b509392505050565b600082601f8301126200049c576200049b62000338565b5b8151620004ae84826020860162000439565b91505092915050565b600060208284031215620004d057620004cf6200032e565b5b600082015167ffffffffffffffff811115620004f157620004f062000333565b5b620004ff8482850162000484565b91505092915050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200055b57607f821691505b60208210810362000571576200057062000513565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620005db7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff826200059c565b620005e786836200059c565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b6000620006346200062e6200062884620005ff565b62000609565b620005ff565b9050919050565b6000819050919050565b620006508362000613565b620006686200065f826200063b565b848454620005a9565b825550505050565b600090565b6200067f62000670565b6200068c81848462000645565b505050565b5b81811015620006b457620006a860008262000675565b60018101905062000692565b5050565b601f8211156200070357620006cd8162000577565b620006d8846200058c565b81016020851015620006e8578190505b62000700620006f7856200058c565b83018262000691565b50505b505050565b600082821c905092915050565b6000620007286000198460080262000708565b1980831691505092915050565b600062000743838362000715565b9150826002028217905092915050565b6200075e8262000508565b67ffffffffffffffff8111156200077a576200077962000353565b5b62000786825462000542565b62000793828285620006b8565b600060209050601f831160018114620007cb5760008415620007b6578287015190505b620007c2858262000735565b86555062000832565b601f198416620007db8662000577565b60005b828110156200080557848901518255600182019150602085019450602081019050620007de565b8683101562000825578489015162000821601f89168262000715565b8355505b6001600288020188555050505b505050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600062000867826200083a565b9050919050565b62000879816200085a565b82525050565b60006020820190506200089660008301846200086e565b92915050565b6080516132c3620008e260003960008181610a3a01528181610ac001528181610dad0152818161102e0152818161152b0152818161179401526118fc01526132c36000f3fe6080604052600436106102515760003560e01c806370a0823111610139578063b88d4fde116100b6578063da0239a61161007a578063da0239a6146107e6578063dc33e68114610811578063e985e9c51461084e578063f04e283e1461088b578063f2fde38b146108a7578063fee81cf4146108c357610251565b8063b88d4fde1461070e578063c87b56dd1461072a578063ca68dc4514610767578063d3dd5fe0146107a4578063d5abeb01146107bb57610251565b80638da5cb5b116100fd5780638da5cb5b1461064a57806395bc503b1461067557806395d89b411461069e578063a0712d68146106c9578063a22cb465146106e557610251565b806370a08231146105ac578063715018a6146105e95780637362377b146105f357806374eb1c651461060a5780637cb647591461062157610251565b80632fc37ab2116101d257806354d1f13d1161019657806354d1f13d146104ab57806355f804b3146104b55780636352211e146104de5780636aaa571d1461051b5780636d3dab9314610546578063702e9ebc1461056f57610251565b80632fc37ab2146103e55780633a71e1ce1461041057806342842e0e1461044d5780634e570223146104695780634e71d92d1461049457610251565b80631342ff4c116102195780631342ff4c1461034257806318160ddd1461036b57806323b872dd1461039657806325692962146103b25780632d6e71b6146103bc57610251565b806301ffc9a71461025657806302b0e4f31461029357806306fdde03146102be578063081812fc146102e9578063095ea7b314610326575b600080fd5b34801561026257600080fd5b5061027d6004803603810190610278919061226a565b610900565b60405161028a91906122b2565b60405180910390f35b34801561029f57600080fd5b506102a8610925565b6040516102b591906122b2565b60405180910390f35b3480156102ca57600080fd5b506102d3610938565b6040516102e0919061235d565b60405180910390f35b3480156102f557600080fd5b50610310600480360381019061030b91906123b5565b6109ca565b60405161031d9190612423565b60405180910390f35b610340600480360381019061033b919061246a565b610a21565b005b34801561034e57600080fd5b50610369600480360381019061036491906123b5565b610a30565b005b34801561037757600080fd5b50610380610ab9565b60405161038d91906124b9565b60405180910390f35b6103b060048036038101906103ab91906124d4565b610aee565b005b6103ba610c20565b005b3480156103c857600080fd5b506103e360048036038101906103de91906123b5565b610c74565b005b3480156103f157600080fd5b506103fa610c86565b6040516104079190612540565b60405180910390f35b34801561041c57600080fd5b506104376004803603810190610432919061255b565b610c8c565b60405161044491906122b2565b60405180910390f35b610467600480360381019061046291906124d4565b610cac565b005b34801561047557600080fd5b5061047e610ce6565b60405161048b91906124b9565b60405180910390f35b3480156104a057600080fd5b506104a9610cec565b005b6104b3610f2d565b005b3480156104c157600080fd5b506104dc60048036038101906104d791906125ed565b610f69565b005b3480156104ea57600080fd5b50610505600480360381019061050091906123b5565b610f87565b6040516105129190612423565b60405180910390f35b34801561052757600080fd5b50610530610fac565b60405161053d91906124b9565b60405180910390f35b34801561055257600080fd5b5061056d600480360381019061056891906127a4565b610fb2565b005b34801561057b57600080fd5b506105966004803603810190610591919061255b565b611258565b6040516105a391906122b2565b60405180910390f35b3480156105b857600080fd5b506105d360048036038101906105ce919061255b565b611278565b6040516105e091906124b9565b60405180910390f35b6105f16112c8565b005b3480156105ff57600080fd5b506106086112dc565b005b34801561061657600080fd5b5061061f611333565b005b34801561062d57600080fd5b5061064860048036038101906106439190612800565b6113d0565b005b34801561065657600080fd5b5061065f6113e2565b60405161066c9190612423565b60405180910390f35b34801561068157600080fd5b5061069c600480360381019061069791906123b5565b61140b565b005b3480156106aa57600080fd5b506106b361141d565b6040516106c0919061235d565b60405180910390f35b6106e360048036038101906106de91906123b5565b6114af565b005b3480156106f157600080fd5b5061070c60048036038101906107079190612859565b61166f565b005b610728600480360381019061072391906128ef565b6116c5565b005b34801561073657600080fd5b50610751600480360381019061074c91906123b5565b611736565b60405161075e919061235d565b60405180910390f35b34801561077357600080fd5b5061078e600480360381019061078991906123b5565b611790565b60405161079b91906124b9565b60405180910390f35b3480156107b057600080fd5b506107b96118c6565b005b3480156107c757600080fd5b506107d06118fa565b6040516107dd91906124b9565b60405180910390f35b3480156107f257600080fd5b506107fb61191e565b60405161080891906124b9565b60405180910390f35b34801561081d57600080fd5b506108386004803603810190610833919061255b565b611924565b6040516108459190612996565b60405180910390f35b34801561085a57600080fd5b50610875600480360381019061087091906129b1565b611958565b60405161088291906122b2565b60405180910390f35b6108a560048036038101906108a0919061255b565b61197c565b005b6108c160048036038101906108bc919061255b565b6119bd565b005b3480156108cf57600080fd5b506108ea60048036038101906108e5919061255b565b6119e7565b6040516108f791906124b9565b60405180910390f35b60008160e01c635b5e139f81146380ac58cd82146301ffc9a783141717915050919050565b600860009054906101000a900460ff1681565b60606000805461094790612a20565b80601f016020809104026020016040519081016040528092919081815260200182805461097390612a20565b80156109c05780601f10610995576101008083540402835291602001916109c0565b820191906000526020600020905b8154815290600101906020018083116109a357829003601f168201915b5050505050905090565b6000816000527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52602060002082018201805460601b610a145763ceea21b66000526004601cfd5b8060010154915050919050565b610a2c338383611a02565b5050565b610a38611ab9565b7f000000000000000000000000000000000000000000000000000000000000000081610a62610ab9565b610a6c9190612a80565b10610aac576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610aa390612b26565b60405180910390fd5b610ab63382611af1565b50565b60006003547f0000000000000000000000000000000000000000000000000000000000000000610ae99190612b46565b905090565b610af9838383611c7c565b60001960601c8381169350828116925081600052337f7d8825530a5a2e7a00000000000000000000000000000000000000000000000017601c5260206000208201820180548083168681148102610b5f5767ceea21b6a1148100811560021b526004601cfd5b86600052826001015480331488331417610b8c576030600c2054610b8b57634b6e7f186000526004601cfd5b5b8015610b9a57600084600101555b5085871882188355601c600c20600181540381555085600052601c600c20600181540163ffffffff81168802610bdf5767ea553b3401336cea881560021b526004601cfd5b80825550508486887fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600038a450505050610c1b838383611c81565b505050565b6000610c2a611c86565b67ffffffffffffffff164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b610c7c611ab9565b8060058190555050565b60095481565b60076020528060005260406000206000915054906101000a900460ff1681565b610cb7838383610aee565b610cc082611c91565b15610ce157610ce083838360405180602001604052806000815250611c9c565b5b505050565b60045481565b3068929eee149b4bd212685403610d0b5763ab143c066000526004601cfd5b3068929eee149b4bd2126855600860009054906101000a900460ff16610d66576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d5d90612bc6565b60405180910390fd5b600060045411610dab576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610da290612c58565b60405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000000600554600454610dda610ab9565b610de49190612a80565b610dee9190612a80565b10610e2e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e2590612b26565b60405180910390fd5b600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615610ebb576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610eb290612cc4565b60405180910390fd5b6001600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550610f1f33600454611af1565b3868929eee149b4bd2126855565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b610f71611ab9565b8181600a9182610f82929190612e9b565b505050565b6000610f9282611d29565b905080610fa75763ceea21b66000526004601cfd5b919050565b60055481565b3068929eee149b4bd212685403610fd15763ab143c066000526004601cfd5b3068929eee149b4bd2126855600860009054906101000a900460ff1661102c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161102390612bc6565b60405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000082611056610ab9565b6110609190612a80565b106110a0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161109790612b26565b60405180910390fd5b600760003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561112d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161112490612cc4565b60405180910390fd5b60003383604051602001611142929190612f6b565b604051602081830303815290604052805190602001206040516020016111689190612fb5565b60405160208183030381529060405280519060200120905061118d8260095483611d6a565b6111cc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111c39061301c565b60405180910390fd5b6001600760003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555082600560008282546112369190612b46565b925050819055506112473384611af1565b503868929eee149b4bd21268555050565b60066020528060005260406000206000915054906101000a900460ff1681565b60008161128d57638f4eb6046000526004601cfd5b7f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c528160005263ffffffff601c600c2054169050919050565b6112d0611ab9565b6112da6000611dc6565b565b6112e4611ab9565b60004790503373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f1935050505015801561132f573d6000803e3d6000fd5b5050565b61133b611ab9565b73430000000000000000000000000000000000000273ffffffffffffffffffffffffffffffffffffffff1663954fa5ee30336040518363ffffffff1660e01b815260040161138a92919061303c565b6020604051808303816000875af11580156113a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113cd919061307a565b50565b6113d8611ab9565b8060098190555050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754905090565b611413611ab9565b8060048190555050565b60606001805461142c90612a20565b80601f016020809104026020016040519081016040528092919081815260200182805461145890612a20565b80156114a55780601f1061147a576101008083540402835291602001916114a5565b820191906000526020600020905b81548152906001019060200180831161148857829003601f168201915b5050505050905090565b3068929eee149b4bd2126854036114ce5763ab143c066000526004601cfd5b3068929eee149b4bd2126855600860009054906101000a900460ff16611529576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161152090612bc6565b60405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000060055482611556610ab9565b6115609190612a80565b61156a9190612a80565b106115aa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016115a190612b26565b60405180910390fd5b60006115b582611790565b9050803410156115fa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016115f1906130f3565b60405180910390fd5b80341115611655573373ffffffffffffffffffffffffffffffffffffffff166108fc82346116289190612b46565b9081150290604051600060405180830381858888f19350505050158015611653573d6000803e3d6000fd5b505b61165f3383611af1565b503868929eee149b4bd212685550565b801515905081601c52670a5a2e7a0000000060085233600052806030600c2055806000528160601b60601c337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160206000a35050565b6116d0858585610aee565b6116d984611c91565b1561172f5761172e85858585858080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050611c9c565b5b5050505050565b60606000611742611e8e565b9050600061174f84611f20565b9050600082510361176457809250505061178b565b818160405160200161177792919061314f565b604051602081830303815290604052925050505b919050565b60007f0000000000000000000000000000000000000000000000000000000000000000600554836117bf610ab9565b6117c99190612a80565b6117d39190612a80565b10611813576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161180a90612b26565b60405180910390fd5b6001820361182a5766071afd498d000090506118c1565b60038203611841576611c37937e0800090506118c1565b60058203611858576618de76816d800090506118c1565b600a820361186f57662386f26fc1000090506118c1565b601e820361188657665fec5b60ef800090506118c1565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016118b8906131bf565b60405180910390fd5b919050565b6118ce611ab9565b600860009054906101000a900460ff1615600860006101000a81548160ff021916908315150217905550565b7f000000000000000000000000000000000000000000000000000000000000000081565b60035481565b600060c061193183611f71565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16901c9050919050565b600081601c52670a5a2e7a00000000600852826000526030600c2054905092915050565b611984611ab9565b63389a75e1600c52806000526020600c2080544211156119ac57636f5e88186000526004601cfd5b60008155506119ba81611dc6565b50565b6119c5611ab9565b8060601b6119db57637448fbae6000526004601cfd5b6119e481611dc6565b50565b600063389a75e1600c52816000526020600c20549050919050565b60001960601c8281169250838116935081600052837f7d8825530a5a2e7a00000000000000000000000000000000000000000000000017601c526020600020820182018054821680611a5c5763ceea21b66000526004601cfd5b808614861517611a8357806000526030600c2054611a8257634b6e7f186000526004601cfd5b5b8482600101558385827f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600038a4505050505050565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927543314611aef576382b429006000526004601cfd5b565b3273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611b56576040517fd9d552c900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008103611b90576040517f4600cfe900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806003541015611bcc576040517f7775abdd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600060405180602001604052804244604051602001611bec929190613200565b6040516020818303038152906040528051906020012060001c81525090506000600354905060005b83811015611c64576000611c318385611fab90919063ffffffff16565b90506000611c3f8285611fd6565b9050611c4b8782612082565b83611c559061322c565b93508260010192505050611c14565b50611c6f8484612156565b8060038190555050505050565b505050565b505050565b60006202a300905090565b6000813b9050919050565b60405163150b7a028082523360208301528560601b60601c604083015283606083015260808083015282518060a08401528015611ce3578060c08401826020870160045afa505b60208360a48301601c860160008a5af1611d07573d15611d06573d6000843e3d83fd5b5b8160e01b835114611d205763d1a57ed66000526004601cfd5b50505050505050565b6000816000527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c526020600020820182015460601b60601c9050919050565b6000835115611dba5760208401845160051b81015b600115611db7578151841160051b8481528251602082185260406000209450602083019250818310611db15750611db7565b50611d7f565b50505b82821490509392505050565b611dce6121b7565b15611e34577fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278160601b60601c91508181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3811560ff1b8217815550611e8b565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278160601b60601c91508181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3818155505b50565b6060600a8054611e9d90612a20565b80601f0160208091040260200160405190810160405280929190818152602001828054611ec990612a20565b8015611f165780601f10611eeb57610100808354040283529160200191611f16565b820191906000526020600020905b815481529060010190602001808311611ef957829003601f168201915b5050505050905090565b60606080604051019050602081016040526000815280600019835b600115611f5c578184019350600a81066030018453600a8104905080611f3b575b50828203602084039350808452505050919050565b60007f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5281600052601c600c205460201c9050919050565b60005b600115611fcb576020832090508083528182600003068110611fae575b818106905092915050565b600080600260008581526020019081526020016000205490506000600184611ffe9190612b46565b9050600060026000838152602001908152602001600020549050600083146120265782612028565b855b9350818614612059576000811461203f5780612041565b815b60026000888152602001908152602001600020819055505b600081146120795760026000838152602001908152602001600020600090555b50505092915050565b61208e60008383611c7c565b8160601b60601c9150806000527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260206000208101810180548060601b156120e15763c991cbb16000526004601cfd5b838117825583600052601c600c20600181540163ffffffff811686026121165767ea553b3401336cea861560021b526004601cfd5b8082555050828460007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600038a4505061215260008383611c81565b5050565b600061216183611924565b9050600082826121719190613255565b9050600061217e85611f71565b90506121b0858277ffffffffffffffffffffffffffffffffffffffffffffffff1660c08563ffffffff16901b176121bc565b5050505050565b600090565b7f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5281600052601c600c2080548060201c831860201b8118825550505050565b6000604051905090565b600080fd5b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61224781612212565b811461225257600080fd5b50565b6000813590506122648161223e565b92915050565b6000602082840312156122805761227f612208565b5b600061228e84828501612255565b91505092915050565b60008115159050919050565b6122ac81612297565b82525050565b60006020820190506122c760008301846122a3565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156123075780820151818401526020810190506122ec565b60008484015250505050565b6000601f19601f8301169050919050565b600061232f826122cd565b61233981856122d8565b93506123498185602086016122e9565b61235281612313565b840191505092915050565b600060208201905081810360008301526123778184612324565b905092915050565b6000819050919050565b6123928161237f565b811461239d57600080fd5b50565b6000813590506123af81612389565b92915050565b6000602082840312156123cb576123ca612208565b5b60006123d9848285016123a0565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061240d826123e2565b9050919050565b61241d81612402565b82525050565b60006020820190506124386000830184612414565b92915050565b61244781612402565b811461245257600080fd5b50565b6000813590506124648161243e565b92915050565b6000806040838503121561248157612480612208565b5b600061248f85828601612455565b92505060206124a0858286016123a0565b9150509250929050565b6124b38161237f565b82525050565b60006020820190506124ce60008301846124aa565b92915050565b6000806000606084860312156124ed576124ec612208565b5b60006124fb86828701612455565b935050602061250c86828701612455565b925050604061251d868287016123a0565b9150509250925092565b6000819050919050565b61253a81612527565b82525050565b60006020820190506125556000830184612531565b92915050565b60006020828403121561257157612570612208565b5b600061257f84828501612455565b91505092915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126125ad576125ac612588565b5b8235905067ffffffffffffffff8111156125ca576125c961258d565b5b6020830191508360018202830111156125e6576125e5612592565b5b9250929050565b6000806020838503121561260457612603612208565b5b600083013567ffffffffffffffff8111156126225761262161220d565b5b61262e85828601612597565b92509250509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61267282612313565b810181811067ffffffffffffffff821117156126915761269061263a565b5b80604052505050565b60006126a46121fe565b90506126b08282612669565b919050565b600067ffffffffffffffff8211156126d0576126cf61263a565b5b602082029050602081019050919050565b6126ea81612527565b81146126f557600080fd5b50565b600081359050612707816126e1565b92915050565b600061272061271b846126b5565b61269a565b9050808382526020820190506020840283018581111561274357612742612592565b5b835b8181101561276c578061275888826126f8565b845260208401935050602081019050612745565b5050509392505050565b600082601f83011261278b5761278a612588565b5b813561279b84826020860161270d565b91505092915050565b600080604083850312156127bb576127ba612208565b5b60006127c9858286016123a0565b925050602083013567ffffffffffffffff8111156127ea576127e961220d565b5b6127f685828601612776565b9150509250929050565b60006020828403121561281657612815612208565b5b6000612824848285016126f8565b91505092915050565b61283681612297565b811461284157600080fd5b50565b6000813590506128538161282d565b92915050565b600080604083850312156128705761286f612208565b5b600061287e85828601612455565b925050602061288f85828601612844565b9150509250929050565b60008083601f8401126128af576128ae612588565b5b8235905067ffffffffffffffff8111156128cc576128cb61258d565b5b6020830191508360018202830111156128e8576128e7612592565b5b9250929050565b60008060008060006080868803121561290b5761290a612208565b5b600061291988828901612455565b955050602061292a88828901612455565b945050604061293b888289016123a0565b935050606086013567ffffffffffffffff81111561295c5761295b61220d565b5b61296888828901612899565b92509250509295509295909350565b600063ffffffff82169050919050565b61299081612977565b82525050565b60006020820190506129ab6000830184612987565b92915050565b600080604083850312156129c8576129c7612208565b5b60006129d685828601612455565b92505060206129e785828601612455565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680612a3857607f821691505b602082108103612a4b57612a4a6129f1565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000612a8b8261237f565b9150612a968361237f565b9250828201905080821115612aae57612aad612a51565b5b92915050565b7f5468657265206172656e27742074686973206d616e792074687261736865727360008201527f206c6566742e0000000000000000000000000000000000000000000000000000602082015250565b6000612b106026836122d8565b9150612b1b82612ab4565b604082019050919050565b60006020820190508181036000830152612b3f81612b03565b9050919050565b6000612b518261237f565b9150612b5c8361237f565b9250828203905081811115612b7457612b73612a51565b5b92915050565b7f4d696e74206973206e6f74206163746976652e00000000000000000000000000600082015250565b6000612bb06013836122d8565b9150612bbb82612b7a565b602082019050919050565b60006020820190508181036000830152612bdf81612ba3565b9050919050565b7f546865726520617265206e6f206672656520746872617368657273206c65667460008201527f2e00000000000000000000000000000000000000000000000000000000000000602082015250565b6000612c426021836122d8565b9150612c4d82612be6565b604082019050919050565b60006020820190508181036000830152612c7181612c35565b9050919050565b7f416c7265647920636c61696d65642e0000000000000000000000000000000000600082015250565b6000612cae600f836122d8565b9150612cb982612c78565b602082019050919050565b60006020820190508181036000830152612cdd81612ca1565b9050919050565b600082905092915050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302612d517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82612d14565b612d5b8683612d14565b95508019841693508086168417925050509392505050565b6000819050919050565b6000612d98612d93612d8e8461237f565b612d73565b61237f565b9050919050565b6000819050919050565b612db283612d7d565b612dc6612dbe82612d9f565b848454612d21565b825550505050565b600090565b612ddb612dce565b612de6818484612da9565b505050565b5b81811015612e0a57612dff600082612dd3565b600181019050612dec565b5050565b601f821115612e4f57612e2081612cef565b612e2984612d04565b81016020851015612e38578190505b612e4c612e4485612d04565b830182612deb565b50505b505050565b600082821c905092915050565b6000612e7260001984600802612e54565b1980831691505092915050565b6000612e8b8383612e61565b9150826002028217905092915050565b612ea58383612ce4565b67ffffffffffffffff811115612ebe57612ebd61263a565b5b612ec88254612a20565b612ed3828285612e0e565b6000601f831160018114612f025760008415612ef0578287013590505b612efa8582612e7f565b865550612f62565b601f198416612f1086612cef565b60005b82811015612f3857848901358255600182019150602085019450602081019050612f13565b86831015612f555784890135612f51601f891682612e61565b8355505b6001600288020188555050505b50505050505050565b6000604082019050612f806000830185612414565b612f8d60208301846124aa565b9392505050565b6000819050919050565b612faf612faa82612527565b612f94565b82525050565b6000612fc18284612f9e565b60208201915081905092915050565b7f596f7520617265206e6f74206c6f63616c2e0000000000000000000000000000600082015250565b60006130066012836122d8565b915061301182612fd0565b602082019050919050565b6000602082019050818103600083015261303581612ff9565b9050919050565b60006040820190506130516000830185612414565b61305e6020830184612414565b9392505050565b60008151905061307481612389565b92915050565b6000602082840312156130905761308f612208565b5b600061309e84828501613065565b91505092915050565b7f4e6f7420656e6f756768206574682e0000000000000000000000000000000000600082015250565b60006130dd600f836122d8565b91506130e8826130a7565b602082019050919050565b6000602082019050818103600083015261310c816130d0565b9050919050565b600081905092915050565b6000613129826122cd565b6131338185613113565b93506131438185602086016122e9565b80840191505092915050565b600061315b828561311e565b9150613167828461311e565b91508190509392505050565b7f556e737570706f72746564206d696e7420616d6f756e74000000000000000000600082015250565b60006131a96017836122d8565b91506131b482613173565b602082019050919050565b600060208201905081810360008301526131d88161319c565b9050919050565b6000819050919050565b6131fa6131f58261237f565b6131df565b82525050565b600061320c82856131e9565b60208201915061321c82846131e9565b6020820191508190509392505050565b60006132378261237f565b91506000820361324a57613249612a51565b5b600182039050919050565b600061326082612977565b915061326b83612977565b9250828201905063ffffffff81111561328757613286612a51565b5b9291505056fea2646970667358221220bae5744a82e892eb5f8cfb6deb61da9a28399019bd54a34530db8c8ff658525464736f6c6343000814003300000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000036697066733a2f2f516d6367387375685470415937314a547657416e73444175536866355444757777743177695254325439687145352f00000000000000000000
Deployed Bytecode
0x6080604052600436106102515760003560e01c806370a0823111610139578063b88d4fde116100b6578063da0239a61161007a578063da0239a6146107e6578063dc33e68114610811578063e985e9c51461084e578063f04e283e1461088b578063f2fde38b146108a7578063fee81cf4146108c357610251565b8063b88d4fde1461070e578063c87b56dd1461072a578063ca68dc4514610767578063d3dd5fe0146107a4578063d5abeb01146107bb57610251565b80638da5cb5b116100fd5780638da5cb5b1461064a57806395bc503b1461067557806395d89b411461069e578063a0712d68146106c9578063a22cb465146106e557610251565b806370a08231146105ac578063715018a6146105e95780637362377b146105f357806374eb1c651461060a5780637cb647591461062157610251565b80632fc37ab2116101d257806354d1f13d1161019657806354d1f13d146104ab57806355f804b3146104b55780636352211e146104de5780636aaa571d1461051b5780636d3dab9314610546578063702e9ebc1461056f57610251565b80632fc37ab2146103e55780633a71e1ce1461041057806342842e0e1461044d5780634e570223146104695780634e71d92d1461049457610251565b80631342ff4c116102195780631342ff4c1461034257806318160ddd1461036b57806323b872dd1461039657806325692962146103b25780632d6e71b6146103bc57610251565b806301ffc9a71461025657806302b0e4f31461029357806306fdde03146102be578063081812fc146102e9578063095ea7b314610326575b600080fd5b34801561026257600080fd5b5061027d6004803603810190610278919061226a565b610900565b60405161028a91906122b2565b60405180910390f35b34801561029f57600080fd5b506102a8610925565b6040516102b591906122b2565b60405180910390f35b3480156102ca57600080fd5b506102d3610938565b6040516102e0919061235d565b60405180910390f35b3480156102f557600080fd5b50610310600480360381019061030b91906123b5565b6109ca565b60405161031d9190612423565b60405180910390f35b610340600480360381019061033b919061246a565b610a21565b005b34801561034e57600080fd5b50610369600480360381019061036491906123b5565b610a30565b005b34801561037757600080fd5b50610380610ab9565b60405161038d91906124b9565b60405180910390f35b6103b060048036038101906103ab91906124d4565b610aee565b005b6103ba610c20565b005b3480156103c857600080fd5b506103e360048036038101906103de91906123b5565b610c74565b005b3480156103f157600080fd5b506103fa610c86565b6040516104079190612540565b60405180910390f35b34801561041c57600080fd5b506104376004803603810190610432919061255b565b610c8c565b60405161044491906122b2565b60405180910390f35b610467600480360381019061046291906124d4565b610cac565b005b34801561047557600080fd5b5061047e610ce6565b60405161048b91906124b9565b60405180910390f35b3480156104a057600080fd5b506104a9610cec565b005b6104b3610f2d565b005b3480156104c157600080fd5b506104dc60048036038101906104d791906125ed565b610f69565b005b3480156104ea57600080fd5b50610505600480360381019061050091906123b5565b610f87565b6040516105129190612423565b60405180910390f35b34801561052757600080fd5b50610530610fac565b60405161053d91906124b9565b60405180910390f35b34801561055257600080fd5b5061056d600480360381019061056891906127a4565b610fb2565b005b34801561057b57600080fd5b506105966004803603810190610591919061255b565b611258565b6040516105a391906122b2565b60405180910390f35b3480156105b857600080fd5b506105d360048036038101906105ce919061255b565b611278565b6040516105e091906124b9565b60405180910390f35b6105f16112c8565b005b3480156105ff57600080fd5b506106086112dc565b005b34801561061657600080fd5b5061061f611333565b005b34801561062d57600080fd5b5061064860048036038101906106439190612800565b6113d0565b005b34801561065657600080fd5b5061065f6113e2565b60405161066c9190612423565b60405180910390f35b34801561068157600080fd5b5061069c600480360381019061069791906123b5565b61140b565b005b3480156106aa57600080fd5b506106b361141d565b6040516106c0919061235d565b60405180910390f35b6106e360048036038101906106de91906123b5565b6114af565b005b3480156106f157600080fd5b5061070c60048036038101906107079190612859565b61166f565b005b610728600480360381019061072391906128ef565b6116c5565b005b34801561073657600080fd5b50610751600480360381019061074c91906123b5565b611736565b60405161075e919061235d565b60405180910390f35b34801561077357600080fd5b5061078e600480360381019061078991906123b5565b611790565b60405161079b91906124b9565b60405180910390f35b3480156107b057600080fd5b506107b96118c6565b005b3480156107c757600080fd5b506107d06118fa565b6040516107dd91906124b9565b60405180910390f35b3480156107f257600080fd5b506107fb61191e565b60405161080891906124b9565b60405180910390f35b34801561081d57600080fd5b506108386004803603810190610833919061255b565b611924565b6040516108459190612996565b60405180910390f35b34801561085a57600080fd5b50610875600480360381019061087091906129b1565b611958565b60405161088291906122b2565b60405180910390f35b6108a560048036038101906108a0919061255b565b61197c565b005b6108c160048036038101906108bc919061255b565b6119bd565b005b3480156108cf57600080fd5b506108ea60048036038101906108e5919061255b565b6119e7565b6040516108f791906124b9565b60405180910390f35b60008160e01c635b5e139f81146380ac58cd82146301ffc9a783141717915050919050565b600860009054906101000a900460ff1681565b60606000805461094790612a20565b80601f016020809104026020016040519081016040528092919081815260200182805461097390612a20565b80156109c05780601f10610995576101008083540402835291602001916109c0565b820191906000526020600020905b8154815290600101906020018083116109a357829003601f168201915b5050505050905090565b6000816000527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52602060002082018201805460601b610a145763ceea21b66000526004601cfd5b8060010154915050919050565b610a2c338383611a02565b5050565b610a38611ab9565b7f0000000000000000000000000000000000000000000000000000000000000bb881610a62610ab9565b610a6c9190612a80565b10610aac576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610aa390612b26565b60405180910390fd5b610ab63382611af1565b50565b60006003547f0000000000000000000000000000000000000000000000000000000000000bb8610ae99190612b46565b905090565b610af9838383611c7c565b60001960601c8381169350828116925081600052337f7d8825530a5a2e7a00000000000000000000000000000000000000000000000017601c5260206000208201820180548083168681148102610b5f5767ceea21b6a1148100811560021b526004601cfd5b86600052826001015480331488331417610b8c576030600c2054610b8b57634b6e7f186000526004601cfd5b5b8015610b9a57600084600101555b5085871882188355601c600c20600181540381555085600052601c600c20600181540163ffffffff81168802610bdf5767ea553b3401336cea881560021b526004601cfd5b80825550508486887fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600038a450505050610c1b838383611c81565b505050565b6000610c2a611c86565b67ffffffffffffffff164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b610c7c611ab9565b8060058190555050565b60095481565b60076020528060005260406000206000915054906101000a900460ff1681565b610cb7838383610aee565b610cc082611c91565b15610ce157610ce083838360405180602001604052806000815250611c9c565b5b505050565b60045481565b3068929eee149b4bd212685403610d0b5763ab143c066000526004601cfd5b3068929eee149b4bd2126855600860009054906101000a900460ff16610d66576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d5d90612bc6565b60405180910390fd5b600060045411610dab576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610da290612c58565b60405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000bb8600554600454610dda610ab9565b610de49190612a80565b610dee9190612a80565b10610e2e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e2590612b26565b60405180910390fd5b600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615610ebb576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610eb290612cc4565b60405180910390fd5b6001600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550610f1f33600454611af1565b3868929eee149b4bd2126855565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b610f71611ab9565b8181600a9182610f82929190612e9b565b505050565b6000610f9282611d29565b905080610fa75763ceea21b66000526004601cfd5b919050565b60055481565b3068929eee149b4bd212685403610fd15763ab143c066000526004601cfd5b3068929eee149b4bd2126855600860009054906101000a900460ff1661102c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161102390612bc6565b60405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000bb882611056610ab9565b6110609190612a80565b106110a0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161109790612b26565b60405180910390fd5b600760003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561112d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161112490612cc4565b60405180910390fd5b60003383604051602001611142929190612f6b565b604051602081830303815290604052805190602001206040516020016111689190612fb5565b60405160208183030381529060405280519060200120905061118d8260095483611d6a565b6111cc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111c39061301c565b60405180910390fd5b6001600760003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555082600560008282546112369190612b46565b925050819055506112473384611af1565b503868929eee149b4bd21268555050565b60066020528060005260406000206000915054906101000a900460ff1681565b60008161128d57638f4eb6046000526004601cfd5b7f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c528160005263ffffffff601c600c2054169050919050565b6112d0611ab9565b6112da6000611dc6565b565b6112e4611ab9565b60004790503373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f1935050505015801561132f573d6000803e3d6000fd5b5050565b61133b611ab9565b73430000000000000000000000000000000000000273ffffffffffffffffffffffffffffffffffffffff1663954fa5ee30336040518363ffffffff1660e01b815260040161138a92919061303c565b6020604051808303816000875af11580156113a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113cd919061307a565b50565b6113d8611ab9565b8060098190555050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754905090565b611413611ab9565b8060048190555050565b60606001805461142c90612a20565b80601f016020809104026020016040519081016040528092919081815260200182805461145890612a20565b80156114a55780601f1061147a576101008083540402835291602001916114a5565b820191906000526020600020905b81548152906001019060200180831161148857829003601f168201915b5050505050905090565b3068929eee149b4bd2126854036114ce5763ab143c066000526004601cfd5b3068929eee149b4bd2126855600860009054906101000a900460ff16611529576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161152090612bc6565b60405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000bb860055482611556610ab9565b6115609190612a80565b61156a9190612a80565b106115aa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016115a190612b26565b60405180910390fd5b60006115b582611790565b9050803410156115fa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016115f1906130f3565b60405180910390fd5b80341115611655573373ffffffffffffffffffffffffffffffffffffffff166108fc82346116289190612b46565b9081150290604051600060405180830381858888f19350505050158015611653573d6000803e3d6000fd5b505b61165f3383611af1565b503868929eee149b4bd212685550565b801515905081601c52670a5a2e7a0000000060085233600052806030600c2055806000528160601b60601c337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160206000a35050565b6116d0858585610aee565b6116d984611c91565b1561172f5761172e85858585858080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050611c9c565b5b5050505050565b60606000611742611e8e565b9050600061174f84611f20565b9050600082510361176457809250505061178b565b818160405160200161177792919061314f565b604051602081830303815290604052925050505b919050565b60007f0000000000000000000000000000000000000000000000000000000000000bb8600554836117bf610ab9565b6117c99190612a80565b6117d39190612a80565b10611813576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161180a90612b26565b60405180910390fd5b6001820361182a5766071afd498d000090506118c1565b60038203611841576611c37937e0800090506118c1565b60058203611858576618de76816d800090506118c1565b600a820361186f57662386f26fc1000090506118c1565b601e820361188657665fec5b60ef800090506118c1565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016118b8906131bf565b60405180910390fd5b919050565b6118ce611ab9565b600860009054906101000a900460ff1615600860006101000a81548160ff021916908315150217905550565b7f0000000000000000000000000000000000000000000000000000000000000bb881565b60035481565b600060c061193183611f71565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16901c9050919050565b600081601c52670a5a2e7a00000000600852826000526030600c2054905092915050565b611984611ab9565b63389a75e1600c52806000526020600c2080544211156119ac57636f5e88186000526004601cfd5b60008155506119ba81611dc6565b50565b6119c5611ab9565b8060601b6119db57637448fbae6000526004601cfd5b6119e481611dc6565b50565b600063389a75e1600c52816000526020600c20549050919050565b60001960601c8281169250838116935081600052837f7d8825530a5a2e7a00000000000000000000000000000000000000000000000017601c526020600020820182018054821680611a5c5763ceea21b66000526004601cfd5b808614861517611a8357806000526030600c2054611a8257634b6e7f186000526004601cfd5b5b8482600101558385827f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600038a4505050505050565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927543314611aef576382b429006000526004601cfd5b565b3273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611b56576040517fd9d552c900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008103611b90576040517f4600cfe900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806003541015611bcc576040517f7775abdd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600060405180602001604052804244604051602001611bec929190613200565b6040516020818303038152906040528051906020012060001c81525090506000600354905060005b83811015611c64576000611c318385611fab90919063ffffffff16565b90506000611c3f8285611fd6565b9050611c4b8782612082565b83611c559061322c565b93508260010192505050611c14565b50611c6f8484612156565b8060038190555050505050565b505050565b505050565b60006202a300905090565b6000813b9050919050565b60405163150b7a028082523360208301528560601b60601c604083015283606083015260808083015282518060a08401528015611ce3578060c08401826020870160045afa505b60208360a48301601c860160008a5af1611d07573d15611d06573d6000843e3d83fd5b5b8160e01b835114611d205763d1a57ed66000526004601cfd5b50505050505050565b6000816000527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c526020600020820182015460601b60601c9050919050565b6000835115611dba5760208401845160051b81015b600115611db7578151841160051b8481528251602082185260406000209450602083019250818310611db15750611db7565b50611d7f565b50505b82821490509392505050565b611dce6121b7565b15611e34577fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278160601b60601c91508181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3811560ff1b8217815550611e8b565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278160601b60601c91508181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3818155505b50565b6060600a8054611e9d90612a20565b80601f0160208091040260200160405190810160405280929190818152602001828054611ec990612a20565b8015611f165780601f10611eeb57610100808354040283529160200191611f16565b820191906000526020600020905b815481529060010190602001808311611ef957829003601f168201915b5050505050905090565b60606080604051019050602081016040526000815280600019835b600115611f5c578184019350600a81066030018453600a8104905080611f3b575b50828203602084039350808452505050919050565b60007f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5281600052601c600c205460201c9050919050565b60005b600115611fcb576020832090508083528182600003068110611fae575b818106905092915050565b600080600260008581526020019081526020016000205490506000600184611ffe9190612b46565b9050600060026000838152602001908152602001600020549050600083146120265782612028565b855b9350818614612059576000811461203f5780612041565b815b60026000888152602001908152602001600020819055505b600081146120795760026000838152602001908152602001600020600090555b50505092915050565b61208e60008383611c7c565b8160601b60601c9150806000527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260206000208101810180548060601b156120e15763c991cbb16000526004601cfd5b838117825583600052601c600c20600181540163ffffffff811686026121165767ea553b3401336cea861560021b526004601cfd5b8082555050828460007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600038a4505061215260008383611c81565b5050565b600061216183611924565b9050600082826121719190613255565b9050600061217e85611f71565b90506121b0858277ffffffffffffffffffffffffffffffffffffffffffffffff1660c08563ffffffff16901b176121bc565b5050505050565b600090565b7f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5281600052601c600c2080548060201c831860201b8118825550505050565b6000604051905090565b600080fd5b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61224781612212565b811461225257600080fd5b50565b6000813590506122648161223e565b92915050565b6000602082840312156122805761227f612208565b5b600061228e84828501612255565b91505092915050565b60008115159050919050565b6122ac81612297565b82525050565b60006020820190506122c760008301846122a3565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156123075780820151818401526020810190506122ec565b60008484015250505050565b6000601f19601f8301169050919050565b600061232f826122cd565b61233981856122d8565b93506123498185602086016122e9565b61235281612313565b840191505092915050565b600060208201905081810360008301526123778184612324565b905092915050565b6000819050919050565b6123928161237f565b811461239d57600080fd5b50565b6000813590506123af81612389565b92915050565b6000602082840312156123cb576123ca612208565b5b60006123d9848285016123a0565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061240d826123e2565b9050919050565b61241d81612402565b82525050565b60006020820190506124386000830184612414565b92915050565b61244781612402565b811461245257600080fd5b50565b6000813590506124648161243e565b92915050565b6000806040838503121561248157612480612208565b5b600061248f85828601612455565b92505060206124a0858286016123a0565b9150509250929050565b6124b38161237f565b82525050565b60006020820190506124ce60008301846124aa565b92915050565b6000806000606084860312156124ed576124ec612208565b5b60006124fb86828701612455565b935050602061250c86828701612455565b925050604061251d868287016123a0565b9150509250925092565b6000819050919050565b61253a81612527565b82525050565b60006020820190506125556000830184612531565b92915050565b60006020828403121561257157612570612208565b5b600061257f84828501612455565b91505092915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126125ad576125ac612588565b5b8235905067ffffffffffffffff8111156125ca576125c961258d565b5b6020830191508360018202830111156125e6576125e5612592565b5b9250929050565b6000806020838503121561260457612603612208565b5b600083013567ffffffffffffffff8111156126225761262161220d565b5b61262e85828601612597565b92509250509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61267282612313565b810181811067ffffffffffffffff821117156126915761269061263a565b5b80604052505050565b60006126a46121fe565b90506126b08282612669565b919050565b600067ffffffffffffffff8211156126d0576126cf61263a565b5b602082029050602081019050919050565b6126ea81612527565b81146126f557600080fd5b50565b600081359050612707816126e1565b92915050565b600061272061271b846126b5565b61269a565b9050808382526020820190506020840283018581111561274357612742612592565b5b835b8181101561276c578061275888826126f8565b845260208401935050602081019050612745565b5050509392505050565b600082601f83011261278b5761278a612588565b5b813561279b84826020860161270d565b91505092915050565b600080604083850312156127bb576127ba612208565b5b60006127c9858286016123a0565b925050602083013567ffffffffffffffff8111156127ea576127e961220d565b5b6127f685828601612776565b9150509250929050565b60006020828403121561281657612815612208565b5b6000612824848285016126f8565b91505092915050565b61283681612297565b811461284157600080fd5b50565b6000813590506128538161282d565b92915050565b600080604083850312156128705761286f612208565b5b600061287e85828601612455565b925050602061288f85828601612844565b9150509250929050565b60008083601f8401126128af576128ae612588565b5b8235905067ffffffffffffffff8111156128cc576128cb61258d565b5b6020830191508360018202830111156128e8576128e7612592565b5b9250929050565b60008060008060006080868803121561290b5761290a612208565b5b600061291988828901612455565b955050602061292a88828901612455565b945050604061293b888289016123a0565b935050606086013567ffffffffffffffff81111561295c5761295b61220d565b5b61296888828901612899565b92509250509295509295909350565b600063ffffffff82169050919050565b61299081612977565b82525050565b60006020820190506129ab6000830184612987565b92915050565b600080604083850312156129c8576129c7612208565b5b60006129d685828601612455565b92505060206129e785828601612455565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680612a3857607f821691505b602082108103612a4b57612a4a6129f1565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000612a8b8261237f565b9150612a968361237f565b9250828201905080821115612aae57612aad612a51565b5b92915050565b7f5468657265206172656e27742074686973206d616e792074687261736865727360008201527f206c6566742e0000000000000000000000000000000000000000000000000000602082015250565b6000612b106026836122d8565b9150612b1b82612ab4565b604082019050919050565b60006020820190508181036000830152612b3f81612b03565b9050919050565b6000612b518261237f565b9150612b5c8361237f565b9250828203905081811115612b7457612b73612a51565b5b92915050565b7f4d696e74206973206e6f74206163746976652e00000000000000000000000000600082015250565b6000612bb06013836122d8565b9150612bbb82612b7a565b602082019050919050565b60006020820190508181036000830152612bdf81612ba3565b9050919050565b7f546865726520617265206e6f206672656520746872617368657273206c65667460008201527f2e00000000000000000000000000000000000000000000000000000000000000602082015250565b6000612c426021836122d8565b9150612c4d82612be6565b604082019050919050565b60006020820190508181036000830152612c7181612c35565b9050919050565b7f416c7265647920636c61696d65642e0000000000000000000000000000000000600082015250565b6000612cae600f836122d8565b9150612cb982612c78565b602082019050919050565b60006020820190508181036000830152612cdd81612ca1565b9050919050565b600082905092915050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302612d517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82612d14565b612d5b8683612d14565b95508019841693508086168417925050509392505050565b6000819050919050565b6000612d98612d93612d8e8461237f565b612d73565b61237f565b9050919050565b6000819050919050565b612db283612d7d565b612dc6612dbe82612d9f565b848454612d21565b825550505050565b600090565b612ddb612dce565b612de6818484612da9565b505050565b5b81811015612e0a57612dff600082612dd3565b600181019050612dec565b5050565b601f821115612e4f57612e2081612cef565b612e2984612d04565b81016020851015612e38578190505b612e4c612e4485612d04565b830182612deb565b50505b505050565b600082821c905092915050565b6000612e7260001984600802612e54565b1980831691505092915050565b6000612e8b8383612e61565b9150826002028217905092915050565b612ea58383612ce4565b67ffffffffffffffff811115612ebe57612ebd61263a565b5b612ec88254612a20565b612ed3828285612e0e565b6000601f831160018114612f025760008415612ef0578287013590505b612efa8582612e7f565b865550612f62565b601f198416612f1086612cef565b60005b82811015612f3857848901358255600182019150602085019450602081019050612f13565b86831015612f555784890135612f51601f891682612e61565b8355505b6001600288020188555050505b50505050505050565b6000604082019050612f806000830185612414565b612f8d60208301846124aa565b9392505050565b6000819050919050565b612faf612faa82612527565b612f94565b82525050565b6000612fc18284612f9e565b60208201915081905092915050565b7f596f7520617265206e6f74206c6f63616c2e0000000000000000000000000000600082015250565b60006130066012836122d8565b915061301182612fd0565b602082019050919050565b6000602082019050818103600083015261303581612ff9565b9050919050565b60006040820190506130516000830185612414565b61305e6020830184612414565b9392505050565b60008151905061307481612389565b92915050565b6000602082840312156130905761308f612208565b5b600061309e84828501613065565b91505092915050565b7f4e6f7420656e6f756768206574682e0000000000000000000000000000000000600082015250565b60006130dd600f836122d8565b91506130e8826130a7565b602082019050919050565b6000602082019050818103600083015261310c816130d0565b9050919050565b600081905092915050565b6000613129826122cd565b6131338185613113565b93506131438185602086016122e9565b80840191505092915050565b600061315b828561311e565b9150613167828461311e565b91508190509392505050565b7f556e737570706f72746564206d696e7420616d6f756e74000000000000000000600082015250565b60006131a96017836122d8565b91506131b482613173565b602082019050919050565b600060208201905081810360008301526131d88161319c565b9050919050565b6000819050919050565b6131fa6131f58261237f565b6131df565b82525050565b600061320c82856131e9565b60208201915061321c82846131e9565b6020820191508190509392505050565b60006132378261237f565b91506000820361324a57613249612a51565b5b600182039050919050565b600061326082612977565b915061326b83612977565b9250828201905063ffffffff81111561328757613286612a51565b5b9291505056fea2646970667358221220bae5744a82e892eb5f8cfb6deb61da9a28399019bd54a34530db8c8ff658525464736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000036697066733a2f2f516d6367387375685470415937314a547657416e73444175536866355444757777743177695254325439687145352f00000000000000000000
-----Decoded View---------------
Arg [0] : _initBaseURI (string): ipfs://Qmcg8suhTpAY71JTvWAnsDAuShf5TDuwwt1wiRT2T9hqE5/
-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000020
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000036
Arg [2] : 697066733a2f2f516d6367387375685470415937314a547657416e7344417553
Arg [3] : 6866355444757777743177695254325439687145352f00000000000000000000
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.