Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x60806040 | 19300501 | 745 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
Alteration
Compiler Version
v0.8.17+commit.8df45f5f
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "./DN404.sol";
import "./DN404Mirror.sol";
import { Ownable } from "./solady/src/auth/Ownable.sol";
import { LibString } from "./solady/src/utils/LibString.sol";
import { SafeTransferLib } from "./solady/src/utils/SafeTransferLib.sol";
contract Alteration is DN404, Ownable {
string private _name;
string private _symbol;
string private _baseURI;
uint192 public publicPrice;
uint32 public totalMinted; // DN404 only supports up to `2**32 - 2` tokens.
bool public live;
mapping(address => uint32) public freeMintsUsed;
uint32 public constant MAX_PER_WALLET = 50;
uint32 public constant MAX_SUPPLY = 4321;
error InvalidProof();
error InvalidMint();
error InvalidPrice();
error TotalSupplyReached();
error NotLive();
constructor(
string memory name_,
string memory symbol_,
uint192 publicPrice_,
uint96 initialTokenSupply,
address initialSupplyOwner
) {
_initializeOwner(msg.sender);
_name = name_;
_symbol = symbol_;
publicPrice = publicPrice_;
address mirror = address(new DN404Mirror(msg.sender));
_initializeDN404(initialTokenSupply, initialSupplyOwner, mirror);
}
modifier onlyLive() {
if (!live) {
revert NotLive();
}
_;
}
modifier checkPrice(uint256 price, uint256 nftAmount) {
uint32 freeMintsRemaining = 2 - freeMintsUsed[msg.sender];
uint32 paidMints = nftAmount > freeMintsRemaining
? uint32(nftAmount) - freeMintsRemaining
: 0;
if (paidMints * publicPrice != msg.value) {
revert InvalidPrice();
}
_;
}
modifier checkAndUpdateTotalMinted(uint256 nftAmount) {
uint256 newTotalMinted = uint256(totalMinted) + nftAmount;
if (newTotalMinted > MAX_SUPPLY) {
revert TotalSupplyReached();
}
totalMinted = uint32(newTotalMinted);
_;
}
modifier checkAndUpdateBuyerMintCount(uint256 nftAmount) {
uint256 currentMintCount = _getAux(msg.sender);
uint256 newMintCount = currentMintCount + nftAmount;
if (newMintCount > MAX_PER_WALLET) {
revert InvalidMint();
}
_setAux(msg.sender, uint88(newMintCount));
_;
}
function mint(
uint256 nftAmount
)
public
payable
onlyLive
checkPrice(publicPrice, nftAmount)
checkAndUpdateBuyerMintCount(nftAmount)
checkAndUpdateTotalMinted(nftAmount)
{
uint32 freeMintsRemaining = 2 - freeMintsUsed[msg.sender];
if (nftAmount > freeMintsRemaining) {
freeMintsUsed[msg.sender] += freeMintsRemaining;
} else {
freeMintsUsed[msg.sender] += uint32(nftAmount);
}
_mint(msg.sender, nftAmount * _unit());
}
function airdrop(
address[] calldata recipients,
uint256 nftAmount
) public onlyOwner checkAndUpdateTotalMinted(nftAmount) {
for (uint256 i = 0; i < recipients.length; i++) {
_mint(recipients[i], nftAmount * _unit());
}
}
function setBaseURI(string calldata baseURI_) public onlyOwner {
_baseURI = baseURI_;
}
function setPrices(uint192 publicPrice_) public onlyOwner {
publicPrice = publicPrice_;
}
function toggleLive() public onlyOwner {
live = !live;
}
function withdraw() public onlyOwner {
SafeTransferLib.safeTransferAllETH(msg.sender);
}
function name() public view override returns (string memory) {
return _name;
}
function symbol() public view override returns (string memory) {
return _symbol;
}
function tokenURI(
uint256 tokenId
) public view override returns (string memory result) {
if (bytes(_baseURI).length != 0) {
result = string(
abi.encodePacked(_baseURI, LibString.toString(tokenId), ".json")
);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @title DN404
/// @notice DN404 is a hybrid ERC20 and ERC721 implementation that mints
/// and burns NFTs based on an account's ERC20 token balance.
///
/// @author vectorized.eth (@optimizoor)
/// @author Quit (@0xQuit)
/// @author Michael Amadi (@AmadiMichaels)
/// @author cygaar (@0xCygaar)
/// @author Thomas (@0xjustadev)
/// @author Harrison (@PopPunkOnChain)
///
/// @dev Note:
/// - The ERC721 data is stored in this base DN404 contract, however a
/// DN404Mirror contract ***MUST*** be deployed and linked during
/// initialization.
abstract contract DN404 {
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* EVENTS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Emitted when `amount` tokens is transferred from `from` to `to`.
event Transfer(address indexed from, address indexed to, uint256 amount);
/// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`.
event Approval(address indexed owner, address indexed spender, uint256 amount);
/// @dev Emitted when `target` sets their skipNFT flag to `status`.
event SkipNFTSet(address indexed target, bool status);
/// @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("SkipNFTSet(address,bool)"))`.
uint256 private constant _SKIP_NFT_SET_EVENT_SIGNATURE =
0xb5a1de456fff688115a4f75380060c23c8532d14ff85f687cc871456d6420393;
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* CUSTOM ERRORS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Thrown when attempting to double-initialize the contract.
error DNAlreadyInitialized();
/// @dev Thrown when attempting to transfer or burn more tokens than sender's balance.
error InsufficientBalance();
/// @dev Thrown when a spender attempts to transfer tokens with an insufficient allowance.
error InsufficientAllowance();
/// @dev Thrown when minting an amount of tokens that would overflow the max tokens.
error TotalSupplyOverflow();
/// @dev The unit cannot be zero.
error UnitIsZero();
/// @dev Thrown when the caller for a fallback NFT function is not the mirror contract.
error SenderNotMirror();
/// @dev Thrown when attempting to transfer tokens to the zero address.
error TransferToZeroAddress();
/// @dev Thrown when the mirror address provided for initialization is the zero address.
error MirrorAddressIsZero();
/// @dev Thrown when the link call to the mirror contract reverts.
error LinkMirrorContractFailed();
/// @dev Thrown when setting an NFT token approval
/// and the caller is not the owner or an approved operator.
error ApprovalCallerNotOwnerNorApproved();
/// @dev Thrown when transferring an NFT
/// and the caller is not the owner or an approved operator.
error TransferCallerNotOwnerNorApproved();
/// @dev Thrown when transferring an NFT and the from address is not the current owner.
error TransferFromIncorrectOwner();
/// @dev Thrown when checking the owner or approved address for a non-existent NFT.
error TokenDoesNotExist();
/// @dev The function selector is not recognized.
error FnSelectorNotRecognized();
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* CONSTANTS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev The flag to denote that the address data is initialized.
uint8 internal constant _ADDRESS_DATA_INITIALIZED_FLAG = 1 << 0;
/// @dev The flag to denote that the address should skip NFTs.
uint8 internal constant _ADDRESS_DATA_SKIP_NFT_FLAG = 1 << 1;
/// @dev The flag to denote that the address has overridden the default Permit2 allowance.
uint8 internal constant _ADDRESS_DATA_OVERRIDE_PERMIT2_FLAG = 1 << 2;
/// @dev The canonical Permit2 address.
/// For signature-based allowance granting for single transaction ERC20 `transferFrom`.
/// To enable, override `_givePermit2DefaultInfiniteAllowance()`.
/// [Github](https://github.com/Uniswap/permit2)
/// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
address internal constant _PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* STORAGE */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Struct containing an address's token data and settings.
struct AddressData {
// Auxiliary data.
uint88 aux;
// Flags for `initialized` and `skipNFT`.
uint8 flags;
// The alias for the address. Zero means absence of an alias.
uint32 addressAlias;
// The number of NFT tokens.
uint32 ownedLength;
// The token balance in wei.
uint96 balance;
}
/// @dev A uint32 map in storage.
struct Uint32Map {
uint256 spacer;
}
/// @dev A bitmap in storage.
struct Bitmap {
uint256 spacer;
}
/// @dev A struct to wrap a uint256 in storage.
struct Uint256Ref {
uint256 value;
}
/// @dev A mapping of an address pair to a Uint256Ref.
struct AddressPairToUint256RefMap {
uint256 spacer;
}
/// @dev Struct containing the base token contract storage.
struct DN404Storage {
// Current number of address aliases assigned.
uint32 numAliases;
// Next NFT ID to assign for a mint.
uint32 nextTokenId;
// The head of the burned pool.
uint32 burnedPoolHead;
// The tail of the burned pool.
uint32 burnedPoolTail;
// Total supply of minted NFTs.
uint32 totalNFTSupply;
// Total supply of tokens.
uint96 totalSupply;
// Address of the NFT mirror contract.
address mirrorERC721;
// Mapping of a user alias number to their address.
mapping(uint32 => address) aliasToAddress;
// Mapping of user operator approvals for NFTs.
AddressPairToUint256RefMap operatorApprovals;
// Mapping of NFT approvals to approved operators.
mapping(uint256 => address) nftApprovals;
// Bitmap of whether an non-zero NFT approval may exist.
Bitmap mayHaveNFTApproval;
// Bitmap of whether a NFT ID exists. Ignored if `_useExistsLookup()` returns false.
Bitmap exists;
// Mapping of user allowances for ERC20 spenders.
AddressPairToUint256RefMap allowance;
// Mapping of NFT IDs owned by an address.
mapping(address => Uint32Map) owned;
// The pool of burned NFT IDs.
Uint32Map burnedPool;
// Even indices: owner aliases. Odd indices: owned indices.
Uint32Map oo;
// Mapping of user account AddressData.
mapping(address => AddressData) addressData;
}
/// @dev Returns a storage pointer for DN404Storage.
function _getDN404Storage() internal pure virtual returns (DN404Storage storage $) {
/// @solidity memory-safe-assembly
assembly {
// `uint72(bytes9(keccak256("DN404_STORAGE")))`.
$.slot := 0xa20d6e21d0e5255308 // Truncate to 9 bytes to reduce bytecode size.
}
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* INITIALIZER */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Initializes the DN404 contract with an
/// `initialTokenSupply`, `initialTokenOwner` and `mirror` NFT contract address.
function _initializeDN404(
uint256 initialTokenSupply,
address initialSupplyOwner,
address mirror
) internal virtual {
DN404Storage storage $ = _getDN404Storage();
if ($.nextTokenId != 0) revert DNAlreadyInitialized();
if (mirror == address(0)) revert MirrorAddressIsZero();
/// @solidity memory-safe-assembly
assembly {
// Make the call to link the mirror contract.
mstore(0x00, 0x0f4599e5) // `linkMirrorContract(address)`.
mstore(0x20, caller())
if iszero(and(eq(mload(0x00), 1), call(gas(), mirror, 0, 0x1c, 0x24, 0x00, 0x20))) {
mstore(0x00, 0xd125259c) // `LinkMirrorContractFailed()`.
revert(0x1c, 0x04)
}
}
$.nextTokenId = 1;
$.mirrorERC721 = mirror;
if (_unit() == 0) revert UnitIsZero();
if (initialTokenSupply != 0) {
if (initialSupplyOwner == address(0)) revert TransferToZeroAddress();
if (_totalSupplyOverflows(initialTokenSupply)) revert TotalSupplyOverflow();
$.totalSupply = uint96(initialTokenSupply);
AddressData storage initialOwnerAddressData = _addressData(initialSupplyOwner);
initialOwnerAddressData.balance = uint96(initialTokenSupply);
/// @solidity memory-safe-assembly
assembly {
// Emit the {Transfer} event.
mstore(0x00, initialTokenSupply)
log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, shl(96, initialSupplyOwner)))
}
_setSkipNFT(initialSupplyOwner, true);
}
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* BASE UNIT FUNCTION TO OVERRIDE */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Amount of token balance that is equal to one NFT.
function _unit() internal view virtual returns (uint256) {
return 10 ** 18;
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* METADATA FUNCTIONS TO OVERRIDE */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Returns the name of the token.
function name() public view virtual returns (string memory);
/// @dev Returns the symbol of the token.
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);
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* ERC20 OPERATIONS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Returns the decimals places of the token. Always 18.
function decimals() public pure returns (uint8) {
return 18;
}
/// @dev Returns the amount of tokens in existence.
function totalSupply() public view virtual returns (uint256) {
return uint256(_getDN404Storage().totalSupply);
}
/// @dev Returns the amount of tokens owned by `owner`.
function balanceOf(address owner) public view virtual returns (uint256) {
return _getDN404Storage().addressData[owner].balance;
}
/// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`.
function allowance(address owner, address spender) public view returns (uint256) {
if (_givePermit2DefaultInfiniteAllowance() && spender == _PERMIT2) {
uint8 flags = _getDN404Storage().addressData[owner].flags;
if (flags & _ADDRESS_DATA_OVERRIDE_PERMIT2_FLAG == 0) return type(uint256).max;
}
return _ref(_getDN404Storage().allowance, owner, spender).value;
}
/// @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
///
/// Emits a {Approval} event.
function approve(address spender, uint256 amount) public virtual returns (bool) {
_approve(msg.sender, spender, amount);
return true;
}
/// @dev Transfer `amount` tokens from the caller to `to`.
///
/// Will burn sender NFTs if balance after transfer is less than
/// the amount required to support the current NFT balance.
///
/// Will mint NFTs to `to` if the recipient's new balance supports
/// additional NFTs ***AND*** the `to` address's skipNFT flag is
/// set to false.
///
/// Requirements:
/// - `from` must at least have `amount`.
///
/// Emits a {Transfer} event.
function transfer(address to, uint256 amount) public virtual returns (bool) {
_transfer(msg.sender, to, amount);
return true;
}
/// @dev Transfers `amount` tokens from `from` to `to`.
///
/// Note: Does not update the allowance if it is the maximum uint256 value.
///
/// Will burn sender NFTs if balance after transfer is less than
/// the amount required to support the current NFT balance.
///
/// Will mint NFTs to `to` if the recipient's new balance supports
/// additional NFTs ***AND*** the `to` address's skipNFT flag is
/// set to false.
///
/// Requirements:
/// - `from` must at least have `amount`.
/// - The caller must have at least `amount` of allowance to transfer the tokens of `from`.
///
/// Emits a {Transfer} event.
function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
Uint256Ref storage a = _ref(_getDN404Storage().allowance, from, msg.sender);
uint256 allowed = _givePermit2DefaultInfiniteAllowance() && msg.sender == _PERMIT2
&& (_getDN404Storage().addressData[from].flags & _ADDRESS_DATA_OVERRIDE_PERMIT2_FLAG) == 0
? type(uint256).max
: a.value;
if (allowed != type(uint256).max) {
if (amount > allowed) revert InsufficientAllowance();
unchecked {
a.value = allowed - amount;
}
}
_transfer(from, to, amount);
return true;
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* PERMIT2 */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Whether Permit2 has infinite allowances by default for all owners.
/// For signature-based allowance granting for single transaction ERC20 `transferFrom`.
/// To enable, override this function to return true.
function _givePermit2DefaultInfiniteAllowance() internal view virtual returns (bool) {
return false;
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* INTERNAL MINT FUNCTIONS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Mints `amount` tokens to `to`, increasing the total supply.
///
/// Will mint NFTs to `to` if the recipient's new balance supports
/// additional NFTs ***AND*** the `to` address's skipNFT flag is
/// set to false.
///
/// Emits a {Transfer} event.
function _mint(address to, uint256 amount) internal virtual {
if (to == address(0)) revert TransferToZeroAddress();
AddressData storage toAddressData = _addressData(to);
DN404Storage storage $ = _getDN404Storage();
_DNMintTemps memory t;
unchecked {
uint256 toBalance = uint256(toAddressData.balance) + amount;
toAddressData.balance = uint96(toBalance);
t.toEnd = toBalance / _unit();
}
uint256 maxId;
unchecked {
uint256 totalSupply_ = uint256($.totalSupply) + amount;
$.totalSupply = uint96(totalSupply_);
uint256 overflows = _toUint(_totalSupplyOverflows(totalSupply_));
if (overflows | _toUint(totalSupply_ < amount) != 0) revert TotalSupplyOverflow();
maxId = totalSupply_ / _unit();
}
unchecked {
if (toAddressData.flags & _ADDRESS_DATA_SKIP_NFT_FLAG == 0) {
Uint32Map storage toOwned = $.owned[to];
Uint32Map storage oo = $.oo;
uint256 toIndex = toAddressData.ownedLength;
_DNPackedLogs memory packedLogs = _packedLogsMalloc(_zeroFloorSub(t.toEnd, toIndex));
if (packedLogs.logs.length != 0) {
_packedLogsSet(packedLogs, to, 0);
$.totalNFTSupply += uint32(packedLogs.logs.length);
toAddressData.ownedLength = uint32(t.toEnd);
t.toAlias = _registerAndResolveAlias(toAddressData, to);
uint32 burnedPoolHead = $.burnedPoolHead;
uint32 burnedPoolTail = $.burnedPoolTail;
uint256 nextTokenId = $.nextTokenId;
// Mint loop.
do {
uint256 id;
if (burnedPoolHead != burnedPoolTail) {
id = _get($.burnedPool, burnedPoolHead++);
} else {
id = nextTokenId;
while (_get(oo, _ownershipIndex(id)) != 0) {
id = _useExistsLookup()
? _wrapNFTId(_findFirstUnset($.exists, id + 1, maxId + 1), maxId)
: _wrapNFTId(id + 1, maxId);
}
nextTokenId = _wrapNFTId(id + 1, maxId);
}
if (_useExistsLookup()) _set($.exists, id, true);
_set(toOwned, toIndex, uint32(id));
_setOwnerAliasAndOwnedIndex(oo, id, t.toAlias, uint32(toIndex++));
_packedLogsAppend(packedLogs, id);
} while (toIndex != t.toEnd);
$.nextTokenId = uint32(nextTokenId);
$.burnedPoolHead = burnedPoolHead;
_packedLogsSend(packedLogs, $.mirrorERC721);
}
}
}
/// @solidity memory-safe-assembly
assembly {
// Emit the {Transfer} event.
mstore(0x00, amount)
log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, shl(96, to)))
}
}
/// @dev Mints `amount` tokens to `to`, increasing the total supply.
/// This variant mints NFT tokens starting from ID `preTotalSupply / _unit() + 1`.
/// It will skip the burned pool.
///
/// Will mint NFTs to `to` if the recipient's new balance supports
/// additional NFTs ***AND*** the `to` address's skipNFT flag is
/// set to false.
///
/// Emits a {Transfer} event.
function _mintNext(address to, uint256 amount) internal virtual {
if (to == address(0)) revert TransferToZeroAddress();
AddressData storage toAddressData = _addressData(to);
DN404Storage storage $ = _getDN404Storage();
_DNMintTemps memory t;
unchecked {
uint256 toBalance = uint256(toAddressData.balance) + amount;
toAddressData.balance = uint96(toBalance);
t.toEnd = toBalance / _unit();
}
uint256 startId;
uint256 maxId;
unchecked {
uint256 preTotalSupply = uint256($.totalSupply);
startId = preTotalSupply / _unit() + 1;
uint256 totalSupply_ = uint256(preTotalSupply) + amount;
$.totalSupply = uint96(totalSupply_);
uint256 overflows = _toUint(_totalSupplyOverflows(totalSupply_));
if (overflows | _toUint(totalSupply_ < amount) != 0) revert TotalSupplyOverflow();
maxId = totalSupply_ / _unit();
}
unchecked {
if (toAddressData.flags & _ADDRESS_DATA_SKIP_NFT_FLAG == 0) {
Uint32Map storage toOwned = $.owned[to];
Uint32Map storage oo = $.oo;
uint256 toIndex = toAddressData.ownedLength;
_DNPackedLogs memory packedLogs = _packedLogsMalloc(_zeroFloorSub(t.toEnd, toIndex));
if (packedLogs.logs.length != 0) {
_packedLogsSet(packedLogs, to, 0);
$.totalNFTSupply += uint32(packedLogs.logs.length);
toAddressData.ownedLength = uint32(t.toEnd);
t.toAlias = _registerAndResolveAlias(toAddressData, to);
// Mint loop.
do {
uint256 id = startId;
while (_get(oo, _ownershipIndex(id)) != 0) {
id = _useExistsLookup()
? _wrapNFTId(_findFirstUnset($.exists, id + 1, maxId + 1), maxId)
: _wrapNFTId(id + 1, maxId);
}
startId = _wrapNFTId(id + 1, maxId);
if (_useExistsLookup()) _set($.exists, id, true);
_set(toOwned, toIndex, uint32(id));
_setOwnerAliasAndOwnedIndex(oo, id, t.toAlias, uint32(toIndex++));
_packedLogsAppend(packedLogs, id);
} while (toIndex != t.toEnd);
_packedLogsSend(packedLogs, $.mirrorERC721);
}
}
}
/// @solidity memory-safe-assembly
assembly {
// Emit the {Transfer} event.
mstore(0x00, amount)
log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, shl(96, to)))
}
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* INTERNAL BURN FUNCTIONS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Burns `amount` tokens from `from`, reducing the total supply.
///
/// Will burn sender NFTs if balance after transfer is less than
/// the amount required to support the current NFT balance.
///
/// Emits a {Transfer} event.
function _burn(address from, uint256 amount) internal virtual {
AddressData storage fromAddressData = _addressData(from);
DN404Storage storage $ = _getDN404Storage();
uint256 fromBalance = fromAddressData.balance;
if (amount > fromBalance) revert InsufficientBalance();
unchecked {
fromAddressData.balance = uint96(fromBalance -= amount);
uint256 totalSupply_ = uint256($.totalSupply) - amount;
$.totalSupply = uint96(totalSupply_);
Uint32Map storage fromOwned = $.owned[from];
uint256 fromIndex = fromAddressData.ownedLength;
uint256 numNFTBurns = _zeroFloorSub(fromIndex, fromBalance / _unit());
if (numNFTBurns != 0) {
_DNPackedLogs memory packedLogs = _packedLogsMalloc(numNFTBurns);
_packedLogsSet(packedLogs, from, 1);
uint256 totalNFTSupply = uint256($.totalNFTSupply) - numNFTBurns;
$.totalNFTSupply = uint32(totalNFTSupply);
bool addToBurnedPool = _addToBurnedPool(totalNFTSupply, totalSupply_);
Uint32Map storage oo = $.oo;
uint256 fromEnd = fromIndex - numNFTBurns;
fromAddressData.ownedLength = uint32(fromEnd);
uint32 burnedPoolTail = $.burnedPoolTail;
// Burn loop.
do {
uint256 id = _get(fromOwned, --fromIndex);
_setOwnerAliasAndOwnedIndex(oo, id, 0, 0);
_packedLogsAppend(packedLogs, id);
if (_useExistsLookup()) _set($.exists, id, false);
if (addToBurnedPool) _set($.burnedPool, burnedPoolTail++, uint32(id));
if (_get($.mayHaveNFTApproval, id)) {
_set($.mayHaveNFTApproval, id, false);
delete $.nftApprovals[id];
}
} while (fromIndex != fromEnd);
if (addToBurnedPool) $.burnedPoolTail = burnedPoolTail;
_packedLogsSend(packedLogs, $.mirrorERC721);
}
}
/// @solidity memory-safe-assembly
assembly {
// Emit the {Transfer} event.
mstore(0x00, amount)
log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0)
}
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* INTERNAL TRANSFER FUNCTIONS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Moves `amount` of tokens from `from` to `to`.
///
/// Will burn sender NFTs if balance after transfer is less than
/// the amount required to support the current NFT balance.
///
/// Will mint NFTs to `to` if the recipient's new balance supports
/// additional NFTs ***AND*** the `to` address's skipNFT flag is
/// set to false.
///
/// Emits a {Transfer} event.
function _transfer(address from, address to, uint256 amount) internal virtual {
if (to == address(0)) revert TransferToZeroAddress();
AddressData storage fromAddressData = _addressData(from);
AddressData storage toAddressData = _addressData(to);
DN404Storage storage $ = _getDN404Storage();
_DNTransferTemps memory t;
t.fromOwnedLength = fromAddressData.ownedLength;
t.toOwnedLength = toAddressData.ownedLength;
t.totalSupply = $.totalSupply;
if (amount > (t.fromBalance = fromAddressData.balance)) revert InsufficientBalance();
unchecked {
fromAddressData.balance = uint96(t.fromBalance -= amount);
toAddressData.balance = uint96(t.toBalance = uint256(toAddressData.balance) + amount);
t.numNFTBurns = _zeroFloorSub(t.fromOwnedLength, t.fromBalance / _unit());
if (toAddressData.flags & _ADDRESS_DATA_SKIP_NFT_FLAG == 0) {
if (from == to) t.toOwnedLength = t.fromOwnedLength - t.numNFTBurns;
t.numNFTMints = _zeroFloorSub(t.toBalance / _unit(), t.toOwnedLength);
}
t.totalNFTSupply = uint256($.totalNFTSupply) + t.numNFTMints - t.numNFTBurns;
$.totalNFTSupply = uint32(t.totalNFTSupply);
Uint32Map storage oo = $.oo;
if (_toUint(_useDirectTransfersIfPossible()) & _toUint(from != to) != 0) {
uint256 n = _min(t.fromOwnedLength, _min(t.numNFTBurns, t.numNFTMints));
if (n != 0) {
_DNDirectLogs memory directLogs = _directLogsMalloc(n, from, to);
t.numNFTBurns -= n;
t.numNFTMints -= n;
Uint32Map storage fromOwned = $.owned[from];
Uint32Map storage toOwned = $.owned[to];
t.toAlias = _registerAndResolveAlias(toAddressData, to);
// Direct transfer loop.
do {
uint256 id = _get(fromOwned, --t.fromOwnedLength);
_set(toOwned, t.toOwnedLength, uint32(id));
_setOwnerAliasAndOwnedIndex(oo, id, t.toAlias, uint32(t.toOwnedLength++));
_directLogsAppend(directLogs, id);
if (_get($.mayHaveNFTApproval, id)) {
_set($.mayHaveNFTApproval, id, false);
delete $.nftApprovals[id];
}
} while (--n != 0);
_directLogsSend(directLogs, $.mirrorERC721);
fromAddressData.ownedLength = uint32(t.fromOwnedLength);
toAddressData.ownedLength = uint32(t.toOwnedLength);
}
}
_DNPackedLogs memory packedLogs = _packedLogsMalloc(t.numNFTBurns + t.numNFTMints);
t.burnedPoolTail = $.burnedPoolTail;
if (t.numNFTBurns != 0) {
_packedLogsSet(packedLogs, from, 1);
bool addToBurnedPool = _addToBurnedPool(t.totalNFTSupply, t.totalSupply);
Uint32Map storage fromOwned = $.owned[from];
uint256 fromIndex = t.fromOwnedLength;
fromAddressData.ownedLength = uint32(t.fromEnd = fromIndex - t.numNFTBurns);
uint32 burnedPoolTail = t.burnedPoolTail;
// Burn loop.
do {
uint256 id = _get(fromOwned, --fromIndex);
_setOwnerAliasAndOwnedIndex(oo, id, 0, 0);
_packedLogsAppend(packedLogs, id);
if (_useExistsLookup()) _set($.exists, id, false);
if (addToBurnedPool) _set($.burnedPool, burnedPoolTail++, uint32(id));
if (_get($.mayHaveNFTApproval, id)) {
_set($.mayHaveNFTApproval, id, false);
delete $.nftApprovals[id];
}
} while (fromIndex != t.fromEnd);
if (addToBurnedPool) $.burnedPoolTail = (t.burnedPoolTail = burnedPoolTail);
}
if (t.numNFTMints != 0) {
_packedLogsSet(packedLogs, to, 0);
t.nextTokenId = $.nextTokenId;
Uint32Map storage toOwned = $.owned[to];
t.toAlias = _registerAndResolveAlias(toAddressData, to);
uint256 maxId = t.totalSupply / _unit();
uint256 toIndex = t.toOwnedLength;
toAddressData.ownedLength = uint32(t.toEnd = toIndex + t.numNFTMints);
uint32 burnedPoolHead = $.burnedPoolHead;
// Mint loop.
do {
uint256 id;
if (burnedPoolHead != t.burnedPoolTail) {
id = _get($.burnedPool, burnedPoolHead++);
} else {
id = t.nextTokenId;
while (_get(oo, _ownershipIndex(id)) != 0) {
id = _useExistsLookup()
? _wrapNFTId(_findFirstUnset($.exists, id + 1, maxId + 1), maxId)
: _wrapNFTId(id + 1, maxId);
}
t.nextTokenId = _wrapNFTId(id + 1, maxId);
}
if (_useExistsLookup()) _set($.exists, id, true);
_set(toOwned, toIndex, uint32(id));
_setOwnerAliasAndOwnedIndex(oo, id, t.toAlias, uint32(toIndex++));
_packedLogsAppend(packedLogs, id);
} while (toIndex != t.toEnd);
$.burnedPoolHead = burnedPoolHead;
$.nextTokenId = uint32(t.nextTokenId);
}
if (packedLogs.logs.length != 0) {
_packedLogsSend(packedLogs, $.mirrorERC721);
}
}
/// @solidity memory-safe-assembly
assembly {
// Emit the {Transfer} event.
mstore(0x00, amount)
// forgefmt: disable-next-item
log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), shr(96, shl(96, to)))
}
}
/// @dev Returns if direct NFT transfers should be used during ERC20 transfers
/// whenever possible, instead of burning and re-minting.
function _useDirectTransfersIfPossible() internal view virtual returns (bool) {
return true;
}
/// @dev Returns if burns should be added to the burn pool.
/// This returns false by default, which means the NFT IDs are re-minted in a cycle.
function _addToBurnedPool(uint256 totalNFTSupplyAfterBurn, uint256 totalSupplyAfterBurn)
internal
view
virtual
returns (bool)
{
totalNFTSupplyAfterBurn = totalNFTSupplyAfterBurn; // Silence compiler warning.
totalSupplyAfterBurn = totalSupplyAfterBurn; // Silence compiler warning.
return false;
}
/// @dev Returns whether to use the exists lookup for more efficient
/// scanning of an empty token ID slot. Highly recommended for collections
/// with near full load factor `totalNFTSupply * _unit() / totalSupply`.
/// The trade off is slightly higher initial storage write costs,
/// which will be quickly amortized away.
function _useExistsLookup() internal pure virtual returns (bool) {
return true;
}
/// @dev Transfers token `id` from `from` to `to`.
///
/// Requirements:
///
/// - Call must originate from the mirror contract.
/// - Token `id` must exist.
/// - `from` must be the owner of the token.
/// - `to` cannot be the zero address.
/// `msgSender` must be the owner of the token, or be approved to manage the token.
///
/// Emits a {Transfer} event.
function _transferFromNFT(address from, address to, uint256 id, address msgSender)
internal
virtual
{
DN404Storage storage $ = _getDN404Storage();
if (to == address(0)) revert TransferToZeroAddress();
Uint32Map storage oo = $.oo;
if (from != $.aliasToAddress[_get(oo, _ownershipIndex(_restrictNFTId(id)))]) {
revert TransferFromIncorrectOwner();
}
if (msgSender != from) {
if (_ref($.operatorApprovals, from, msgSender).value == 0) {
if (msgSender != $.nftApprovals[id]) {
revert TransferCallerNotOwnerNorApproved();
}
}
}
AddressData storage fromAddressData = _addressData(from);
AddressData storage toAddressData = _addressData(to);
uint256 unit = _unit();
unchecked {
{
uint256 fromBalance = fromAddressData.balance;
if (unit > fromBalance) revert InsufficientBalance();
fromAddressData.balance = uint96(fromBalance - unit);
toAddressData.balance += uint96(unit);
}
mapping(address => Uint32Map) storage owned = $.owned;
Uint32Map storage fromOwned = owned[from];
if (_get($.mayHaveNFTApproval, id)) {
_set($.mayHaveNFTApproval, id, false);
delete $.nftApprovals[id];
}
{
uint32 updatedId = _get(fromOwned, --fromAddressData.ownedLength);
uint32 i = _get(oo, _ownedIndex(id));
_set(fromOwned, i, updatedId);
_set(oo, _ownedIndex(updatedId), i);
}
uint32 n = toAddressData.ownedLength++;
_set(owned[to], n, uint32(id));
_setOwnerAliasAndOwnedIndex(oo, id, _registerAndResolveAlias(toAddressData, to), n);
}
/// @solidity memory-safe-assembly
assembly {
// Emit the {Transfer} event.
mstore(0x00, unit)
// forgefmt: disable-next-item
log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), shr(96, shl(96, to)))
}
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* INTERNAL APPROVE FUNCTIONS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`.
///
/// Emits a {Approval} event.
function _approve(address owner, address spender, uint256 amount) internal virtual {
if (_givePermit2DefaultInfiniteAllowance() && spender == _PERMIT2) {
_getDN404Storage().addressData[owner].flags |= _ADDRESS_DATA_OVERRIDE_PERMIT2_FLAG;
}
_ref(_getDN404Storage().allowance, owner, spender).value = amount;
/// @solidity memory-safe-assembly
assembly {
// Emit the {Approval} event.
mstore(0x00, amount)
// forgefmt: disable-next-item
log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, shl(96, owner)), shr(96, shl(96, spender)))
}
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* DATA HITCHHIKING FUNCTIONS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @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 (uint88) {
return _getDN404Storage().addressData[owner].aux;
}
/// @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, uint88 value) internal virtual {
_getDN404Storage().addressData[owner].aux = value;
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* SKIP NFT FUNCTIONS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Returns true if minting and transferring ERC20s to `owner` will skip minting NFTs.
/// Returns false otherwise.
function getSkipNFT(address owner) public view virtual returns (bool) {
AddressData storage d = _getDN404Storage().addressData[owner];
if (d.flags & _ADDRESS_DATA_INITIALIZED_FLAG == 0) return _hasCode(owner);
return d.flags & _ADDRESS_DATA_SKIP_NFT_FLAG != 0;
}
/// @dev Sets the caller's skipNFT flag to `skipNFT`. Returns true.
///
/// Emits a {SkipNFTSet} event.
function setSkipNFT(bool skipNFT) public virtual returns (bool) {
_setSkipNFT(msg.sender, skipNFT);
return true;
}
/// @dev Internal function to set account `owner` skipNFT flag to `state`
///
/// Initializes account `owner` AddressData if it is not currently initialized.
///
/// Emits a {SkipNFTSet} event.
function _setSkipNFT(address owner, bool state) internal virtual {
AddressData storage d = _addressData(owner);
if ((d.flags & _ADDRESS_DATA_SKIP_NFT_FLAG != 0) != state) {
d.flags ^= _ADDRESS_DATA_SKIP_NFT_FLAG;
}
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, iszero(iszero(state)))
log2(0x00, 0x20, _SKIP_NFT_SET_EVENT_SIGNATURE, shr(96, shl(96, owner)))
}
}
/// @dev Returns a storage data pointer for account `owner` AddressData
///
/// Initializes account `owner` AddressData if it is not currently initialized.
function _addressData(address owner) internal virtual returns (AddressData storage d) {
d = _getDN404Storage().addressData[owner];
unchecked {
if (d.flags & _ADDRESS_DATA_INITIALIZED_FLAG == 0) {
uint256 skipNFT = (_toUint(_hasCode(owner)) * _ADDRESS_DATA_SKIP_NFT_FLAG);
d.flags = uint8(skipNFT | _ADDRESS_DATA_INITIALIZED_FLAG);
}
}
}
/// @dev Returns the `addressAlias` of account `to`.
///
/// Assigns and registers the next alias if `to` alias was not previously registered.
function _registerAndResolveAlias(AddressData storage toAddressData, address to)
internal
virtual
returns (uint32 addressAlias)
{
DN404Storage storage $ = _getDN404Storage();
addressAlias = toAddressData.addressAlias;
if (addressAlias == 0) {
unchecked {
addressAlias = ++$.numAliases;
}
toAddressData.addressAlias = addressAlias;
$.aliasToAddress[addressAlias] = to;
if (addressAlias == 0) revert(); // Overflow.
}
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* MIRROR OPERATIONS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Returns the address of the mirror NFT contract.
function mirrorERC721() public view virtual returns (address) {
return _getDN404Storage().mirrorERC721;
}
/// @dev Returns the total NFT supply.
function _totalNFTSupply() internal view virtual returns (uint256) {
return _getDN404Storage().totalNFTSupply;
}
/// @dev Returns `owner` NFT balance.
function _balanceOfNFT(address owner) internal view virtual returns (uint256) {
return _getDN404Storage().addressData[owner].ownedLength;
}
/// @dev Returns the owner of token `id`.
/// Returns the zero address instead of reverting if the token does not exist.
function _ownerAt(uint256 id) internal view virtual returns (address) {
DN404Storage storage $ = _getDN404Storage();
return $.aliasToAddress[_get($.oo, _ownershipIndex(_restrictNFTId(id)))];
}
/// @dev Returns the owner of token `id`.
///
/// Requirements:
/// - Token `id` must exist.
function _ownerOf(uint256 id) internal view virtual returns (address) {
if (!_exists(id)) revert TokenDoesNotExist();
return _ownerAt(id);
}
/// @dev Returns if token `id` exists.
function _exists(uint256 id) internal view virtual returns (bool) {
return _ownerAt(id) != address(0);
}
/// @dev Returns the account approved to manage token `id`.
///
/// Requirements:
/// - Token `id` must exist.
function _getApproved(uint256 id) internal view virtual returns (address) {
if (!_exists(id)) revert TokenDoesNotExist();
return _getDN404Storage().nftApprovals[id];
}
/// @dev Sets `spender` as the approved account to manage token `id`, using `msgSender`.
///
/// Requirements:
/// - `msgSender` must be the owner or an approved operator for the token owner.
function _approveNFT(address spender, uint256 id, address msgSender)
internal
virtual
returns (address owner)
{
DN404Storage storage $ = _getDN404Storage();
owner = $.aliasToAddress[_get($.oo, _ownershipIndex(_restrictNFTId(id)))];
if (msgSender != owner) {
if (_ref($.operatorApprovals, owner, msgSender).value == 0) {
revert ApprovalCallerNotOwnerNorApproved();
}
}
$.nftApprovals[id] = spender;
_set($.mayHaveNFTApproval, id, spender != address(0));
}
/// @dev Approve or remove the `operator` as an operator for `msgSender`,
/// without authorization checks.
function _setApprovalForAll(address operator, bool approved, address msgSender)
internal
virtual
{
_ref(_getDN404Storage().operatorApprovals, msgSender, operator).value = _toUint(approved);
}
/// @dev Returns the NFT IDs of `owner` in range `[begin, end)`.
/// Optimized for smaller bytecode size, as this function is intended for off-chain calling.
function _ownedIds(address owner, uint256 begin, uint256 end)
internal
view
virtual
returns (uint256[] memory ids)
{
DN404Storage storage $ = _getDN404Storage();
Uint32Map storage owned = $.owned[owner];
uint256 n = $.addressData[owner].ownedLength;
/// @solidity memory-safe-assembly
assembly {
n := xor(n, mul(lt(end, n), xor(end, n))) // `min(n, end)`.
ids := mload(0x40)
let i := begin
for {} lt(i, n) { i := add(i, 1) } {
let s := add(shl(96, owned.slot), shr(3, i)) // Storage slot.
let id := and(0xffffffff, shr(shl(5, and(i, 7)), sload(s)))
mstore(add(add(ids, 0x20), shl(5, sub(i, begin))), id) // Append to.
}
mstore(ids, sub(i, begin)) // Store the length.
mstore(0x40, add(add(ids, 0x20), shl(5, sub(i, begin)))) // Allocate memory.
}
}
/// @dev Fallback modifier to dispatch calls from the mirror NFT contract
/// to internal functions in this contract.
modifier dn404Fallback() virtual {
DN404Storage storage $ = _getDN404Storage();
uint256 fnSelector = _calldataload(0x00) >> 224;
// `transferFromNFT(address,address,uint256,address)`.
if (fnSelector == 0xe5eb36c8) {
if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
if (msg.data.length < 0x84) revert();
address from = address(uint160(_calldataload(0x04)));
address to = address(uint160(_calldataload(0x24)));
uint256 id = _calldataload(0x44);
address msgSender = address(uint160(_calldataload(0x64)));
_transferFromNFT(from, to, id, msgSender);
_return(1);
}
// `setApprovalForAll(address,bool,address)`.
if (fnSelector == 0x813500fc) {
if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
if (msg.data.length < 0x64) revert();
address spender = address(uint160(_calldataload(0x04)));
bool status = _calldataload(0x24) != 0;
address msgSender = address(uint160(_calldataload(0x44)));
_setApprovalForAll(spender, status, msgSender);
_return(1);
}
// `isApprovedForAll(address,address)`.
if (fnSelector == 0xe985e9c5) {
if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
if (msg.data.length < 0x44) revert();
address owner = address(uint160(_calldataload(0x04)));
address operator = address(uint160(_calldataload(0x24)));
_return(_ref($.operatorApprovals, owner, operator).value);
}
// `ownerOf(uint256)`.
if (fnSelector == 0x6352211e) {
if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
if (msg.data.length < 0x24) revert();
uint256 id = _calldataload(0x04);
_return(uint160(_ownerOf(id)));
}
// `ownerAt(uint256)`.
if (fnSelector == 0x24359879) {
if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
if (msg.data.length < 0x24) revert();
uint256 id = _calldataload(0x04);
_return(uint160(_ownerAt(id)));
}
// `approveNFT(address,uint256,address)`.
if (fnSelector == 0xd10b6e0c) {
if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
if (msg.data.length < 0x64) revert();
address spender = address(uint160(_calldataload(0x04)));
uint256 id = _calldataload(0x24);
address msgSender = address(uint160(_calldataload(0x44)));
_return(uint160(_approveNFT(spender, id, msgSender)));
}
// `getApproved(uint256)`.
if (fnSelector == 0x081812fc) {
if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
if (msg.data.length < 0x24) revert();
uint256 id = _calldataload(0x04);
_return(uint160(_getApproved(id)));
}
// `balanceOfNFT(address)`.
if (fnSelector == 0xf5b100ea) {
if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
if (msg.data.length < 0x24) revert();
address owner = address(uint160(_calldataload(0x04)));
_return(_balanceOfNFT(owner));
}
// `totalNFTSupply()`.
if (fnSelector == 0xe2c79281) {
if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
if (msg.data.length < 0x04) revert();
_return(_totalNFTSupply());
}
// `implementsDN404()`.
if (fnSelector == 0xb7a94eb8) {
_return(1);
}
_;
}
/// @dev Fallback function for calls from mirror NFT contract.
/// Override this if you need to implement your custom
/// fallback with utilities like Solady's `LibZip.cdFallback()`.
/// And always remember to always wrap the fallback with `dn404Fallback`.
fallback() external payable virtual dn404Fallback {
revert FnSelectorNotRecognized(); // Not mandatory. Just for quality of life.
}
/// @dev This is to silence the compiler warning.
/// Override and remove the revert if you want your contract to receive ETH via receive.
receive() external payable virtual {
if (msg.value != 0) revert();
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* INTERNAL / PRIVATE HELPERS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Returns `(i - 1) << 1`.
function _ownershipIndex(uint256 i) internal pure returns (uint256) {
unchecked {
return (i - 1) << 1; // Minus 1 as token IDs start from 1.
}
}
/// @dev Returns `((i - 1) << 1) + 1`.
function _ownedIndex(uint256 i) internal pure returns (uint256) {
unchecked {
return ((i - 1) << 1) + 1; // Minus 1 as token IDs start from 1.
}
}
/// @dev Returns the uint32 value at `index` in `map`.
function _get(Uint32Map storage map, uint256 index) internal view returns (uint32 result) {
/// @solidity memory-safe-assembly
assembly {
let s := add(shl(96, map.slot), shr(3, index)) // Storage slot.
result := and(0xffffffff, shr(shl(5, and(index, 7)), sload(s)))
}
}
/// @dev Updates the uint32 value at `index` in `map`.
function _set(Uint32Map storage map, uint256 index, uint32 value) internal {
/// @solidity memory-safe-assembly
assembly {
let s := add(shl(96, map.slot), shr(3, index)) // Storage slot.
let o := shl(5, and(index, 7)) // Storage slot offset (bits).
let v := sload(s) // Storage slot value.
let m := 0xffffffff // Value mask.
sstore(s, xor(v, shl(o, and(m, xor(shr(o, v), value)))))
}
}
/// @dev Sets the owner alias and the owned index together.
function _setOwnerAliasAndOwnedIndex(
Uint32Map storage map,
uint256 id,
uint32 ownership,
uint32 ownedIndex
) internal {
/// @solidity memory-safe-assembly
assembly {
let i := sub(id, 1) // Index of the uint64 combined value.
let s := add(shl(96, map.slot), shr(2, i)) // Storage slot.
let o := shl(6, and(i, 3)) // Storage slot offset (bits).
let v := sload(s) // Storage slot value.
let m := 0xffffffffffffffff // Value mask.
let combined := or(shl(32, ownedIndex), and(0xffffffff, ownership))
sstore(s, xor(v, shl(o, and(m, xor(shr(o, v), combined)))))
}
}
/// @dev Returns the boolean value of the bit at `index` in `bitmap`.
function _get(Bitmap storage bitmap, uint256 index) internal view returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
let s := add(shl(96, bitmap.slot), shr(8, index)) // Storage slot.
result := and(1, shr(and(0xff, index), sload(s)))
}
}
/// @dev Updates the bit at `index` in `bitmap` to `value`.
function _set(Bitmap storage bitmap, uint256 index, bool value) internal {
/// @solidity memory-safe-assembly
assembly {
let s := add(shl(96, bitmap.slot), shr(8, index)) // Storage slot.
let o := and(0xff, index) // Storage slot offset (bits).
sstore(s, or(and(sload(s), not(shl(o, 1))), shl(o, iszero(iszero(value)))))
}
}
/// @dev Returns the index of the least significant unset bit in `[begin, end)`.
/// If no unset bit is found, returns `type(uint256).max`.
function _findFirstUnset(Bitmap storage bitmap, uint256 begin, uint256 end)
internal
view
returns (uint256 unsetBitIndex)
{
/// @solidity memory-safe-assembly
assembly {
unsetBitIndex := not(0) // Initialize to `type(uint256).max`.
let s := shl(96, bitmap.slot) // Storage offset of the bitmap.
let bucket := add(s, shr(8, begin))
let negBits := shl(and(0xff, begin), shr(and(0xff, begin), not(sload(bucket))))
if iszero(negBits) {
let lastBucket := add(s, shr(8, end))
for {} 1 {} {
bucket := add(bucket, 1)
negBits := not(sload(bucket))
if or(negBits, gt(bucket, lastBucket)) { break }
}
if gt(bucket, lastBucket) {
negBits := shr(and(0xff, not(end)), shl(and(0xff, not(end)), negBits))
}
}
if negBits {
// Find-first-set routine.
let b := and(negBits, add(not(negBits), 1)) // Isolate the least significant bit.
let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, b))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, b))))
r := or(r, shl(5, lt(0xffffffff, shr(r, b))))
// For the remaining 32 bits, use a De Bruijn lookup.
// forgefmt: disable-next-item
r := or(r, byte(and(div(0xd76453e0, shr(r, b)), 0x1f),
0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
r := or(shl(8, sub(bucket, s)), r)
unsetBitIndex := or(r, sub(0, or(iszero(lt(r, end)), lt(r, begin))))
}
}
}
/// @dev Returns a storage reference to the value at (`a0`, `a1`) in `map`.
function _ref(AddressPairToUint256RefMap storage map, address a0, address a1)
internal
pure
returns (Uint256Ref storage ref)
{
/// @solidity memory-safe-assembly
assembly {
mstore(0x28, a1)
mstore(0x14, a0)
mstore(0x00, map.slot)
ref.slot := keccak256(0x00, 0x48)
// Clear the part of the free memory pointer that was overwritten.
mstore(0x28, 0x00)
}
}
/// @dev Wraps the NFT ID.
function _wrapNFTId(uint256 id, uint256 maxId) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
result := or(mul(iszero(gt(id, maxId)), id), gt(id, maxId))
}
}
/// @dev Returns `id > type(uint32).max ? 0 : id`.
function _restrictNFTId(uint256 id) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
result := mul(id, lt(id, 0x100000000))
}
}
/// @dev Returns whether `amount` is a valid `totalSupply`.
function _totalSupplyOverflows(uint256 amount) internal view returns (bool) {
unchecked {
return _toUint(amount > type(uint96).max)
| _toUint(amount / _unit() > type(uint32).max - 1) != 0;
}
}
/// @dev Returns `max(0, x - y)`.
function _zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(gt(x, y), sub(x, y))
}
}
/// @dev Returns `x < y ? x : y`.
function _min(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), lt(y, x)))
}
}
/// @dev Returns `b ? 1 : 0`.
function _toUint(bool b) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
result := iszero(iszero(b))
}
}
/// @dev Struct containing direct transfer log data for {Transfer} events to be
/// emitted by the mirror NFT contract.
struct _DNDirectLogs {
uint256 offset;
address from;
address to;
uint256[] logs;
}
/// @dev Initiates memory allocation for direct logs with `n` log items.
function _directLogsMalloc(uint256 n, address from, address to)
private
pure
returns (_DNDirectLogs memory p)
{
/// @solidity memory-safe-assembly
assembly {
// Note that `p` implicitly allocates and advances the free memory pointer by
// 4 words, which we can safely mutate in `_packedLogsSend`.
let logs := mload(0x40)
mstore(logs, n) // Store the length.
let offset := add(0x20, logs) // Skip the word for `p.logs.length`.
mstore(0x40, add(offset, shl(5, n))) // Allocate memory.
mstore(add(0x60, p), logs) // Set `p.logs`.
mstore(add(0x40, p), to) // Set `p.to`.
mstore(add(0x20, p), from) // Set `p.from`.
mstore(p, offset) // Set `p.offset`.
}
}
/// @dev Adds a direct log item to `p` with token `id`.
function _directLogsAppend(_DNDirectLogs memory p, uint256 id) private pure {
/// @solidity memory-safe-assembly
assembly {
let offset := mload(p)
mstore(offset, id)
mstore(p, add(offset, 0x20))
}
}
/// @dev Calls the `mirror` NFT contract to emit {Transfer} events for packed logs `p`.
function _directLogsSend(_DNDirectLogs memory p, address mirror) private {
/// @solidity memory-safe-assembly
assembly {
let logs := mload(add(p, 0x60))
let n := add(0x84, shl(5, mload(logs))) // Length of calldata to send.
let o := sub(logs, 0x80) // Start of calldata to send.
mstore(o, 0x144027d3) // `logDirectTransfer(address,address,uint256[])`.
mstore(add(o, 0x20), mload(add(0x20, p)))
mstore(add(o, 0x40), mload(add(0x40, p)))
mstore(add(o, 0x60), 0x60) // Offset of `logs` in the calldata to send.
if iszero(and(eq(mload(o), 1), call(gas(), mirror, 0, add(o, 0x1c), n, o, 0x20))) {
revert(o, 0x00)
}
}
}
/// @dev Struct containing packed log data for {Transfer} events to be
/// emitted by the mirror NFT contract.
struct _DNPackedLogs {
uint256 offset;
uint256 addressAndBit;
uint256[] logs;
}
/// @dev Initiates memory allocation for packed logs with `n` log items.
function _packedLogsMalloc(uint256 n) private pure returns (_DNPackedLogs memory p) {
/// @solidity memory-safe-assembly
assembly {
// Note that `p` implicitly allocates and advances the free memory pointer by
// 3 words, which we can safely mutate in `_packedLogsSend`.
let logs := mload(0x40)
mstore(logs, n) // Store the length.
let offset := add(0x20, logs) // Skip the word for `p.logs.length`.
mstore(0x40, add(offset, shl(5, n))) // Allocate memory.
mstore(add(0x40, p), logs) // Set `p.logs`.
mstore(p, offset) // Set `p.offset`.
}
}
/// @dev Set the current address and the burn bit.
function _packedLogsSet(_DNPackedLogs memory p, address a, uint256 burnBit) private pure {
/// @solidity memory-safe-assembly
assembly {
mstore(add(p, 0x20), or(shl(96, a), burnBit)) // Set `p.addressAndBit`.
}
}
/// @dev Adds a packed log item to `p` with token `id`.
function _packedLogsAppend(_DNPackedLogs memory p, uint256 id) private pure {
/// @solidity memory-safe-assembly
assembly {
let offset := mload(p)
mstore(offset, or(mload(add(p, 0x20)), shl(8, id))) // `p.addressAndBit | (id << 8)`.
mstore(p, add(offset, 0x20))
}
}
/// @dev Calls the `mirror` NFT contract to emit {Transfer} events for packed logs `p`.
function _packedLogsSend(_DNPackedLogs memory p, address mirror) private {
/// @solidity memory-safe-assembly
assembly {
let logs := mload(add(p, 0x40))
let o := sub(logs, 0x40) // Start of calldata to send.
mstore(o, 0x263c69d6) // `logTransfer(uint256[])`.
mstore(add(o, 0x20), 0x20) // Offset of `logs` in the calldata to send.
let n := add(0x44, shl(5, mload(logs))) // Length of calldata to send.
if iszero(and(eq(mload(o), 1), call(gas(), mirror, 0, add(o, 0x1c), n, o, 0x20))) {
revert(o, 0x00)
}
}
}
/// @dev Struct of temporary variables for transfers.
struct _DNTransferTemps {
uint256 numNFTBurns;
uint256 numNFTMints;
uint256 fromBalance;
uint256 toBalance;
uint256 fromOwnedLength;
uint256 toOwnedLength;
uint256 totalSupply;
uint256 totalNFTSupply;
uint256 fromEnd;
uint256 toEnd;
uint32 toAlias;
uint256 nextTokenId;
uint32 burnedPoolTail;
}
/// @dev Struct of temporary variables for mints.
struct _DNMintTemps {
uint256 toEnd;
uint32 toAlias;
}
/// @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 Returns the calldata value at `offset`.
function _calldataload(uint256 offset) private pure returns (uint256 value) {
/// @solidity memory-safe-assembly
assembly {
value := calldataload(offset)
}
}
/// @dev Executes a return opcode to return `x` and end the current call frame.
function _return(uint256 x) private pure {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, x)
return(0x00, 0x20)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @title DN404Mirror
/// @notice DN404Mirror provides an interface for interacting with the
/// NFT tokens in a DN404 implementation.
///
/// @author vectorized.eth (@optimizoor)
/// @author Quit (@0xQuit)
/// @author Michael Amadi (@AmadiMichaels)
/// @author cygaar (@0xCygaar)
/// @author Thomas (@0xjustadev)
/// @author Harrison (@PopPunkOnChain)
///
/// @dev Note:
/// - The ERC721 data is stored in the base DN404 contract.
contract DN404Mirror {
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* 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 The ownership is transferred from `oldOwner` to `newOwner`.
/// This is for marketplace signaling purposes. This contract has a `pullOwner()`
/// function that will sync the owner from the base contract.
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
/// @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;
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* CUSTOM ERRORS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Thrown when a call for an NFT function did not originate
/// from the base DN404 contract.
error SenderNotBase();
/// @dev Thrown when a call for an NFT function did not originate from the deployer.
error SenderNotDeployer();
/// @dev Thrown when transferring an NFT to a contract address that
/// does not implement ERC721Receiver.
error TransferToNonERC721ReceiverImplementer();
/// @dev Thrown when linking to the DN404 base contract and the
/// DN404 supportsInterface check fails or the call reverts.
error CannotLink();
/// @dev Thrown when a linkMirrorContract call is received and the
/// NFT mirror contract has already been linked to a DN404 base contract.
error AlreadyLinked();
/// @dev Thrown when retrieving the base DN404 address when a link has not
/// been established.
error NotLinked();
/// @dev The function selector is not recognized.
error FnSelectorNotRecognized();
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* STORAGE */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Struct contain the NFT mirror contract storage.
struct DN404NFTStorage {
address baseERC20;
address deployer;
address owner;
}
/// @dev Returns a storage pointer for DN404NFTStorage.
function _getDN404NFTStorage() internal pure virtual returns (DN404NFTStorage storage $) {
/// @solidity memory-safe-assembly
assembly {
// `uint72(bytes9(keccak256("DN404_MIRROR_STORAGE")))`.
$.slot := 0x3602298b8c10b01230 // Truncate to 9 bytes to reduce bytecode size.
}
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* CONSTRUCTOR */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
constructor(address deployer) {
// For non-proxies, we will store the deployer so that only the deployer can
// link the base contract.
_getDN404NFTStorage().deployer = deployer;
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* ERC721 OPERATIONS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Returns the token collection name from the base DN404 contract.
function name() public view virtual returns (string memory result) {
return _readString(0x06fdde03, 0); // `name()`.
}
/// @dev Returns the token collection symbol from the base DN404 contract.
function symbol() public view virtual returns (string memory result) {
return _readString(0x95d89b41, 0); // `symbol()`.
}
/// @dev Returns the Uniform Resource Identifier (URI) for token `id` from
/// the base DN404 contract.
function tokenURI(uint256 id) public view virtual returns (string memory result) {
return _readString(0xc87b56dd, id); // `tokenURI()`.
}
/// @dev Returns the total NFT supply from the base DN404 contract.
function totalSupply() public view virtual returns (uint256 result) {
return _readWord(0xe2c79281, 0, 0); // `totalNFTSupply()`.
}
/// @dev Returns the number of NFT tokens owned by `nftOwner` from the base DN404 contract.
///
/// Requirements:
/// - `nftOwner` must not be the zero address.
function balanceOf(address nftOwner) public view virtual returns (uint256 result) {
return _readWord(0xf5b100ea, uint160(nftOwner), 0); // `balanceOfNFT(address)`.
}
/// @dev Returns the owner of token `id` from the base DN404 contract.
///
/// Requirements:
/// - Token `id` must exist.
function ownerOf(uint256 id) public view virtual returns (address result) {
return address(uint160(_readWord(0x6352211e, id, 0))); // `ownerOf(uint256)`.
}
/// @dev Returns the owner of token `id` from the base DN404 contract.
/// Returns `address(0)` instead of reverting if the token does not exist.
function ownerAt(uint256 id) public view virtual returns (address result) {
return address(uint160(_readWord(0x24359879, id, 0))); // `ownerAt(uint256)`.
}
/// @dev Sets `spender` as the approved account to manage token `id` in
/// the base DN404 contract.
///
/// 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 spender, uint256 id) public virtual {
address base = baseERC20();
/// @solidity memory-safe-assembly
assembly {
spender := shr(96, shl(96, spender))
let m := mload(0x40)
mstore(0x00, 0xd10b6e0c) // `approveNFT(address,uint256,address)`.
mstore(0x20, spender)
mstore(0x40, id)
mstore(0x60, caller())
if iszero(
and(
gt(returndatasize(), 0x1f),
call(gas(), base, callvalue(), 0x1c, 0x64, 0x00, 0x20)
)
) {
returndatacopy(m, 0x00, returndatasize())
revert(m, returndatasize())
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero pointer.
// Emit the {Approval} event.
log4(codesize(), 0x00, _APPROVAL_EVENT_SIGNATURE, shr(96, mload(0x0c)), spender, id)
}
}
/// @dev Returns the account approved to manage token `id` from
/// the base DN404 contract.
///
/// Requirements:
/// - Token `id` must exist.
function getApproved(uint256 id) public view virtual returns (address) {
return address(uint160(_readWord(0x081812fc, id, 0))); // `getApproved(uint256)`.
}
/// @dev Sets whether `operator` is approved to manage the tokens of the caller in
/// the base DN404 contract.
///
/// Emits an {ApprovalForAll} event.
function setApprovalForAll(address operator, bool approved) public virtual {
address base = baseERC20();
/// @solidity memory-safe-assembly
assembly {
operator := shr(96, shl(96, operator))
let m := mload(0x40)
mstore(0x00, 0x813500fc) // `setApprovalForAll(address,bool,address)`.
mstore(0x20, operator)
mstore(0x40, iszero(iszero(approved)))
mstore(0x60, caller())
if iszero(
and(eq(mload(0x00), 1), call(gas(), base, callvalue(), 0x1c, 0x64, 0x00, 0x20))
) {
returndatacopy(m, 0x00, returndatasize())
revert(m, returndatasize())
}
// Emit the {ApprovalForAll} event.
// The `approved` value is already at 0x40.
log3(0x40, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, caller(), operator)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero pointer.
}
}
/// @dev Returns whether `operator` is approved to manage the tokens of `nftOwner` from
/// the base DN404 contract.
function isApprovedForAll(address nftOwner, address operator)
public
view
virtual
returns (bool result)
{
// `isApprovedForAll(address,address)`.
return _readWord(0xe985e9c5, uint160(nftOwner), uint160(operator)) != 0;
}
/// @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 virtual {
address base = baseERC20();
/// @solidity memory-safe-assembly
assembly {
from := shr(96, shl(96, from))
to := shr(96, shl(96, to))
let m := mload(0x40)
mstore(m, 0xe5eb36c8) // `transferFromNFT(address,address,uint256,address)`.
mstore(add(m, 0x20), from)
mstore(add(m, 0x40), to)
mstore(add(m, 0x60), id)
mstore(add(m, 0x80), caller())
if iszero(
and(eq(mload(m), 1), call(gas(), base, callvalue(), add(m, 0x1c), 0x84, m, 0x20))
) {
returndatacopy(m, 0x00, returndatasize())
revert(m, returndatasize())
}
// Emit the {Transfer} event.
log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, 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
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))
}
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* OWNER SYNCING OPERATIONS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Returns the `owner` of the contract, for marketplace signaling purposes.
function owner() public view virtual returns (address) {
return _getDN404NFTStorage().owner;
}
/// @dev Permissionless function to pull the owner from the base DN404 contract
/// if it implements ownable, for marketplace signaling purposes.
function pullOwner() public virtual {
address newOwner;
address base = baseERC20();
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x8da5cb5b) // `owner()`.
if and(gt(returndatasize(), 0x1f), staticcall(gas(), base, 0x1c, 0x04, 0x00, 0x20)) {
newOwner := shr(96, mload(0x0c))
}
}
DN404NFTStorage storage $ = _getDN404NFTStorage();
address oldOwner = $.owner;
if (oldOwner != newOwner) {
$.owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* MIRROR OPERATIONS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Returns the address of the base DN404 contract.
function baseERC20() public view virtual returns (address base) {
base = _getDN404NFTStorage().baseERC20;
if (base == address(0)) revert NotLinked();
}
/// @dev Fallback modifier to execute calls from the base DN404 contract.
modifier dn404NFTFallback() virtual {
DN404NFTStorage storage $ = _getDN404NFTStorage();
uint256 fnSelector = _calldataload(0x00) >> 224;
// `logTransfer(uint256[])`.
if (fnSelector == 0x263c69d6) {
if (msg.sender != $.baseERC20) revert SenderNotBase();
/// @solidity memory-safe-assembly
assembly {
let o := add(0x24, calldataload(0x04)) // Packed logs offset.
let end := add(o, shl(5, calldataload(sub(o, 0x20))))
for {} iszero(eq(o, end)) { o := add(0x20, o) } {
let d := calldataload(o) // Entry in the packed logs.
let a := shr(96, d) // The address.
let b := and(1, d) // Whether it is a burn.
log4(
codesize(),
0x00,
_TRANSFER_EVENT_SIGNATURE,
mul(a, b), // `from`.
mul(a, iszero(b)), // `to`.
shr(168, shl(160, d)) // `id`.
)
}
mstore(0x00, 0x01)
return(0x00, 0x20)
}
}
// `logDirectTransfer(address,address,uint256[])`.
if (fnSelector == 0x144027d3) {
if (msg.sender != $.baseERC20) revert SenderNotBase();
/// @solidity memory-safe-assembly
assembly {
let from := calldataload(0x04)
let to := calldataload(0x24)
let o := add(0x24, calldataload(0x44)) // Direct logs offset.
let end := add(o, shl(5, calldataload(sub(o, 0x20))))
for {} iszero(eq(o, end)) { o := add(0x20, o) } {
log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, calldataload(o))
}
mstore(0x00, 0x01)
return(0x00, 0x20)
}
}
// `linkMirrorContract(address)`.
if (fnSelector == 0x0f4599e5) {
if ($.deployer != address(0)) {
if (address(uint160(_calldataload(0x04))) != $.deployer) {
revert SenderNotDeployer();
}
}
if ($.baseERC20 != address(0)) revert AlreadyLinked();
$.baseERC20 = msg.sender;
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x01)
return(0x00, 0x20)
}
}
_;
}
/// @dev Fallback function for calls from base DN404 contract.
/// Override this if you need to implement your custom
/// fallback with utilities like Solady's `LibZip.cdFallback()`.
/// And always remember to always wrap the fallback with `dn404NFTFallback`.
fallback() external payable virtual dn404NFTFallback {
revert FnSelectorNotRecognized(); // Not mandatory. Just for quality of life.
}
/// @dev This is to silence the compiler warning.
/// Override and remove the revert if you want your contract to receive ETH via receive.
receive() external payable virtual {
if (msg.value != 0) revert();
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* PRIVATE HELPERS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Helper to read a string from the base DN404 contract.
function _readString(uint256 fnSelector, uint256 arg0)
private
view
returns (string memory result)
{
address base = baseERC20();
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
mstore(0x00, fnSelector)
mstore(0x20, arg0)
if iszero(staticcall(gas(), base, 0x1c, 0x24, 0x00, 0x00)) {
returndatacopy(result, 0x00, returndatasize())
revert(result, returndatasize())
}
returndatacopy(0x00, 0x00, 0x20) // Copy the offset of the string in returndata.
returndatacopy(result, mload(0x00), 0x20) // Copy the length of the string.
returndatacopy(add(result, 0x20), add(mload(0x00), 0x20), mload(result)) // Copy the string.
mstore(0x40, add(add(result, 0x20), mload(result))) // Allocate memory.
}
}
/// @dev Helper to read a word from the base DN404 contract.
function _readWord(uint256 fnSelector, uint256 arg0, uint256 arg1)
private
view
returns (uint256 result)
{
address base = baseERC20();
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(0x00, fnSelector)
mstore(0x20, arg0)
mstore(0x40, arg1)
if iszero(
and(gt(returndatasize(), 0x1f), staticcall(gas(), base, 0x1c, 0x44, 0x00, 0x20))
) {
returndatacopy(m, 0x00, returndatasize())
revert(m, returndatasize())
}
mstore(0x40, m) // Restore the free memory pointer.
result := mload(0x00)
}
}
/// @dev Returns the calldata value at `offset`.
function _calldataload(uint256 offset) private pure returns (uint256 value) {
/// @solidity memory-safe-assembly
assembly {
value := calldataload(offset)
}
}
/// @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 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 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 Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
/// - For ERC20s, this implementation won't check that a token has code,
/// responsibility is delegated to the caller.
library SafeTransferLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ETH transfer has failed.
error ETHTransferFailed();
/// @dev The ERC20 `transferFrom` has failed.
error TransferFromFailed();
/// @dev The ERC20 `transfer` has failed.
error TransferFailed();
/// @dev The ERC20 `approve` has failed.
error ApproveFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;
/// @dev Suggested gas stipend for contract receiving ETH to perform a few
/// storage reads and writes, but low enough to prevent griefing.
uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ETH OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
//
// The regular variants:
// - Forwards all remaining gas to the target.
// - Reverts if the target reverts.
// - Reverts if the current contract has insufficient balance.
//
// The force variants:
// - Forwards with an optional gas stipend
// (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
// - If the target reverts, or if the gas stipend is exhausted,
// creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
// Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
// - Reverts if the current contract has insufficient balance.
//
// The try variants:
// - Forwards with a mandatory gas stipend.
// - Instead of reverting, returns whether the transfer succeeded.
/// @dev Sends `amount` (in wei) ETH to `to`.
function safeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Sends all the ETH in the current contract to `to`.
function safeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// Transfer all the ETH and check if it succeeded or not.
if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// forgefmt: disable-next-item
if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
}
}
/// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
function trySafeTransferAllETH(address to, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for
/// the current contract to manage.
function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends all of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have their entire balance approved for
/// the current contract to manage.
function safeTransferAllFrom(address token, address from, address to)
internal
returns (uint256 amount)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
// Read the balance, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransfer(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sends all of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransferAll(address token, address to) internal returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
mstore(0x20, address()) // Store the address of the current contract.
// Read the balance, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x14, to) // Store the `to` argument.
amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// Reverts upon failure.
function safeApprove(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
// Perform the approval, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
/// then retries the approval again (some tokens, e.g. USDT, requires this).
/// Reverts upon failure.
function safeApproveWithRetry(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
// Perform the approval, retrying upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x34, 0) // Store 0 for the `amount`.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
mstore(0x34, amount) // Store back the original `amount`.
// Retry the approval, reverting upon failure.
if iszero(
and(
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Returns the amount of ERC20 `token` owned by `account`.
/// Returns zero if the `token` does not exist.
function balanceOf(address token, address account) internal view returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, account) // Store the `account` argument.
mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
amount :=
mul(
mload(0x20),
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
)
)
}
}
}{
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [],
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint192","name":"publicPrice_","type":"uint192"},{"internalType":"uint96","name":"initialTokenSupply","type":"uint96"},{"internalType":"address","name":"initialSupplyOwner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"DNAlreadyInitialized","type":"error"},{"inputs":[],"name":"FnSelectorNotRecognized","type":"error"},{"inputs":[],"name":"InsufficientAllowance","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidMint","type":"error"},{"inputs":[],"name":"InvalidPrice","type":"error"},{"inputs":[],"name":"InvalidProof","type":"error"},{"inputs":[],"name":"LinkMirrorContractFailed","type":"error"},{"inputs":[],"name":"MirrorAddressIsZero","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"NotLive","type":"error"},{"inputs":[],"name":"SenderNotMirror","type":"error"},{"inputs":[],"name":"TokenDoesNotExist","type":"error"},{"inputs":[],"name":"TotalSupplyOverflow","type":"error"},{"inputs":[],"name":"TotalSupplyReached","type":"error"},{"inputs":[],"name":"TransferCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"UnitIsZero","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","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":"target","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"SkipNFTSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"MAX_PER_WALLET","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SUPPLY","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"recipients","type":"address[]"},{"internalType":"uint256","name":"nftAmount","type":"uint256"}],"name":"airdrop","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"freeMintsUsed","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"getSkipNFT","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"live","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nftAmount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"mirrorERC721","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","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":"publicPrice","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"string","name":"baseURI_","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint192","name":"publicPrice_","type":"uint192"}],"name":"setPrices","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"skipNFT","type":"bool"}],"name":"setSkipNFT","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"toggleLive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"result","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalMinted","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60806040523480156200001157600080fd5b5060405162004175380380620041758339810160408190526200003491620004c2565b6200003f33620000dd565b60006200004d868262000610565b5060016200005c858262000610565b50600380546001600160c01b0319166001600160c01b03851617905560405160009033906200008b90620003d7565b6001600160a01b039091168152602001604051809103906000f080158015620000b8573d6000803e3d6000fd5b509050620000d16001600160601b038416838362000119565b505050505050620006dc565b6001600160a01b0316638b78c6d8198190558060007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a350565b68a20d6e21d0e52553088054640100000000900463ffffffff16156200015257604051633ab534b960e21b815260040160405180910390fd5b6001600160a01b0382166200017a576040516339a84a7b60e01b815260040160405180910390fd5b630f4599e560005233602052602060006024601c6000865af160016000511416620001ad5763d125259c6000526004601cfd5b805463ffffffff60201b19166401000000001781556001810180546001600160a01b0384166001600160a01b03199091161790558315620002d5576001600160a01b0383166200021057604051633a954ecd60e21b815260040160405180910390fd5b6001600160601b03841163fffffffe670de0b6b3a764000086041117156200024b5760405163e5cfe95760e01b815260040160405180910390fd5b80546001600160a01b0316600160a01b6001600160601b0386160217815560006200027684620002db565b80546001600160601b038716600160a01b026001600160a01b0391821617825560008781529192508516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602082a3620002d384600162000346565b505b50505050565b6001600160a01b038116600090815268a20d6e21d0e525531360205260408120805490916b01000000000000000000000090910460011690036200034157805460ff60581b19166b01000000000000000000000060ff843b151560020260011716021781555b919050565b60006200035383620002db565b80549091506b01000000000000000000000090046002161515821515146200039f57805460ff6b01000000000000000000000080830482166002189091160260ff60581b199091161781555b8115156000528260601b60601c7fb5a1de456fff688115a4f75380060c23c8532d14ff85f687cc871456d642039360206000a2505050565b610da080620033d583390190565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200040d57600080fd5b81516001600160401b03808211156200042a576200042a620003e5565b604051601f8301601f19908116603f01168101908282118183101715620004555762000455620003e5565b816040528381526020925086838588010111156200047257600080fd5b600091505b8382101562000496578582018301518183018401529082019062000477565b600093810190920192909252949350505050565b80516001600160a01b03811681146200034157600080fd5b600080600080600060a08688031215620004db57600080fd5b85516001600160401b0380821115620004f357600080fd5b6200050189838a01620003fb565b965060208801519150808211156200051857600080fd5b506200052788828901620003fb565b604088015190955090506001600160c01b03811681146200054757600080fd5b60608701519093506001600160601b03811681146200056557600080fd5b91506200057560808701620004aa565b90509295509295909350565b600181811c908216806200059657607f821691505b602082108103620005b757634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200060b57600081815260208120601f850160051c81016020861015620005e65750805b601f850160051c820191505b818110156200060757828155600101620005f2565b5050505b505050565b81516001600160401b038111156200062c576200062c620003e5565b62000644816200063d845462000581565b84620005bd565b602080601f8311600181146200067c5760008415620006635750858301515b600019600386901b1c1916600185901b17855562000607565b600085815260208120601f198616915b82811015620006ad578886015182559484019460019091019084016200068c565b5085821015620006cc5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b612ce980620006ec6000396000f3fe6080604052600436106101e75760003560e01c8063715018a611610102578063a945bf8011610095578063ec0d755711610064578063ec0d7557146109d3578063f04e283e146109e8578063f2fde38b146109fb578063fee81cf414610a0e576101f9565b8063a945bf801461091c578063c204642c14610954578063c87b56dd14610974578063dd62ed3e14610994576101f9565b806395d89b41116100d157806395d89b41146108b0578063a0712d68146108c5578063a2309ff8146108d8578063a9059cbb146108fc576101f9565b8063715018a61461084e5780638da5cb5b14610856578063930e19c91461086f578063957aa58c1461088f576101f9565b80632a6a935d1161017a5780634ef41efc116101495780634ef41efc1461079e57806354d1f13d146107d857806355f804b3146107e057806370a0823114610800576101f9565b80632a6a935d14610737578063313ce5671461075757806332cb6b0c146107735780633ccfd60b14610789576101f9565b806323b872dd116101b657806323b872dd146106bc57806325692962146106dc5780632709c7a1146106e4578063274e430b14610717576101f9565b806306fdde0314610600578063095ea7b31461062b5780630f2cdd6c1461065b57806318160ddd14610685576101f9565b366101f95734156101f757600080fd5b005b68a20d6e21d0e525530860003560e01c63e5eb36c88190036102775760018201546001600160a01b031633146102425760405163ce5a776b60e01b815260040160405180910390fd5b608436101561025057600080fd5b60043560243560443560643561026884848484610a41565b6102726001610db9565b505050505b8063813500fc036102f95760018201546001600160a01b031633146102af5760405163ce5a776b60e01b815260040160405180910390fd5b60643610156102bd57600080fd5b6004356028818152604435601481905268a20d6e21d0e525530b60009081526048812092526024351515918290556102f56001610db9565b5050505b8063e985e9c50361036f5760018201546001600160a01b031633146103315760405163ce5a776b60e01b815260040160405180910390fd5b604436101561033f57600080fd5b60243560288181526004356014819052600385016000908152604881209252905490919061036c90610db9565b50505b80636352211e036103d45760018201546001600160a01b031633146103a75760405163ce5a776b60e01b815260040160405180910390fd5b60243610156103b557600080fd5b6004356103d26103c482610dc3565b6001600160a01b0316610db9565b505b8063243598790361042b5760018201546001600160a01b0316331461040c5760405163ce5a776b60e01b815260040160405180910390fd5b602436101561041a57600080fd5b6004356104296103c482610dfa565b505b8063d10b6e0c0361048c5760018201546001600160a01b031633146104635760405163ce5a776b60e01b815260040160405180910390fd5b606436101561047157600080fd5b6004356024356044356104886103c4848484610e56565b5050505b8063081812fc036104e35760018201546001600160a01b031633146104c45760405163ce5a776b60e01b815260040160405180910390fd5b60243610156104d257600080fd5b6004356104e16103c482610f4d565b505b8063f5b100ea0361056b5760018201546001600160a01b0316331461051b5760405163ce5a776b60e01b815260040160405180910390fd5b602436101561052957600080fd5b600435610569610564826001600160a01b0316600090815268a20d6e21d0e5255313602052604090205463ffffffff600160801b9091041690565b610db9565b505b8063e2c79281036105d25760018201546001600160a01b031633146105a35760405163ce5a776b60e01b815260040160405180910390fd5b60043610156105b157600080fd5b68a20d6e21d0e5255308546105d290600160801b900463ffffffff16610db9565b8063b7a94eb8036105e7576105e76001610db9565b604051631e085ca760e11b815260040160405180910390fd5b34801561060c57600080fd5b50610615610f99565b6040516106229190612785565b60405180910390f35b34801561063757600080fd5b5061064b6106463660046127cf565b61102b565b6040519015158152602001610622565b34801561066757600080fd5b50610670603281565b60405163ffffffff9091168152602001610622565b34801561069157600080fd5b5068a20d6e21d0e525530854600160a01b90046001600160601b03165b604051908152602001610622565b3480156106c857600080fd5b5061064b6106d73660046127f9565b611041565b6101f76110af565b3480156106f057600080fd5b506106706106ff366004612835565b60046020526000908152604090205463ffffffff1681565b34801561072357600080fd5b5061064b610732366004612835565b6110ff565b34801561074357600080fd5b5061064b610752366004612850565b61114e565b34801561076357600080fd5b5060405160128152602001610622565b34801561077f57600080fd5b506106706110e181565b34801561079557600080fd5b506101f7611162565b3480156107aa57600080fd5b5068a20d6e21d0e5255309546001600160a01b03165b6040516001600160a01b039091168152602001610622565b6101f7611175565b3480156107ec57600080fd5b506101f76107fb366004612872565b6111b1565b34801561080c57600080fd5b506106ae61081b366004612835565b6001600160a01b0316600090815268a20d6e21d0e52553136020526040902054600160a01b90046001600160601b031690565b6101f76111cb565b34801561086257600080fd5b50638b78c6d819546107c0565b34801561087b57600080fd5b506101f761088a3660046128e4565b6111dd565b34801561089b57600080fd5b5060035461064b90600160e01b900460ff1681565b3480156108bc57600080fd5b50610615611207565b6101f76108d336600461290d565b611216565b3480156108e457600080fd5b5060035461067090600160c01b900463ffffffff1681565b34801561090857600080fd5b5061064b6109173660046127cf565b6114b8565b34801561092857600080fd5b5060035461093c906001600160c01b031681565b6040516001600160c01b039091168152602001610622565b34801561096057600080fd5b506101f761096f366004612926565b6114c5565b34801561098057600080fd5b5061061561098f36600461290d565b611591565b3480156109a057600080fd5b506106ae6109af3660046129a1565b602890815260149190915268a20d6e21d0e525530f60009081526048812091525490565b3480156109df57600080fd5b506101f76115da565b6101f76109f6366004612835565b611603565b6101f7610a09366004612835565b611643565b348015610a1a57600080fd5b506106ae610a29366004612835565b63389a75e1600c908152600091909152602090205490565b68a20d6e21d0e52553086001600160a01b038416610a7257604051633a954ecd60e21b815260040160405180910390fd5b600a8101600282016000610abb83610a97600160201b891089025b6000190160011b90565b60008160031c8360601b0180546007841660051b1c63ffffffff1691505092915050565b63ffffffff1681526020810191909152604001600020546001600160a01b03878116911614610afc5760405162a1148160e81b815260040160405180910390fd5b856001600160a01b0316836001600160a01b031614610b72576028838152601487905260038301600090815260488120915254600003610b725760008481526004830160205260409020546001600160a01b03848116911614610b7257604051632ce44b5f60e11b815260040160405180910390fd5b6000610b7d8761166a565b90506000610b8a8761166a565b8254909150670de0b6b3a764000090600160a01b90046001600160601b031680821115610bca57604051631e9acf1760e31b815260040160405180910390fd5b83546001600160601b03918390038216600160a01b9081026001600160a01b0392831617865584548181048416850190931602918116919091178355891660009081526008860160208190526040909120610c3c600588018a60609190911b600882901c0154600160ff9092161c1690565b15610c7a576005870160601b60088a901c018054600160ff8c161b191690556000898152600488016020526040902080546001600160a01b03191690555b845463ffffffff60801b198116600160801b9182900463ffffffff90811660001901808216909302919091178755606083901b631fffffff600384901c16015460009260e060059190911b161c1690506000610ce0886000198d01600190811b01610a97565b9050610cf3838263ffffffff16846116d8565b610d0e8860001963ffffffff851601600190811b01836116d8565b5050835463ffffffff60801b198116600160801b9182900463ffffffff908116600181019091169092021785556001600160a01b038b166000908152602084905260409020610d5e90828c6116d8565b610d73878b610d6d888f611705565b84611798565b505050806000528760601b60601c8960601b60601c7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206000a3505050505050505050565b8060005260206000f35b6000610dce826117dd565b610deb5760405163677510db60e11b815260040160405180910390fd5b610df482610dfa565b92915050565b600068a20d6e21d0e525530868a20d6e21d0e525530a82610e2f68a20d6e21d0e5255312610a97600160201b88108802610a8d565b63ffffffff1681526020810191909152604001600020546001600160a01b03169392505050565b600068a20d6e21d0e525530868a20d6e21d0e525530a82610e8b68a20d6e21d0e5255312610a97600160201b89108902610a8d565b63ffffffff1681526020810191909152604001600020546001600160a01b03908116925083168214610ef1576028838152601483905260038201600090815260488120915254600003610ef1576040516367d9dca160e11b815260040160405180910390fd5b6000848152600482016020526040902080546001600160a01b0319166001600160a01b0387169081179091556005820160601b600886901c018054600160ff881690811b1991909116921515901b919091179055509392505050565b6000610f58826117dd565b610f755760405163677510db60e11b815260040160405180910390fd5b50600090815268a20d6e21d0e525530c60205260409020546001600160a01b031690565b606060008054610fa8906129d4565b80601f0160208091040260200160405190810160405280929190818152602001828054610fd4906129d4565b80156110215780601f10610ff657610100808354040283529160200191611021565b820191906000526020600020905b81548152906001019060200180831161100457829003601f168201915b5050505050905090565b60006110383384846117fa565b50600192915050565b336028908152601484905268a20d6e21d0e525530f60009081526048812091819052815490919060001981146110985780841115611092576040516313be252b60e01b815260040160405180910390fd5b83810382555b6110a386868661185c565b50600195945050505050565b60006202a30067ffffffffffffffff164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b6001600160a01b038116600090815268a20d6e21d0e5255313602052604081208054600160581b9004600116820361113b57823b5b9392505050565b54600160581b9004600216151592915050565b600061115a33836120cb565b506001919050565b61116a612149565b61117333612164565b565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b6111b9612149565b60026111c6828483612a6a565b505050565b6111d3612149565b6111736000612180565b6111e5612149565b600380546001600160c01b0319166001600160c01b0392909216919091179055565b606060018054610fa8906129d4565b600354600160e01b900460ff166112405760405163baf13b3f60e01b815260040160405180910390fd5b600354336000908152600460205260408120546001600160c01b03909216918391906112739063ffffffff166002612b41565b905060008163ffffffff16831161128b576000611295565b6112958284612b41565b60035490915034906112b6906001600160c01b031663ffffffff8416612b5e565b6001600160c01b0316146112dc5760405162bfc92160e01b815260040160405180910390fd5b33600090815268a20d6e21d0e5255313602052604081205486916affffffffffffffffffffff909116906113108383612b90565b905060328111156113345760405163201dc6f560e01b815260040160405180910390fd5b33600090815268a20d6e21d0e52553136020526040902080546affffffffffffffffffffff19166affffffffffffffffffffff8316179055600354889060009061138c908390600160c01b900463ffffffff16612b90565b90506110e18111156113b157604051637be9badb60e01b815260040160405180910390fd5b6003805463ffffffff808416600160c01b0263ffffffff60c01b19909216919091179091553360009081526004602052604081205490916113f491166002612b41565b90508063ffffffff168b111561144c57336000908152600460205260408120805483929061142990849063ffffffff16612ba3565b92506101000a81548163ffffffff021916908363ffffffff160217905550611490565b33600090815260046020526040812080548d929061147190849063ffffffff16612ba3565b92506101000a81548163ffffffff021916908363ffffffff1602179055505b6114ab336114a6670de0b6b3a76400008e612bc0565b6121be565b5050505050505050505050565b600061103833848461185c565b6114cd612149565b60035481906000906114ed908390600160c01b900463ffffffff16612b90565b90506110e181111561151257604051637be9badb60e01b815260040160405180910390fd5b6003805463ffffffff60c01b1916600160c01b63ffffffff84160217905560005b848110156115895761157786868381811061155057611550612bd7565b90506020020160208101906115659190612835565b6114a6670de0b6b3a764000087612bc0565b8061158181612bed565b915050611533565b505050505050565b6060600280546115a0906129d4565b1590506115d55760026115b28361255d565b6040516020016115c3929190612c06565b60405160208183030381529060405290505b919050565b6115e2612149565b6003805460ff60e01b198116600160e01b9182900460ff1615909102179055565b61160b612149565b63389a75e1600c52806000526020600c20805442111561163357636f5e88186000526004601cfd5b6000905561164081612180565b50565b61164b612149565b8060601b61166157637448fbae6000526004601cfd5b61164081612180565b6001600160a01b038116600090815268a20d6e21d0e52553136020526040812080549091600160581b90910460011690036115d55760006002833b15156116b1565b151590565b83546001929091029190911760ff16600160581b0260ff60581b1990911617825550919050565b8160031c8360601b016007831660051b815463ffffffff8482841c188116831b8218845550505050505050565b8154600160601b900463ffffffff1668a20d6e21d0e5255308600082900361179157805463ffffffff198116600163ffffffff928316019182169081178355855463ffffffff60601b1916600160601b82021786556000818152600284016020526040812080546001600160a01b0319166001600160a01b038816179055919350900361179157600080fd5b5092915050565b600183038060021c8560601b016003821660061b9150805467ffffffffffffffff8563ffffffff168560201b178083861c188216851b83188455505050505050505050565b6000806117e983610dfa565b6001600160a01b0316141592915050565b6028828152601484905268a20d6e21d0e525530f600090815260488120915281905560008181526001600160a01b0380841691908516907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590602090a3505050565b6001600160a01b03821661188357604051633a954ecd60e21b815260040160405180910390fd5b600061188e8461166a565b9050600061189b8461166a565b9050600068a20d6e21d0e52553089050611922604051806101a0016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600063ffffffff16815260200160008152602001600063ffffffff1681525090565b835463ffffffff600160801b808304821660808501528554041660a083015282546001600160601b03600160a01b91829004811660c08501529104166040820181905285111561198557604051631e9acf1760e31b815260040160405180910390fd5b6040810180518690039081905284546001600160601b03918216600160a01b9081026001600160a01b03928316178755855481810484168901606086018190529093160291161783556080810151611a02906119e6670de0b6b3a764000090565b8360400151816119f8576119f8612c9d565b0480821191030290565b81528254600160581b9004600216600003611a7157856001600160a01b0316876001600160a01b031603611a3e57805160808201510360a08201525b611a6b670de0b6b3a7640000826060015181611a5c57611a5c612c9d565b048260a0015180821191030290565b60208201525b80516020820151835463ffffffff600160801b80830482169093019390930360e085018190529092160263ffffffff60801b19909116178255600a82016001600160a01b03888116908816141560011615611cad576000611af38360800151611ae885600001518660200151808218908211021890565b808218908211021890565b90508015611cab57604080516080810182526000808252602080830182815283850183815260608086019081528651888152600589901b81018501885290819052908e9052908e90528101835286518590038752868101805186900390526001600160a01b03808e16835260088901909152838220908c168252929020909190611b7d888c611705565b63ffffffff166101408701525b6080860180516000190190819052600381901c606084901b015460009160051b60e0161c63ffffffff1663ffffffff169050611bcb828860a00151836116d8565b611bea86828961014001518a60a0018051809190600101815250611798565b83518181526020018452600881901c6005890160601b015460ff82161c60011615611c48576005880160601b600882901c018054600160ff84161b191690556000818152600489016020526040902080546001600160a01b03191690555b50600019840193600003600101611b8a576001870154611c729084906001600160a01b03166125a1565b5050506080830151865463ffffffff918216600160801b90810263ffffffff60801b1992831617895560a0860151885493160291161785555b505b6000611cc283602001518460000151016125f7565b8454600160601b900463ffffffff16610180850152835190915015611e5557606089901b6001176020828101919091526001600160a01b038a16600090815260088601909152604081206080850151855181036101008701819052895463ffffffff909116600160801b0263ffffffff60801b199091161789556101808601515b60001991909101600381901c606084901b0154909190600583901b60e0161c63ffffffff16611d758782600080611798565b8551602080880151600884901b1782520186526006890160601b600882901c018054600160ff84161b191690558415611dc257611dc2896009018380600101945063ffffffff16836116d8565b600881901c60058a0160601b015460ff82161c60011615611e16576005890160601b600882901c018054600160ff84161b19169055600081815260048a016020526040902080546001600160a01b03191690555b508661010001518203611d43578315611e505763ffffffff81166101808801819052885463ffffffff60601b1916600160601b9091021788555b505050505b60208301511561206457606088901b60208201528354600160201b900463ffffffff166101608401526001600160a01b03881660009081526008850160205260409020611ea2868a611705565b63ffffffff166101408501526000670de0b6b3a76400008560c0015181611ecb57611ecb612c9d565b60a08701516020880151810161012089018190528a5463ffffffff60801b1916600160801b63ffffffff92831602178b55895493909204935091600160401b9004165b600087610180015163ffffffff168263ffffffff1614611f5e576009890160601b631fffffff600384901c160154600183019260e060059190911b161c63ffffffff1663ffffffff169050611fc0565b506101608701515b611f7787600019830160011b610a97565b63ffffffff1615611fac57611fa5611f998a600601836001018760010161263e565b85811180159091021790565b9050611f66565b600181018481118015909102176101608901525b600881901c60068a0160601b018054600160ff84161b8019909116179055611fe98584836116d8565b61200087828a610140015186806001019750611798565b8551602080880151600884901b178252018652508661012001518203611f0e57875461016088015163ffffffff908116600160201b0267ffffffff000000001991909316600160401b02166bffffffffffffffff0000000019909116171787555050505b604081015151156120885760018401546120889082906001600160a01b0316612725565b5050846000528560601b60601c8760601b60601c7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206000a350505050505050565b60006120d68361166a565b8054909150600160581b900460021615158215151461211157805460ff600160581b80830482166002189091160260ff60581b199091161781555b8115156000528260601b60601c7fb5a1de456fff688115a4f75380060c23c8532d14ff85f687cc871456d642039360206000a2505050565b638b78c6d819543314611173576382b429006000526004601cfd5b60003860003847855af16116405763b12d13eb6000526004601cfd5b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b6001600160a01b0382166121e557604051633a954ecd60e21b815260040160405180910390fd5b60006121f08361166a565b604080518082019091526000808252602082015281546001600160601b03600160a01b80830482168701918216026001600160a01b0390921691909117835591925068a20d6e21d0e525530891670de0b6b3a7640000810482525081546001600160601b03600160a01b80830482168701918216026001600160a01b03909216919091178355600090816122a76116ac83600063fffffffe670de0b6b3a76400008304116001600160601b03831117151592915050565b90508682108117156122cc5760405163e5cfe95760e01b815260040160405180910390fd5b508454670de0b6b3a76400009091049150600160581b9004600216600003612520576001600160a01b03861660009081526008840160205260408120855484519192600a870192600160801b90920463ffffffff169161233290838103908410026125f7565b905080604001515160001461251b5760608a901b6020820152604081015151875463ffffffff600160801b80830482169093018116830263ffffffff60801b19928316178a5588518b5491169092029116178855612390888b611705565b63ffffffff90811660208801528754600160401b8104821691600160601b8204811691600160201b9004165b60008263ffffffff168463ffffffff16146124075760098b0160601b631fffffff600386901c160154600185019460e060059190911b161c63ffffffff1663ffffffff169050612460565b50805b61241b87600019830160011b610a97565b63ffffffff16156124505761244961243d8c600601836001018c60010161263e565b8a811180159091021790565b905061240a565b6001810189811180159091021791505b600881901c60068c0160601b018054600160ff84161b80199091161790556124898887836116d8565b61249f87828c6020015189806001019a50611798565b8451602080870151600884901b17825201855250885185036123bc57895463ffffffff848116600160401b026bffffffff000000000000000019918416600160201b02919091166bffffffffffffffff000000001990921691909117178a5560018a01546125179085906001600160a01b0316612725565b5050505b505050505b60008581526001600160a01b038716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602082a3505050505050565b60606080604051019050602081016040526000815280600019835b928101926030600a8206018453600a900480612578575050819003601f19909101908152919050565b6060820151805160051b60840160808203915063144027d38252836020015160208301528360400151604083015260608083015260208282601c85016000875af16001835114166125f157600082fd5b50505050565b61261b60405180606001604052806000815260200160008152602001606081525090565b604051828152806020018360051b81016040528183604001528083525050919050565b6000801990508360601b8360081c81018054198560ff161c8560ff161b80612693578460081c83015b600183019250825419915080831182171561266757808311156126915760ff86191691821b90911c905b505b801561271b5782820360081b7e1f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405821960010183166fffffffffffffffffffffffffffffffff811160071b81811c67ffffffffffffffff1060061b1781811c63ffffffff1060051b1790811c63d76453e004601f169190911a171785811015878210176000031793505b5050509392505050565b60408201516040810363263c69d68152602080820152815160051b604401915060208183601c84016000875af16001825114166125f157600081fd5b60005b8381101561277c578181015183820152602001612764565b50506000910152565b60208152600082518060208401526127a4816040850160208701612761565b601f01601f19169190910160400192915050565b80356001600160a01b03811681146115d557600080fd5b600080604083850312156127e257600080fd5b6127eb836127b8565b946020939093013593505050565b60008060006060848603121561280e57600080fd5b612817846127b8565b9250612825602085016127b8565b9150604084013590509250925092565b60006020828403121561284757600080fd5b611134826127b8565b60006020828403121561286257600080fd5b8135801515811461113457600080fd5b6000806020838503121561288557600080fd5b823567ffffffffffffffff8082111561289d57600080fd5b818501915085601f8301126128b157600080fd5b8135818111156128c057600080fd5b8660208285010111156128d257600080fd5b60209290920196919550909350505050565b6000602082840312156128f657600080fd5b81356001600160c01b038116811461113457600080fd5b60006020828403121561291f57600080fd5b5035919050565b60008060006040848603121561293b57600080fd5b833567ffffffffffffffff8082111561295357600080fd5b818601915086601f83011261296757600080fd5b81358181111561297657600080fd5b8760208260051b850101111561298b57600080fd5b6020928301989097509590910135949350505050565b600080604083850312156129b457600080fd5b6129bd836127b8565b91506129cb602084016127b8565b90509250929050565b600181811c908216806129e857607f821691505b602082108103612a0857634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052604160045260246000fd5b601f8211156111c657600081815260208120601f850160051c81016020861015612a4b5750805b601f850160051c820191505b8181101561158957828155600101612a57565b67ffffffffffffffff831115612a8257612a82612a0e565b612a9683612a9083546129d4565b83612a24565b6000601f841160018114612aca5760008515612ab25750838201355b600019600387901b1c1916600186901b178355612b24565b600083815260209020601f19861690835b82811015612afb5786850135825560209485019460019092019101612adb565b5086821015612b185760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b634e487b7160e01b600052601160045260246000fd5b63ffffffff82811682821603908082111561179157611791612b2b565b6001600160c01b03828116828216818102831692918115828504821417612b8757612b87612b2b565b50505092915050565b80820180821115610df457610df4612b2b565b63ffffffff81811683821601908082111561179157611791612b2b565b8082028115828204841417610df457610df4612b2b565b634e487b7160e01b600052603260045260246000fd5b600060018201612bff57612bff612b2b565b5060010190565b6000808454612c14816129d4565b60018281168015612c2c5760018114612c4157612c70565b60ff1984168752821515830287019450612c70565b8860005260208060002060005b85811015612c675781548a820152908401908201612c4e565b50505082870194505b505050508351612c84818360208801612761565b64173539b7b760d91b9101908152600501949350505050565b634e487b7160e01b600052601260045260246000fdfea2646970667358221220764b1f7ff93e90b0fb54b316bcdce374d778d713bd8bc4d91700cce57f581f5a64736f6c63430008110033608060405234801561001057600080fd5b50604051610da0380380610da083398101604081905261002f9161005c565b683602298b8c10b0123180546001600160a01b0319166001600160a01b039290921691909117905561008c565b60006020828403121561006e57600080fd5b81516001600160a01b038116811461008557600080fd5b9392505050565b610d058061009b6000396000f3fe60806040526004361061010d5760003560e01c80636cef16e61161009557806397e5311c1161006457806397e5311c146104f1578063a22cb46514610506578063b88d4fde14610526578063c87b56dd14610546578063e985e9c5146105665761011f565b80636cef16e61461048157806370a08231146104965780638da5cb5b146104b657806395d89b41146104dc5761011f565b806318160ddd116100dc57806318160ddd146103eb57806323b872dd1461040e578063243598791461042e57806342842e0e1461044e5780636352211e146104615761011f565b806301ffc9a71461031f57806306fdde0314610371578063081812fc14610393578063095ea7b3146103cb5761011f565b3661011f57341561011d57600080fd5b005b683602298b8c10b0123060003560e01c63263c69d68190036101d55781546001600160a01b0316331461016557604051631b1e598960e11b815260040160405180910390fd5b600435602401602081033560051b81015b8082146101c85781358060601c816001168260a01b60a81c811583028284027fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600038a4505050816020019150610176565b5050600160005260206000f35b8063144027d30361026b5781546001600160a01b0316331461020a57604051631b1e598960e11b815260040160405180910390fd5b600435602435604435602401602081033560051b81015b80821461025c57813583857fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600038a4816020019150610221565b50505050600160005260206000f35b80630f4599e5036103065760018201546001600160a01b0316156102c15760018201546001600160a01b03166004356001600160a01b0316146102c1576040516362cf623d60e11b815260040160405180910390fd5b81546001600160a01b0316156102ea57604051635fb2b52360e11b815260040160405180910390fd5b81546001600160a01b0319163317825560016000908152602090f35b604051631e085ca760e11b815260040160405180910390fd5b34801561032b57600080fd5b5061035c61033a366004610a90565b6301ffc9a760e09190911c9081146380ac58cd821417635b5e139f9091141790565b60405190151581526020015b60405180910390f35b34801561037d57600080fd5b50610386610586565b6040516103689190610ac1565b34801561039f57600080fd5b506103b36103ae366004610b0f565b61059c565b6040516001600160a01b039091168152602001610368565b3480156103d757600080fd5b5061011d6103e6366004610b44565b6105b4565b3480156103f757600080fd5b5061040061063a565b604051908152602001610368565b34801561041a57600080fd5b5061011d610429366004610b6e565b61064c565b34801561043a57600080fd5b506103b3610449366004610b0f565b6106db565b61011d61045c366004610b6e565b6106ed565b34801561046d57600080fd5b506103b361047c366004610b0f565b61071f565b34801561048d57600080fd5b5061011d610731565b3480156104a257600080fd5b506104006104b1366004610baa565b6107e4565b3480156104c257600080fd5b50683602298b8c10b01232546001600160a01b03166103b3565b3480156104e857600080fd5b506103866107ff565b3480156104fd57600080fd5b506103b3610810565b34801561051257600080fd5b5061011d610521366004610bc5565b610845565b34801561053257600080fd5b5061011d610541366004610c01565b6108c8565b34801561055257600080fd5b50610386610561366004610b0f565b610923565b34801561057257600080fd5b5061035c610581366004610c9c565b610933565b60606105976306fdde03600061095f565b905090565b60006105ae63081812fc8360006109bc565b92915050565b60006105be610810565b90508260601b60601c925060405163d10b6e0c600052836020528260405233606052602060006064601c34865af1601f3d11166105fe573d6000823e3d81fd5b806040525060006060528183600c5160601c7f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600038a4505050565b600061059763e2c792816000806109bc565b6000610656610810565b90508360601b60601c93508260601b60601c925060405163e5eb36c881528460208201528360408201528260608201523360808201526020816084601c840134865af16001825114166106ac573d6000823e3d81fd5b508183857fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600038a450505050565b60006105ae63243598798360006109bc565b6106f883838361064c565b813b1561071a5761071a83838360405180602001604052806000815250610a04565b505050565b60006105ae636352211e8360006109bc565b60008061073c610810565b9050638da5cb5b600052602060006004601c845afa601f3d11161561076457600c5160601c91505b683602298b8c10b0123254683602298b8c10b01230906001600160a01b0390811690841681146107de576002820180546001600160a01b0319166001600160a01b0386811691821790925560405190918316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35b50505050565b60006105ae63f5b100ea836001600160a01b031660006109bc565b60606105976395d89b41600061095f565b683602298b8c10b01230546001600160a01b03168061084257604051632d9523d760e11b815260040160405180910390fd5b90565b600061084f610810565b90508260601b60601c925060405163813500fc6000528360205282151560405233606052602060006064601c34865af160016000511416610893573d6000823e3d81fd5b83337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160206040a36040525050600060605250565b6108d385858561064c565b833b1561091c5761091c85858585858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610a0492505050565b5050505050565b60606105ae63c87b56dd8361095f565b600061095663e985e9c5846001600160a01b0316846001600160a01b03166109bc565b15159392505050565b6060600061096b610810565b9050604051915083600052826020526000806024601c845afa610991573d6000833e3d82fd5b60206000803e6020600051833e8151602060005101602084013e815160208301016040525092915050565b6000806109c7610810565b9050604051856000528460205283604052602060006044601c855afa601f3d11166109f5573d6000823e3d81fd5b60405250506000519392505050565b60405163150b7a028082523360208301528560601b60601c604083015283606083015260808083015282518060a08401528015610a4b578060c08401826020870160045afa505b60208360a48301601c860160008a5af1610a6e573d15610a6e573d6000843e3d83fd5b508060e01b825114610a885763d1a57ed66000526004601cfd5b505050505050565b600060208284031215610aa257600080fd5b81356001600160e01b031981168114610aba57600080fd5b9392505050565b600060208083528351808285015260005b81811015610aee57858101830151858201604001528201610ad2565b506000604082860101526040601f19601f8301168501019250505092915050565b600060208284031215610b2157600080fd5b5035919050565b80356001600160a01b0381168114610b3f57600080fd5b919050565b60008060408385031215610b5757600080fd5b610b6083610b28565b946020939093013593505050565b600080600060608486031215610b8357600080fd5b610b8c84610b28565b9250610b9a60208501610b28565b9150604084013590509250925092565b600060208284031215610bbc57600080fd5b610aba82610b28565b60008060408385031215610bd857600080fd5b610be183610b28565b915060208301358015158114610bf657600080fd5b809150509250929050565b600080600080600060808688031215610c1957600080fd5b610c2286610b28565b9450610c3060208701610b28565b935060408601359250606086013567ffffffffffffffff80821115610c5457600080fd5b818801915088601f830112610c6857600080fd5b813581811115610c7757600080fd5b896020828501011115610c8957600080fd5b9699959850939650602001949392505050565b60008060408385031215610caf57600080fd5b610cb883610b28565b9150610cc660208401610b28565b9050925092905056fea2646970667358221220ad975947cfce7eccb20597078a77b649a26c7914558fdad9b60e30d088a9224b64736f6c6343000811003300000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d4577ccde1ea4096429ac258fe78b25cd4a6518d000000000000000000000000000000000000000000000000000000000000000e34303420416c7465726174696f6e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006343034414c540000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x6080604052600436106101e75760003560e01c8063715018a611610102578063a945bf8011610095578063ec0d755711610064578063ec0d7557146109d3578063f04e283e146109e8578063f2fde38b146109fb578063fee81cf414610a0e576101f9565b8063a945bf801461091c578063c204642c14610954578063c87b56dd14610974578063dd62ed3e14610994576101f9565b806395d89b41116100d157806395d89b41146108b0578063a0712d68146108c5578063a2309ff8146108d8578063a9059cbb146108fc576101f9565b8063715018a61461084e5780638da5cb5b14610856578063930e19c91461086f578063957aa58c1461088f576101f9565b80632a6a935d1161017a5780634ef41efc116101495780634ef41efc1461079e57806354d1f13d146107d857806355f804b3146107e057806370a0823114610800576101f9565b80632a6a935d14610737578063313ce5671461075757806332cb6b0c146107735780633ccfd60b14610789576101f9565b806323b872dd116101b657806323b872dd146106bc57806325692962146106dc5780632709c7a1146106e4578063274e430b14610717576101f9565b806306fdde0314610600578063095ea7b31461062b5780630f2cdd6c1461065b57806318160ddd14610685576101f9565b366101f95734156101f757600080fd5b005b68a20d6e21d0e525530860003560e01c63e5eb36c88190036102775760018201546001600160a01b031633146102425760405163ce5a776b60e01b815260040160405180910390fd5b608436101561025057600080fd5b60043560243560443560643561026884848484610a41565b6102726001610db9565b505050505b8063813500fc036102f95760018201546001600160a01b031633146102af5760405163ce5a776b60e01b815260040160405180910390fd5b60643610156102bd57600080fd5b6004356028818152604435601481905268a20d6e21d0e525530b60009081526048812092526024351515918290556102f56001610db9565b5050505b8063e985e9c50361036f5760018201546001600160a01b031633146103315760405163ce5a776b60e01b815260040160405180910390fd5b604436101561033f57600080fd5b60243560288181526004356014819052600385016000908152604881209252905490919061036c90610db9565b50505b80636352211e036103d45760018201546001600160a01b031633146103a75760405163ce5a776b60e01b815260040160405180910390fd5b60243610156103b557600080fd5b6004356103d26103c482610dc3565b6001600160a01b0316610db9565b505b8063243598790361042b5760018201546001600160a01b0316331461040c5760405163ce5a776b60e01b815260040160405180910390fd5b602436101561041a57600080fd5b6004356104296103c482610dfa565b505b8063d10b6e0c0361048c5760018201546001600160a01b031633146104635760405163ce5a776b60e01b815260040160405180910390fd5b606436101561047157600080fd5b6004356024356044356104886103c4848484610e56565b5050505b8063081812fc036104e35760018201546001600160a01b031633146104c45760405163ce5a776b60e01b815260040160405180910390fd5b60243610156104d257600080fd5b6004356104e16103c482610f4d565b505b8063f5b100ea0361056b5760018201546001600160a01b0316331461051b5760405163ce5a776b60e01b815260040160405180910390fd5b602436101561052957600080fd5b600435610569610564826001600160a01b0316600090815268a20d6e21d0e5255313602052604090205463ffffffff600160801b9091041690565b610db9565b505b8063e2c79281036105d25760018201546001600160a01b031633146105a35760405163ce5a776b60e01b815260040160405180910390fd5b60043610156105b157600080fd5b68a20d6e21d0e5255308546105d290600160801b900463ffffffff16610db9565b8063b7a94eb8036105e7576105e76001610db9565b604051631e085ca760e11b815260040160405180910390fd5b34801561060c57600080fd5b50610615610f99565b6040516106229190612785565b60405180910390f35b34801561063757600080fd5b5061064b6106463660046127cf565b61102b565b6040519015158152602001610622565b34801561066757600080fd5b50610670603281565b60405163ffffffff9091168152602001610622565b34801561069157600080fd5b5068a20d6e21d0e525530854600160a01b90046001600160601b03165b604051908152602001610622565b3480156106c857600080fd5b5061064b6106d73660046127f9565b611041565b6101f76110af565b3480156106f057600080fd5b506106706106ff366004612835565b60046020526000908152604090205463ffffffff1681565b34801561072357600080fd5b5061064b610732366004612835565b6110ff565b34801561074357600080fd5b5061064b610752366004612850565b61114e565b34801561076357600080fd5b5060405160128152602001610622565b34801561077f57600080fd5b506106706110e181565b34801561079557600080fd5b506101f7611162565b3480156107aa57600080fd5b5068a20d6e21d0e5255309546001600160a01b03165b6040516001600160a01b039091168152602001610622565b6101f7611175565b3480156107ec57600080fd5b506101f76107fb366004612872565b6111b1565b34801561080c57600080fd5b506106ae61081b366004612835565b6001600160a01b0316600090815268a20d6e21d0e52553136020526040902054600160a01b90046001600160601b031690565b6101f76111cb565b34801561086257600080fd5b50638b78c6d819546107c0565b34801561087b57600080fd5b506101f761088a3660046128e4565b6111dd565b34801561089b57600080fd5b5060035461064b90600160e01b900460ff1681565b3480156108bc57600080fd5b50610615611207565b6101f76108d336600461290d565b611216565b3480156108e457600080fd5b5060035461067090600160c01b900463ffffffff1681565b34801561090857600080fd5b5061064b6109173660046127cf565b6114b8565b34801561092857600080fd5b5060035461093c906001600160c01b031681565b6040516001600160c01b039091168152602001610622565b34801561096057600080fd5b506101f761096f366004612926565b6114c5565b34801561098057600080fd5b5061061561098f36600461290d565b611591565b3480156109a057600080fd5b506106ae6109af3660046129a1565b602890815260149190915268a20d6e21d0e525530f60009081526048812091525490565b3480156109df57600080fd5b506101f76115da565b6101f76109f6366004612835565b611603565b6101f7610a09366004612835565b611643565b348015610a1a57600080fd5b506106ae610a29366004612835565b63389a75e1600c908152600091909152602090205490565b68a20d6e21d0e52553086001600160a01b038416610a7257604051633a954ecd60e21b815260040160405180910390fd5b600a8101600282016000610abb83610a97600160201b891089025b6000190160011b90565b60008160031c8360601b0180546007841660051b1c63ffffffff1691505092915050565b63ffffffff1681526020810191909152604001600020546001600160a01b03878116911614610afc5760405162a1148160e81b815260040160405180910390fd5b856001600160a01b0316836001600160a01b031614610b72576028838152601487905260038301600090815260488120915254600003610b725760008481526004830160205260409020546001600160a01b03848116911614610b7257604051632ce44b5f60e11b815260040160405180910390fd5b6000610b7d8761166a565b90506000610b8a8761166a565b8254909150670de0b6b3a764000090600160a01b90046001600160601b031680821115610bca57604051631e9acf1760e31b815260040160405180910390fd5b83546001600160601b03918390038216600160a01b9081026001600160a01b0392831617865584548181048416850190931602918116919091178355891660009081526008860160208190526040909120610c3c600588018a60609190911b600882901c0154600160ff9092161c1690565b15610c7a576005870160601b60088a901c018054600160ff8c161b191690556000898152600488016020526040902080546001600160a01b03191690555b845463ffffffff60801b198116600160801b9182900463ffffffff90811660001901808216909302919091178755606083901b631fffffff600384901c16015460009260e060059190911b161c1690506000610ce0886000198d01600190811b01610a97565b9050610cf3838263ffffffff16846116d8565b610d0e8860001963ffffffff851601600190811b01836116d8565b5050835463ffffffff60801b198116600160801b9182900463ffffffff908116600181019091169092021785556001600160a01b038b166000908152602084905260409020610d5e90828c6116d8565b610d73878b610d6d888f611705565b84611798565b505050806000528760601b60601c8960601b60601c7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206000a3505050505050505050565b8060005260206000f35b6000610dce826117dd565b610deb5760405163677510db60e11b815260040160405180910390fd5b610df482610dfa565b92915050565b600068a20d6e21d0e525530868a20d6e21d0e525530a82610e2f68a20d6e21d0e5255312610a97600160201b88108802610a8d565b63ffffffff1681526020810191909152604001600020546001600160a01b03169392505050565b600068a20d6e21d0e525530868a20d6e21d0e525530a82610e8b68a20d6e21d0e5255312610a97600160201b89108902610a8d565b63ffffffff1681526020810191909152604001600020546001600160a01b03908116925083168214610ef1576028838152601483905260038201600090815260488120915254600003610ef1576040516367d9dca160e11b815260040160405180910390fd5b6000848152600482016020526040902080546001600160a01b0319166001600160a01b0387169081179091556005820160601b600886901c018054600160ff881690811b1991909116921515901b919091179055509392505050565b6000610f58826117dd565b610f755760405163677510db60e11b815260040160405180910390fd5b50600090815268a20d6e21d0e525530c60205260409020546001600160a01b031690565b606060008054610fa8906129d4565b80601f0160208091040260200160405190810160405280929190818152602001828054610fd4906129d4565b80156110215780601f10610ff657610100808354040283529160200191611021565b820191906000526020600020905b81548152906001019060200180831161100457829003601f168201915b5050505050905090565b60006110383384846117fa565b50600192915050565b336028908152601484905268a20d6e21d0e525530f60009081526048812091819052815490919060001981146110985780841115611092576040516313be252b60e01b815260040160405180910390fd5b83810382555b6110a386868661185c565b50600195945050505050565b60006202a30067ffffffffffffffff164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b6001600160a01b038116600090815268a20d6e21d0e5255313602052604081208054600160581b9004600116820361113b57823b5b9392505050565b54600160581b9004600216151592915050565b600061115a33836120cb565b506001919050565b61116a612149565b61117333612164565b565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b6111b9612149565b60026111c6828483612a6a565b505050565b6111d3612149565b6111736000612180565b6111e5612149565b600380546001600160c01b0319166001600160c01b0392909216919091179055565b606060018054610fa8906129d4565b600354600160e01b900460ff166112405760405163baf13b3f60e01b815260040160405180910390fd5b600354336000908152600460205260408120546001600160c01b03909216918391906112739063ffffffff166002612b41565b905060008163ffffffff16831161128b576000611295565b6112958284612b41565b60035490915034906112b6906001600160c01b031663ffffffff8416612b5e565b6001600160c01b0316146112dc5760405162bfc92160e01b815260040160405180910390fd5b33600090815268a20d6e21d0e5255313602052604081205486916affffffffffffffffffffff909116906113108383612b90565b905060328111156113345760405163201dc6f560e01b815260040160405180910390fd5b33600090815268a20d6e21d0e52553136020526040902080546affffffffffffffffffffff19166affffffffffffffffffffff8316179055600354889060009061138c908390600160c01b900463ffffffff16612b90565b90506110e18111156113b157604051637be9badb60e01b815260040160405180910390fd5b6003805463ffffffff808416600160c01b0263ffffffff60c01b19909216919091179091553360009081526004602052604081205490916113f491166002612b41565b90508063ffffffff168b111561144c57336000908152600460205260408120805483929061142990849063ffffffff16612ba3565b92506101000a81548163ffffffff021916908363ffffffff160217905550611490565b33600090815260046020526040812080548d929061147190849063ffffffff16612ba3565b92506101000a81548163ffffffff021916908363ffffffff1602179055505b6114ab336114a6670de0b6b3a76400008e612bc0565b6121be565b5050505050505050505050565b600061103833848461185c565b6114cd612149565b60035481906000906114ed908390600160c01b900463ffffffff16612b90565b90506110e181111561151257604051637be9badb60e01b815260040160405180910390fd5b6003805463ffffffff60c01b1916600160c01b63ffffffff84160217905560005b848110156115895761157786868381811061155057611550612bd7565b90506020020160208101906115659190612835565b6114a6670de0b6b3a764000087612bc0565b8061158181612bed565b915050611533565b505050505050565b6060600280546115a0906129d4565b1590506115d55760026115b28361255d565b6040516020016115c3929190612c06565b60405160208183030381529060405290505b919050565b6115e2612149565b6003805460ff60e01b198116600160e01b9182900460ff1615909102179055565b61160b612149565b63389a75e1600c52806000526020600c20805442111561163357636f5e88186000526004601cfd5b6000905561164081612180565b50565b61164b612149565b8060601b61166157637448fbae6000526004601cfd5b61164081612180565b6001600160a01b038116600090815268a20d6e21d0e52553136020526040812080549091600160581b90910460011690036115d55760006002833b15156116b1565b151590565b83546001929091029190911760ff16600160581b0260ff60581b1990911617825550919050565b8160031c8360601b016007831660051b815463ffffffff8482841c188116831b8218845550505050505050565b8154600160601b900463ffffffff1668a20d6e21d0e5255308600082900361179157805463ffffffff198116600163ffffffff928316019182169081178355855463ffffffff60601b1916600160601b82021786556000818152600284016020526040812080546001600160a01b0319166001600160a01b038816179055919350900361179157600080fd5b5092915050565b600183038060021c8560601b016003821660061b9150805467ffffffffffffffff8563ffffffff168560201b178083861c188216851b83188455505050505050505050565b6000806117e983610dfa565b6001600160a01b0316141592915050565b6028828152601484905268a20d6e21d0e525530f600090815260488120915281905560008181526001600160a01b0380841691908516907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590602090a3505050565b6001600160a01b03821661188357604051633a954ecd60e21b815260040160405180910390fd5b600061188e8461166a565b9050600061189b8461166a565b9050600068a20d6e21d0e52553089050611922604051806101a0016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600063ffffffff16815260200160008152602001600063ffffffff1681525090565b835463ffffffff600160801b808304821660808501528554041660a083015282546001600160601b03600160a01b91829004811660c08501529104166040820181905285111561198557604051631e9acf1760e31b815260040160405180910390fd5b6040810180518690039081905284546001600160601b03918216600160a01b9081026001600160a01b03928316178755855481810484168901606086018190529093160291161783556080810151611a02906119e6670de0b6b3a764000090565b8360400151816119f8576119f8612c9d565b0480821191030290565b81528254600160581b9004600216600003611a7157856001600160a01b0316876001600160a01b031603611a3e57805160808201510360a08201525b611a6b670de0b6b3a7640000826060015181611a5c57611a5c612c9d565b048260a0015180821191030290565b60208201525b80516020820151835463ffffffff600160801b80830482169093019390930360e085018190529092160263ffffffff60801b19909116178255600a82016001600160a01b03888116908816141560011615611cad576000611af38360800151611ae885600001518660200151808218908211021890565b808218908211021890565b90508015611cab57604080516080810182526000808252602080830182815283850183815260608086019081528651888152600589901b81018501885290819052908e9052908e90528101835286518590038752868101805186900390526001600160a01b03808e16835260088901909152838220908c168252929020909190611b7d888c611705565b63ffffffff166101408701525b6080860180516000190190819052600381901c606084901b015460009160051b60e0161c63ffffffff1663ffffffff169050611bcb828860a00151836116d8565b611bea86828961014001518a60a0018051809190600101815250611798565b83518181526020018452600881901c6005890160601b015460ff82161c60011615611c48576005880160601b600882901c018054600160ff84161b191690556000818152600489016020526040902080546001600160a01b03191690555b50600019840193600003600101611b8a576001870154611c729084906001600160a01b03166125a1565b5050506080830151865463ffffffff918216600160801b90810263ffffffff60801b1992831617895560a0860151885493160291161785555b505b6000611cc283602001518460000151016125f7565b8454600160601b900463ffffffff16610180850152835190915015611e5557606089901b6001176020828101919091526001600160a01b038a16600090815260088601909152604081206080850151855181036101008701819052895463ffffffff909116600160801b0263ffffffff60801b199091161789556101808601515b60001991909101600381901c606084901b0154909190600583901b60e0161c63ffffffff16611d758782600080611798565b8551602080880151600884901b1782520186526006890160601b600882901c018054600160ff84161b191690558415611dc257611dc2896009018380600101945063ffffffff16836116d8565b600881901c60058a0160601b015460ff82161c60011615611e16576005890160601b600882901c018054600160ff84161b19169055600081815260048a016020526040902080546001600160a01b03191690555b508661010001518203611d43578315611e505763ffffffff81166101808801819052885463ffffffff60601b1916600160601b9091021788555b505050505b60208301511561206457606088901b60208201528354600160201b900463ffffffff166101608401526001600160a01b03881660009081526008850160205260409020611ea2868a611705565b63ffffffff166101408501526000670de0b6b3a76400008560c0015181611ecb57611ecb612c9d565b60a08701516020880151810161012089018190528a5463ffffffff60801b1916600160801b63ffffffff92831602178b55895493909204935091600160401b9004165b600087610180015163ffffffff168263ffffffff1614611f5e576009890160601b631fffffff600384901c160154600183019260e060059190911b161c63ffffffff1663ffffffff169050611fc0565b506101608701515b611f7787600019830160011b610a97565b63ffffffff1615611fac57611fa5611f998a600601836001018760010161263e565b85811180159091021790565b9050611f66565b600181018481118015909102176101608901525b600881901c60068a0160601b018054600160ff84161b8019909116179055611fe98584836116d8565b61200087828a610140015186806001019750611798565b8551602080880151600884901b178252018652508661012001518203611f0e57875461016088015163ffffffff908116600160201b0267ffffffff000000001991909316600160401b02166bffffffffffffffff0000000019909116171787555050505b604081015151156120885760018401546120889082906001600160a01b0316612725565b5050846000528560601b60601c8760601b60601c7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206000a350505050505050565b60006120d68361166a565b8054909150600160581b900460021615158215151461211157805460ff600160581b80830482166002189091160260ff60581b199091161781555b8115156000528260601b60601c7fb5a1de456fff688115a4f75380060c23c8532d14ff85f687cc871456d642039360206000a2505050565b638b78c6d819543314611173576382b429006000526004601cfd5b60003860003847855af16116405763b12d13eb6000526004601cfd5b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b6001600160a01b0382166121e557604051633a954ecd60e21b815260040160405180910390fd5b60006121f08361166a565b604080518082019091526000808252602082015281546001600160601b03600160a01b80830482168701918216026001600160a01b0390921691909117835591925068a20d6e21d0e525530891670de0b6b3a7640000810482525081546001600160601b03600160a01b80830482168701918216026001600160a01b03909216919091178355600090816122a76116ac83600063fffffffe670de0b6b3a76400008304116001600160601b03831117151592915050565b90508682108117156122cc5760405163e5cfe95760e01b815260040160405180910390fd5b508454670de0b6b3a76400009091049150600160581b9004600216600003612520576001600160a01b03861660009081526008840160205260408120855484519192600a870192600160801b90920463ffffffff169161233290838103908410026125f7565b905080604001515160001461251b5760608a901b6020820152604081015151875463ffffffff600160801b80830482169093018116830263ffffffff60801b19928316178a5588518b5491169092029116178855612390888b611705565b63ffffffff90811660208801528754600160401b8104821691600160601b8204811691600160201b9004165b60008263ffffffff168463ffffffff16146124075760098b0160601b631fffffff600386901c160154600185019460e060059190911b161c63ffffffff1663ffffffff169050612460565b50805b61241b87600019830160011b610a97565b63ffffffff16156124505761244961243d8c600601836001018c60010161263e565b8a811180159091021790565b905061240a565b6001810189811180159091021791505b600881901c60068c0160601b018054600160ff84161b80199091161790556124898887836116d8565b61249f87828c6020015189806001019a50611798565b8451602080870151600884901b17825201855250885185036123bc57895463ffffffff848116600160401b026bffffffff000000000000000019918416600160201b02919091166bffffffffffffffff000000001990921691909117178a5560018a01546125179085906001600160a01b0316612725565b5050505b505050505b60008581526001600160a01b038716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602082a3505050505050565b60606080604051019050602081016040526000815280600019835b928101926030600a8206018453600a900480612578575050819003601f19909101908152919050565b6060820151805160051b60840160808203915063144027d38252836020015160208301528360400151604083015260608083015260208282601c85016000875af16001835114166125f157600082fd5b50505050565b61261b60405180606001604052806000815260200160008152602001606081525090565b604051828152806020018360051b81016040528183604001528083525050919050565b6000801990508360601b8360081c81018054198560ff161c8560ff161b80612693578460081c83015b600183019250825419915080831182171561266757808311156126915760ff86191691821b90911c905b505b801561271b5782820360081b7e1f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405821960010183166fffffffffffffffffffffffffffffffff811160071b81811c67ffffffffffffffff1060061b1781811c63ffffffff1060051b1790811c63d76453e004601f169190911a171785811015878210176000031793505b5050509392505050565b60408201516040810363263c69d68152602080820152815160051b604401915060208183601c84016000875af16001825114166125f157600081fd5b60005b8381101561277c578181015183820152602001612764565b50506000910152565b60208152600082518060208401526127a4816040850160208701612761565b601f01601f19169190910160400192915050565b80356001600160a01b03811681146115d557600080fd5b600080604083850312156127e257600080fd5b6127eb836127b8565b946020939093013593505050565b60008060006060848603121561280e57600080fd5b612817846127b8565b9250612825602085016127b8565b9150604084013590509250925092565b60006020828403121561284757600080fd5b611134826127b8565b60006020828403121561286257600080fd5b8135801515811461113457600080fd5b6000806020838503121561288557600080fd5b823567ffffffffffffffff8082111561289d57600080fd5b818501915085601f8301126128b157600080fd5b8135818111156128c057600080fd5b8660208285010111156128d257600080fd5b60209290920196919550909350505050565b6000602082840312156128f657600080fd5b81356001600160c01b038116811461113457600080fd5b60006020828403121561291f57600080fd5b5035919050565b60008060006040848603121561293b57600080fd5b833567ffffffffffffffff8082111561295357600080fd5b818601915086601f83011261296757600080fd5b81358181111561297657600080fd5b8760208260051b850101111561298b57600080fd5b6020928301989097509590910135949350505050565b600080604083850312156129b457600080fd5b6129bd836127b8565b91506129cb602084016127b8565b90509250929050565b600181811c908216806129e857607f821691505b602082108103612a0857634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052604160045260246000fd5b601f8211156111c657600081815260208120601f850160051c81016020861015612a4b5750805b601f850160051c820191505b8181101561158957828155600101612a57565b67ffffffffffffffff831115612a8257612a82612a0e565b612a9683612a9083546129d4565b83612a24565b6000601f841160018114612aca5760008515612ab25750838201355b600019600387901b1c1916600186901b178355612b24565b600083815260209020601f19861690835b82811015612afb5786850135825560209485019460019092019101612adb565b5086821015612b185760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b634e487b7160e01b600052601160045260246000fd5b63ffffffff82811682821603908082111561179157611791612b2b565b6001600160c01b03828116828216818102831692918115828504821417612b8757612b87612b2b565b50505092915050565b80820180821115610df457610df4612b2b565b63ffffffff81811683821601908082111561179157611791612b2b565b8082028115828204841417610df457610df4612b2b565b634e487b7160e01b600052603260045260246000fd5b600060018201612bff57612bff612b2b565b5060010190565b6000808454612c14816129d4565b60018281168015612c2c5760018114612c4157612c70565b60ff1984168752821515830287019450612c70565b8860005260208060002060005b85811015612c675781548a820152908401908201612c4e565b50505082870194505b505050508351612c84818360208801612761565b64173539b7b760d91b9101908152600501949350505050565b634e487b7160e01b600052601260045260246000fdfea2646970667358221220764b1f7ff93e90b0fb54b316bcdce374d778d713bd8bc4d91700cce57f581f5a64736f6c63430008110033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d4577ccde1ea4096429ac258fe78b25cd4a6518d000000000000000000000000000000000000000000000000000000000000000e34303420416c7465726174696f6e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006343034414c540000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : name_ (string): 404 Alteration
Arg [1] : symbol_ (string): 404ALT
Arg [2] : publicPrice_ (uint192): 5000000000000000
Arg [3] : initialTokenSupply (uint96): 0
Arg [4] : initialSupplyOwner (address): 0xD4577CcDE1eA4096429ac258Fe78b25Cd4A6518d
-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [1] : 00000000000000000000000000000000000000000000000000000000000000e0
Arg [2] : 0000000000000000000000000000000000000000000000000011c37937e08000
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [4] : 000000000000000000000000d4577ccde1ea4096429ac258fe78b25cd4a6518d
Arg [5] : 000000000000000000000000000000000000000000000000000000000000000e
Arg [6] : 34303420416c7465726174696f6e000000000000000000000000000000000000
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [8] : 343034414c540000000000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 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.