Source Code
Latest 25 from a total of 153 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Set Skip NFT | 19272003 | 666 days ago | IN | 0 ETH | 0.0027843 | ||||
| Set Skip NFT | 19271262 | 666 days ago | IN | 0 ETH | 0.00170741 | ||||
| Set Skip NFT | 19270873 | 666 days ago | IN | 0 ETH | 0.00146872 | ||||
| Set Skip NFT | 19270873 | 666 days ago | IN | 0 ETH | 0.00146872 | ||||
| Set Skip NFT | 19270873 | 666 days ago | IN | 0 ETH | 0.00146872 | ||||
| Set Skip NFT | 19270873 | 666 days ago | IN | 0 ETH | 0.00146872 | ||||
| Set Skip NFT | 19270873 | 666 days ago | IN | 0 ETH | 0.00146872 | ||||
| Set Skip NFT | 19270873 | 666 days ago | IN | 0 ETH | 0.00146872 | ||||
| Set Skip NFT | 19270873 | 666 days ago | IN | 0 ETH | 0.00146872 | ||||
| Set Skip NFT | 19270873 | 666 days ago | IN | 0 ETH | 0.00146872 | ||||
| Set Skip NFT | 19270873 | 666 days ago | IN | 0 ETH | 0.00146872 | ||||
| Set Skip NFT | 19270873 | 666 days ago | IN | 0 ETH | 0.00146872 | ||||
| Set Skip NFT | 19270840 | 666 days ago | IN | 0 ETH | 0.0015168 | ||||
| Set Skip NFT | 19270823 | 666 days ago | IN | 0 ETH | 0.00151494 | ||||
| Set Skip NFT | 19270731 | 666 days ago | IN | 0 ETH | 0.00178324 | ||||
| Set Skip NFT | 19270615 | 666 days ago | IN | 0 ETH | 0.00171992 | ||||
| Set Skip NFT | 19270542 | 666 days ago | IN | 0 ETH | 0.00198331 | ||||
| Set Skip NFT | 19270541 | 666 days ago | IN | 0 ETH | 0.00179318 | ||||
| Set Skip NFT | 19270537 | 666 days ago | IN | 0 ETH | 0.00209762 | ||||
| Set Skip NFT | 19270532 | 666 days ago | IN | 0 ETH | 0.00219503 | ||||
| Set Skip NFT | 19270496 | 666 days ago | IN | 0 ETH | 0.00187558 | ||||
| Set Skip NFT | 19270451 | 666 days ago | IN | 0 ETH | 0.00264937 | ||||
| Set Skip NFT | 19270449 | 666 days ago | IN | 0 ETH | 0.00246705 | ||||
| Set Skip NFT | 19270441 | 666 days ago | IN | 0 ETH | 0.001115 | ||||
| Set Skip NFT | 19270437 | 666 days ago | IN | 0 ETH | 0.00116961 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
Sheboshi
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "./lib/DN404/DN404.sol";
import "./lib/DN404/DN404Mirror.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.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";
import {MerkleProofLib} from "solady/src/utils/MerkleProofLib.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
interface IERC20 {
function balanceOf(address account) external view returns (uint256);
}
contract Sheboshi is
Initializable,
Ownable,
UUPSUpgradeable,
ReentrancyGuardUpgradeable,
DN404
{
event NameChanged(uint256 indexed tokenId, string name);
event ContractURIUpdated();
string private _name;
string private _symbol;
string private _baseURI;
uint256 public numMinted;
bytes32 private allowlistRoot;
uint256 public saleStartTime;
IERC20 public tokenLEASH;
bool public contractsApproval;
bool public reveal;
uint256 public maxSupply;
uint256 public mintPrice;
mapping(address => bool) claims;
uint8 public LEASHHolderMintLimit;
mapping(address => uint8) LEASHHoldermints;
error InvalidMint();
error InvalidPrice();
error TotalSupplyReached();
error SaleNotStarted();
error InvalidProof();
modifier isValidMint(uint256 amount) {
if (saleStartTime == 0) {
revert SaleNotStarted();
}
if (mintPrice * amount != msg.value) {
revert InvalidPrice();
}
if (numMinted + amount > maxSupply) {
revert TotalSupplyReached();
}
_;
}
function initialize(
string memory name_,
string memory symbol_,
uint96 initialTokenSupply,
bytes32 _allowlistRoot,
uint256 _maxSupply,
uint256 _mintPrice,
uint8 _LEASHHolderMintLimit
) public initializer {
_initializeOwner(msg.sender);
__ReentrancyGuard_init();
__UUPSUpgradeable_init();
_name = name_;
_symbol = symbol_;
allowlistRoot = _allowlistRoot;
maxSupply = _maxSupply;
mintPrice = _mintPrice;
LEASHHolderMintLimit = _LEASHHolderMintLimit;
contractsApproval = true;
address mirror = address(new DN404Mirror(msg.sender));
_initializeDN404(0, msg.sender, mirror);
_mint(msg.sender, initialTokenSupply * _unit());
}
function _authorizeUpgrade(
address _newImplementation
) internal override onlyOwner {}
function name() public view override returns (string memory) {
return _name;
}
function symbol() public view override returns (string memory) {
return _symbol;
}
function setBaseURI(string calldata baseURI_) public onlyOwner {
_baseURI = baseURI_;
}
function setReveal() public onlyOwner {
reveal = true;
}
function setLEASH(address addr) public onlyOwner {
tokenLEASH = IERC20(addr);
}
function startSale() public onlyOwner {
saleStartTime = block.timestamp;
}
function setAllowlistRoot(bytes32 _allowlistRoot) public onlyOwner {
allowlistRoot = _allowlistRoot;
}
function setMaxSupply(uint256 _maxSupply) public onlyOwner {
maxSupply = _maxSupply;
}
function setMintPrice(uint256 _mintPrice) public onlyOwner {
mintPrice = _mintPrice;
}
function setLEASHHolderMintLimit(uint8 _LEASHHolderMintLimit) public onlyOwner {
LEASHHolderMintLimit = _LEASHHolderMintLimit;
}
function tokenURI(
uint256 tokenId
) public view override returns (string memory result) {
if (_ownerAt(tokenId) == address(0)) revert TokenDoesNotExist();
return !reveal ? _baseURI : string.concat(_baseURI, LibString.toString(tokenId));
}
function changeName(uint256 tokenId, string memory newName) public {
require(
_ownerOf(tokenId) == msg.sender,
"You are not the owner of this NFT"
);
emit NameChanged(tokenId, newName);
}
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
function claim(
uint256 amount,
bytes32[] calldata proof
) external nonReentrant {
require(
block.timestamp < saleStartTime + 2 days,
"Sale is not in the SHIBOSHIS phase"
);
require(!claims[msg.sender], "Already claimed");
bytes32 fullRewardBytes = bytes32(amount);
bytes12 rewardBytes = bytes12(fullRewardBytes << 160);
bytes32 leaf = keccak256(abi.encodePacked(msg.sender, rewardBytes));
if (!MerkleProofLib.verifyCalldata(proof, allowlistRoot, leaf)) {
revert InvalidProof();
}
_mint(msg.sender, amount * _unit());
claims[msg.sender] = true;
}
function approvalToContracts(bool _value) public onlyOwner {
contractsApproval = _value;
}
function mint(
uint256 amount
) public payable isValidMint(amount) nonReentrant {
if (
block.timestamp >= saleStartTime + 2 days &&
block.timestamp <= saleStartTime + 3 days
) {
bool istokenLEASHHolder = tokenLEASH.balanceOf(msg.sender) > 0;
require(istokenLEASHHolder, "Only LEASH holders can mint now");
require(LEASHHoldermints[msg.sender] + amount <= LEASHHolderMintLimit, "Mint limit exceeded");
LEASHHoldermints[msg.sender] += uint8(amount);
}
_mint(msg.sender, amount * _unit());
}
function approve(
address spender,
uint256 amount
) public virtual override returns (bool) {
if (contractsApproval) {
require(
!isContract(spender),
"Approvals to contracts blocked for now"
);
}
return super.approve(spender, amount);
}
function withdraw() public onlyOwner {
SafeTransferLib.safeTransferAllETH(msg.sender);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.20;
import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Initializable} from "./Initializable.sol";
/**
* @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
* {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
*
* A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
* reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
* `UUPSUpgradeable` with a custom implementation of upgrades.
*
* The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
*/
abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable __self = address(this);
/**
* @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
* and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
* while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
* If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
* be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
* during an upgrade.
*/
string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
/**
* @dev The call is from an unauthorized context.
*/
error UUPSUnauthorizedCallContext();
/**
* @dev The storage `slot` is unsupported as a UUID.
*/
error UUPSUnsupportedProxiableUUID(bytes32 slot);
/**
* @dev Check that the execution is being performed through a delegatecall call and that the execution context is
* a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
* for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
* function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
* fail.
*/
modifier onlyProxy() {
_checkProxy();
_;
}
/**
* @dev Check that the execution is not being performed through a delegate call. This allows a function to be
* callable on the implementing contract but not through proxies.
*/
modifier notDelegated() {
_checkNotDelegated();
_;
}
function __UUPSUpgradeable_init() internal onlyInitializing {
}
function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
}
/**
* @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
* implementation. It is used to validate the implementation's compatibility when performing an upgrade.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
*/
function proxiableUUID() external view virtual notDelegated returns (bytes32) {
return ERC1967Utils.IMPLEMENTATION_SLOT;
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
* encoded in `data`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, data);
}
/**
* @dev Reverts if the execution is not performed via delegatecall or the execution
* context is not of a proxy with an ERC1967-compliant implementation pointing to self.
* See {_onlyProxy}.
*/
function _checkProxy() internal view virtual {
if (
address(this) == __self || // Must be called through delegatecall
ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
) {
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Reverts if the execution is performed via delegatecall.
* See {notDelegated}.
*/
function _checkNotDelegated() internal view virtual {
if (address(this) != __self) {
// Must not be called through delegatecall
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
* {upgradeToAndCall}.
*
* Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
*
* ```solidity
* function _authorizeUpgrade(address) internal onlyOwner {}
* ```
*/
function _authorizeUpgrade(address newImplementation) internal virtual;
/**
* @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
*
* As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
* is expected to be the implementation slot in ERC1967.
*
* Emits an {IERC1967-Upgraded} event.
*/
function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
revert UUPSUnsupportedProxiableUUID(slot);
}
ERC1967Utils.upgradeToAndCall(newImplementation, data);
} catch {
// The implementation is not UUPS
revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuardUpgradeable is Initializable {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
/// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard
struct ReentrancyGuardStorage {
uint256 _status;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) {
assembly {
$.slot := ReentrancyGuardStorageLocation
}
}
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
function __ReentrancyGuard_init() internal onlyInitializing {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal onlyInitializing {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
$._status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
// On the first call to nonReentrant, _status will be NOT_ENTERED
if ($._status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
$._status = ENTERED;
}
function _nonReentrantAfter() private {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
$._status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
return $._status == ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC1822.sol)
pragma solidity ^0.8.20;
/**
* @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
* proxy whose upgrades are fully controlled by the current implementation.
*/
interface IERC1822Proxiable {
/**
* @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
* address.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy.
*/
function proxiableUUID() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.20;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {UpgradeableBeacon} will check that this address is a contract.
*/
function implementation() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)
pragma solidity ^0.8.20;
import {IBeacon} from "../beacon/IBeacon.sol";
import {Address} from "../../utils/Address.sol";
import {StorageSlot} from "../../utils/StorageSlot.sol";
/**
* @dev This abstract contract provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
*/
library ERC1967Utils {
// We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
// This will be fixed in Solidity 0.8.21. At that point we should remove these events.
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Emitted when the beacon is changed.
*/
event BeaconUpgraded(address indexed beacon);
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev The `implementation` of the proxy is invalid.
*/
error ERC1967InvalidImplementation(address implementation);
/**
* @dev The `admin` of the proxy is invalid.
*/
error ERC1967InvalidAdmin(address admin);
/**
* @dev The `beacon` of the proxy is invalid.
*/
error ERC1967InvalidBeacon(address beacon);
/**
* @dev An upgrade function sees `msg.value > 0` that may be lost.
*/
error ERC1967NonPayable();
/**
* @dev Returns the current implementation address.
*/
function getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
if (newImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(newImplementation);
}
StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Performs implementation upgrade with additional setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-Upgraded} event.
*/
function upgradeToAndCall(address newImplementation, bytes memory data) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
if (data.length > 0) {
Address.functionDelegateCall(newImplementation, data);
} else {
_checkNonPayable();
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Returns the current admin.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
* the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
*/
function getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
if (newAdmin == address(0)) {
revert ERC1967InvalidAdmin(address(0));
}
StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {IERC1967-AdminChanged} event.
*/
function changeAdmin(address newAdmin) internal {
emit AdminChanged(getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Returns the current beacon.
*/
function getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the EIP1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
if (newBeacon.code.length == 0) {
revert ERC1967InvalidBeacon(newBeacon);
}
StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
address beaconImplementation = IBeacon(newBeacon).implementation();
if (beaconImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(beaconImplementation);
}
}
/**
* @dev Change the beacon and trigger a setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-BeaconUpgraded} event.
*
* CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
* it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
* efficiency.
*/
function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length > 0) {
Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
} else {
_checkNonPayable();
}
}
/**
* @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
* if an upgrade doesn't perform an initialization call.
*/
function _checkNonPayable() private {
if (msg.value > 0) {
revert ERC1967NonPayable();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}// 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();
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* 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;
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* 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 token ID to assign for an NFT mint.
uint32 nextTokenId;
// Total number of IDs in the burned pool.
uint32 burnedPoolSize;
// 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 token approvals to approved operators.
mapping(uint256 => address) nftApprovals;
// Bitmap of whether an non-zero NFT approval may exist.
Bitmap mayHaveNFTApproval;
// Mapping of user allowances for token 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) {
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 = a.value;
if (allowed != type(uint256).max) {
if (amount > allowed) revert InsufficientAllowance();
unchecked {
a.value = allowed - amount;
}
}
_transfer(from, to, amount);
return true;
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* 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();
DN404Storage storage $ = _getDN404Storage();
AddressData storage toAddressData = _addressData(to);
unchecked {
uint256 maxNFTId;
{
uint256 totalSupply_ = uint256($.totalSupply) + amount;
$.totalSupply = uint96(totalSupply_);
uint256 overflows = _toUint(_totalSupplyOverflows(totalSupply_));
if (overflows | _toUint(totalSupply_ < amount) != 0) revert TotalSupplyOverflow();
maxNFTId = totalSupply_ / _unit();
}
uint256 toEnd;
{
uint256 toBalance = uint256(toAddressData.balance) + amount;
toAddressData.balance = uint96(toBalance);
toEnd = toBalance / _unit();
}
if (toAddressData.flags & _ADDRESS_DATA_SKIP_NFT_FLAG == 0) {
Uint32Map storage toOwned = $.owned[to];
Uint32Map storage oo = $.oo;
uint256 toIndex = toAddressData.ownedLength;
_PackedLogs memory packedLogs = _packedLogsMalloc(_zeroFloorSub(toEnd, toIndex));
if (packedLogs.logs.length != 0) {
_packedLogsSet(packedLogs, to, 0);
uint256 burnedPoolSize = $.burnedPoolSize;
uint256 nextTokenId = $.nextTokenId;
uint32 toAlias = _registerAndResolveAlias(toAddressData, to);
$.totalNFTSupply += uint32(packedLogs.logs.length);
toAddressData.ownedLength = uint32(toEnd);
// Mint loop.
do {
uint256 id;
if (burnedPoolSize != 0) {
id = _get($.burnedPool, --burnedPoolSize);
} else {
id = nextTokenId;
while (_get(oo, _ownershipIndex(id)) != 0) {
id = _wrapNFTId(id + 1, maxNFTId);
}
nextTokenId = _wrapNFTId(id + 1, maxNFTId);
}
_set(toOwned, toIndex, uint32(id));
_setOwnerAliasAndOwnedIndex(oo, id, toAlias, uint32(toIndex++));
_packedLogsAppend(packedLogs, id);
} while (toIndex != toEnd);
// Leave some spacing between minted batches for better open addressing.
$.nextTokenId = uint32(_wrapNFTId(nextTokenId + 7, maxNFTId));
$.burnedPoolSize = uint32(burnedPoolSize);
_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 {
DN404Storage storage $ = _getDN404Storage();
AddressData storage fromAddressData = _addressData(from);
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) {
_PackedLogs 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);
uint256 burnedPoolSize = $.burnedPoolSize;
// Burn loop.
do {
uint256 id = _get(fromOwned, --fromIndex);
_setOwnerAliasAndOwnedIndex(oo, id, 0, 0);
_packedLogsAppend(packedLogs, id);
if (addToBurnedPool) {
_set($.burnedPool, burnedPoolSize++, uint32(id));
}
if (_get($.mayHaveNFTApproval, id)) {
_set($.mayHaveNFTApproval, id, false);
delete $.nftApprovals[id];
}
} while (fromIndex != fromEnd);
$.burnedPoolSize = uint32(burnedPoolSize);
_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();
DN404Storage storage $ = _getDN404Storage();
AddressData storage fromAddressData = _addressData(from);
AddressData storage toAddressData = _addressData(to);
_TransferTemps 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);
_PackedLogs memory packedLogs = _packedLogsMalloc(t.numNFTBurns + t.numNFTMints);
Uint32Map storage oo = $.oo;
uint256 burnedPoolSize = $.burnedPoolSize;
if (t.numNFTBurns != 0) {
_packedLogsSet(packedLogs, from, 1);
bool addToBurnedPool = _addToBurnedPool(t.totalNFTSupply, t.totalSupply);
Uint32Map storage fromOwned = $.owned[from];
uint256 fromIndex = t.fromOwnedLength;
uint256 fromEnd = fromIndex - t.numNFTBurns;
fromAddressData.ownedLength = uint32(fromEnd);
// Burn loop.
do {
uint256 id = _get(fromOwned, --fromIndex);
_setOwnerAliasAndOwnedIndex(oo, id, 0, 0);
_packedLogsAppend(packedLogs, id);
if (addToBurnedPool) {
_set($.burnedPool, burnedPoolSize++, uint32(id));
}
if (_get($.mayHaveNFTApproval, id)) {
_set($.mayHaveNFTApproval, id, false);
delete $.nftApprovals[id];
}
} while (fromIndex != fromEnd);
}
if (t.numNFTMints != 0) {
_packedLogsSet(packedLogs, to, 0);
uint256 nextTokenId = $.nextTokenId;
Uint32Map storage toOwned = $.owned[to];
uint256 toIndex = t.toOwnedLength;
uint256 toEnd = toIndex + t.numNFTMints;
uint32 toAlias = _registerAndResolveAlias(toAddressData, to);
uint256 maxNFTId = t.totalSupply / _unit();
toAddressData.ownedLength = uint32(toEnd);
// Mint loop.
do {
uint256 id;
if (burnedPoolSize != 0) {
id = _get($.burnedPool, --burnedPoolSize);
} else {
id = nextTokenId;
while (_get(oo, _ownershipIndex(id)) != 0) {
id = _wrapNFTId(id + 1, maxNFTId);
}
nextTokenId = _wrapNFTId(id + 1, maxNFTId);
}
_set(toOwned, toIndex, uint32(id));
_setOwnerAliasAndOwnedIndex(oo, id, toAlias, uint32(toIndex++));
_packedLogsAppend(packedLogs, id);
} while (toIndex != toEnd);
// Leave some spacing between minted batches for better open addressing.
$.nextTokenId = uint32(_wrapNFTId(nextTokenId + 7, maxNFTId));
}
if (packedLogs.logs.length != 0) {
$.burnedPoolSize = uint32(burnedPoolSize);
_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 burns should be added to the burn pool.
function _addToBurnedPool(uint256 totalNFTSupplyAfterBurn, uint256 totalSupplyAfterBurn)
internal
view
virtual
returns (bool)
{
// Add to burned pool if the load factor > 50%, and collection is not small.
uint256 thres = (totalSupplyAfterBurn / _unit()) >> 1;
return _toUint(totalNFTSupplyAfterBurn > thres) & _toUint(thres > 128) != 0;
}
/// @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(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 {
_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(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(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 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.
fallback() external payable virtual dn404Fallback {}
receive() external payable virtual {}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* 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 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 maxNFTId) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
result := or(mul(iszero(gt(id, maxNFTId)), id), gt(id, maxNFTId))
}
}
/// @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 `b ? 1 : 0`.
function _toUint(bool b) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
result := iszero(iszero(b))
}
}
/// @dev Struct containing packed log data for {Transfer} events to be
/// emitted by the mirror NFT contract.
struct _PackedLogs {
uint256 offset;
uint256 addressAndBit;
uint256[] logs;
}
/// @dev Initiates memory allocation for packed logs with `n` log items.
function _packedLogsMalloc(uint256 n) private pure returns (_PackedLogs 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(_PackedLogs 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(_PackedLogs 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(_PackedLogs 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 _TransferTemps {
uint256 numNFTBurns;
uint256 numNFTMints;
uint256 fromBalance;
uint256 toBalance;
uint256 fromOwnedLength;
uint256 toOwnedLength;
uint256 totalSupply;
uint256 totalNFTSupply;
}
/// @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();
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* 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); // `symbol()`.
}
/// @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 {
// When returndatacopy copies 1 or more out-of-bounds bytes, it reverts.
returndatacopy(0x00, returndatasize(), lt(calldatasize(), 0x20))
let o := add(0x24, calldataload(0x04)) // Packed logs offset.
returndatacopy(0x00, returndatasize(), lt(calldatasize(), o))
let end := add(o, shl(5, calldataload(sub(o, 0x20))))
returndatacopy(0x00, returndatasize(), lt(calldatasize(), end))
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)
}
}
// `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.
fallback() external payable virtual dn404NFTFallback {}
receive() external payable virtual {}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* 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 Gas optimized verification of proof of inclusion for a leaf in a Merkle tree.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol)
library MerkleProofLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MERKLE PROOF VERIFICATION OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf)
internal
pure
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
if mload(proof) {
// Initialize `offset` to the offset of `proof` elements in memory.
let offset := add(proof, 0x20)
// Left shift by 5 is equivalent to multiplying by 0x20.
let end := add(offset, shl(5, mload(proof)))
// Iterate over proof elements to compute root hash.
for {} 1 {} {
// Slot of `leaf` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(leaf, mload(offset)))
// Store elements to hash contiguously in scratch space.
// Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
mstore(scratch, leaf)
mstore(xor(scratch, 0x20), mload(offset))
// Reuse `leaf` to store the hash to reduce stack operations.
leaf := keccak256(0x00, 0x40)
offset := add(offset, 0x20)
if iszero(lt(offset, end)) { break }
}
}
isValid := eq(leaf, root)
}
}
/// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf)
internal
pure
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
if proof.length {
// Left shift by 5 is equivalent to multiplying by 0x20.
let end := add(proof.offset, shl(5, proof.length))
// Initialize `offset` to the offset of `proof` in the calldata.
let offset := proof.offset
// Iterate over proof elements to compute root hash.
for {} 1 {} {
// Slot of `leaf` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(leaf, calldataload(offset)))
// Store elements to hash contiguously in scratch space.
// Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
mstore(scratch, leaf)
mstore(xor(scratch, 0x20), calldataload(offset))
// Reuse `leaf` to store the hash to reduce stack operations.
leaf := keccak256(0x00, 0x40)
offset := add(offset, 0x20)
if iszero(lt(offset, end)) { break }
}
}
isValid := eq(leaf, root)
}
}
/// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
/// given `proof` and `flags`.
///
/// Note:
/// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
/// will always return false.
/// - The sum of the lengths of `proof` and `leaves` must never overflow.
/// - Any non-zero word in the `flags` array is treated as true.
/// - The memory offset of `proof` must be non-zero
/// (i.e. `proof` is not pointing to the scratch space).
function verifyMultiProof(
bytes32[] memory proof,
bytes32 root,
bytes32[] memory leaves,
bool[] memory flags
) internal pure returns (bool isValid) {
// Rebuilds the root by consuming and producing values on a queue.
// The queue starts with the `leaves` array, and goes into a `hashes` array.
// After the process, the last element on the queue is verified
// to be equal to the `root`.
//
// The `flags` array denotes whether the sibling
// should be popped from the queue (`flag == true`), or
// should be popped from the `proof` (`flag == false`).
/// @solidity memory-safe-assembly
assembly {
// Cache the lengths of the arrays.
let leavesLength := mload(leaves)
let proofLength := mload(proof)
let flagsLength := mload(flags)
// Advance the pointers of the arrays to point to the data.
leaves := add(0x20, leaves)
proof := add(0x20, proof)
flags := add(0x20, flags)
// If the number of flags is correct.
for {} eq(add(leavesLength, proofLength), add(flagsLength, 1)) {} {
// For the case where `proof.length + leaves.length == 1`.
if iszero(flagsLength) {
// `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
isValid := eq(mload(xor(leaves, mul(xor(proof, leaves), proofLength))), root)
break
}
// The required final proof offset if `flagsLength` is not zero, otherwise zero.
let proofEnd := add(proof, shl(5, proofLength))
// We can use the free memory space for the queue.
// We don't need to allocate, since the queue is temporary.
let hashesFront := mload(0x40)
// Copy the leaves into the hashes.
// Sometimes, a little memory expansion costs less than branching.
// Should cost less, even with a high free memory offset of 0x7d00.
leavesLength := shl(5, leavesLength)
for { let i := 0 } iszero(eq(i, leavesLength)) { i := add(i, 0x20) } {
mstore(add(hashesFront, i), mload(add(leaves, i)))
}
// Compute the back of the hashes.
let hashesBack := add(hashesFront, leavesLength)
// This is the end of the memory for the queue.
// We recycle `flagsLength` to save on stack variables (sometimes save gas).
flagsLength := add(hashesBack, shl(5, flagsLength))
for {} 1 {} {
// Pop from `hashes`.
let a := mload(hashesFront)
// Pop from `hashes`.
let b := mload(add(hashesFront, 0x20))
hashesFront := add(hashesFront, 0x40)
// If the flag is false, load the next proof,
// else, pops from the queue.
if iszero(mload(flags)) {
// Loads the next proof.
b := mload(proof)
proof := add(proof, 0x20)
// Unpop from `hashes`.
hashesFront := sub(hashesFront, 0x20)
}
// Advance to the next flag.
flags := add(flags, 0x20)
// Slot of `a` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(a, b))
// Hash the scratch space and push the result onto the queue.
mstore(scratch, a)
mstore(xor(scratch, 0x20), b)
mstore(hashesBack, keccak256(0x00, 0x40))
hashesBack := add(hashesBack, 0x20)
if iszero(lt(hashesBack, flagsLength)) { break }
}
isValid :=
and(
// Checks if the last value in the queue is same as the root.
eq(mload(sub(hashesBack, 0x20)), root),
// And whether all the proofs are used, if required.
eq(proofEnd, proof)
)
break
}
}
}
/// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
/// given `proof` and `flags`.
///
/// Note:
/// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
/// will always return false.
/// - Any non-zero word in the `flags` array is treated as true.
/// - The calldata offset of `proof` must be non-zero
/// (i.e. `proof` is from a regular Solidity function with a 4-byte selector).
function verifyMultiProofCalldata(
bytes32[] calldata proof,
bytes32 root,
bytes32[] calldata leaves,
bool[] calldata flags
) internal pure returns (bool isValid) {
// Rebuilds the root by consuming and producing values on a queue.
// The queue starts with the `leaves` array, and goes into a `hashes` array.
// After the process, the last element on the queue is verified
// to be equal to the `root`.
//
// The `flags` array denotes whether the sibling
// should be popped from the queue (`flag == true`), or
// should be popped from the `proof` (`flag == false`).
/// @solidity memory-safe-assembly
assembly {
// If the number of flags is correct.
for {} eq(add(leaves.length, proof.length), add(flags.length, 1)) {} {
// For the case where `proof.length + leaves.length == 1`.
if iszero(flags.length) {
// `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
// forgefmt: disable-next-item
isValid := eq(
calldataload(
xor(leaves.offset, mul(xor(proof.offset, leaves.offset), proof.length))
),
root
)
break
}
// The required final proof offset if `flagsLength` is not zero, otherwise zero.
let proofEnd := add(proof.offset, shl(5, proof.length))
// We can use the free memory space for the queue.
// We don't need to allocate, since the queue is temporary.
let hashesFront := mload(0x40)
// Copy the leaves into the hashes.
// Sometimes, a little memory expansion costs less than branching.
// Should cost less, even with a high free memory offset of 0x7d00.
calldatacopy(hashesFront, leaves.offset, shl(5, leaves.length))
// Compute the back of the hashes.
let hashesBack := add(hashesFront, shl(5, leaves.length))
// This is the end of the memory for the queue.
// We recycle `flagsLength` to save on stack variables (sometimes save gas).
flags.length := add(hashesBack, shl(5, flags.length))
// We don't need to make a copy of `proof.offset` or `flags.offset`,
// as they are pass-by-value (this trick may not always save gas).
for {} 1 {} {
// Pop from `hashes`.
let a := mload(hashesFront)
// Pop from `hashes`.
let b := mload(add(hashesFront, 0x20))
hashesFront := add(hashesFront, 0x40)
// If the flag is false, load the next proof,
// else, pops from the queue.
if iszero(calldataload(flags.offset)) {
// Loads the next proof.
b := calldataload(proof.offset)
proof.offset := add(proof.offset, 0x20)
// Unpop from `hashes`.
hashesFront := sub(hashesFront, 0x20)
}
// Advance to the next flag offset.
flags.offset := add(flags.offset, 0x20)
// Slot of `a` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(a, b))
// Hash the scratch space and push the result onto the queue.
mstore(scratch, a)
mstore(xor(scratch, 0x20), b)
mstore(hashesBack, keccak256(0x00, 0x40))
hashesBack := add(hashesBack, 0x20)
if iszero(lt(hashesBack, flags.length)) { break }
}
isValid :=
and(
// Checks if the last value in the queue is same as the root.
eq(mload(sub(hashesBack, 0x20)), root),
// And whether all the proofs are used, if required.
eq(proofEnd, proof.offset)
)
break
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EMPTY CALLDATA HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns an empty calldata bytes32 array.
function emptyProof() internal pure returns (bytes32[] calldata proof) {
/// @solidity memory-safe-assembly
assembly {
proof.length := 0
}
}
/// @dev Returns an empty calldata bytes32 array.
function emptyLeaves() internal pure returns (bytes32[] calldata leaves) {
/// @solidity memory-safe-assembly
assembly {
leaves.length := 0
}
}
/// @dev Returns an empty calldata bool array.
function emptyFlags() internal pure returns (bool[] calldata flags) {
/// @solidity memory-safe-assembly
assembly {
flags.length := 0
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice 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)
)
)
}
}
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"DNAlreadyInitialized","type":"error"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"name":"ERC1967InvalidImplementation","type":"error"},{"inputs":[],"name":"ERC1967NonPayable","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InsufficientAllowance","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidInitialization","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":"NotInitializing","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"SaleNotStarted","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":"UUPSUnauthorizedCallContext","type":"error"},{"inputs":[{"internalType":"bytes32","name":"slot","type":"bytes32"}],"name":"UUPSUnsupportedProxiableUUID","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":[],"name":"ContractURIUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"string","name":"name","type":"string"}],"name":"NameChanged","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"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"LEASHHolderMintLimit","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UPGRADE_INTERFACE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","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":"bool","name":"_value","type":"bool"}],"name":"approvalToContracts","outputs":[],"stateMutability":"nonpayable","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":"uint256","name":"tokenId","type":"uint256"},{"internalType":"string","name":"newName","type":"string"}],"name":"changeName","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"contractsApproval","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"getSkipNFT","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint96","name":"initialTokenSupply","type":"uint96"},{"internalType":"bytes32","name":"_allowlistRoot","type":"bytes32"},{"internalType":"uint256","name":"_maxSupply","type":"uint256"},{"internalType":"uint256","name":"_mintPrice","type":"uint256"},{"internalType":"uint8","name":"_LEASHHolderMintLimit","type":"uint8"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"mintPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","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":"numMinted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"reveal","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"saleStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_allowlistRoot","type":"bytes32"}],"name":"setAllowlistRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"baseURI_","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setLEASH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"_LEASHHolderMintLimit","type":"uint8"}],"name":"setLEASHHolderMintLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxSupply","type":"uint256"}],"name":"setMaxSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_mintPrice","type":"uint256"}],"name":"setMintPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setReveal","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":"startSale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenLEASH","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"result","type":"string"}],"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":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60a06040523060805234801561001457600080fd5b506080516147dc61003e60003960008181612c5c01528181612c860152612dd601526147dc6000f3fe608060405260043610620002b35760003560e01c806370a08231116200016f578063b66a0e5d11620000d3578063f04e283e1162000085578063f04e283e1462000c2c578063f2fde38b1462000c43578063f406de2a1462000c5a578063f4a0a5281462000c7c578063f6ec89b81462000ca1578063fee81cf41462000cc657620002bb565b8063b66a0e5d1462000b57578063c39cbef11462000b6f578063c87b56dd1462000b94578063d52079b41462000bb9578063d5abeb011462000bd1578063dd62ed3e1462000be957620002bb565b806395d89b41116200012d57806395d89b411462000a88578063a0712d681462000aa0578063a475b5dd1462000ab7578063a9059cbb1462000ada578063ad3cb1cc1462000aff578063b089ae871462000b3257620002bb565b806370a0823114620009d4578063715018a61462000a26578063766453151462000a305780638da5cb5b1462000a485780638e0acd121462000a6357620002bb565b8063313ce567116200021757806352d1902d11620001d557806352d1902d146200093457806354d1f13d146200094c57806355f804b314620009565780635744cc0e146200097b5780636817c76c14620009975780636f8b44b014620009af57620002bb565b8063313ce567146200088257806336e8df9514620008a65780633ccfd60b14620008c95780634ef41efc14620008e15780634f1ef286146200091d57620002bb565b806323b872dd116200027157806323b872dd14620007bf5780632569296214620007e4578063274e430b14620007ee5780632a6a935d14620008135780632e37fc5a14620008385780632f52ebb7146200085d57620002bb565b806306fdde0314620006e3578063095ea7b3146200071357806316c44ae3146200074957806318160ddd146200076e5780631cbaee2d14620007a757620002bb565b36620002bb57005b68a20d6e21d0e525530860003560e01c63e5eb36c8819003620003405760018201546001600160a01b03163314620003065760405163ce5a776b60e01b815260040160405180910390fd5b60843610156200031557600080fd5b6004356024356044356064356200032f8484848462000cfd565b6200033b60016200106f565b505050505b8063813500fc03620003c75760018201546001600160a01b031633146200037a5760405163ce5a776b60e01b815260040160405180910390fd5b60643610156200038957600080fd5b6004356028818152604435601481905268a20d6e21d0e525530b6000908152604881209252602435151591829055620003c360016200106f565b5050505b8063e985e9c503620004425760018201546001600160a01b03163314620004015760405163ce5a776b60e01b815260040160405180910390fd5b60443610156200041057600080fd5b6024356028818152600435601481905260038501600090815260488120925290549091906200043f906200106f565b50505b80636352211e03620004ae5760018201546001600160a01b031633146200047c5760405163ce5a776b60e01b815260040160405180910390fd5b60243610156200048b57600080fd5b600435620004ac6200049d8262001079565b6001600160a01b03166200106f565b505b806324359879036200050b5760018201546001600160a01b03163314620004e85760405163ce5a776b60e01b815260040160405180910390fd5b6024361015620004f757600080fd5b600435620005096200049d82620010b5565b505b8063d10b6e0c03620005725760018201546001600160a01b03163314620005455760405163ce5a776b60e01b815260040160405180910390fd5b60643610156200055457600080fd5b6004356024356044356200056e6200049d8484846200110f565b5050505b8063081812fc03620005cf5760018201546001600160a01b03163314620005ac5760405163ce5a776b60e01b815260040160405180910390fd5b6024361015620005bb57600080fd5b600435620005cd6200049d8262001206565b505b8063f5b100ea036200065d5760018201546001600160a01b03163314620006095760405163ce5a776b60e01b815260040160405180910390fd5b60243610156200061857600080fd5b6004356200065b62000655826001600160a01b0316600090815268a20d6e21d0e5255312602052604090205463ffffffff600160801b9091041690565b6200106f565b505b8063e2c7928103620006c95760018201546001600160a01b03163314620006975760405163ce5a776b60e01b815260040160405180910390fd5b6004361015620006a657600080fd5b68a20d6e21d0e525530854620006c990600160601b900463ffffffff166200106f565b8063b7a94eb803620006e157620006e160016200106f565b005b348015620006f057600080fd5b50620006fb62001255565b6040516200070a919062003225565b60405180910390f35b3480156200072057600080fd5b50620007386200073236600462003272565b620012ef565b60405190151581526020016200070a565b3480156200075657600080fd5b50620006e162000768366004620032b1565b6200137c565b3480156200077b57600080fd5b5068a20d6e21d0e525530854600160801b90046001600160601b03165b6040519081526020016200070a565b348015620007b457600080fd5b506200079860055481565b348015620007cc57600080fd5b5062000738620007de366004620032cf565b6200139c565b620006e16200140e565b348015620007fb57600080fd5b50620007386200080d36600462003310565b6200145e565b3480156200082057600080fd5b5062000738620008323660046200332e565b620014ac565b3480156200084557600080fd5b50620006e16200085736600462003408565b620014c2565b3480156200086a57600080fd5b50620006e16200087c366004620034c2565b620016bf565b3480156200088f57600080fd5b5060125b60405160ff90911681526020016200070a565b348015620008b357600080fd5b506006546200073890600160a01b900460ff1681565b348015620008d657600080fd5b50620006e16200185d565b348015620008ee57600080fd5b5068a20d6e21d0e5255309546001600160a01b03165b6040516001600160a01b0390911681526020016200070a565b620006e16200092e36600462003546565b62001874565b3480156200094157600080fd5b506200079862001899565b620006e1620018b9565b3480156200096357600080fd5b50620006e162000975366004620035af565b620018f5565b3480156200098857600080fd5b50600a54620008939060ff1681565b348015620009a457600080fd5b506200079860085481565b348015620009bc57600080fd5b50620006e1620009ce36600462003626565b6200190e565b348015620009e157600080fd5b5062000798620009f336600462003310565b6001600160a01b0316600090815268a20d6e21d0e52553126020526040902054600160a01b90046001600160601b031690565b620006e16200191d565b34801562000a3d57600080fd5b50620006e162001933565b34801562000a5557600080fd5b50638b78c6d8195462000904565b34801562000a7057600080fd5b50620006e162000a8236600462003626565b62001952565b34801562000a9557600080fd5b50620006fb62001961565b620006e162000ab136600462003626565b62001972565b34801562000ac457600080fd5b506006546200073890600160a81b900460ff1681565b34801562000ae757600080fd5b506200073862000af936600462003272565b62001bdf565b34801562000b0c57600080fd5b50620006fb604051806040016040528060058152602001640352e302e360dc1b81525081565b34801562000b3f57600080fd5b50620006e162000b513660046200332e565b62001bf7565b34801562000b6457600080fd5b50620006e162001c1f565b34801562000b7c57600080fd5b50620006e162000b8e36600462003640565b62001c2f565b34801562000ba157600080fd5b50620006fb62000bb336600462003626565b62001cdb565b34801562000bc657600080fd5b506200079860035481565b34801562000bde57600080fd5b506200079860075481565b34801562000bf657600080fd5b506200079862000c0836600462003681565b602890815260149190915268a20d6e21d0e525530e60009081526048812091525490565b620006e162000c3d36600462003310565b62001df3565b620006e162000c5436600462003310565b62001e38565b34801562000c6757600080fd5b5060065462000904906001600160a01b031681565b34801562000c8957600080fd5b50620006e162000c9b36600462003626565b62001e64565b34801562000cae57600080fd5b50620006e162000cc036600462003310565b62001e73565b34801562000cd357600080fd5b506200079862000ce536600462003310565b63389a75e1600c908152600091909152602090205490565b68a20d6e21d0e52553086001600160a01b03841662000d2f57604051633a954ecd60e21b815260040160405180910390fd5b6009810160028201600062000d6b83600019880160011b5b60008160031c8360601b0180546007841660051b1c63ffffffff1691505092915050565b63ffffffff1681526020810191909152604001600020546001600160a01b0387811691161462000dad5760405162a1148160e81b815260040160405180910390fd5b856001600160a01b0316836001600160a01b03161462000e2657602883815260148790526003830160009081526048812091525460000362000e265760008481526004830160205260409020546001600160a01b0384811691161462000e2657604051632ce44b5f60e11b815260040160405180910390fd5b600062000e338762001e9f565b9050600062000e428762001e9f565b8254909150670de0b6b3a764000090600160a01b90046001600160601b03168082111562000e8357604051631e9acf1760e31b815260040160405180910390fd5b83546001600160601b03918390038216600160a01b9081026001600160a01b039283161786558454818104841685019093160291811691909117835589166000908152600786016020819052604090912062000ef6600588018a60609190911b600882901c0154600160ff9092161c1690565b1562000f35576005870160601b60088a901c018054600160ff8c161b191690556000898152600488016020526040902080546001600160a01b03191690555b845463ffffffff60801b198116600160801b9182900463ffffffff90811660001901808216909302919091178755606083901b631fffffff600384901c16015460009260e060059190911b161c169050600062000f9d886000198d01600190811b0162000d47565b905062000fb2838263ffffffff168462001efa565b62000fcf8860001963ffffffff851601600190811b018362001efa565b5050835463ffffffff60801b198116600160801b9182900463ffffffff908116600181019091169092021785556001600160a01b038b1660009081526020849052604090206200102190828c62001efa565b6200103a878b62001033888f62001f27565b8462001fbc565b505050806000528760601b60601c8960601b60601c6000805160206200476783398151915260206000a3505050505050505050565b8060005260206000f35b6000620010868262002001565b620010a45760405163677510db60e11b815260040160405180910390fd5b620010af82620010b5565b92915050565b600068a20d6e21d0e525530868a20d6e21d0e525530a82620010e868a20d6e21d0e5255311600019870160011b62000d47565b63ffffffff1681526020810191909152604001600020546001600160a01b03169392505050565b600068a20d6e21d0e525530868a20d6e21d0e525530a826200114268a20d6e21d0e5255311600019880160011b62000d47565b63ffffffff1681526020810191909152604001600020546001600160a01b03908116925083168214620011aa576028838152601483905260038201600090815260488120915254600003620011aa576040516367d9dca160e11b815260040160405180910390fd5b6000848152600482016020526040902080546001600160a01b0319166001600160a01b0387169081179091556005820160601b600886901c018054600160ff881690811b1991909116921515901b919091179055509392505050565b6000620012138262002001565b620012315760405163677510db60e11b815260040160405180910390fd5b50600090815268a20d6e21d0e525530c60205260409020546001600160a01b031690565b6060600080546200126690620036b9565b80601f01602080910402602001604051908101604052809291908181526020018280546200129490620036b9565b8015620012e55780601f10620012b957610100808354040283529160200191620012e5565b820191906000526020600020905b815481529060010190602001808311620012c757829003601f168201915b5050505050905090565b600654600090600160a01b900460ff16156200136957823b15620013695760405162461bcd60e51b815260206004820152602660248201527f417070726f76616c7320746f20636f6e74726163747320626c6f636b656420666044820152656f72206e6f7760d01b60648201526084015b60405180910390fd5b62001375838362002020565b9392505050565b620013866200202f565b600a805460ff191660ff92909216919091179055565b336028908152601484905268a20d6e21d0e525530e6000908152604881209181905281549091906000198114620013f55780841115620013ef576040516313be252b60e01b815260040160405180910390fd5b83810382555b620014028686866200204b565b50600195945050505050565b60006202a30067ffffffffffffffff164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b6001600160a01b038116600090815268a20d6e21d0e5255312602052604081208054600160581b900460011682036200149957823b62001375565b54600160581b9004600216151592915050565b6000620014ba3383620025e6565b506001919050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff16600081158015620015095750825b905060008267ffffffffffffffff166001148015620015275750303b155b90508115801562001536575080155b15620015555760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156200158057845460ff60401b1916600160401b1785555b6200158b3362002667565b62001595620026a3565b6200159f620026b7565b6000620015ad8d8262003749565b506001620015bc8c8262003749565b50600489905560078890556008879055600a805460ff881660ff199091161790556006805460ff60a01b1916600160a01b17905560405160009033906200160390620031f1565b6001600160a01b039091168152602001604051809103906000f08015801562001630573d6000803e3d6000fd5b5090506200164160003383620026c1565b620016693362001663670de0b6b3a76400006001600160601b038f166200382c565b6200287c565b508315620016b157845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050505050565b620016c962002ba9565b600554620016db906202a30062003846565b4210620017365760405162461bcd60e51b815260206004820152602260248201527f53616c65206973206e6f7420696e2074686520534849424f5348495320706861604482015261736560f01b606482015260840162001360565b3360009081526009602052604090205460ff16156200178a5760405162461bcd60e51b815260206004820152600f60248201526e105b1c9958591e4818db185a5b5959608a1b604482015260640162001360565b604080513360601b6bffffffffffffffffffffffff1916602082015260a085901b6001600160a01b0319811660348301528592909160009101604051602081830303815290604052805190602001209050620017eb85856004548462002be3565b62001809576040516309bde33960e01b815260040160405180910390fd5b620018223362001663670de0b6b3a7640000896200382c565b5050336000908152600960205260409020805460ff19166001179055506200185860016000805160206200478783398151915255565b505050565b620018676200202f565b620018723362002c34565b565b6200187e62002c51565b620018898262002cfa565b62001895828262002d04565b5050565b6000620018a562002dcb565b506000805160206200474783398151915290565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b620018ff6200202f565b6002620018588284836200385c565b620019186200202f565b600755565b620019276200202f565b62001872600062002e15565b6200193d6200202f565b6006805460ff60a81b1916600160a81b179055565b6200195c6200202f565b600455565b6060600180546200126690620036b9565b8060055460000362001997576040516316851a3760e11b815260040160405180910390fd5b3481600854620019a891906200382c565b14620019c65760405162bfc92160e01b815260040160405180910390fd5b60075481600354620019d9919062003846565b1115620019f957604051637be9badb60e01b815260040160405180910390fd5b62001a0362002ba9565b60055462001a15906202a30062003846565b421015801562001a36575060055462001a32906203f48062003846565b4211155b1562001bad576006546040516370a0823160e01b815233600482015260009182916001600160a01b03909116906370a0823190602401602060405180830381865afa15801562001a8a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001ab0919062003929565b1190508062001b025760405162461bcd60e51b815260206004820152601f60248201527f4f6e6c79204c4541534820686f6c646572732063616e206d696e74206e6f7700604482015260640162001360565b600a54336000908152600b602052604090205460ff9182169162001b299186911662003846565b111562001b6f5760405162461bcd60e51b8152602060048201526013602482015272135a5b9d081b1a5b5a5d08195e18d959591959606a1b604482015260640162001360565b336000908152600b60205260408120805485929062001b9390849060ff1662003943565b92506101000a81548160ff021916908360ff160217905550505b62001bc63362001663670de0b6b3a7640000856200382c565b6200189560016000805160206200478783398151915255565b600062001bee3384846200204b565b50600192915050565b62001c016200202f565b60068054911515600160a01b0260ff60a01b19909216919091179055565b62001c296200202f565b42600555565b3362001c3b8362001079565b6001600160a01b03161462001c9d5760405162461bcd60e51b815260206004820152602160248201527f596f7520617265206e6f7420746865206f776e6572206f662074686973204e466044820152601560fa1b606482015260840162001360565b817f8edfa912e70e283a8ef6d6f52cd1faef9690ff989eff2f11a134e8478ba7b28b8260405162001ccf919062003225565b60405180910390a25050565b6060600062001cea83620010b5565b6001600160a01b03160362001d125760405163677510db60e11b815260040160405180910390fd5b600654600160a81b900460ff161562001d5a57600262001d328362002e53565b60405160200162001d459291906200395f565b604051602081830303815290604052620010af565b6002805462001d6990620036b9565b80601f016020809104026020016040519081016040528092919081815260200182805462001d9790620036b9565b801562001de85780601f1062001dbc5761010080835404028352916020019162001de8565b820191906000526020600020905b81548152906001019060200180831162001dca57829003601f168201915b505050505092915050565b62001dfd6200202f565b63389a75e1600c52806000526020600c20805442111562001e2657636f5e88186000526004601cfd5b6000905562001e358162002e15565b50565b62001e426200202f565b8060601b62001e5957637448fbae6000526004601cfd5b62001e358162002e15565b62001e6e6200202f565b600855565b62001e7d6200202f565b600680546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b038116600090815268a20d6e21d0e52553126020526040812080549091600160581b909104600116900362001ef557805460ff60581b1916600160581b60ff843b151560020260011716021781555b919050565b8160031c8360601b016007831660051b815463ffffffff8482841c188116831b8218845550505050505050565b8154600160601b900463ffffffff1668a20d6e21d0e5255308600082900362001fb557805463ffffffff198116600163ffffffff928316019182169081178355855463ffffffff60601b1916600160601b82021786556000818152600284016020526040812080546001600160a01b0319166001600160a01b038816179055919350900362001fb557600080fd5b5092915050565b600183038060021c8560601b016003821660061b9150805467ffffffffffffffff8563ffffffff168560201b178083861c188216851b83188455505050505050505050565b6000806200200f83620010b5565b6001600160a01b0316141592915050565b600062001bee33848462002e98565b638b78c6d81954331462001872576382b429006000526004601cfd5b6001600160a01b0382166200207357604051633a954ecd60e21b815260040160405180910390fd5b68a20d6e21d0e525530860006200208a8562001e9f565b90506000620020998562001e9f565b9050620020e460405180610100016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b8254600160801b80820463ffffffff908116608085015284548290041660a08401528554046001600160601b0390811660c0840152600160a01b90910416604082018190528511156200214a57604051631e9acf1760e31b815260040160405180910390fd5b6040810180518690039081905283546001600160601b03918216600160a01b9081026001600160a01b03928316178655845481810484168901606086018190529093160291161782556080810151620021cc90620021ad670de0b6b3a764000090565b836040015181620021c257620021c2620039f0565b0480821191030290565b81528154600160581b90046002166000036200224157856001600160a01b0316876001600160a01b0316036200220a57805160808201510360a08201525b6200223b670de0b6b3a76400008260600151816200222c576200222c620039f0565b048260a0015180821191030290565b60208201525b80516020820151855463ffffffff600160601b8083048216840185900360e087018190529091160263ffffffff60601b1990911617865560009162002287910162002efa565b855483519192506009870191600160401b90910463ffffffff169015620023db5760608a901b60011760208401526000620022cb8560e001518660c0015162002f42565b6001600160a01b038c16600090815260078a0160205260409020608087015187518a5463ffffffff918303918216600160801b0263ffffffff60801b19909116178b5592935090915b60001991909101600381901c606084901b0154909190600583901b60e0161c63ffffffff1662002348878260008062001fbc565b87516020808a0151600884901b17825201885284156200237857620023788c600801878060010198508362001efa565b600881901c60058d0160601b015460ff82161c60011615620023cd5760058c0160601b600882901c018054600160ff84161b19169055600081815260048d016020526040902080546001600160a01b03191690555b508082036200231457505050505b6020840151156200257057606089901b602084015286546001600160a01b038a1660009081526007890160209081526040822060a08801519188015164010000000090940463ffffffff1693909290820190620024398a8f62001f27565b90506000670de0b6b3a76400008a60c00151816200245b576200245b620039f0565b8c5463ffffffff60801b1916600160801b63ffffffff871602178d550490505b60008715620024b95760001997909701600381901c60088f0160601b0154909790600589901b60e0161c63ffffffff1663ffffffff169050620024fd565b50855b620024cf89600019830160011b62000d47565b63ffffffff1615620024ed57600101818111801590910217620024bc565b6001810182811180159091021796505b6200250a86868362001efa565b6200251e8982858880600101995062001fbc565b89516020808c0151600884901b178252018a52508284036200247b57600786018181118015909102178d5463ffffffff919091166401000000000267ffffffff0000000019909116178d555050505050505b60408301515115620025b357865463ffffffff60401b1916600160401b63ffffffff8316021787556001870154620025b39084906001600160a01b031662002f71565b505050846000528560601b60601c8760601b60601c6000805160206200476783398151915260206000a350505050505050565b6000620025f38362001e9f565b8054909150600160581b90046002161515821515146200262f57805460ff600160581b80830482166002189091160260ff60581b199091161781555b8115156000528260601b60601c7fb5a1de456fff688115a4f75380060c23c8532d14ff85f687cc871456d642039360206000a2505050565b6001600160a01b0316638b78c6d8198190558060007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a350565b620026ad62002fae565b6200187262002ff8565b6200187262002fae565b68a20d6e21d0e52553088054640100000000900463ffffffff1615620026fa57604051633ab534b960e21b815260040160405180910390fd5b6001600160a01b03821662002722576040516339a84a7b60e01b815260040160405180910390fd5b630f4599e560005233602052602060006024601c6000865af160016000511416620027555763d125259c6000526004601cfd5b805467ffffffff0000000019166401000000001781556001810180546001600160a01b0384166001600160a01b0319909116179055831562002876576001600160a01b038316620027b957604051633a954ecd60e21b815260040160405180910390fd5b6001600160601b03841163fffffffe670de0b6b3a76400008604111715620027f45760405163e5cfe95760e01b815260040160405180910390fd5b80546bffffffffffffffffffffffff60801b1916600160801b6001600160601b038616021781556000620028288462001e9f565b80546001600160601b038716600160a01b026001600160a01b03918216178255600087815291925085169060008051602062004767833981519152602082a362002874846001620025e6565b505b50505050565b6001600160a01b038216620028a457604051633a954ecd60e21b815260040160405180910390fd5b68a20d6e21d0e52553086000620028bb8462001e9f565b82546bffffffffffffffffffffffff60801b198116600160801b918290046001600160601b039081168701808216909302919091178555919250600091811163fffffffe670de0b6b3a7640000830411171515858210811715620029325760405163e5cfe95760e01b815260040160405180910390fd5b5082546001600160601b03600160a01b80830482168801918216026001600160a01b0390921691909117808555670de0b6b3a764000092839004935091900490600160581b900460021660000362002b7d576001600160a01b03861660009081526007850160205260408120845490916009870191600160801b900463ffffffff1690620029c68286038387110262002efa565b905080604001515160001462002b785760608a901b6020820152875463ffffffff600160401b8204811691640100000000900416600062002a088a8e62001f27565b6040850151518c5463ffffffff600160601b8083048216909301811690920263ffffffff60601b19909116178d558b54908a16600160801b0263ffffffff60801b19909116178b5590505b6000831562002a915760001993909301600381901c60088d0160601b0154909390600585901b60e0161c63ffffffff1663ffffffff16905062002ad5565b50815b62002aa787600019830160011b62000d47565b63ffffffff161562002ac55760010189811180159091021762002a94565b600181018a811180159091021792505b62002ae288878362001efa565b62002af687828489806001019a5062001fbc565b8451602080870151600884901b1782520185525087850362002a5357600782018981118015909102178b546bffffffffffffffff00000000191664010000000063ffffffff9283160263ffffffff60401b191617600160401b91851691909102178b5560018b015462002b749085906001600160a01b031662002f71565b5050505b505050505b505060008381526001600160a01b0385169060008051602062004767833981519152602082a350505050565b6000805160206200478783398151915280546001190162002bdd57604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b6000831562002c17578360051b8501855b803580851160051b9485526020948518526040600020930181811062002bf45750505b501492915050565b60016000805160206200478783398151915255565b60003860003847855af162001e355763b12d13eb6000526004601cfd5b306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016148062002cdb57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031662002ccf60008051602062004747833981519152546001600160a01b031690565b6001600160a01b031614155b15620018725760405163703e46dd60e11b815260040160405180910390fd5b62001e356200202f565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa92505050801562002d61575060408051601f3d908101601f1916820190925262002d5e9181019062003929565b60015b62002d8b57604051634c9c8ce360e01b81526001600160a01b038316600482015260240162001360565b60008051602062004747833981519152811462002dbf57604051632a87526960e21b81526004810182905260240162001360565b62001858838362003002565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614620018725760405163703e46dd60e11b815260040160405180910390fd5b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b60606080604051019050602081016040526000815280600019835b928101926030600a8206018453600a90048062002e6e575050819003601f19909101908152919050565b6028828152601484905268a20d6e21d0e525530e600090815260488120915281905560008181526001600160a01b0380841691908516907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590602090a3505050565b62002f1f60405180606001604052806000815260200160008152602001606081525090565b604051828152806020018360051b81016040528183604001528083525050919050565b600080600162002f5b670de0b6b3a76400008562003a06565b901c905060808111818511161515949350505050565b60408201516040810363263c69d68152602080820152815160051b604401915060208183601c84016000875af16001825114166200287657600081fd5b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff166200187257604051631afcd79f60e31b815260040160405180910390fd5b62002c1f62002fae565b6200300d826200305f565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a28051156200305557620018588282620030c7565b6200189562003143565b806001600160a01b03163b6000036200309757604051634c9c8ce360e01b81526001600160a01b038216600482015260240162001360565b6000805160206200474783398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080846001600160a01b031684604051620030e6919062003a29565b600060405180830381855af49150503d806000811462003123576040519150601f19603f3d011682016040523d82523d6000602084013e62003128565b606091505b50915091506200313a85838362003163565b95945050505050565b3415620018725760405163b398979f60e01b815260040160405180910390fd5b6060826200317c576200317682620031c7565b62001375565b81511580156200319457506001600160a01b0384163b155b15620031bf57604051639996b31560e01b81526001600160a01b038516600482015260240162001360565b508062001375565b805115620031d85780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b610cff8062003a4883390190565b60005b838110156200321c57818101518382015260200162003202565b50506000910152565b602081526000825180602084015262003246816040850160208701620031ff565b601f01601f19169190910160400192915050565b80356001600160a01b038116811462001ef557600080fd5b600080604083850312156200328657600080fd5b62003291836200325a565b946020939093013593505050565b803560ff8116811462001ef557600080fd5b600060208284031215620032c457600080fd5b62001375826200329f565b600080600060608486031215620032e557600080fd5b620032f0846200325a565b925062003300602085016200325a565b9150604084013590509250925092565b6000602082840312156200332357600080fd5b62001375826200325a565b6000602082840312156200334157600080fd5b813580151581146200137557600080fd5b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff8084111562003386576200338662003352565b604051601f8501601f19908116603f01168101908282118183101715620033b157620033b162003352565b81604052809350858152868686011115620033cb57600080fd5b858560208301376000602087830101525050509392505050565b600082601f830112620033f757600080fd5b620013758383356020850162003368565b600080600080600080600060e0888a0312156200342457600080fd5b873567ffffffffffffffff808211156200343d57600080fd5b6200344b8b838c01620033e5565b985060208a01359150808211156200346257600080fd5b50620034718a828b01620033e5565b96505060408801356001600160601b03811681146200348f57600080fd5b9450606088013593506080880135925060a08801359150620034b460c089016200329f565b905092959891949750929550565b600080600060408486031215620034d857600080fd5b83359250602084013567ffffffffffffffff80821115620034f857600080fd5b818601915086601f8301126200350d57600080fd5b8135818111156200351d57600080fd5b8760208260051b85010111156200353357600080fd5b6020830194508093505050509250925092565b600080604083850312156200355a57600080fd5b62003565836200325a565b9150602083013567ffffffffffffffff8111156200358257600080fd5b8301601f810185136200359457600080fd5b620035a58582356020840162003368565b9150509250929050565b60008060208385031215620035c357600080fd5b823567ffffffffffffffff80821115620035dc57600080fd5b818501915085601f830112620035f157600080fd5b8135818111156200360157600080fd5b8660208285010111156200361457600080fd5b60209290920196919550909350505050565b6000602082840312156200363957600080fd5b5035919050565b600080604083850312156200365457600080fd5b82359150602083013567ffffffffffffffff8111156200367357600080fd5b620035a585828601620033e5565b600080604083850312156200369557600080fd5b620036a0836200325a565b9150620036b0602084016200325a565b90509250929050565b600181811c90821680620036ce57607f821691505b602082108103620036ef57634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111562001858576000816000526020600020601f850160051c81016020861015620037205750805b601f850160051c820191505b8181101562003741578281556001016200372c565b505050505050565b815167ffffffffffffffff81111562003766576200376662003352565b6200377e81620037778454620036b9565b84620036f5565b602080601f831160018114620037b657600084156200379d5750858301515b600019600386901b1c1916600185901b17855562003741565b600085815260208120601f198616915b82811015620037e757888601518255948401946001909101908401620037c6565b5085821015620038065787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417620010af57620010af62003816565b80820180821115620010af57620010af62003816565b67ffffffffffffffff83111562003877576200387762003352565b6200388f83620038888354620036b9565b83620036f5565b6000601f841160018114620038c65760008515620038ad5750838201355b600019600387901b1c1916600186901b17835562002874565b600083815260209020601f19861690835b82811015620038f95786850135825560209485019460019092019101620038d7565b5086821015620039175760001960f88860031b161c19848701351681555b505060018560011b0183555050505050565b6000602082840312156200393c57600080fd5b5051919050565b60ff8181168382160190811115620010af57620010af62003816565b60008084546200396f81620036b9565b600182811680156200398a5760018114620039a057620039d1565b60ff1984168752821515830287019450620039d1565b8860005260208060002060005b85811015620039c85781548a820152908401908201620039ad565b50505082870194505b505050508351620039e7818360208801620031ff565b01949350505050565b634e487b7160e01b600052601260045260246000fd5b60008262003a2457634e487b7160e01b600052601260045260246000fd5b500490565b6000825162003a3d818460208701620031ff565b919091019291505056fe608060405234801561001057600080fd5b50604051610cff380380610cff83398101604081905261002f9161005c565b683602298b8c10b0123180546001600160a01b0319166001600160a01b039290921691909117905561008c565b60006020828403121561006e57600080fd5b81516001600160a01b038116811461008557600080fd5b9392505050565b610c648061009b6000396000f3fe60806040526004361061010d5760003560e01c80636cef16e61161009557806397e5311c1161006457806397e5311c1461044f578063a22cb46514610464578063b88d4fde14610484578063c87b56dd146104a4578063e985e9c5146104c457610114565b80636cef16e6146103df57806370a08231146103f45780638da5cb5b1461041457806395d89b411461043a57610114565b806318160ddd116100dc57806318160ddd1461034957806323b872dd1461036c578063243598791461038c57806342842e0e146103ac5780636352211e146103bf57610114565b806301ffc9a71461027d57806306fdde03146102cf578063081812fc146102f1578063095ea7b31461032957610114565b3661011457005b683602298b8c10b0123060003560e01c63263c69d68190036101e05781546001600160a01b0316331461015a57604051631b1e598960e11b815260040160405180910390fd5b602036103d60003e6004356024018036103d60003e602081033560051b81018036103d60003e5b8082146101d35781358060601c816001168260a01b60a81c811583028284027fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600038a4505050816020019150610181565b5050600160005260206000f35b80630f4599e50361027b5760018201546001600160a01b0316156102365760018201546001600160a01b03166004356001600160a01b031614610236576040516362cf623d60e11b815260040160405180910390fd5b81546001600160a01b03161561025f57604051635fb2b52360e11b815260040160405180910390fd5b81546001600160a01b0319163317825560016000908152602090f35b005b34801561028957600080fd5b506102ba6102983660046109ee565b6301ffc9a760e09190911c9081146380ac58cd821417635b5e139f9091141790565b60405190151581526020015b60405180910390f35b3480156102db57600080fd5b506102e46104e4565b6040516102c69190610a1f565b3480156102fd57600080fd5b5061031161030c366004610a6e565b6104fa565b6040516001600160a01b0390911681526020016102c6565b34801561033557600080fd5b5061027b610344366004610aa3565b610512565b34801561035557600080fd5b5061035e610598565b6040519081526020016102c6565b34801561037857600080fd5b5061027b610387366004610acd565b6105aa565b34801561039857600080fd5b506103116103a7366004610a6e565b610639565b61027b6103ba366004610acd565b61064b565b3480156103cb57600080fd5b506103116103da366004610a6e565b61067d565b3480156103eb57600080fd5b5061027b61068f565b34801561040057600080fd5b5061035e61040f366004610b09565b610742565b34801561042057600080fd5b50683602298b8c10b01232546001600160a01b0316610311565b34801561044657600080fd5b506102e461075d565b34801561045b57600080fd5b5061031161076e565b34801561047057600080fd5b5061027b61047f366004610b24565b6107a3565b34801561049057600080fd5b5061027b61049f366004610b60565b610826565b3480156104b057600080fd5b506102e46104bf366004610a6e565b610881565b3480156104d057600080fd5b506102ba6104df366004610bfb565b610891565b60606104f56306fdde0360006108bd565b905090565b600061050c63081812fc83600061091a565b92915050565b600061051c61076e565b90508260601b60601c925060405163d10b6e0c600052836020528260405233606052602060006064601c34865af1601f3d111661055c573d6000823e3d81fd5b806040525060006060528183600c5160601c7f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600038a4505050565b60006104f563e2c7928160008061091a565b60006105b461076e565b90508360601b60601c93508260601b60601c925060405163e5eb36c881528460208201528360408201528260608201523360808201526020816084601c840134865af160018251141661060a573d6000823e3d81fd5b508183857fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600038a450505050565b600061050c632435987983600061091a565b6106568383836105aa565b813b156106785761067883838360405180602001604052806000815250610962565b505050565b600061050c636352211e83600061091a565b60008061069a61076e565b9050638da5cb5b600052602060006004601c845afa601f3d1116156106c257600c5160601c91505b683602298b8c10b0123254683602298b8c10b01230906001600160a01b03908116908416811461073c576002820180546001600160a01b0319166001600160a01b0386811691821790925560405190918316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35b50505050565b600061050c63f5b100ea836001600160a01b0316600061091a565b60606104f56395d89b4160006108bd565b683602298b8c10b01230546001600160a01b0316806107a057604051632d9523d760e11b815260040160405180910390fd5b90565b60006107ad61076e565b90508260601b60601c925060405163813500fc6000528360205282151560405233606052602060006064601c34865af1600160005114166107f1573d6000823e3d81fd5b83337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160206040a36040525050600060605250565b6108318585856105aa565b833b1561087a5761087a85858585858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061096292505050565b5050505050565b606061050c63c87b56dd836108bd565b60006108b463e985e9c5846001600160a01b0316846001600160a01b031661091a565b15159392505050565b606060006108c961076e565b9050604051915083600052826020526000806024601c845afa6108ef573d6000833e3d82fd5b60206000803e6020600051833e8151602060005101602084013e815160208301016040525092915050565b60008061092561076e565b9050604051856000528460205283604052602060006044601c855afa601f3d1116610953573d6000823e3d81fd5b60405250506000519392505050565b60405163150b7a028082523360208301528560601b60601c604083015283606083015260808083015282518060a084015280156109a9578060c08401826020870160045afa505b60208360a48301601c860160008a5af16109cc573d156109cc573d6000843e3d83fd5b508060e01b8251146109e65763d1a57ed66000526004601cfd5b505050505050565b600060208284031215610a0057600080fd5b81356001600160e01b031981168114610a1857600080fd5b9392505050565b60006020808352835180602085015260005b81811015610a4d57858101830151858201604001528201610a31565b506000604082860101526040601f19601f8301168501019250505092915050565b600060208284031215610a8057600080fd5b5035919050565b80356001600160a01b0381168114610a9e57600080fd5b919050565b60008060408385031215610ab657600080fd5b610abf83610a87565b946020939093013593505050565b600080600060608486031215610ae257600080fd5b610aeb84610a87565b9250610af960208501610a87565b9150604084013590509250925092565b600060208284031215610b1b57600080fd5b610a1882610a87565b60008060408385031215610b3757600080fd5b610b4083610a87565b915060208301358015158114610b5557600080fd5b809150509250929050565b600080600080600060808688031215610b7857600080fd5b610b8186610a87565b9450610b8f60208701610a87565b935060408601359250606086013567ffffffffffffffff80821115610bb357600080fd5b818801915088601f830112610bc757600080fd5b813581811115610bd657600080fd5b896020828501011115610be857600080fd5b9699959850939650602001949392505050565b60008060408385031215610c0e57600080fd5b610c1783610a87565b9150610c2560208401610a87565b9050925092905056fea264697066735822122025271acaeeef935a9720ae022d10da12004eb2b0c320597c15207a6090b472c964736f6c63430008170033360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a264697066735822122068fe8ed661781f83e21833256171d9de4991b910bb6a14409d2ea562e5b1beff64736f6c63430008170033
Deployed Bytecode
0x608060405260043610620002b35760003560e01c806370a08231116200016f578063b66a0e5d11620000d3578063f04e283e1162000085578063f04e283e1462000c2c578063f2fde38b1462000c43578063f406de2a1462000c5a578063f4a0a5281462000c7c578063f6ec89b81462000ca1578063fee81cf41462000cc657620002bb565b8063b66a0e5d1462000b57578063c39cbef11462000b6f578063c87b56dd1462000b94578063d52079b41462000bb9578063d5abeb011462000bd1578063dd62ed3e1462000be957620002bb565b806395d89b41116200012d57806395d89b411462000a88578063a0712d681462000aa0578063a475b5dd1462000ab7578063a9059cbb1462000ada578063ad3cb1cc1462000aff578063b089ae871462000b3257620002bb565b806370a0823114620009d4578063715018a61462000a26578063766453151462000a305780638da5cb5b1462000a485780638e0acd121462000a6357620002bb565b8063313ce567116200021757806352d1902d11620001d557806352d1902d146200093457806354d1f13d146200094c57806355f804b314620009565780635744cc0e146200097b5780636817c76c14620009975780636f8b44b014620009af57620002bb565b8063313ce567146200088257806336e8df9514620008a65780633ccfd60b14620008c95780634ef41efc14620008e15780634f1ef286146200091d57620002bb565b806323b872dd116200027157806323b872dd14620007bf5780632569296214620007e4578063274e430b14620007ee5780632a6a935d14620008135780632e37fc5a14620008385780632f52ebb7146200085d57620002bb565b806306fdde0314620006e3578063095ea7b3146200071357806316c44ae3146200074957806318160ddd146200076e5780631cbaee2d14620007a757620002bb565b36620002bb57005b68a20d6e21d0e525530860003560e01c63e5eb36c8819003620003405760018201546001600160a01b03163314620003065760405163ce5a776b60e01b815260040160405180910390fd5b60843610156200031557600080fd5b6004356024356044356064356200032f8484848462000cfd565b6200033b60016200106f565b505050505b8063813500fc03620003c75760018201546001600160a01b031633146200037a5760405163ce5a776b60e01b815260040160405180910390fd5b60643610156200038957600080fd5b6004356028818152604435601481905268a20d6e21d0e525530b6000908152604881209252602435151591829055620003c360016200106f565b5050505b8063e985e9c503620004425760018201546001600160a01b03163314620004015760405163ce5a776b60e01b815260040160405180910390fd5b60443610156200041057600080fd5b6024356028818152600435601481905260038501600090815260488120925290549091906200043f906200106f565b50505b80636352211e03620004ae5760018201546001600160a01b031633146200047c5760405163ce5a776b60e01b815260040160405180910390fd5b60243610156200048b57600080fd5b600435620004ac6200049d8262001079565b6001600160a01b03166200106f565b505b806324359879036200050b5760018201546001600160a01b03163314620004e85760405163ce5a776b60e01b815260040160405180910390fd5b6024361015620004f757600080fd5b600435620005096200049d82620010b5565b505b8063d10b6e0c03620005725760018201546001600160a01b03163314620005455760405163ce5a776b60e01b815260040160405180910390fd5b60643610156200055457600080fd5b6004356024356044356200056e6200049d8484846200110f565b5050505b8063081812fc03620005cf5760018201546001600160a01b03163314620005ac5760405163ce5a776b60e01b815260040160405180910390fd5b6024361015620005bb57600080fd5b600435620005cd6200049d8262001206565b505b8063f5b100ea036200065d5760018201546001600160a01b03163314620006095760405163ce5a776b60e01b815260040160405180910390fd5b60243610156200061857600080fd5b6004356200065b62000655826001600160a01b0316600090815268a20d6e21d0e5255312602052604090205463ffffffff600160801b9091041690565b6200106f565b505b8063e2c7928103620006c95760018201546001600160a01b03163314620006975760405163ce5a776b60e01b815260040160405180910390fd5b6004361015620006a657600080fd5b68a20d6e21d0e525530854620006c990600160601b900463ffffffff166200106f565b8063b7a94eb803620006e157620006e160016200106f565b005b348015620006f057600080fd5b50620006fb62001255565b6040516200070a919062003225565b60405180910390f35b3480156200072057600080fd5b50620007386200073236600462003272565b620012ef565b60405190151581526020016200070a565b3480156200075657600080fd5b50620006e162000768366004620032b1565b6200137c565b3480156200077b57600080fd5b5068a20d6e21d0e525530854600160801b90046001600160601b03165b6040519081526020016200070a565b348015620007b457600080fd5b506200079860055481565b348015620007cc57600080fd5b5062000738620007de366004620032cf565b6200139c565b620006e16200140e565b348015620007fb57600080fd5b50620007386200080d36600462003310565b6200145e565b3480156200082057600080fd5b5062000738620008323660046200332e565b620014ac565b3480156200084557600080fd5b50620006e16200085736600462003408565b620014c2565b3480156200086a57600080fd5b50620006e16200087c366004620034c2565b620016bf565b3480156200088f57600080fd5b5060125b60405160ff90911681526020016200070a565b348015620008b357600080fd5b506006546200073890600160a01b900460ff1681565b348015620008d657600080fd5b50620006e16200185d565b348015620008ee57600080fd5b5068a20d6e21d0e5255309546001600160a01b03165b6040516001600160a01b0390911681526020016200070a565b620006e16200092e36600462003546565b62001874565b3480156200094157600080fd5b506200079862001899565b620006e1620018b9565b3480156200096357600080fd5b50620006e162000975366004620035af565b620018f5565b3480156200098857600080fd5b50600a54620008939060ff1681565b348015620009a457600080fd5b506200079860085481565b348015620009bc57600080fd5b50620006e1620009ce36600462003626565b6200190e565b348015620009e157600080fd5b5062000798620009f336600462003310565b6001600160a01b0316600090815268a20d6e21d0e52553126020526040902054600160a01b90046001600160601b031690565b620006e16200191d565b34801562000a3d57600080fd5b50620006e162001933565b34801562000a5557600080fd5b50638b78c6d8195462000904565b34801562000a7057600080fd5b50620006e162000a8236600462003626565b62001952565b34801562000a9557600080fd5b50620006fb62001961565b620006e162000ab136600462003626565b62001972565b34801562000ac457600080fd5b506006546200073890600160a81b900460ff1681565b34801562000ae757600080fd5b506200073862000af936600462003272565b62001bdf565b34801562000b0c57600080fd5b50620006fb604051806040016040528060058152602001640352e302e360dc1b81525081565b34801562000b3f57600080fd5b50620006e162000b513660046200332e565b62001bf7565b34801562000b6457600080fd5b50620006e162001c1f565b34801562000b7c57600080fd5b50620006e162000b8e36600462003640565b62001c2f565b34801562000ba157600080fd5b50620006fb62000bb336600462003626565b62001cdb565b34801562000bc657600080fd5b506200079860035481565b34801562000bde57600080fd5b506200079860075481565b34801562000bf657600080fd5b506200079862000c0836600462003681565b602890815260149190915268a20d6e21d0e525530e60009081526048812091525490565b620006e162000c3d36600462003310565b62001df3565b620006e162000c5436600462003310565b62001e38565b34801562000c6757600080fd5b5060065462000904906001600160a01b031681565b34801562000c8957600080fd5b50620006e162000c9b36600462003626565b62001e64565b34801562000cae57600080fd5b50620006e162000cc036600462003310565b62001e73565b34801562000cd357600080fd5b506200079862000ce536600462003310565b63389a75e1600c908152600091909152602090205490565b68a20d6e21d0e52553086001600160a01b03841662000d2f57604051633a954ecd60e21b815260040160405180910390fd5b6009810160028201600062000d6b83600019880160011b5b60008160031c8360601b0180546007841660051b1c63ffffffff1691505092915050565b63ffffffff1681526020810191909152604001600020546001600160a01b0387811691161462000dad5760405162a1148160e81b815260040160405180910390fd5b856001600160a01b0316836001600160a01b03161462000e2657602883815260148790526003830160009081526048812091525460000362000e265760008481526004830160205260409020546001600160a01b0384811691161462000e2657604051632ce44b5f60e11b815260040160405180910390fd5b600062000e338762001e9f565b9050600062000e428762001e9f565b8254909150670de0b6b3a764000090600160a01b90046001600160601b03168082111562000e8357604051631e9acf1760e31b815260040160405180910390fd5b83546001600160601b03918390038216600160a01b9081026001600160a01b039283161786558454818104841685019093160291811691909117835589166000908152600786016020819052604090912062000ef6600588018a60609190911b600882901c0154600160ff9092161c1690565b1562000f35576005870160601b60088a901c018054600160ff8c161b191690556000898152600488016020526040902080546001600160a01b03191690555b845463ffffffff60801b198116600160801b9182900463ffffffff90811660001901808216909302919091178755606083901b631fffffff600384901c16015460009260e060059190911b161c169050600062000f9d886000198d01600190811b0162000d47565b905062000fb2838263ffffffff168462001efa565b62000fcf8860001963ffffffff851601600190811b018362001efa565b5050835463ffffffff60801b198116600160801b9182900463ffffffff908116600181019091169092021785556001600160a01b038b1660009081526020849052604090206200102190828c62001efa565b6200103a878b62001033888f62001f27565b8462001fbc565b505050806000528760601b60601c8960601b60601c6000805160206200476783398151915260206000a3505050505050505050565b8060005260206000f35b6000620010868262002001565b620010a45760405163677510db60e11b815260040160405180910390fd5b620010af82620010b5565b92915050565b600068a20d6e21d0e525530868a20d6e21d0e525530a82620010e868a20d6e21d0e5255311600019870160011b62000d47565b63ffffffff1681526020810191909152604001600020546001600160a01b03169392505050565b600068a20d6e21d0e525530868a20d6e21d0e525530a826200114268a20d6e21d0e5255311600019880160011b62000d47565b63ffffffff1681526020810191909152604001600020546001600160a01b03908116925083168214620011aa576028838152601483905260038201600090815260488120915254600003620011aa576040516367d9dca160e11b815260040160405180910390fd5b6000848152600482016020526040902080546001600160a01b0319166001600160a01b0387169081179091556005820160601b600886901c018054600160ff881690811b1991909116921515901b919091179055509392505050565b6000620012138262002001565b620012315760405163677510db60e11b815260040160405180910390fd5b50600090815268a20d6e21d0e525530c60205260409020546001600160a01b031690565b6060600080546200126690620036b9565b80601f01602080910402602001604051908101604052809291908181526020018280546200129490620036b9565b8015620012e55780601f10620012b957610100808354040283529160200191620012e5565b820191906000526020600020905b815481529060010190602001808311620012c757829003601f168201915b5050505050905090565b600654600090600160a01b900460ff16156200136957823b15620013695760405162461bcd60e51b815260206004820152602660248201527f417070726f76616c7320746f20636f6e74726163747320626c6f636b656420666044820152656f72206e6f7760d01b60648201526084015b60405180910390fd5b62001375838362002020565b9392505050565b620013866200202f565b600a805460ff191660ff92909216919091179055565b336028908152601484905268a20d6e21d0e525530e6000908152604881209181905281549091906000198114620013f55780841115620013ef576040516313be252b60e01b815260040160405180910390fd5b83810382555b620014028686866200204b565b50600195945050505050565b60006202a30067ffffffffffffffff164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b6001600160a01b038116600090815268a20d6e21d0e5255312602052604081208054600160581b900460011682036200149957823b62001375565b54600160581b9004600216151592915050565b6000620014ba3383620025e6565b506001919050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff16600081158015620015095750825b905060008267ffffffffffffffff166001148015620015275750303b155b90508115801562001536575080155b15620015555760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156200158057845460ff60401b1916600160401b1785555b6200158b3362002667565b62001595620026a3565b6200159f620026b7565b6000620015ad8d8262003749565b506001620015bc8c8262003749565b50600489905560078890556008879055600a805460ff881660ff199091161790556006805460ff60a01b1916600160a01b17905560405160009033906200160390620031f1565b6001600160a01b039091168152602001604051809103906000f08015801562001630573d6000803e3d6000fd5b5090506200164160003383620026c1565b620016693362001663670de0b6b3a76400006001600160601b038f166200382c565b6200287c565b508315620016b157845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050505050565b620016c962002ba9565b600554620016db906202a30062003846565b4210620017365760405162461bcd60e51b815260206004820152602260248201527f53616c65206973206e6f7420696e2074686520534849424f5348495320706861604482015261736560f01b606482015260840162001360565b3360009081526009602052604090205460ff16156200178a5760405162461bcd60e51b815260206004820152600f60248201526e105b1c9958591e4818db185a5b5959608a1b604482015260640162001360565b604080513360601b6bffffffffffffffffffffffff1916602082015260a085901b6001600160a01b0319811660348301528592909160009101604051602081830303815290604052805190602001209050620017eb85856004548462002be3565b62001809576040516309bde33960e01b815260040160405180910390fd5b620018223362001663670de0b6b3a7640000896200382c565b5050336000908152600960205260409020805460ff19166001179055506200185860016000805160206200478783398151915255565b505050565b620018676200202f565b620018723362002c34565b565b6200187e62002c51565b620018898262002cfa565b62001895828262002d04565b5050565b6000620018a562002dcb565b506000805160206200474783398151915290565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b620018ff6200202f565b6002620018588284836200385c565b620019186200202f565b600755565b620019276200202f565b62001872600062002e15565b6200193d6200202f565b6006805460ff60a81b1916600160a81b179055565b6200195c6200202f565b600455565b6060600180546200126690620036b9565b8060055460000362001997576040516316851a3760e11b815260040160405180910390fd5b3481600854620019a891906200382c565b14620019c65760405162bfc92160e01b815260040160405180910390fd5b60075481600354620019d9919062003846565b1115620019f957604051637be9badb60e01b815260040160405180910390fd5b62001a0362002ba9565b60055462001a15906202a30062003846565b421015801562001a36575060055462001a32906203f48062003846565b4211155b1562001bad576006546040516370a0823160e01b815233600482015260009182916001600160a01b03909116906370a0823190602401602060405180830381865afa15801562001a8a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001ab0919062003929565b1190508062001b025760405162461bcd60e51b815260206004820152601f60248201527f4f6e6c79204c4541534820686f6c646572732063616e206d696e74206e6f7700604482015260640162001360565b600a54336000908152600b602052604090205460ff9182169162001b299186911662003846565b111562001b6f5760405162461bcd60e51b8152602060048201526013602482015272135a5b9d081b1a5b5a5d08195e18d959591959606a1b604482015260640162001360565b336000908152600b60205260408120805485929062001b9390849060ff1662003943565b92506101000a81548160ff021916908360ff160217905550505b62001bc63362001663670de0b6b3a7640000856200382c565b6200189560016000805160206200478783398151915255565b600062001bee3384846200204b565b50600192915050565b62001c016200202f565b60068054911515600160a01b0260ff60a01b19909216919091179055565b62001c296200202f565b42600555565b3362001c3b8362001079565b6001600160a01b03161462001c9d5760405162461bcd60e51b815260206004820152602160248201527f596f7520617265206e6f7420746865206f776e6572206f662074686973204e466044820152601560fa1b606482015260840162001360565b817f8edfa912e70e283a8ef6d6f52cd1faef9690ff989eff2f11a134e8478ba7b28b8260405162001ccf919062003225565b60405180910390a25050565b6060600062001cea83620010b5565b6001600160a01b03160362001d125760405163677510db60e11b815260040160405180910390fd5b600654600160a81b900460ff161562001d5a57600262001d328362002e53565b60405160200162001d459291906200395f565b604051602081830303815290604052620010af565b6002805462001d6990620036b9565b80601f016020809104026020016040519081016040528092919081815260200182805462001d9790620036b9565b801562001de85780601f1062001dbc5761010080835404028352916020019162001de8565b820191906000526020600020905b81548152906001019060200180831162001dca57829003601f168201915b505050505092915050565b62001dfd6200202f565b63389a75e1600c52806000526020600c20805442111562001e2657636f5e88186000526004601cfd5b6000905562001e358162002e15565b50565b62001e426200202f565b8060601b62001e5957637448fbae6000526004601cfd5b62001e358162002e15565b62001e6e6200202f565b600855565b62001e7d6200202f565b600680546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b038116600090815268a20d6e21d0e52553126020526040812080549091600160581b909104600116900362001ef557805460ff60581b1916600160581b60ff843b151560020260011716021781555b919050565b8160031c8360601b016007831660051b815463ffffffff8482841c188116831b8218845550505050505050565b8154600160601b900463ffffffff1668a20d6e21d0e5255308600082900362001fb557805463ffffffff198116600163ffffffff928316019182169081178355855463ffffffff60601b1916600160601b82021786556000818152600284016020526040812080546001600160a01b0319166001600160a01b038816179055919350900362001fb557600080fd5b5092915050565b600183038060021c8560601b016003821660061b9150805467ffffffffffffffff8563ffffffff168560201b178083861c188216851b83188455505050505050505050565b6000806200200f83620010b5565b6001600160a01b0316141592915050565b600062001bee33848462002e98565b638b78c6d81954331462001872576382b429006000526004601cfd5b6001600160a01b0382166200207357604051633a954ecd60e21b815260040160405180910390fd5b68a20d6e21d0e525530860006200208a8562001e9f565b90506000620020998562001e9f565b9050620020e460405180610100016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b8254600160801b80820463ffffffff908116608085015284548290041660a08401528554046001600160601b0390811660c0840152600160a01b90910416604082018190528511156200214a57604051631e9acf1760e31b815260040160405180910390fd5b6040810180518690039081905283546001600160601b03918216600160a01b9081026001600160a01b03928316178655845481810484168901606086018190529093160291161782556080810151620021cc90620021ad670de0b6b3a764000090565b836040015181620021c257620021c2620039f0565b0480821191030290565b81528154600160581b90046002166000036200224157856001600160a01b0316876001600160a01b0316036200220a57805160808201510360a08201525b6200223b670de0b6b3a76400008260600151816200222c576200222c620039f0565b048260a0015180821191030290565b60208201525b80516020820151855463ffffffff600160601b8083048216840185900360e087018190529091160263ffffffff60601b1990911617865560009162002287910162002efa565b855483519192506009870191600160401b90910463ffffffff169015620023db5760608a901b60011760208401526000620022cb8560e001518660c0015162002f42565b6001600160a01b038c16600090815260078a0160205260409020608087015187518a5463ffffffff918303918216600160801b0263ffffffff60801b19909116178b5592935090915b60001991909101600381901c606084901b0154909190600583901b60e0161c63ffffffff1662002348878260008062001fbc565b87516020808a0151600884901b17825201885284156200237857620023788c600801878060010198508362001efa565b600881901c60058d0160601b015460ff82161c60011615620023cd5760058c0160601b600882901c018054600160ff84161b19169055600081815260048d016020526040902080546001600160a01b03191690555b508082036200231457505050505b6020840151156200257057606089901b602084015286546001600160a01b038a1660009081526007890160209081526040822060a08801519188015164010000000090940463ffffffff1693909290820190620024398a8f62001f27565b90506000670de0b6b3a76400008a60c00151816200245b576200245b620039f0565b8c5463ffffffff60801b1916600160801b63ffffffff871602178d550490505b60008715620024b95760001997909701600381901c60088f0160601b0154909790600589901b60e0161c63ffffffff1663ffffffff169050620024fd565b50855b620024cf89600019830160011b62000d47565b63ffffffff1615620024ed57600101818111801590910217620024bc565b6001810182811180159091021796505b6200250a86868362001efa565b6200251e8982858880600101995062001fbc565b89516020808c0151600884901b178252018a52508284036200247b57600786018181118015909102178d5463ffffffff919091166401000000000267ffffffff0000000019909116178d555050505050505b60408301515115620025b357865463ffffffff60401b1916600160401b63ffffffff8316021787556001870154620025b39084906001600160a01b031662002f71565b505050846000528560601b60601c8760601b60601c6000805160206200476783398151915260206000a350505050505050565b6000620025f38362001e9f565b8054909150600160581b90046002161515821515146200262f57805460ff600160581b80830482166002189091160260ff60581b199091161781555b8115156000528260601b60601c7fb5a1de456fff688115a4f75380060c23c8532d14ff85f687cc871456d642039360206000a2505050565b6001600160a01b0316638b78c6d8198190558060007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a350565b620026ad62002fae565b6200187262002ff8565b6200187262002fae565b68a20d6e21d0e52553088054640100000000900463ffffffff1615620026fa57604051633ab534b960e21b815260040160405180910390fd5b6001600160a01b03821662002722576040516339a84a7b60e01b815260040160405180910390fd5b630f4599e560005233602052602060006024601c6000865af160016000511416620027555763d125259c6000526004601cfd5b805467ffffffff0000000019166401000000001781556001810180546001600160a01b0384166001600160a01b0319909116179055831562002876576001600160a01b038316620027b957604051633a954ecd60e21b815260040160405180910390fd5b6001600160601b03841163fffffffe670de0b6b3a76400008604111715620027f45760405163e5cfe95760e01b815260040160405180910390fd5b80546bffffffffffffffffffffffff60801b1916600160801b6001600160601b038616021781556000620028288462001e9f565b80546001600160601b038716600160a01b026001600160a01b03918216178255600087815291925085169060008051602062004767833981519152602082a362002874846001620025e6565b505b50505050565b6001600160a01b038216620028a457604051633a954ecd60e21b815260040160405180910390fd5b68a20d6e21d0e52553086000620028bb8462001e9f565b82546bffffffffffffffffffffffff60801b198116600160801b918290046001600160601b039081168701808216909302919091178555919250600091811163fffffffe670de0b6b3a7640000830411171515858210811715620029325760405163e5cfe95760e01b815260040160405180910390fd5b5082546001600160601b03600160a01b80830482168801918216026001600160a01b0390921691909117808555670de0b6b3a764000092839004935091900490600160581b900460021660000362002b7d576001600160a01b03861660009081526007850160205260408120845490916009870191600160801b900463ffffffff1690620029c68286038387110262002efa565b905080604001515160001462002b785760608a901b6020820152875463ffffffff600160401b8204811691640100000000900416600062002a088a8e62001f27565b6040850151518c5463ffffffff600160601b8083048216909301811690920263ffffffff60601b19909116178d558b54908a16600160801b0263ffffffff60801b19909116178b5590505b6000831562002a915760001993909301600381901c60088d0160601b0154909390600585901b60e0161c63ffffffff1663ffffffff16905062002ad5565b50815b62002aa787600019830160011b62000d47565b63ffffffff161562002ac55760010189811180159091021762002a94565b600181018a811180159091021792505b62002ae288878362001efa565b62002af687828489806001019a5062001fbc565b8451602080870151600884901b1782520185525087850362002a5357600782018981118015909102178b546bffffffffffffffff00000000191664010000000063ffffffff9283160263ffffffff60401b191617600160401b91851691909102178b5560018b015462002b749085906001600160a01b031662002f71565b5050505b505050505b505060008381526001600160a01b0385169060008051602062004767833981519152602082a350505050565b6000805160206200478783398151915280546001190162002bdd57604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b6000831562002c17578360051b8501855b803580851160051b9485526020948518526040600020930181811062002bf45750505b501492915050565b60016000805160206200478783398151915255565b60003860003847855af162001e355763b12d13eb6000526004601cfd5b306001600160a01b037f0000000000000000000000001b8e7475b91e2303885c3cab1ef9725f3ce2417416148062002cdb57507f0000000000000000000000001b8e7475b91e2303885c3cab1ef9725f3ce241746001600160a01b031662002ccf60008051602062004747833981519152546001600160a01b031690565b6001600160a01b031614155b15620018725760405163703e46dd60e11b815260040160405180910390fd5b62001e356200202f565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa92505050801562002d61575060408051601f3d908101601f1916820190925262002d5e9181019062003929565b60015b62002d8b57604051634c9c8ce360e01b81526001600160a01b038316600482015260240162001360565b60008051602062004747833981519152811462002dbf57604051632a87526960e21b81526004810182905260240162001360565b62001858838362003002565b306001600160a01b037f0000000000000000000000001b8e7475b91e2303885c3cab1ef9725f3ce241741614620018725760405163703e46dd60e11b815260040160405180910390fd5b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b60606080604051019050602081016040526000815280600019835b928101926030600a8206018453600a90048062002e6e575050819003601f19909101908152919050565b6028828152601484905268a20d6e21d0e525530e600090815260488120915281905560008181526001600160a01b0380841691908516907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590602090a3505050565b62002f1f60405180606001604052806000815260200160008152602001606081525090565b604051828152806020018360051b81016040528183604001528083525050919050565b600080600162002f5b670de0b6b3a76400008562003a06565b901c905060808111818511161515949350505050565b60408201516040810363263c69d68152602080820152815160051b604401915060208183601c84016000875af16001825114166200287657600081fd5b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff166200187257604051631afcd79f60e31b815260040160405180910390fd5b62002c1f62002fae565b6200300d826200305f565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a28051156200305557620018588282620030c7565b6200189562003143565b806001600160a01b03163b6000036200309757604051634c9c8ce360e01b81526001600160a01b038216600482015260240162001360565b6000805160206200474783398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080846001600160a01b031684604051620030e6919062003a29565b600060405180830381855af49150503d806000811462003123576040519150601f19603f3d011682016040523d82523d6000602084013e62003128565b606091505b50915091506200313a85838362003163565b95945050505050565b3415620018725760405163b398979f60e01b815260040160405180910390fd5b6060826200317c576200317682620031c7565b62001375565b81511580156200319457506001600160a01b0384163b155b15620031bf57604051639996b31560e01b81526001600160a01b038516600482015260240162001360565b508062001375565b805115620031d85780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b610cff8062003a4883390190565b60005b838110156200321c57818101518382015260200162003202565b50506000910152565b602081526000825180602084015262003246816040850160208701620031ff565b601f01601f19169190910160400192915050565b80356001600160a01b038116811462001ef557600080fd5b600080604083850312156200328657600080fd5b62003291836200325a565b946020939093013593505050565b803560ff8116811462001ef557600080fd5b600060208284031215620032c457600080fd5b62001375826200329f565b600080600060608486031215620032e557600080fd5b620032f0846200325a565b925062003300602085016200325a565b9150604084013590509250925092565b6000602082840312156200332357600080fd5b62001375826200325a565b6000602082840312156200334157600080fd5b813580151581146200137557600080fd5b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff8084111562003386576200338662003352565b604051601f8501601f19908116603f01168101908282118183101715620033b157620033b162003352565b81604052809350858152868686011115620033cb57600080fd5b858560208301376000602087830101525050509392505050565b600082601f830112620033f757600080fd5b620013758383356020850162003368565b600080600080600080600060e0888a0312156200342457600080fd5b873567ffffffffffffffff808211156200343d57600080fd5b6200344b8b838c01620033e5565b985060208a01359150808211156200346257600080fd5b50620034718a828b01620033e5565b96505060408801356001600160601b03811681146200348f57600080fd5b9450606088013593506080880135925060a08801359150620034b460c089016200329f565b905092959891949750929550565b600080600060408486031215620034d857600080fd5b83359250602084013567ffffffffffffffff80821115620034f857600080fd5b818601915086601f8301126200350d57600080fd5b8135818111156200351d57600080fd5b8760208260051b85010111156200353357600080fd5b6020830194508093505050509250925092565b600080604083850312156200355a57600080fd5b62003565836200325a565b9150602083013567ffffffffffffffff8111156200358257600080fd5b8301601f810185136200359457600080fd5b620035a58582356020840162003368565b9150509250929050565b60008060208385031215620035c357600080fd5b823567ffffffffffffffff80821115620035dc57600080fd5b818501915085601f830112620035f157600080fd5b8135818111156200360157600080fd5b8660208285010111156200361457600080fd5b60209290920196919550909350505050565b6000602082840312156200363957600080fd5b5035919050565b600080604083850312156200365457600080fd5b82359150602083013567ffffffffffffffff8111156200367357600080fd5b620035a585828601620033e5565b600080604083850312156200369557600080fd5b620036a0836200325a565b9150620036b0602084016200325a565b90509250929050565b600181811c90821680620036ce57607f821691505b602082108103620036ef57634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111562001858576000816000526020600020601f850160051c81016020861015620037205750805b601f850160051c820191505b8181101562003741578281556001016200372c565b505050505050565b815167ffffffffffffffff81111562003766576200376662003352565b6200377e81620037778454620036b9565b84620036f5565b602080601f831160018114620037b657600084156200379d5750858301515b600019600386901b1c1916600185901b17855562003741565b600085815260208120601f198616915b82811015620037e757888601518255948401946001909101908401620037c6565b5085821015620038065787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417620010af57620010af62003816565b80820180821115620010af57620010af62003816565b67ffffffffffffffff83111562003877576200387762003352565b6200388f83620038888354620036b9565b83620036f5565b6000601f841160018114620038c65760008515620038ad5750838201355b600019600387901b1c1916600186901b17835562002874565b600083815260209020601f19861690835b82811015620038f95786850135825560209485019460019092019101620038d7565b5086821015620039175760001960f88860031b161c19848701351681555b505060018560011b0183555050505050565b6000602082840312156200393c57600080fd5b5051919050565b60ff8181168382160190811115620010af57620010af62003816565b60008084546200396f81620036b9565b600182811680156200398a5760018114620039a057620039d1565b60ff1984168752821515830287019450620039d1565b8860005260208060002060005b85811015620039c85781548a820152908401908201620039ad565b50505082870194505b505050508351620039e7818360208801620031ff565b01949350505050565b634e487b7160e01b600052601260045260246000fd5b60008262003a2457634e487b7160e01b600052601260045260246000fd5b500490565b6000825162003a3d818460208701620031ff565b919091019291505056fe608060405234801561001057600080fd5b50604051610cff380380610cff83398101604081905261002f9161005c565b683602298b8c10b0123180546001600160a01b0319166001600160a01b039290921691909117905561008c565b60006020828403121561006e57600080fd5b81516001600160a01b038116811461008557600080fd5b9392505050565b610c648061009b6000396000f3fe60806040526004361061010d5760003560e01c80636cef16e61161009557806397e5311c1161006457806397e5311c1461044f578063a22cb46514610464578063b88d4fde14610484578063c87b56dd146104a4578063e985e9c5146104c457610114565b80636cef16e6146103df57806370a08231146103f45780638da5cb5b1461041457806395d89b411461043a57610114565b806318160ddd116100dc57806318160ddd1461034957806323b872dd1461036c578063243598791461038c57806342842e0e146103ac5780636352211e146103bf57610114565b806301ffc9a71461027d57806306fdde03146102cf578063081812fc146102f1578063095ea7b31461032957610114565b3661011457005b683602298b8c10b0123060003560e01c63263c69d68190036101e05781546001600160a01b0316331461015a57604051631b1e598960e11b815260040160405180910390fd5b602036103d60003e6004356024018036103d60003e602081033560051b81018036103d60003e5b8082146101d35781358060601c816001168260a01b60a81c811583028284027fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600038a4505050816020019150610181565b5050600160005260206000f35b80630f4599e50361027b5760018201546001600160a01b0316156102365760018201546001600160a01b03166004356001600160a01b031614610236576040516362cf623d60e11b815260040160405180910390fd5b81546001600160a01b03161561025f57604051635fb2b52360e11b815260040160405180910390fd5b81546001600160a01b0319163317825560016000908152602090f35b005b34801561028957600080fd5b506102ba6102983660046109ee565b6301ffc9a760e09190911c9081146380ac58cd821417635b5e139f9091141790565b60405190151581526020015b60405180910390f35b3480156102db57600080fd5b506102e46104e4565b6040516102c69190610a1f565b3480156102fd57600080fd5b5061031161030c366004610a6e565b6104fa565b6040516001600160a01b0390911681526020016102c6565b34801561033557600080fd5b5061027b610344366004610aa3565b610512565b34801561035557600080fd5b5061035e610598565b6040519081526020016102c6565b34801561037857600080fd5b5061027b610387366004610acd565b6105aa565b34801561039857600080fd5b506103116103a7366004610a6e565b610639565b61027b6103ba366004610acd565b61064b565b3480156103cb57600080fd5b506103116103da366004610a6e565b61067d565b3480156103eb57600080fd5b5061027b61068f565b34801561040057600080fd5b5061035e61040f366004610b09565b610742565b34801561042057600080fd5b50683602298b8c10b01232546001600160a01b0316610311565b34801561044657600080fd5b506102e461075d565b34801561045b57600080fd5b5061031161076e565b34801561047057600080fd5b5061027b61047f366004610b24565b6107a3565b34801561049057600080fd5b5061027b61049f366004610b60565b610826565b3480156104b057600080fd5b506102e46104bf366004610a6e565b610881565b3480156104d057600080fd5b506102ba6104df366004610bfb565b610891565b60606104f56306fdde0360006108bd565b905090565b600061050c63081812fc83600061091a565b92915050565b600061051c61076e565b90508260601b60601c925060405163d10b6e0c600052836020528260405233606052602060006064601c34865af1601f3d111661055c573d6000823e3d81fd5b806040525060006060528183600c5160601c7f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600038a4505050565b60006104f563e2c7928160008061091a565b60006105b461076e565b90508360601b60601c93508260601b60601c925060405163e5eb36c881528460208201528360408201528260608201523360808201526020816084601c840134865af160018251141661060a573d6000823e3d81fd5b508183857fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600038a450505050565b600061050c632435987983600061091a565b6106568383836105aa565b813b156106785761067883838360405180602001604052806000815250610962565b505050565b600061050c636352211e83600061091a565b60008061069a61076e565b9050638da5cb5b600052602060006004601c845afa601f3d1116156106c257600c5160601c91505b683602298b8c10b0123254683602298b8c10b01230906001600160a01b03908116908416811461073c576002820180546001600160a01b0319166001600160a01b0386811691821790925560405190918316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35b50505050565b600061050c63f5b100ea836001600160a01b0316600061091a565b60606104f56395d89b4160006108bd565b683602298b8c10b01230546001600160a01b0316806107a057604051632d9523d760e11b815260040160405180910390fd5b90565b60006107ad61076e565b90508260601b60601c925060405163813500fc6000528360205282151560405233606052602060006064601c34865af1600160005114166107f1573d6000823e3d81fd5b83337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160206040a36040525050600060605250565b6108318585856105aa565b833b1561087a5761087a85858585858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061096292505050565b5050505050565b606061050c63c87b56dd836108bd565b60006108b463e985e9c5846001600160a01b0316846001600160a01b031661091a565b15159392505050565b606060006108c961076e565b9050604051915083600052826020526000806024601c845afa6108ef573d6000833e3d82fd5b60206000803e6020600051833e8151602060005101602084013e815160208301016040525092915050565b60008061092561076e565b9050604051856000528460205283604052602060006044601c855afa601f3d1116610953573d6000823e3d81fd5b60405250506000519392505050565b60405163150b7a028082523360208301528560601b60601c604083015283606083015260808083015282518060a084015280156109a9578060c08401826020870160045afa505b60208360a48301601c860160008a5af16109cc573d156109cc573d6000843e3d83fd5b508060e01b8251146109e65763d1a57ed66000526004601cfd5b505050505050565b600060208284031215610a0057600080fd5b81356001600160e01b031981168114610a1857600080fd5b9392505050565b60006020808352835180602085015260005b81811015610a4d57858101830151858201604001528201610a31565b506000604082860101526040601f19601f8301168501019250505092915050565b600060208284031215610a8057600080fd5b5035919050565b80356001600160a01b0381168114610a9e57600080fd5b919050565b60008060408385031215610ab657600080fd5b610abf83610a87565b946020939093013593505050565b600080600060608486031215610ae257600080fd5b610aeb84610a87565b9250610af960208501610a87565b9150604084013590509250925092565b600060208284031215610b1b57600080fd5b610a1882610a87565b60008060408385031215610b3757600080fd5b610b4083610a87565b915060208301358015158114610b5557600080fd5b809150509250929050565b600080600080600060808688031215610b7857600080fd5b610b8186610a87565b9450610b8f60208701610a87565b935060408601359250606086013567ffffffffffffffff80821115610bb357600080fd5b818801915088601f830112610bc757600080fd5b813581811115610bd657600080fd5b896020828501011115610be857600080fd5b9699959850939650602001949392505050565b60008060408385031215610c0e57600080fd5b610c1783610a87565b9150610c2560208401610a87565b9050925092905056fea264697066735822122025271acaeeef935a9720ae022d10da12004eb2b0c320597c15207a6090b472c964736f6c63430008170033360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a264697066735822122068fe8ed661781f83e21833256171d9de4991b910bb6a14409d2ea562e5b1beff64736f6c63430008170033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ 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.