Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
CollectionImplementation
Compiler Version
v0.8.20+commit.a1b79de6
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.20;
// OpenZeppelin
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
// DN404
import "../DN404/DN404.sol";
// Own Interfaces
import "./interfaces/ICollectionImpl.sol";
import "./interfaces/ITokenUri.sol";
import "./interfaces/IRouter.sol";
// Blast
import "../Blast/BlastBaseUpgradeable.sol";
contract CollectionImplementation is
Initializable,
DN404,
OwnableUpgradeable,
ReentrancyGuardUpgradeable,
BlastBaseUpgradeable,
ICollectionImpl
{
using Strings for uint256;
/************************
* Events *
***********************/
event PaymentReceived(address from, uint256 amount);
event Buy(
address indexed buyer,
uint256 amount,
uint256 price,
uint256 protocolFee,
uint256 creatorFee
);
event Sell(
address indexed seller,
uint256 amount,
uint256 price,
uint256 protocolFee,
uint256 creatorFee
);
event DeflationarySell(
address indexed seller,
uint256 amount,
uint256 price,
uint256 protocolFee,
uint256 creatorFee
);
event CreatorFeePercentageChanged(uint256 newCreatorFeePercentage);
event CreatorFeeFrozen();
event BaseUriChanged(string newBaseUri);
event TokenUriContractChanged(address newTokenUriContract);
event MetadataFrozen();
event WhitelistDisabled();
event WhitelistModified(address[] addresses, bool isWhitelisted);
event WhitelistMintLimitChanged(uint256 newWhitelistMintLimit);
/***********************
* Constants *
***********************/
uint256 public constant MAX_NAME_LENGTH = 30;
uint256 public constant MAX_SYMBOL_LENGTH = 10;
// limited by dn404
uint256 public constant MAX_NFT_SUPPLY = 2 ** 32 - 2;
uint256 public constant MAX_UNITS = 100_000 ether;
/***********************
* Immutable State (set at init)*
***********************/
/* Bonding Curve */
BondingCurveSpecs public bondingCurveSpecs;
/* Shared Ownership */
address payable public creatorVault;
/* Roles */
address public router;
/* DN404 */
uint256 public units;
/***********************
* Mutable State *
***********************/
string private _name;
string private _symbol;
/* ERC721 */
string public baseUri;
address public tokenUriContract;
address public tokenUriFallbackContract;
// supply limit. MAX_NFT_SUPPLY * _uint() is the maximum value for maxSupply
uint256 public maxSupply;
/* Whitelist */
// if enabled, only whitelisted addresses can mint. Must be set at init
bool public isWhitelistEnabled;
mapping(address => bool) public whitelist;
uint256 public whitelistMintLimit;
mapping(address => uint256) public whitelistMinted;
/**
* @dev We track how much supply is on the bonding curve for a given collection.
* This is not always equal to the total supply of the collection,
* because some tokens may have been burned.
*/
uint256 public bondingCurveSupply;
bool public metadataFreeze;
bool public bondingCurveFreeze;
uint256 public creatorFeePercentage; // 1e18 = 100%
// deflationary bonding curve
bool public isDeflationary;
uint256 public deflationBurnCount;
/*********************************
* Constructur & Initializer *
********************************/
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize(
InitializeParams memory params
) public override initializer {
uint256 nameLength = _utfStringLength(params.collectionSpecs.name_);
require(
nameLength > 0 && nameLength <= MAX_NAME_LENGTH,
"Invalid name length"
);
uint256 symbolLength = _utfStringLength(params.collectionSpecs.symbol_);
require(
symbolLength > 0 && symbolLength <= MAX_SYMBOL_LENGTH,
"Invalid symbol length"
);
/* name & symbol */
_name = params.collectionSpecs.name_;
_symbol = params.collectionSpecs.symbol_;
/* DN404 */
require(
params.collectionSpecs.units_ > 0 &&
params.collectionSpecs.units_ <= MAX_UNITS,
"CollectionImpl: invalid units"
);
units = params.collectionSpecs.units_;
/* Other */
__Ownable_init(params.creator);
__BlastBase_init(
params.gasClaimer,
params.creator,
params.blastPoints,
params.pointsOperator
);
__ReentrancyGuard_init();
/* DN404 */
_initializeDN404(0, address(0), params.dn404Mirror);
/* erc721 */
baseUri = params.collectionSpecs.baseUri_;
tokenUriContract = params.collectionSpecs.tokenUriContract_;
tokenUriFallbackContract = params.tokenUriFallbackContract;
/* bonding curve */
bondingCurveSpecs = params.bondingCurveSpecs;
/* shared ownership */
creatorVault = payable(params.creatorVault);
/* roles */
router = params.router;
creatorFeePercentage = 0.05 * 1e18; // 5%
if (params.collectionSpecs.maxNftSupply_ > 0) {
require(
params.collectionSpecs.maxNftSupply_ <= MAX_NFT_SUPPLY,
"CollectionImpl: maxSupply too high"
);
maxSupply = params.collectionSpecs.maxNftSupply_ * _unit();
} else {
maxSupply = MAX_NFT_SUPPLY * _unit();
}
isWhitelistEnabled = params.collectionSpecs.useWhitelist_;
whitelistMintLimit = params.collectionSpecs.whitelistMintLimit_;
isDeflationary = params.collectionSpecs.isDeflationary_;
}
/*************************
* Mint & Burn Functions *
*************************/
function _mintMultipleTo(address to, uint256 nftAmount) internal {
uint256 amount = nftAmount * _unit();
require(
totalSupply() + deflationBurnCount * _unit() + amount <= maxSupply,
"CollectionImpl: max supply reached"
);
_mint(to, amount);
}
function _bondingCurveBurnMultiple(uint256[] memory tokenIds_) internal {
for (uint256 i = 0; i < tokenIds_.length; i++) {
uint256 tokenId = tokenIds_[i];
address owner = _ownerOf(tokenIds_[i]);
// to burn specific NFTs, we need to transfer them to this contract first (due to dn404)
_initiateTransferFromNFT(owner, address(this), tokenId, msg.sender);
}
_burn(address(this), tokenIds_.length * _unit());
}
/***********************
* Owner Functions *
***********************/
/**
* @dev Sets the creator fee that is charged on every buy and sell. The fee is scaled to 1e18 = 100%.
* Has to be smaller than 10%
*/
function setCreatorFee(uint256 creatorFeePercentage_) external onlyOwner {
require(
creatorFeePercentage_ <= 10 * 1e18,
"CollectionImpl: creator fee percentage must be <= 10%"
);
// if frozen, the creator fee can only be lowered
if (bondingCurveFreeze) {
require(
creatorFeePercentage_ <= creatorFeePercentage,
"CollectionImpl: creator fee can only be lowered"
);
}
creatorFeePercentage = creatorFeePercentage_;
emit CreatorFeePercentageChanged(creatorFeePercentage_);
}
function freezeCreatorFee() external onlyOwner {
bondingCurveFreeze = true;
emit CreatorFeeFrozen();
}
function setBaseUri(string memory baseUri_) external onlyOwner {
if (metadataFreeze) {
revert("CollectionImpl: metadata freeze");
}
baseUri = baseUri_;
emit BaseUriChanged(baseUri_);
}
function setTokenUriContract(address tokenUriContract_) external onlyOwner {
if (metadataFreeze) {
revert("CollectionImpl: metadata freeze");
}
tokenUriContract = tokenUriContract_;
emit TokenUriContractChanged(tokenUriContract_);
}
function freezeMetadata() external onlyOwner {
metadataFreeze = true;
emit MetadataFrozen();
}
function disableWhitelist() external onlyOwner {
isWhitelistEnabled = false;
emit WhitelistDisabled();
}
function modifyWhitelist(
address[] memory addresses_,
bool isWhitelisted_
) external onlyOwner {
require(isWhitelistEnabled, "CollectionImpl: whitelist is not enabled");
for (uint256 i = 0; i < addresses_.length; ) {
whitelist[addresses_[i]] = isWhitelisted_;
unchecked {
i++;
}
}
emit WhitelistModified(addresses_, isWhitelisted_);
}
function setWhitelistMintLimit(
uint256 whitelistMintLimit_
) external onlyOwner {
require(isWhitelistEnabled, "CollectionImpl: whitelist is not enabled");
whitelistMintLimit = whitelistMintLimit_;
emit WhitelistMintLimitChanged(whitelistMintLimit_);
}
/************************
* Buy and Sell *
***********************/
function buy(
uint256 nftAmount,
uint256 deadline
) external payable nonReentrant {
require(nftAmount > 0, "CollectionImpl: amount must be > 0");
if (isWhitelistEnabled) {
require(whitelist[msg.sender], "CollectionImpl: not whitelisted");
if (whitelistMintLimit > 0) {
require(
whitelistMinted[msg.sender] + nftAmount <=
whitelistMintLimit,
"CollectionImpl: whitelist mint limit reached"
);
whitelistMinted[msg.sender] += nftAmount;
}
}
require(block.timestamp <= deadline, "CollectionImpl: deadline passed");
// get the price and fees
uint256 price = getPrice(bondingCurveSupply, nftAmount);
ProtocolFeeSpecs memory protocolFeeSpecs = _getProtocolFeeSpecs();
uint256 protocolFee = (price * protocolFeeSpecs.protocolFeePercentage) /
1e18;
uint256 creatorFee = (price * creatorFeePercentage) / 1e18;
// check if the user sent enough eth incl fees
require(
msg.value >= price + protocolFee + creatorFee,
"CollectionImpl: not enough eth sent"
);
bondingCurveSupply += nftAmount;
// mint the nfts to the user
_mintMultipleTo(msg.sender, nftAmount);
// send out the fees
Address.sendValue(
payable(protocolFeeSpecs.protocolFeeCollector),
protocolFee
);
Address.sendValue(creatorVault, creatorFee);
// send the dust back to the sender
uint256 dust = msg.value - price - protocolFee - creatorFee;
if (dust > 0) {
Address.sendValue(payable(msg.sender), dust);
}
emit Buy(msg.sender, nftAmount, price, protocolFee, creatorFee);
}
function sell(
uint256[] memory tokenIds_,
uint256 minPrice_, // min total eth the user wants to receive
uint256 deadline_
) external nonReentrant {
uint256 amount = tokenIds_.length;
require(amount > 0, "CollectionImpl: no tokenIds");
require(amount <= 100, "CollectionImpl: max 100 tokens");
require(
block.timestamp <= deadline_,
"CollectionImpl: deadline passed"
);
// get the price and fees
uint256 price = getPrice(bondingCurveSupply - amount, amount);
ProtocolFeeSpecs memory protocolFeeSpecs = _getProtocolFeeSpecs();
uint256 protocolFee = (price * protocolFeeSpecs.protocolFeePercentage) /
1e18;
uint256 creatorFee = (price * creatorFeePercentage) / 1e18;
require(
price - protocolFee - creatorFee >= minPrice_,
"CollectionImpl: price too low"
);
bondingCurveSupply -= amount;
// burn the NFTs from the owner
_bondingCurveBurnMultiple(tokenIds_);
// send out the fees
Address.sendValue(
payable(protocolFeeSpecs.protocolFeeCollector),
protocolFee
);
Address.sendValue(creatorVault, creatorFee);
// send the price minus the fees back
Address.sendValue(
payable(msg.sender),
price - protocolFee - creatorFee
);
emit Sell(msg.sender, amount, price, protocolFee, creatorFee);
}
function deflationarySell(
uint256[] memory tokenIds_
) external nonReentrant {
require(isDeflationary, "CollectionImpl: not deflationary");
uint256 amount = tokenIds_.length;
require(amount > 0, "CollectionImpl: no tokenIds");
require(amount <= 100, "CollectionImpl: max 100 tokens");
// get the price and fees
uint256 price = getPrice(deflationBurnCount, amount);
ProtocolFeeSpecs memory protocolFeeSpecs = _getProtocolFeeSpecs();
uint256 protocolFee = (price * protocolFeeSpecs.protocolFeePercentage) /
1e18;
uint256 creatorFee = (price * creatorFeePercentage) / 1e18;
// burn the NFTs from the owner
_bondingCurveBurnMultiple(tokenIds_);
deflationBurnCount += tokenIds_.length;
// send out the fees
Address.sendValue(
payable(protocolFeeSpecs.protocolFeeCollector),
protocolFee
);
Address.sendValue(creatorVault, creatorFee);
// send the price minus the fees back
Address.sendValue(
payable(msg.sender),
price - protocolFee - creatorFee
);
emit DeflationarySell(
msg.sender,
amount,
price,
protocolFee,
creatorFee
);
}
/********************************
* Price Related calculations *
*******************************/
// returns the price for a given supply and purchase-amount
function getPrice(
uint256 supply,
uint256 amount
) public view returns (uint256) {
// integral(supply): supply * ((factor * supply ** exponent) / (exponent + 1) + c)
// price: integral(supply + amount) - integral(supply)
// This formula is derived from the above calculation.
uint256 ePlusOne = bondingCurveSpecs.exponent + 1;
// prettier-ignore
return ((supply + amount) ** ePlusOne - supply ** ePlusOne)
* bondingCurveSpecs.factor / ePlusOne
+ bondingCurveSpecs.c * amount;
}
function getBuyPriceExclusiveFees(
uint256 amount_
) public view returns (uint256) {
uint256 supply = bondingCurveSupply;
uint256 price = getPrice(supply, amount_);
return price;
}
function getSellPriceExclusiveFees(
uint256 amount_
) public view returns (uint256) {
uint256 supply = bondingCurveSupply;
uint256 price = getPrice(supply - amount_, amount_);
uint256 protocolFee = (price * getProtocolFeePercentage()) / 1e18;
uint256 creatorFee = (price * creatorFeePercentage) / 1e18;
return price - protocolFee - creatorFee;
}
function getBuyPriceInclFees(
uint256 amount_
) public view returns (uint256) {
uint256 supply = bondingCurveSupply;
uint256 price = getPrice(supply, amount_);
uint256 protocolFee = (price * getProtocolFeePercentage()) / 1e18;
uint256 creatorFee = (price * creatorFeePercentage) / 1e18;
return price + protocolFee + creatorFee;
}
function getSellPriceInclFees(
uint256 amount_
) public view returns (uint256) {
uint256 supply = bondingCurveSupply;
uint256 price = getPrice(supply - amount_, amount_);
return price;
}
function getProtocolFeePercentage() public view returns (uint256) {
return _getProtocolFeeSpecs().protocolFeePercentage;
}
/************************
* Internal
***********************/
function _getProtocolFeeSpecs()
internal
view
returns (ProtocolFeeSpecs memory)
{
return IRouter(router).protocolFeeSpecs();
}
/************************
* Receive Function *
***********************/
receive() external payable override {
emit PaymentReceived(msg.sender, msg.value);
}
/************************
* Other *
***********************/
function name() public view virtual override returns (string memory) {
return _name;
}
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
function _tokenURI(
uint256 id
) internal view virtual override returns (string memory) {
if (tokenUriContract != address(0)) {
return ITokenUri(tokenUriContract).tokenURI(id, address(this));
} else if (bytes(baseUri).length > 0) {
return string.concat(baseUri, id.toString());
} else {
return
ITokenUri(tokenUriFallbackContract).tokenURI(id, address(this));
}
}
/// @dev Amount of token balance that is equal to one NFT.
function _unit() internal view virtual override returns (uint256) {
return units;
}
function _utfStringLength(string memory str) internal pure returns (uint length) {
uint256 i = 0;
bytes memory string_rep = bytes(str);
while (i < string_rep.length) {
if (string_rep[i] >> 7 == 0)
i += 1;
else if (string_rep[i] >> 5 == bytes1(uint8(0x6)))
i += 2;
else if (string_rep[i] >> 4 == bytes1(uint8(0xE)))
i += 3;
else if (string_rep[i] >>3 == bytes1(uint8(0x1E)))
i += 4;
else
i += 1;
length++;
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Ownable
struct OwnableStorage {
address _owner;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;
function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
assembly {
$.slot := OwnableStorageLocation
}
}
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
function __Ownable_init(address initialOwner) internal onlyInitializing {
__Ownable_init_unchained(initialOwner);
}
function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
OwnableStorage storage $ = _getOwnableStorage();
return $._owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
OwnableStorage storage $ = _getOwnableStorage();
address oldOwner = $._owner;
$._owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// 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.1) (utils/Context.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// 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) (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/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {IBlast} from "./IBlast.sol";
import {IBlastPoints} from "./IBlastPoints.sol";
/**
* @title BlastBaseUpgradeable
* @notice Blast documentation:
* Gas fees:
* https://docs.blast.io/building/guides/gas-fees
* Yield:
* https://docs.blast.io/building/guides/eth-yield
* Points:
* https://docs.blast.io/airdrop/api
*/
contract BlastBaseUpgradeable is Initializable {
IBlast public constant BLAST =
IBlast(0x4300000000000000000000000000000000000002);
address public gasClaimer;
address public yieldClaimer;
IBlastPoints public blastPoints;
address public pointsOperator;
modifier onlyGasClaimer() {
require(
msg.sender == gasClaimer,
"BlastBaseUpgradeable: caller is not the gasClaimer"
);
_;
}
modifier onlyYieldClaimer() {
require(
msg.sender == yieldClaimer,
"BlastBaseUpgradeable: caller is not the yieldClaimer"
);
_;
}
modifier onlyPointsOperator() {
require(
msg.sender == pointsOperator,
"BlastBaseUpgradeable: caller is not the pointsOperator"
);
_;
}
function __BlastBase_init(
address _gasClaimer,
address _yieldClaimer,
IBlastPoints _blastPoints,
address _pointsOperator
) internal onlyInitializing {
gasClaimer = _gasClaimer;
yieldClaimer = _yieldClaimer;
BLAST.configureClaimableGas();
BLAST.configureClaimableYield();
blastPoints = _blastPoints;
pointsOperator = _pointsOperator;
blastPoints.configurePointsOperator(_pointsOperator);
}
/**********************************
* Gas *
*********************************/
function claimAllGas(
address recipientOfGas
) external onlyGasClaimer returns (uint256) {
return BLAST.claimAllGas(address(this), recipientOfGas);
}
function claimGasAtMinClaimRate(
address recipientOfGas,
uint256 minClaimRateBips
) external onlyGasClaimer returns (uint256) {
return
BLAST.claimGasAtMinClaimRate(
address(this),
recipientOfGas,
minClaimRateBips
);
}
function claimMaxGas(
address recipientOfGas
) external onlyGasClaimer returns (uint256) {
return BLAST.claimMaxGas(address(this), recipientOfGas);
}
function claimGas(
address recipientOfGas,
uint256 gasToClaim,
uint256 gasSecondsToConsume
) external onlyGasClaimer returns (uint256) {
return
BLAST.claimGas(
address(this),
recipientOfGas,
gasToClaim,
gasSecondsToConsume
);
}
function setGasClaimer(address gasClaimer_) external onlyGasClaimer {
gasClaimer = gasClaimer_;
}
/**********************************
* Yield *
*********************************/
function claimAllYield(
address recipientOfYield
) external onlyYieldClaimer returns (uint256) {
return BLAST.claimAllYield(address(this), recipientOfYield);
}
function claimYield(
address recipientOfYield,
uint256 amount
) external onlyYieldClaimer returns (uint256) {
return BLAST.claimYield(address(this), recipientOfYield, amount);
}
function setYieldClaimer(address yieldClaimer_) external onlyYieldClaimer {
yieldClaimer = yieldClaimer_;
}
/**********************************
* Points *
*********************************/
/// @dev The current points operator must set the new points operator on the blastPoints contract directly with the
/// function onfigurePointsOperatorOnBehalf(address contractAddress, address operatorAddress).
function setPointsOperator(
address pointsOperator_
) external onlyPointsOperator {
pointsOperator = pointsOperator_;
}
function setBlastPoints(
IBlastPoints blastPoints_
) external onlyPointsOperator {
blastPoints = blastPoints_;
blastPoints.configurePointsOperator(pointsOperator);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
enum YieldMode {
AUTOMATIC,
VOID,
CLAIMABLE
}
enum GasMode {
VOID,
CLAIMABLE
}
interface IBlast {
// configure
function configureContract(
address contractAddress,
YieldMode _yield,
GasMode gasMode,
address governor
) external;
function configure(
YieldMode _yield,
GasMode gasMode,
address governor
) external;
// base configuration options
function configureClaimableYield() external;
function configureClaimableYieldOnBehalf(address contractAddress) external;
function configureAutomaticYield() external;
function configureAutomaticYieldOnBehalf(address contractAddress) external;
function configureVoidYield() external;
function configureVoidYieldOnBehalf(address contractAddress) external;
function configureClaimableGas() external;
function configureClaimableGasOnBehalf(address contractAddress) external;
function configureVoidGas() external;
function configureVoidGasOnBehalf(address contractAddress) external;
function configureGovernor(address _governor) external;
function configureGovernorOnBehalf(
address _newGovernor,
address contractAddress
) external;
// claim yield
function claimYield(
address contractAddress,
address recipientOfYield,
uint256 amount
) external returns (uint256);
function claimAllYield(
address contractAddress,
address recipientOfYield
) external returns (uint256);
// claim gas
function claimAllGas(
address contractAddress,
address recipientOfGas
) external returns (uint256);
function claimGasAtMinClaimRate(
address contractAddress,
address recipientOfGas,
uint256 minClaimRateBips
) external returns (uint256);
function claimMaxGas(
address contractAddress,
address recipientOfGas
) external returns (uint256);
function claimGas(
address contractAddress,
address recipientOfGas,
uint256 gasToClaim,
uint256 gasSecondsToConsume
) external returns (uint256);
// read functions
function readClaimableYield(
address contractAddress
) external view returns (uint256);
function readYieldConfiguration(
address contractAddress
) external view returns (uint8);
function readGasParams(
address contractAddress
)
external
view
returns (
uint256 etherSeconds,
uint256 etherBalance,
uint256 lastUpdated,
GasMode
);
function configurePointsOperator(address operator) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
interface IBlastPoints {
function configurePointsOperator(address operator) external;
function configurePointsOperatorOnBehalf(
address contractAddress,
address operator
) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import "../../Blast/IBlastPoints.sol";
import "./Structs.sol";
interface ICollectionImpl {
/***********************
* Structs *
***********************/
struct InitializeParams {
/* erc721 */
CollectionSpecs collectionSpecs;
address tokenUriFallbackContract;
/* bonding curve */
BondingCurveSpecs bondingCurveSpecs;
/* shared ownership */
address creatorVault;
/* roles */
address creator;
address router;
/* blast */
address gasClaimer;
address yieldClaimer;
address pointsOperator;
IBlastPoints blastPoints;
/* dn404 */
address dn404Mirror;
}
function initialize(InitializeParams memory params) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
// Own Interfaces
import "../interfaces/Structs.sol";
interface IRouter {
function protocolFeeSpecs() external view returns (ProtocolFeeSpecs memory);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
interface ITokenUri {
function tokenURI(
uint256 tokenId,
address collection
) external view returns (string memory);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
struct ProtocolFeeSpecs {
uint256 protocolFeePercentage; // 1e18 = 100%
address protocolFeeCollector;
}
struct BondingCurveSpecs {
uint256 factor; // with precision of 1e18
uint256 exponent;
uint256 c; // constant; with precision of 1e18
}
struct CollectionSpecs {
string name_;
string symbol_;
uint256 units_; // the amount of tokens that represent a single NFT
string baseUri_; // leave blank if tokenURI is provided by a contract. If tokenUriProvider is provided, it will override this tokenUri
address tokenUriContract_; // if provided, overrides the tokenUri
uint256 maxNftSupply_; // the max supply in NFTs. 0 defaults to the max (CollectionImplementation::MAX_NFT_SUPPLY)
bool useWhitelist_; // if enabled, only whitelisted addresses can mint. Must be set at init
uint256 whitelistMintLimit_; // 0 for unlimited minting.
bool isDeflationary_; // allows to burn from the bottom of the curve
}// 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 `owner` sets their skipNFT flag to `status`.
event SkipNFTSet(address indexed owner, 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 The function can only be called after the contract has been initialized.
error DNNotInitialized();
/// @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 must be greater than zero and less than `2**96`.
error InvalidUnit();
/// @dev Thrown when the caller for a fallback NFT function is not the mirror contract.
error SenderNotMirror();
/// @dev Thrown when attempting to transfer tokens to the zero address.
error TransferToZeroAddress();
/// @dev Thrown when the mirror address provided for initialization is the zero address.
error MirrorAddressIsZero();
/// @dev Thrown when the link call to the mirror contract reverts.
error LinkMirrorContractFailed();
/// @dev Thrown when setting an NFT token approval
/// and the caller is not the owner or an approved operator.
error ApprovalCallerNotOwnerNorApproved();
/// @dev Thrown when transferring an NFT
/// and the caller is not the owner or an approved operator.
error TransferCallerNotOwnerNorApproved();
/// @dev Thrown when transferring an NFT and the from address is not the current owner.
error TransferFromIncorrectOwner();
/// @dev Thrown when checking the owner or approved address for a non-existent NFT.
error TokenDoesNotExist();
/// @dev The function selector is not recognized.
error FnSelectorNotRecognized();
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* CONSTANTS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev The flag to denote that the address data is initialized.
uint8 internal constant _ADDRESS_DATA_INITIALIZED_FLAG = 1 << 0;
/// @dev The flag to denote that the address should skip NFTs.
uint8 internal constant _ADDRESS_DATA_SKIP_NFT_FLAG = 1 << 1;
/// @dev The flag to denote that the address has overridden the default Permit2 allowance.
uint8 internal constant _ADDRESS_DATA_OVERRIDE_PERMIT2_FLAG = 1 << 2;
/// @dev The canonical Permit2 address.
/// For signature-based allowance granting for single transaction ERC20 `transferFrom`.
/// To enable, override `_givePermit2DefaultInfiniteAllowance()`.
/// [Github](https://github.com/Uniswap/permit2)
/// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
address internal constant _PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* STORAGE */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Struct containing an address's token data and settings.
struct AddressData {
// Auxiliary data.
uint88 aux;
// Flags for `initialized` and `skipNFT`.
uint8 flags;
// The alias for the address. Zero means absence of an alias.
uint32 addressAlias;
// The number of NFT tokens.
uint32 ownedLength;
// The token balance in wei.
uint96 balance;
}
/// @dev A uint32 map in storage.
struct Uint32Map {
uint256 spacer;
}
/// @dev A bitmap in storage.
struct Bitmap {
uint256 spacer;
}
/// @dev A struct to wrap a uint256 in storage.
struct Uint256Ref {
uint256 value;
}
/// @dev A mapping of an address pair to a Uint256Ref.
struct AddressPairToUint256RefMap {
uint256 spacer;
}
/// @dev Struct containing the base token contract storage.
struct DN404Storage {
// Current number of address aliases assigned.
uint32 numAliases;
// Next NFT ID to assign for a mint.
uint32 nextTokenId;
// The head of the burned pool.
uint32 burnedPoolHead;
// The tail of the burned pool.
uint32 burnedPoolTail;
// Total number of NFTs in existence.
uint32 totalNFTSupply;
// Total supply of tokens.
uint96 totalSupply;
// Address of the NFT mirror contract.
address mirrorERC721;
// Mapping of a user alias number to their address.
mapping(uint32 => address) aliasToAddress;
// Mapping of user operator approvals for NFTs.
AddressPairToUint256RefMap operatorApprovals;
// Mapping of NFT approvals to approved operators.
mapping(uint256 => address) nftApprovals;
// Bitmap of whether an non-zero NFT approval may exist.
Bitmap mayHaveNFTApproval;
// Bitmap of whether a NFT ID exists. Ignored if `_useExistsLookup()` returns false.
Bitmap exists;
// Mapping of user allowances for ERC20 spenders.
AddressPairToUint256RefMap allowance;
// Mapping of NFT IDs owned by an address.
mapping(address => Uint32Map) owned;
// The pool of burned NFT IDs.
Uint32Map burnedPool;
// Even indices: owner aliases. Odd indices: owned indices.
Uint32Map oo;
// Mapping of user account AddressData.
mapping(address => AddressData) addressData;
}
/// @dev Returns a storage pointer for DN404Storage.
function _getDN404Storage() internal pure virtual returns (DN404Storage storage $) {
/// @solidity memory-safe-assembly
assembly {
// `uint72(bytes9(keccak256("DN404_STORAGE")))`.
$.slot := 0xa20d6e21d0e5255308 // Truncate to 9 bytes to reduce bytecode size.
}
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* INITIALIZER */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Initializes the DN404 contract with an
/// `initialTokenSupply`, `initialTokenOwner` and `mirror` NFT contract address.
function _initializeDN404(
uint256 initialTokenSupply,
address initialSupplyOwner,
address mirror
) internal virtual {
DN404Storage storage $ = _getDN404Storage();
unchecked {
if (_unit() - 1 >= 2 ** 96 - 1) revert InvalidUnit();
}
if ($.mirrorERC721 != address(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 (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) internal view virtual returns (string memory);
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* CONFIGURABLES */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Returns if direct NFT transfers should be used during ERC20 transfers
/// whenever possible, instead of burning and re-minting.
function _useDirectTransfersIfPossible() internal view virtual returns (bool) {
return true;
}
/// @dev Returns if burns should be added to the burn pool.
/// This returns false by default, which means the NFT IDs are re-minted in a cycle.
function _addToBurnedPool(uint256 totalNFTSupplyAfterBurn, uint256 totalSupplyAfterBurn)
internal
view
virtual
returns (bool)
{
// Silence unused variable compiler warning.
totalSupplyAfterBurn = totalNFTSupplyAfterBurn;
return false;
}
/// @dev Returns whether to use the exists bitmap for more efficient
/// scanning of an empty token ID slot.
/// Recommended for collections that do not use the burn pool,
/// and are expected to have nearly all possible NFTs materialized.
///
/// Note: The returned value must be constant after initialization.
function _useExistsLookup() internal view virtual returns (bool) {
return true;
}
/// @dev Hook that is called after any NFT token transfers, including minting and burning.
function _afterNFTTransfer(address from, address to, uint256 id) internal virtual {}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* ERC20 OPERATIONS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Returns the decimals places of the token. Always 18.
function decimals() public pure returns (uint8) {
return 18;
}
/// @dev Returns the amount of tokens in existence.
function totalSupply() public view virtual returns (uint256) {
return uint256(_getDN404Storage().totalSupply);
}
/// @dev Returns the amount of tokens owned by `owner`.
function balanceOf(address owner) public view virtual returns (uint256) {
return _getDN404Storage().addressData[owner].balance;
}
/// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`.
function allowance(address owner, address spender) public view returns (uint256) {
if (_givePermit2DefaultInfiniteAllowance() && spender == _PERMIT2) {
uint8 flags = _getDN404Storage().addressData[owner].flags;
if (_isZero(flags & _ADDRESS_DATA_OVERRIDE_PERMIT2_FLAG)) return type(uint256).max;
}
return _ref(_getDN404Storage().allowance, owner, spender).value;
}
/// @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
///
/// Emits a {Approval} event.
function approve(address spender, uint256 amount) public virtual returns (bool) {
_approve(msg.sender, spender, amount);
return true;
}
/// @dev Transfer `amount` tokens from the caller to `to`.
///
/// Will burn sender NFTs if balance after transfer is less than
/// the amount required to support the current NFT balance.
///
/// Will mint NFTs to `to` if the recipient's new balance supports
/// additional NFTs ***AND*** the `to` address's skipNFT flag is
/// set to false.
///
/// Requirements:
/// - `from` must at least have `amount`.
///
/// Emits a {Transfer} event.
function transfer(address to, uint256 amount) public virtual returns (bool) {
_transfer(msg.sender, to, amount);
return true;
}
/// @dev Transfers `amount` tokens from `from` to `to`.
///
/// Note: Does not update the allowance if it is the maximum uint256 value.
///
/// Will burn sender NFTs if balance after transfer is less than
/// the amount required to support the current NFT balance.
///
/// Will mint NFTs to `to` if the recipient's new balance supports
/// additional NFTs ***AND*** the `to` address's skipNFT flag is
/// set to false.
///
/// Requirements:
/// - `from` must at least have `amount`.
/// - The caller must have at least `amount` of allowance to transfer the tokens of `from`.
///
/// Emits a {Transfer} event.
function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
Uint256Ref storage a = _ref(_getDN404Storage().allowance, from, msg.sender);
uint256 allowed = _givePermit2DefaultInfiniteAllowance() && msg.sender == _PERMIT2
&& _isZero(_getDN404Storage().addressData[from].flags & _ADDRESS_DATA_OVERRIDE_PERMIT2_FLAG)
? type(uint256).max
: a.value;
if (allowed != type(uint256).max) {
if (amount > allowed) revert InsufficientAllowance();
unchecked {
a.value = allowed - amount;
}
}
_transfer(from, to, amount);
return true;
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* PERMIT2 */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Whether Permit2 has infinite allowances by default for all owners.
/// For signature-based allowance granting for single transaction ERC20 `transferFrom`.
/// To enable, override this function to return true.
function _givePermit2DefaultInfiniteAllowance() internal view virtual returns (bool) {
return false;
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* INTERNAL MINT FUNCTIONS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Mints `amount` tokens to `to`, increasing the total supply.
///
/// Will mint NFTs to `to` if the recipient's new balance supports
/// additional NFTs ***AND*** the `to` address's skipNFT flag is set to false.
///
/// Emits a {Transfer} event.
function _mint(address to, uint256 amount) internal virtual {
if (to == address(0)) revert TransferToZeroAddress();
AddressData storage toAddressData = _addressData(to);
DN404Storage storage $ = _getDN404Storage();
if ($.mirrorERC721 == address(0)) revert DNNotInitialized();
_DNMintTemps memory t;
unchecked {
uint256 toBalance = uint256(toAddressData.balance) + amount;
toAddressData.balance = uint96(toBalance);
t.toEnd = toBalance / _unit();
}
uint256 maxId;
unchecked {
uint256 totalSupply_ = uint256($.totalSupply) + amount;
$.totalSupply = uint96(totalSupply_);
uint256 overflows = _toUint(_totalSupplyOverflows(totalSupply_));
if (overflows | _toUint(totalSupply_ < amount) != 0) revert TotalSupplyOverflow();
maxId = totalSupply_ / _unit();
}
unchecked {
if (_isZero(toAddressData.flags & _ADDRESS_DATA_SKIP_NFT_FLAG)) {
Uint32Map storage toOwned = $.owned[to];
Uint32Map storage oo = $.oo;
uint256 toIndex = toAddressData.ownedLength;
_DNPackedLogs memory packedLogs = _packedLogsMalloc(_zeroFloorSub(t.toEnd, toIndex));
if (packedLogs.logs.length != 0) {
_packedLogsSet(packedLogs, to, 0);
$.totalNFTSupply += uint32(packedLogs.logs.length);
toAddressData.ownedLength = uint32(t.toEnd);
t.toAlias = _registerAndResolveAlias(toAddressData, to);
uint32 burnedPoolHead = $.burnedPoolHead;
t.burnedPoolTail = $.burnedPoolTail;
t.nextTokenId = _wrapNFTId($.nextTokenId, maxId);
// Mint loop.
do {
uint256 id;
if (burnedPoolHead != t.burnedPoolTail) {
id = _get($.burnedPool, burnedPoolHead++);
} else {
id = t.nextTokenId;
while (_get(oo, _ownershipIndex(id)) != 0) {
id = _useExistsLookup()
? _wrapNFTId(_findFirstUnset($.exists, id + 1, maxId), maxId)
: _wrapNFTId(id + 1, maxId);
}
t.nextTokenId = _wrapNFTId(id + 1, maxId);
}
if (_useExistsLookup()) _set($.exists, id, true);
_set(toOwned, toIndex, uint32(id));
_setOwnerAliasAndOwnedIndex(oo, id, t.toAlias, uint32(toIndex++));
_packedLogsAppend(packedLogs, id);
_afterNFTTransfer(address(0), to, id);
} while (toIndex != t.toEnd);
$.nextTokenId = uint32(t.nextTokenId);
$.burnedPoolHead = burnedPoolHead;
_packedLogsSend(packedLogs, $.mirrorERC721);
}
}
}
/// @solidity memory-safe-assembly
assembly {
// Emit the {Transfer} event.
mstore(0x00, amount)
log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, shl(96, to)))
}
}
/// @dev Mints `amount` tokens to `to`, increasing the total supply.
/// This variant mints NFT tokens starting from ID `preTotalSupply / _unit() + 1`.
/// This variant will not touch the `burnedPool` and `nextTokenId`.
///
/// Will mint NFTs to `to` if the recipient's new balance supports
/// additional NFTs ***AND*** the `to` address's skipNFT flag is set to false.
///
/// Emits a {Transfer} event.
function _mintNext(address to, uint256 amount) internal virtual {
if (to == address(0)) revert TransferToZeroAddress();
AddressData storage toAddressData = _addressData(to);
DN404Storage storage $ = _getDN404Storage();
if ($.mirrorERC721 == address(0)) revert DNNotInitialized();
_DNMintTemps memory t;
unchecked {
uint256 toBalance = uint256(toAddressData.balance) + amount;
toAddressData.balance = uint96(toBalance);
t.toEnd = toBalance / _unit();
}
uint256 startId;
uint256 maxId;
unchecked {
uint256 preTotalSupply = uint256($.totalSupply);
startId = preTotalSupply / _unit() + 1;
uint256 totalSupply_ = uint256(preTotalSupply) + amount;
$.totalSupply = uint96(totalSupply_);
uint256 overflows = _toUint(_totalSupplyOverflows(totalSupply_));
if (overflows | _toUint(totalSupply_ < amount) != 0) revert TotalSupplyOverflow();
maxId = totalSupply_ / _unit();
}
unchecked {
if (_isZero(toAddressData.flags & _ADDRESS_DATA_SKIP_NFT_FLAG)) {
Uint32Map storage toOwned = $.owned[to];
Uint32Map storage oo = $.oo;
uint256 toIndex = toAddressData.ownedLength;
_DNPackedLogs memory packedLogs = _packedLogsMalloc(_zeroFloorSub(t.toEnd, toIndex));
if (packedLogs.logs.length != 0) {
_packedLogsSet(packedLogs, to, 0);
$.totalNFTSupply += uint32(packedLogs.logs.length);
toAddressData.ownedLength = uint32(t.toEnd);
t.toAlias = _registerAndResolveAlias(toAddressData, to);
// Mint loop.
do {
uint256 id = startId;
while (_get(oo, _ownershipIndex(id)) != 0) {
id = _useExistsLookup()
? _wrapNFTId(_findFirstUnset($.exists, id + 1, maxId), maxId)
: _wrapNFTId(id + 1, maxId);
}
startId = _wrapNFTId(id + 1, maxId);
if (_useExistsLookup()) _set($.exists, id, true);
_set(toOwned, toIndex, uint32(id));
_setOwnerAliasAndOwnedIndex(oo, id, t.toAlias, uint32(toIndex++));
_packedLogsAppend(packedLogs, id);
_afterNFTTransfer(address(0), to, id);
} while (toIndex != t.toEnd);
_packedLogsSend(packedLogs, $.mirrorERC721);
}
}
}
/// @solidity memory-safe-assembly
assembly {
// Emit the {Transfer} event.
mstore(0x00, amount)
log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, shl(96, to)))
}
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* INTERNAL BURN FUNCTIONS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Burns `amount` tokens from `from`, reducing the total supply.
///
/// Will burn sender NFTs if balance after transfer is less than
/// the amount required to support the current NFT balance.
///
/// Emits a {Transfer} event.
function _burn(address from, uint256 amount) internal virtual {
DN404Storage storage $ = _getDN404Storage();
if ($.mirrorERC721 == address(0)) revert DNNotInitialized();
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) {
_DNPackedLogs memory packedLogs = _packedLogsMalloc(numNFTBurns);
_packedLogsSet(packedLogs, from, 1);
bool addToBurnedPool;
{
uint256 totalNFTSupply = uint256($.totalNFTSupply) - numNFTBurns;
$.totalNFTSupply = uint32(totalNFTSupply);
addToBurnedPool = _addToBurnedPool(totalNFTSupply, totalSupply_);
}
Uint32Map storage oo = $.oo;
uint256 fromEnd = fromIndex - numNFTBurns;
fromAddressData.ownedLength = uint32(fromEnd);
uint32 burnedPoolTail = $.burnedPoolTail;
// Burn loop.
do {
uint256 id = _get(fromOwned, --fromIndex);
_setOwnerAliasAndOwnedIndex(oo, id, 0, 0);
_packedLogsAppend(packedLogs, id);
if (_useExistsLookup()) _set($.exists, id, false);
if (addToBurnedPool) _set($.burnedPool, burnedPoolTail++, uint32(id));
if (_get($.mayHaveNFTApproval, id)) {
_set($.mayHaveNFTApproval, id, false);
delete $.nftApprovals[id];
}
_afterNFTTransfer(from, address(0), id);
} while (fromIndex != fromEnd);
if (addToBurnedPool) $.burnedPoolTail = burnedPoolTail;
_packedLogsSend(packedLogs, $.mirrorERC721);
}
}
/// @solidity memory-safe-assembly
assembly {
// Emit the {Transfer} event.
mstore(0x00, amount)
log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0)
}
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* INTERNAL TRANSFER FUNCTIONS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Moves `amount` of tokens from `from` to `to`.
///
/// Will burn sender NFTs if balance after transfer is less than
/// the amount required to support the current NFT balance.
///
/// Will mint NFTs to `to` if the recipient's new balance supports
/// additional NFTs ***AND*** the `to` address's skipNFT flag is
/// set to false.
///
/// Emits a {Transfer} event.
function _transfer(address from, address to, uint256 amount) internal virtual {
if (to == address(0)) revert TransferToZeroAddress();
DN404Storage storage $ = _getDN404Storage();
AddressData storage fromAddressData = $.addressData[from];
AddressData storage toAddressData = _addressData(to);
if ($.mirrorERC721 == address(0)) revert DNNotInitialized();
_DNTransferTemps memory t;
t.fromOwnedLength = fromAddressData.ownedLength;
t.toOwnedLength = toAddressData.ownedLength;
unchecked {
{
uint256 fromBalance = fromAddressData.balance;
if (amount > fromBalance) revert InsufficientBalance();
fromAddressData.balance = uint96(fromBalance -= amount);
uint256 toBalance = uint256(toAddressData.balance) + amount;
toAddressData.balance = uint96(toBalance);
t.numNFTBurns = _zeroFloorSub(t.fromOwnedLength, fromBalance / _unit());
if (_isZero(toAddressData.flags & _ADDRESS_DATA_SKIP_NFT_FLAG)) {
if (from == to) t.toOwnedLength = t.fromOwnedLength - t.numNFTBurns;
t.numNFTMints = _zeroFloorSub(toBalance / _unit(), t.toOwnedLength);
}
}
while (_useDirectTransfersIfPossible()) {
uint256 n = _min(t.fromOwnedLength, _min(t.numNFTBurns, t.numNFTMints));
if (_isZero(n)) break;
t.numNFTBurns -= n;
t.numNFTMints -= n;
if (from == to) {
t.toOwnedLength += n;
break;
}
_DNDirectLogs memory directLogs = _directLogsMalloc(n, from, to);
Uint32Map storage fromOwned = $.owned[from];
Uint32Map storage toOwned = $.owned[to];
t.toAlias = _registerAndResolveAlias(toAddressData, to);
uint256 toIndex = t.toOwnedLength;
n = toIndex + n;
// Direct transfer loop.
do {
uint256 id = _get(fromOwned, --t.fromOwnedLength);
_set(toOwned, toIndex, uint32(id));
_setOwnerAliasAndOwnedIndex($.oo, id, t.toAlias, uint32(toIndex));
_directLogsAppend(directLogs, id);
if (_get($.mayHaveNFTApproval, id)) {
_set($.mayHaveNFTApproval, id, false);
delete $.nftApprovals[id];
}
_afterNFTTransfer(from, to, id);
} while (++toIndex != n);
toAddressData.ownedLength = uint32(t.toOwnedLength = toIndex);
fromAddressData.ownedLength = uint32(t.fromOwnedLength);
_directLogsSend(directLogs, $.mirrorERC721);
break;
}
t.totalNFTSupply = uint256($.totalNFTSupply) + t.numNFTMints - t.numNFTBurns;
$.totalNFTSupply = uint32(t.totalNFTSupply);
Uint32Map storage oo = $.oo;
_DNPackedLogs memory packedLogs = _packedLogsMalloc(t.numNFTBurns + t.numNFTMints);
t.burnedPoolTail = $.burnedPoolTail;
if (t.numNFTBurns != 0) {
_packedLogsSet(packedLogs, from, 1);
bool addToBurnedPool = _addToBurnedPool(t.totalNFTSupply, $.totalSupply);
Uint32Map storage fromOwned = $.owned[from];
uint256 fromIndex = t.fromOwnedLength;
fromAddressData.ownedLength = uint32(t.fromEnd = fromIndex - t.numNFTBurns);
uint32 burnedPoolTail = t.burnedPoolTail;
// Burn loop.
do {
uint256 id = _get(fromOwned, --fromIndex);
_setOwnerAliasAndOwnedIndex(oo, id, 0, 0);
_packedLogsAppend(packedLogs, id);
if (_useExistsLookup()) _set($.exists, id, false);
if (addToBurnedPool) _set($.burnedPool, burnedPoolTail++, uint32(id));
if (_get($.mayHaveNFTApproval, id)) {
_set($.mayHaveNFTApproval, id, false);
delete $.nftApprovals[id];
}
_afterNFTTransfer(from, address(0), id);
} while (fromIndex != t.fromEnd);
if (addToBurnedPool) $.burnedPoolTail = (t.burnedPoolTail = burnedPoolTail);
}
if (t.numNFTMints != 0) {
_packedLogsSet(packedLogs, to, 0);
Uint32Map storage toOwned = $.owned[to];
t.toAlias = _registerAndResolveAlias(toAddressData, to);
uint256 maxId = $.totalSupply / _unit();
t.nextTokenId = _wrapNFTId($.nextTokenId, maxId);
uint256 toIndex = t.toOwnedLength;
toAddressData.ownedLength = uint32(t.toEnd = toIndex + t.numNFTMints);
uint32 burnedPoolHead = $.burnedPoolHead;
// Mint loop.
do {
uint256 id;
if (burnedPoolHead != t.burnedPoolTail) {
id = _get($.burnedPool, burnedPoolHead++);
} else {
id = t.nextTokenId;
while (_get(oo, _ownershipIndex(id)) != 0) {
id = _useExistsLookup()
? _wrapNFTId(_findFirstUnset($.exists, id + 1, maxId), maxId)
: _wrapNFTId(id + 1, maxId);
}
t.nextTokenId = _wrapNFTId(id + 1, maxId);
}
if (_useExistsLookup()) _set($.exists, id, true);
_set(toOwned, toIndex, uint32(id));
_setOwnerAliasAndOwnedIndex(oo, id, t.toAlias, uint32(toIndex++));
_packedLogsAppend(packedLogs, id);
_afterNFTTransfer(address(0), to, id);
} while (toIndex != t.toEnd);
$.burnedPoolHead = burnedPoolHead;
$.nextTokenId = uint32(t.nextTokenId);
}
if (packedLogs.logs.length != 0) _packedLogsSend(packedLogs, $.mirrorERC721);
}
/// @solidity memory-safe-assembly
assembly {
// Emit the {Transfer} event.
mstore(0x00, amount)
// forgefmt: disable-next-item
log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), shr(96, shl(96, to)))
}
}
/// @dev Transfers token `id` from `from` to `to`. Also emits ERC721 Transfer event.
///
/// Requirements:
///
/// - 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 _initiateTransferFromNFT(address from, address to, uint256 id, address msgSender)
internal
virtual
{
_transferFromNFT(from, to, id, msgSender);
// Emit ERC721 Transfer
DN404Storage storage $ = _getDN404Storage();
_DNDirectLogs memory directLogs = _directLogsMalloc(1, from, to);
_directLogsAppend(directLogs, id);
_directLogsSend(directLogs, $.mirrorERC721);
}
/// @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
{
if (to == address(0)) revert TransferToZeroAddress();
DN404Storage storage $ = _getDN404Storage();
if ($.mirrorERC721 == address(0)) revert DNNotInitialized();
Uint32Map storage oo = $.oo;
if (from != $.aliasToAddress[_get(oo, _ownershipIndex(_restrictNFTId(id)))]) {
revert TransferFromIncorrectOwner();
}
if (msgSender != from) {
if (!_isApprovedForAll(from, msgSender)) {
if (_getApproved(id) != msgSender) {
revert TransferCallerNotOwnerNorApproved();
}
}
}
AddressData storage fromAddressData = $.addressData[from];
AddressData storage toAddressData = $.addressData[to];
uint256 unit = _unit();
mapping(address => Uint32Map) storage owned = $.owned;
unchecked {
uint256 fromBalance = fromAddressData.balance;
if (unit > fromBalance) revert InsufficientBalance();
fromAddressData.balance = uint96(fromBalance - unit);
toAddressData.balance += uint96(unit);
}
if (_get($.mayHaveNFTApproval, id)) {
_set($.mayHaveNFTApproval, id, false);
delete $.nftApprovals[id];
}
unchecked {
Uint32Map storage fromOwned = owned[from];
uint32 updatedId = _get(fromOwned, --fromAddressData.ownedLength);
uint32 i = _get(oo, _ownedIndex(id));
_set(fromOwned, i, updatedId);
_set(oo, _ownedIndex(updatedId), i);
}
unchecked {
uint32 n = toAddressData.ownedLength++;
_set(owned[to], n, uint32(id));
_setOwnerAliasAndOwnedIndex(oo, id, _registerAndResolveAlias(toAddressData, to), n);
}
_afterNFTTransfer(from, to, id);
/// @solidity memory-safe-assembly
assembly {
// Emit the {Transfer} event.
mstore(0x00, unit)
// forgefmt: disable-next-item
log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), shr(96, shl(96, to)))
}
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* INTERNAL APPROVE FUNCTIONS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`.
///
/// Emits a {Approval} event.
function _approve(address owner, address spender, uint256 amount) internal virtual {
if (_givePermit2DefaultInfiniteAllowance() && spender == _PERMIT2) {
_getDN404Storage().addressData[owner].flags |= _ADDRESS_DATA_OVERRIDE_PERMIT2_FLAG;
}
_ref(_getDN404Storage().allowance, owner, spender).value = amount;
/// @solidity memory-safe-assembly
assembly {
// Emit the {Approval} event.
mstore(0x00, amount)
// forgefmt: disable-next-item
log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, shl(96, owner)), shr(96, shl(96, spender)))
}
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* DATA HITCHHIKING FUNCTIONS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Returns the auxiliary data for `owner`.
/// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data.
/// Auxiliary data can be set for any address, even if it does not have any tokens.
function _getAux(address owner) internal view virtual returns (uint88) {
return _getDN404Storage().addressData[owner].aux;
}
/// @dev Set the auxiliary data for `owner` to `value`.
/// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data.
/// Auxiliary data can be set for any address, even if it does not have any tokens.
function _setAux(address owner, uint88 value) internal virtual {
_getDN404Storage().addressData[owner].aux = value;
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* SKIP NFT FUNCTIONS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Returns true if minting and transferring ERC20s to `owner` will skip minting NFTs.
/// Returns false otherwise.
function getSkipNFT(address owner) public view virtual returns (bool) {
AddressData storage d = _getDN404Storage().addressData[owner];
if (_isZero(d.flags & _ADDRESS_DATA_INITIALIZED_FLAG)) 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 (_isZero(d.flags & _ADDRESS_DATA_INITIALIZED_FLAG)) {
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 (_isZero(addressAlias)) {
unchecked {
addressAlias = ++$.numAliases;
}
toAddressData.addressAlias = addressAlias;
$.aliasToAddress[addressAlias] = to;
if (_isZero(addressAlias)) revert(); // Overflow.
}
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* MIRROR OPERATIONS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Returns the address of the mirror NFT contract.
function mirrorERC721() public view virtual returns (address) {
return _getDN404Storage().mirrorERC721;
}
/// @dev Returns the total NFT supply.
function _totalNFTSupply() internal view virtual returns (uint256) {
return _getDN404Storage().totalNFTSupply;
}
/// @dev Returns `owner` NFT balance.
function _balanceOfNFT(address owner) internal view virtual returns (uint256) {
return _getDN404Storage().addressData[owner].ownedLength;
}
/// @dev Returns the owner of token `id`.
/// Returns the zero address instead of reverting if the token does not exist.
function _ownerAt(uint256 id) internal view virtual returns (address) {
DN404Storage storage $ = _getDN404Storage();
return $.aliasToAddress[_get($.oo, _ownershipIndex(_restrictNFTId(id)))];
}
/// @dev Returns the owner of token `id`.
///
/// Requirements:
/// - Token `id` must exist.
function _ownerOf(uint256 id) internal view virtual returns (address) {
if (!_exists(id)) revert TokenDoesNotExist();
return _ownerAt(id);
}
/// @dev Returns whether `operator` is approved to manage the NFT tokens of `owner`.
function _isApprovedForAll(address owner, address operator)
internal
view
virtual
returns (bool)
{
return !_isZero(_ref(_getDN404Storage().operatorApprovals, owner, operator).value);
}
/// @dev Returns if token `id` exists.
function _exists(uint256 id) internal view virtual returns (bool) {
return _ownerAt(id) != address(0);
}
/// @dev Returns the account approved to manage token `id`.
///
/// Requirements:
/// - Token `id` must exist.
function _getApproved(uint256 id) internal view virtual returns (address) {
if (!_exists(id)) revert TokenDoesNotExist();
return _getDN404Storage().nftApprovals[id];
}
/// @dev Sets `spender` as the approved account to manage token `id`, using `msgSender`.
///
/// Requirements:
/// - `msgSender` must be the owner or an approved operator for the token owner.
function _approveNFT(address spender, uint256 id, address msgSender)
internal
virtual
returns (address owner)
{
DN404Storage storage $ = _getDN404Storage();
owner = $.aliasToAddress[_get($.oo, _ownershipIndex(_restrictNFTId(id)))];
if (msgSender != owner) {
if (!_isApprovedForAll(owner, msgSender)) {
revert ApprovalCallerNotOwnerNorApproved();
}
}
$.nftApprovals[id] = spender;
_set($.mayHaveNFTApproval, id, spender != address(0));
}
/// @dev Approve or remove the `operator` as an operator for `msgSender`,
/// without authorization checks.
function _setApprovalForAll(address operator, bool approved, address msgSender)
internal
virtual
{
_ref(_getDN404Storage().operatorApprovals, msgSender, operator).value = _toUint(approved);
}
/// @dev Returns the NFT IDs of `owner` in the range `[begin..end)` (exclusive of `end`).
/// `begin` and `end` are indices in the owner's token ID array, not the entire token range.
/// Optimized for smaller bytecode size, as this function is intended for off-chain calling.
function _ownedIds(address owner, uint256 begin, uint256 end)
internal
view
virtual
returns (uint256[] memory ids)
{
DN404Storage storage $ = _getDN404Storage();
Uint32Map storage owned = $.owned[owner];
end = _min($.addressData[owner].ownedLength, end);
/// @solidity memory-safe-assembly
assembly {
ids := mload(0x40)
let i := begin
for {} lt(i, end) { i := add(i, 1) } {
let s := add(shl(96, owned.slot), shr(3, i)) // Storage slot.
let id := and(0xffffffff, shr(shl(5, and(i, 7)), sload(s)))
mstore(add(add(ids, 0x20), shl(5, sub(i, begin))), id) // Append to.
}
mstore(ids, sub(i, begin)) // Store the length.
mstore(0x40, add(add(ids, 0x20), shl(5, sub(i, begin)))) // Allocate memory.
}
}
/// @dev Fallback modifier to dispatch calls from the mirror NFT contract
/// to internal functions in this contract.
modifier dn404Fallback() virtual {
DN404Storage storage $ = _getDN404Storage();
uint256 fnSelector = _calldataload(0x00) >> 224;
// `transferFromNFT(address,address,uint256,address)`.
if (fnSelector == 0xe5eb36c8) {
if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
_transferFromNFT(
address(uint160(_calldataload(0x04))), // `from`.
address(uint160(_calldataload(0x24))), // `to`.
_calldataload(0x44), // `id`.
address(uint160(_calldataload(0x64))) // `msgSender`.
);
_return(1);
}
// `setApprovalForAll(address,bool,address)`.
if (fnSelector == 0x813500fc) {
if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
_setApprovalForAll(
address(uint160(_calldataload(0x04))), // `spender`.
_calldataload(0x24) != 0, // `status`.
address(uint160(_calldataload(0x44))) // `msgSender`.
);
_return(1);
}
// `isApprovedForAll(address,address)`.
if (fnSelector == 0xe985e9c5) {
bool result = _isApprovedForAll(
address(uint160(_calldataload(0x04))), // `owner`.
address(uint160(_calldataload(0x24))) // `operator`.
);
_return(_toUint(result));
}
// `ownerOf(uint256)`.
if (fnSelector == 0x6352211e) {
_return(uint160(_ownerOf(_calldataload(0x04))));
}
// `ownerAt(uint256)`.
if (fnSelector == 0x24359879) {
_return(uint160(_ownerAt(_calldataload(0x04))));
}
// `approveNFT(address,uint256,address)`.
if (fnSelector == 0xd10b6e0c) {
if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
address owner = _approveNFT(
address(uint160(_calldataload(0x04))), // `spender`.
_calldataload(0x24), // `id`.
address(uint160(_calldataload(0x44))) // `msgSender`.
);
_return(uint160(owner));
}
// `getApproved(uint256)`.
if (fnSelector == 0x081812fc) {
_return(uint160(_getApproved(_calldataload(0x04))));
}
// `balanceOfNFT(address)`.
if (fnSelector == 0xf5b100ea) {
_return(_balanceOfNFT(address(uint160(_calldataload(0x04)))));
}
// `totalNFTSupply()`.
if (fnSelector == 0xe2c79281) {
_return(_totalNFTSupply());
}
// `tokenURI(uint256)`.
if (fnSelector == 0xc87b56dd) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x40, add(mload(0x40), 0x20))
}
string memory uri = _tokenURI(_calldataload(0x04));
/// @solidity memory-safe-assembly
assembly {
// Memory safe, as we've advanced the free memory pointer by a word.
let o := sub(uri, 0x20)
mstore(o, 0x20) // Store the offset of `uri`.
return(o, add(0x60, mload(uri)))
}
}
// `implementsDN404()`.
if (fnSelector == 0xb7a94eb8) {
_return(1);
}
_;
}
/// @dev Fallback function for calls from mirror NFT contract.
/// Override this if you need to implement your custom
/// fallback with utilities like Solady's `LibZip.cdFallback()`.
/// And always remember to always wrap the fallback with `dn404Fallback`.
fallback() external payable virtual dn404Fallback {
revert FnSelectorNotRecognized(); // Not mandatory. Just for quality of life.
}
/// @dev This is to silence the compiler warning.
/// Override and remove the revert if you want your contract to receive ETH via receive.
receive() external payable virtual {
if (msg.value != 0) revert();
}
/*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
/* INTERNAL / PRIVATE HELPERS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
/// @dev Returns `(i - 1) << 1`.
function _ownershipIndex(uint256 i) internal pure returns (uint256) {
unchecked {
return (i - 1) << 1; // Minus 1 as token IDs start from 1.
}
}
/// @dev Returns `((i - 1) << 1) + 1`.
function _ownedIndex(uint256 i) internal pure returns (uint256) {
unchecked {
return ((i - 1) << 1) + 1; // Minus 1 as token IDs start from 1.
}
}
/// @dev Returns the uint32 value at `index` in `map`.
function _get(Uint32Map storage map, uint256 index) internal view returns (uint32 result) {
/// @solidity memory-safe-assembly
assembly {
let s := add(shl(96, map.slot), shr(3, index)) // Storage slot.
result := and(0xffffffff, shr(shl(5, and(index, 7)), sload(s)))
}
}
/// @dev Updates the uint32 value at `index` in `map`.
function _set(Uint32Map storage map, uint256 index, uint32 value) internal {
/// @solidity memory-safe-assembly
assembly {
let s := add(shl(96, map.slot), shr(3, index)) // Storage slot.
let o := shl(5, and(index, 7)) // Storage slot offset (bits).
let v := sload(s) // Storage slot value.
sstore(s, xor(v, shl(o, and(0xffffffff, xor(value, shr(o, v))))))
}
}
/// @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 v := sload(s) // Storage slot value.
let o := shl(6, and(i, 3)) // Storage slot offset (bits).
let combined := or(shl(32, ownedIndex), and(0xffffffff, ownership))
sstore(s, xor(v, shl(o, and(0xffffffffffffffff, xor(shr(o, v), combined)))))
}
}
/// @dev Returns the boolean value of the bit at `index` in `bitmap`.
function _get(Bitmap storage bitmap, uint256 index) internal view returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
let s := add(shl(96, bitmap.slot), shr(8, index)) // Storage slot.
result := and(1, shr(and(0xff, index), sload(s)))
}
}
/// @dev Updates the bit at `index` in `bitmap` to `value`.
function _set(Bitmap storage bitmap, uint256 index, bool value) internal {
/// @solidity memory-safe-assembly
assembly {
let s := add(shl(96, bitmap.slot), shr(8, index)) // Storage slot.
let o := and(0xff, index) // Storage slot offset (bits).
sstore(s, or(and(sload(s), not(shl(o, 1))), shl(o, iszero(iszero(value)))))
}
}
/// @dev Returns the index of the least significant unset bit in `[begin..upTo]`.
/// If no set bit is found, returns `type(uint256).max`.
function _findFirstUnset(Bitmap storage bitmap, uint256 begin, uint256 upTo)
internal
view
returns (uint256 unsetBitIndex)
{
/// @solidity memory-safe-assembly
assembly {
unsetBitIndex := not(0) // Initialize to `type(uint256).max`.
let s := shl(96, bitmap.slot) // Storage offset of the bitmap.
let bucket := add(s, shr(8, begin))
let negBits := shl(and(0xff, begin), shr(and(0xff, begin), not(sload(bucket))))
if iszero(negBits) {
let lastBucket := add(s, shr(8, upTo))
for {} 1 {} {
bucket := add(bucket, 1)
negBits := not(sload(bucket))
if or(negBits, gt(bucket, lastBucket)) { break }
}
if gt(bucket, lastBucket) {
negBits := shr(and(0xff, not(upTo)), shl(and(0xff, not(upTo)), negBits))
}
}
if negBits {
// Find-first-set routine.
let b := and(negBits, add(not(negBits), 1)) // Isolate the least significant bit.
let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, b))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, b))))
r := or(r, shl(5, lt(0xffffffff, shr(r, b))))
// For the remaining 32 bits, use a De Bruijn lookup.
// forgefmt: disable-next-item
r := or(r, byte(and(div(0xd76453e0, shr(r, b)), 0x1f),
0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
r := or(shl(8, sub(bucket, s)), r)
unsetBitIndex := or(r, sub(0, or(gt(r, upTo), lt(r, begin))))
}
}
}
/// @dev Returns a storage reference to the value at (`a0`, `a1`) in `map`.
function _ref(AddressPairToUint256RefMap storage map, address a0, address a1)
internal
pure
returns (Uint256Ref storage ref)
{
/// @solidity memory-safe-assembly
assembly {
mstore(0x28, a1)
mstore(0x14, a0)
mstore(0x00, map.slot)
ref.slot := keccak256(0x00, 0x48)
// Clear the part of the free memory pointer that was overwritten.
mstore(0x28, 0x00)
}
}
/// @dev Wraps the NFT ID.
function _wrapNFTId(uint256 id, uint256 maxId) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
result := or(mul(iszero(gt(id, maxId)), id), gt(id, maxId))
}
}
/// @dev Returns `id > type(uint32).max ? 0 : id`.
function _restrictNFTId(uint256 id) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
result := mul(id, lt(id, 0x100000000))
}
}
/// @dev Returns whether `amount` is a valid `totalSupply`.
function _totalSupplyOverflows(uint256 amount) internal view returns (bool result) {
uint256 unit = _unit();
/// @solidity memory-safe-assembly
assembly {
result := iszero(iszero(or(shr(96, amount), lt(0xfffffffe, div(amount, unit)))))
}
}
/// @dev Returns `max(0, x - y)`.
function _zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(gt(x, y), sub(x, y))
}
}
/// @dev Returns `x < y ? x : y`.
function _min(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), lt(y, x)))
}
}
/// @dev Returns `b ? 1 : 0`.
function _toUint(bool b) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
result := iszero(iszero(b))
}
}
/// @dev Returns `b == 0`. This is because solc is sometimes dumb.
function _isZero(uint256 x) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := iszero(x)
}
}
/// @dev Struct containing direct transfer log data for {Transfer} events to be
/// emitted by the mirror NFT contract.
struct _DNDirectLogs {
uint256 offset;
uint256[] logs;
}
/// @dev Initiates memory allocation for direct logs with `n` log items.
function _directLogsMalloc(uint256 n, address from, address to)
private
pure
returns (_DNDirectLogs memory p)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, 0x144027d3) // `logDirectTransfer(address,address,uint256[])`.
mstore(add(m, 0x20), shr(96, shl(96, from)))
mstore(add(m, 0x40), shr(96, shl(96, to)))
mstore(add(m, 0x60), 0x60) // Offset of `logs` in the calldata to send.
// Skip 4 words: `fnSelector`, `from`, `to`, `calldataLogsOffset`.
let logs := add(0x80, m)
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(0x20, p), logs) // Set `p.logs`.
mstore(p, offset) // Set `p.offset`.
}
}
/// @dev Adds a direct log item to `p` with token `id`.
function _directLogsAppend(_DNDirectLogs memory p, uint256 id) private pure {
/// @solidity memory-safe-assembly
assembly {
let offset := mload(p)
mstore(offset, id)
mstore(p, add(offset, 0x20))
}
}
/// @dev Calls the `mirror` NFT contract to emit {Transfer} events for packed logs `p`.
function _directLogsSend(_DNDirectLogs memory p, address mirror) private {
/// @solidity memory-safe-assembly
assembly {
let logs := mload(add(p, 0x20))
let n := add(0x84, shl(5, mload(logs))) // Length of calldata to send.
let o := sub(logs, 0x80) // Start 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 containing packed log data for {Transfer} events to be
/// emitted by the mirror NFT contract.
struct _DNPackedLogs {
uint256 offset;
uint256 addressAndBit;
uint256[] logs;
}
/// @dev Initiates memory allocation for packed logs with `n` log items.
function _packedLogsMalloc(uint256 n) private pure returns (_DNPackedLogs memory p) {
/// @solidity memory-safe-assembly
assembly {
// Note that `p` implicitly allocates and advances the free memory pointer by
// 3 words, which we can safely mutate in `_packedLogsSend`.
let logs := mload(0x40)
mstore(logs, n) // Store the length.
let offset := add(0x20, logs) // Skip the word for `p.logs.length`.
mstore(0x40, add(offset, shl(5, n))) // Allocate memory.
mstore(add(0x40, p), logs) // Set `p.logs`.
mstore(p, offset) // Set `p.offset`.
}
}
/// @dev Set the current address and the burn bit.
function _packedLogsSet(_DNPackedLogs memory p, address a, uint256 burnBit) private pure {
/// @solidity memory-safe-assembly
assembly {
mstore(add(p, 0x20), or(shl(96, a), burnBit)) // Set `p.addressAndBit`.
}
}
/// @dev Adds a packed log item to `p` with token `id`.
function _packedLogsAppend(_DNPackedLogs memory p, uint256 id) private pure {
/// @solidity memory-safe-assembly
assembly {
let offset := mload(p)
mstore(offset, or(mload(add(p, 0x20)), shl(8, id))) // `p.addressAndBit | (id << 8)`.
mstore(p, add(offset, 0x20))
}
}
/// @dev Calls the `mirror` NFT contract to emit {Transfer} events for packed logs `p`.
function _packedLogsSend(_DNPackedLogs memory p, address mirror) private {
/// @solidity memory-safe-assembly
assembly {
let logs := mload(add(p, 0x40))
let o := sub(logs, 0x40) // Start of calldata to send.
mstore(o, 0x263c69d6) // `logTransfer(uint256[])`.
mstore(add(o, 0x20), 0x20) // Offset of `logs` in the calldata to send.
let n := add(0x44, shl(5, mload(logs))) // Length of calldata to send.
if iszero(and(eq(mload(o), 1), call(gas(), mirror, 0, add(o, 0x1c), n, o, 0x20))) {
revert(o, 0x00)
}
}
}
/// @dev Struct of temporary variables for transfers.
struct _DNTransferTemps {
uint256 numNFTBurns;
uint256 numNFTMints;
uint256 fromOwnedLength;
uint256 toOwnedLength;
uint256 totalNFTSupply;
uint256 fromEnd;
uint256 toEnd;
uint32 toAlias;
uint256 nextTokenId;
uint32 burnedPoolTail;
}
/// @dev Struct of temporary variables for mints.
struct _DNMintTemps {
uint256 nextTokenId;
uint32 burnedPoolTail;
uint256 toEnd;
uint32 toAlias;
}
/// @dev Returns if `a` has bytecode of non-zero length.
function _hasCode(address a) private view returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := extcodesize(a) // Can handle dirty upper bits.
}
}
/// @dev Returns the calldata value at `offset`.
function _calldataload(uint256 offset) private pure returns (uint256 value) {
/// @solidity memory-safe-assembly
assembly {
value := calldataload(offset)
}
}
/// @dev Executes a return opcode to return `x` and end the current call frame.
function _return(uint256 x) private pure {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, x)
return(0x00, 0x20)
}
}
}{
"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":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"DNAlreadyInitialized","type":"error"},{"inputs":[],"name":"DNNotInitialized","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"FnSelectorNotRecognized","type":"error"},{"inputs":[],"name":"InsufficientAllowance","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidUnit","type":"error"},{"inputs":[],"name":"LinkMirrorContractFailed","type":"error"},{"inputs":[],"name":"MirrorAddressIsZero","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"SenderNotMirror","type":"error"},{"inputs":[],"name":"TokenDoesNotExist","type":"error"},{"inputs":[],"name":"TotalSupplyOverflow","type":"error"},{"inputs":[],"name":"TransferCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"newBaseUri","type":"string"}],"name":"BaseUriChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"protocolFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"creatorFee","type":"uint256"}],"name":"Buy","type":"event"},{"anonymous":false,"inputs":[],"name":"CreatorFeeFrozen","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newCreatorFeePercentage","type":"uint256"}],"name":"CreatorFeePercentageChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"protocolFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"creatorFee","type":"uint256"}],"name":"DeflationarySell","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[],"name":"MetadataFrozen","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"PaymentReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"protocolFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"creatorFee","type":"uint256"}],"name":"Sell","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"SkipNFTSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newTokenUriContract","type":"address"}],"name":"TokenUriContractChanged","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":[],"name":"WhitelistDisabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newWhitelistMintLimit","type":"uint256"}],"name":"WhitelistMintLimitChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"addresses","type":"address[]"},{"indexed":false,"internalType":"bool","name":"isWhitelisted","type":"bool"}],"name":"WhitelistModified","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"BLAST","outputs":[{"internalType":"contract IBlast","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_NAME_LENGTH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_NFT_SUPPLY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SYMBOL_LENGTH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_UNITS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":"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":"baseUri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"blastPoints","outputs":[{"internalType":"contract IBlastPoints","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bondingCurveFreeze","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bondingCurveSpecs","outputs":[{"internalType":"uint256","name":"factor","type":"uint256"},{"internalType":"uint256","name":"exponent","type":"uint256"},{"internalType":"uint256","name":"c","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bondingCurveSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nftAmount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"buy","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"recipientOfGas","type":"address"}],"name":"claimAllGas","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipientOfYield","type":"address"}],"name":"claimAllYield","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipientOfGas","type":"address"},{"internalType":"uint256","name":"gasToClaim","type":"uint256"},{"internalType":"uint256","name":"gasSecondsToConsume","type":"uint256"}],"name":"claimGas","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipientOfGas","type":"address"},{"internalType":"uint256","name":"minClaimRateBips","type":"uint256"}],"name":"claimGasAtMinClaimRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipientOfGas","type":"address"}],"name":"claimMaxGas","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipientOfYield","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"claimYield","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"creatorFeePercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"creatorVault","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"deflationBurnCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds_","type":"uint256[]"}],"name":"deflationarySell","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"disableWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"freezeCreatorFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"freezeMetadata","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"gasClaimer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"getBuyPriceExclusiveFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"getBuyPriceInclFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"supply","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProtocolFeePercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"getSellPriceExclusiveFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"getSellPriceInclFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"getSkipNFT","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint256","name":"units_","type":"uint256"},{"internalType":"string","name":"baseUri_","type":"string"},{"internalType":"address","name":"tokenUriContract_","type":"address"},{"internalType":"uint256","name":"maxNftSupply_","type":"uint256"},{"internalType":"bool","name":"useWhitelist_","type":"bool"},{"internalType":"uint256","name":"whitelistMintLimit_","type":"uint256"},{"internalType":"bool","name":"isDeflationary_","type":"bool"}],"internalType":"struct CollectionSpecs","name":"collectionSpecs","type":"tuple"},{"internalType":"address","name":"tokenUriFallbackContract","type":"address"},{"components":[{"internalType":"uint256","name":"factor","type":"uint256"},{"internalType":"uint256","name":"exponent","type":"uint256"},{"internalType":"uint256","name":"c","type":"uint256"}],"internalType":"struct BondingCurveSpecs","name":"bondingCurveSpecs","type":"tuple"},{"internalType":"address","name":"creatorVault","type":"address"},{"internalType":"address","name":"creator","type":"address"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"gasClaimer","type":"address"},{"internalType":"address","name":"yieldClaimer","type":"address"},{"internalType":"address","name":"pointsOperator","type":"address"},{"internalType":"contract IBlastPoints","name":"blastPoints","type":"address"},{"internalType":"address","name":"dn404Mirror","type":"address"}],"internalType":"struct ICollectionImpl.InitializeParams","name":"params","type":"tuple"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isDeflationary","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isWhitelistEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"metadataFreeze","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mirrorERC721","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"addresses_","type":"address[]"},{"internalType":"bool","name":"isWhitelisted_","type":"bool"}],"name":"modifyWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pointsOperator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"router","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds_","type":"uint256[]"},{"internalType":"uint256","name":"minPrice_","type":"uint256"},{"internalType":"uint256","name":"deadline_","type":"uint256"}],"name":"sell","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"baseUri_","type":"string"}],"name":"setBaseUri","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IBlastPoints","name":"blastPoints_","type":"address"}],"name":"setBlastPoints","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"creatorFeePercentage_","type":"uint256"}],"name":"setCreatorFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"gasClaimer_","type":"address"}],"name":"setGasClaimer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pointsOperator_","type":"address"}],"name":"setPointsOperator","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":[{"internalType":"address","name":"tokenUriContract_","type":"address"}],"name":"setTokenUriContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"whitelistMintLimit_","type":"uint256"}],"name":"setWhitelistMintLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"yieldClaimer_","type":"address"}],"name":"setYieldClaimer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenUriContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenUriFallbackContract","outputs":[{"internalType":"address","name":"","type":"address"}],"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":"nonpayable","type":"function"},{"inputs":[],"name":"units","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whitelistMintLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelistMinted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"yieldClaimer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60806040523480156200001157600080fd5b506200001c62000022565b620000d6565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff1615620000735760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b0390811614620000d35780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b615ad480620000e66000396000f3fe6080604052600436106104095760003560e01c806395d89b4111610213578063c693b9b811610123578063d6febde8116100ab578063e3d891711161007a578063e3d8917114610f28578063f210ace414610f3d578063f2fde38b14610f5d578063f887ea4014610f7d578063fe889e0e14610f9d57610448565b8063d6febde814610ea0578063d8b5138a14610eb3578063dd62ed3e14610ed3578063ddd1783a14610f1257610448565b8063d111515d116100f2578063d111515d14610e20578063d1e8308b14610e35578063d570deff14610e55578063d5abeb0114610e75578063d6b0f48414610e8b57610448565b8063c693b9b814610daa578063c6d2cc9414610dca578063c8cc517514610de0578063cd2002f514610e0057610448565b8063a1c32725116101a6578063b2bd6b5011610175578063b2bd6b5014610d12578063b5077f4414610d32578063bcd2bf2514610d4a578063c2d94aec14610d6a578063c3c5658414610d8a57610448565b8063a1c3272514610c9c578063a4cfbe4d14610cbc578063a9059cbb14610cd2578063b1f63c3814610cf257610448565b806398a8cffe116101e257806398a8cffe14610c0a5780639abc832014610c375780639b19251a14610c4c578063a0bcfc7f14610c7c57610448565b806395d89b4114610ba4578063976a843514610bb957806397d7577614610bcf5780639869e07214610bea57610448565b80633d24557111610319578063706d9f78116102a157806384bfcb621161027057806384bfcb6214610af85780638607498514610b18578063870b08ac14610b2d5780638da5cb5b14610b4d5780638e8c10a214610b8a57610448565b8063706d9f7814610a6057806370a0823114610a75578063715018a614610ac357806378e808d514610ad857610448565b806349ad7e0b116102e857806349ad7e0b146109c55780634ac5a1e6146109e55780634babc79b146109fa5780634ef41efc14610a1a5780635cf4ee9114610a4057610448565b80633d2455711461092c5780634077ec301461094b57806344048e3d1461098557806347fb4553146109a557610448565b806323b872dd1161039c5780632a6a935d1161036b5780632a6a935d1461089a5780632b8a30d2146108ba578063313ce567146108d0578063381b965c146108ec5780633c0ae2c71461090c57610448565b806323b872dd1461081a578063272b13231461083a578063274e430b1461085a578063292691441461087a57610448565b806318160ddd116103d857806318160ddd1461078b578063184d69ab146107c25780631869ebda146107dc5780632163837f146107fc57610448565b806306fdde03146106d6578063095ea7b3146107015780630dd16fd5146107315780631760d1cd1461075357610448565b3661044857604080513381523460208201527f6ef95f06320e7a25a04a175ca677b7052bdd97131872c2192525a629f51be770910160405180910390a1005b68a20d6e21d0e525530860003560e01c63e5eb36c88190036104af5760018201546001600160a01b031633146104915760405163ce5a776b60e01b815260040160405180910390fd5b6104a5600435602435604435606435610fb7565b6104af6001611342565b8063813500fc0361051b5760018201546001600160a01b031633146104e75760405163ce5a776b60e01b815260040160405180910390fd5b600435602890815260443560145268a20d6e21d0e525530b60009081526048812091526024351515905561051b6001611342565b8063e985e9c50361054557600061053660043560243561134c565b9050610543811515611342565b505b80636352211e0361056c5761056c61055e600435611375565b6001600160a01b0316611342565b806324359879036105855761058561055e6004356113a2565b8063d10b6e0c036105e65760018201546001600160a01b031633146105bd5760405163ce5a776b60e01b815260040160405180910390fd5b60006105d06004356024356044356113fe565b90506105e4816001600160a01b0316611342565b505b8063081812fc036105ff576105ff61055e6004356114e2565b8063f5b100ea03610643576001600160a01b0360043516600090815268a20d6e21d0e5255313602052604090205461064390600160801b900463ffffffff16611342565b8063e2c792810361066f5768a20d6e21d0e52553085461066f90600160801b900463ffffffff16611342565b8063c87b56dd036106a857602060405101604052600061069661069160043590565b61152e565b90506020810360208152815160600181f35b8063b7a94eb8036106bd576106bd6001611342565b604051631e085ca760e11b815260040160405180910390fd5b3480156106e257600080fd5b506106eb61163f565b6040516106f89190614d68565b60405180910390f35b34801561070d57600080fd5b5061072161071c366004614dbb565b6116d1565b60405190151581526020016106f8565b34801561073d57600080fd5b5061075161074c366004614de7565b6116e7565b005b34801561075f57600080fd5b50600054610773906001600160a01b031681565b6040516001600160a01b0390911681526020016106f8565b34801561079757600080fd5b5068a20d6e21d0e525530854600160a01b90046001600160601b03165b6040519081526020016106f8565b3480156107ce57600080fd5b506010546107219060ff1681565b3480156107e857600080fd5b506107b46107f7366004614dbb565b611820565b34801561080857600080fd5b506107b469152d02c7e14af680000081565b34801561082657600080fd5b50610721610835366004614e00565b6118d3565b34801561084657600080fd5b506107b4610855366004614e41565b611941565b34801561086657600080fd5b50610721610875366004614e41565b6119e4565b34801561088657600080fd5b50600754610773906001600160a01b031681565b3480156108a657600080fd5b506107216108b5366004614e6e565b611a2d565b3480156108c657600080fd5b506107b460145481565b3480156108dc57600080fd5b50604051601281526020016106f8565b3480156108f857600080fd5b50610751610907366004614de7565b611a41565b34801561091857600080fd5b50610751610927366004614e41565b611aa0565b34801561093857600080fd5b5060155461072190610100900460ff1681565b34801561095757600080fd5b5060045460055460065461096a92919083565b604080519384526020840192909252908201526060016106f8565b34801561099157600080fd5b506107516109a0366004614e41565b611b3f565b3480156109b157600080fd5b50600354610773906001600160a01b031681565b3480156109d157600080fd5b506107516109e0366004614e41565b611b8b565b3480156109f157600080fd5b50610751611bd7565b348015610a0657600080fd5b506107b4610a15366004614de7565b611c19565b348015610a2657600080fd5b5068a20d6e21d0e5255309546001600160a01b0316610773565b348015610a4c57600080fd5b506107b4610a5b366004614e89565b611ca2565b348015610a6c57600080fd5b506107b4611d1a565b348015610a8157600080fd5b506107b4610a90366004614e41565b6001600160a01b0316600090815268a20d6e21d0e52553136020526040902054600160a01b90046001600160601b031690565b348015610acf57600080fd5b50610751611d2a565b348015610ae457600080fd5b50600154610773906001600160a01b031681565b348015610b0457600080fd5b50600e54610773906001600160a01b031681565b348015610b2457600080fd5b506107b4601e81565b348015610b3957600080fd5b50610751610b48366004614f60565b611d3e565b348015610b5957600080fd5b507f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b0316610773565b348015610b9657600080fd5b506017546107219060ff1681565b348015610bb057600080fd5b506106eb611e03565b348015610bc557600080fd5b506107b460095481565b348015610bdb57600080fd5b506107736002604360981b0181565b348015610bf657600080fd5b50610751610c053660046151c9565b611e12565b348015610c1657600080fd5b506107b4610c25366004614e41565b60136020526000908152604090205481565b348015610c4357600080fd5b506106eb612232565b348015610c5857600080fd5b50610721610c67366004614e41565b60116020526000908152604090205460ff1681565b348015610c8857600080fd5b50610751610c973660046152e9565b6122c0565b348015610ca857600080fd5b506107b4610cb736600461531d565b612357565b348015610cc857600080fd5b506107b460165481565b348015610cde57600080fd5b50610721610ced366004614dbb565b612407565b348015610cfe57600080fd5b506107b4610d0d366004614de7565b612414565b348015610d1e57600080fd5b50600254610773906001600160a01b031681565b348015610d3e57600080fd5b506107b463fffffffe81565b348015610d5657600080fd5b506107b4610d65366004614dbb565b612425565b348015610d7657600080fd5b506107b4610d85366004614e41565b612490565b348015610d9657600080fd5b506107b4610da5366004614de7565b6124f6565b348015610db657600080fd5b50610751610dc53660046153b8565b61256c565b348015610dd657600080fd5b506107b460185481565b348015610dec57600080fd5b50600d54610773906001600160a01b031681565b348015610e0c57600080fd5b506107b4610e1b366004614e41565b61279b565b348015610e2c57600080fd5b506107516127ff565b348015610e4157600080fd5b50610751610e503660046153ec565b61283f565b348015610e6157600080fd5b506107b4610e70366004614de7565b612acd565b348015610e8157600080fd5b506107b4600f5481565b348015610e9757600080fd5b50610751612ae1565b610751610eae366004614e89565b612b1e565b348015610ebf57600080fd5b50610751610ece366004614e41565b612ea5565b348015610edf57600080fd5b506107b4610eee366004615439565b602890815260149190915268a20d6e21d0e525530f60009081526048812091525490565b348015610f1e57600080fd5b506107b460125481565b348015610f3457600080fd5b506107b4600a81565b348015610f4957600080fd5b50610751610f58366004614e41565b612ef1565b348015610f6957600080fd5b50610751610f78366004614e41565b612f9a565b348015610f8957600080fd5b50600854610773906001600160a01b031681565b348015610fa957600080fd5b506015546107219060ff1681565b6001600160a01b038316610fde57604051633a954ecd60e21b815260040160405180910390fd5b68a20d6e21d0e52553095468a20d6e21d0e5255308906001600160a01b031661101a5760405163040739bf60e41b815260040160405180910390fd5b600a81016002820160006110638361103f600160201b891089025b6000190160011b90565b60008160031c8360601b0180546007841660051b1c63ffffffff1691505092915050565b63ffffffff1681526020810191909152604001600020546001600160a01b038781169116146110a45760405162a1148160e81b815260040160405180910390fd5b856001600160a01b0316836001600160a01b031614611105576110c7868461134c565b61110557826001600160a01b03166110de856114e2565b6001600160a01b03161461110557604051632ce44b5f60e11b815260040160405180910390fd5b6001600160a01b038087166000908152600b84016020526040808220928816825281209061113260095490565b83549091506008860190600160a01b90046001600160601b03168083111561116d57604051631e9acf1760e31b815260040160405180910390fd5b84546001600160601b03918490038216600160a01b9081026001600160a01b039283161787558554818104841686019093160291161783556005860160601b600889901c015460ff89161c600116156111f9576005860160601b600889901c018054600160ff8b161b191690556000888152600487016020526040902080546001600160a01b03191690555b6001600160a01b038a166000908152602082905260408120855460001963ffffffff600160801b80840482169290920180821690920263ffffffff60801b19909316929092178855600381901c631fffffff16606084901b015492939260059190911b60e0161c1690506000611279886000198d01600190811b0161103f565b905061128c838263ffffffff1684612fd5565b6112a78860001963ffffffff851601600190811b0183612fd5565b5050835463ffffffff60801b198116600160801b9182900463ffffffff908116600181019091169092021785556001600160a01b038b1660009081526020849052604090209091506112fa90828b612fd5565b61130f868a611309878e613000565b8461308d565b50816000528860601b60601c8a60601b60601c600080516020615a5f83398151915260206000a350505050505050505050565b8060005260206000f35b6028818152601483905268a20d6e21d0e525530b60009081526048812091525415155b92915050565b6000611380826130cf565b61139d5760405163677510db60e11b815260040160405180910390fd5b61136f825b600068a20d6e21d0e525530868a20d6e21d0e525530a826113d768a20d6e21d0e525531261103f600160201b88108802611035565b63ffffffff1681526020810191909152604001600020546001600160a01b03169392505050565b600068a20d6e21d0e525530868a20d6e21d0e525530a8261143368a20d6e21d0e525531261103f600160201b89108902611035565b63ffffffff1681526020810191909152604001600020546001600160a01b0390811692508316821461148657611469828461134c565b611486576040516367d9dca160e11b815260040160405180910390fd5b6000848152600482016020526040902080546001600160a01b0319166001600160a01b0387169081179091556005820160601b600886901c018054600160ff881690811b1991909116921515901b919091179055509392505050565b60006114ed826130cf565b61150a5760405163677510db60e11b815260040160405180910390fd5b50600090815268a20d6e21d0e525530c60205260409020546001600160a01b031690565b600d546060906001600160a01b0316156115ba57600d54604051638c4c1b2f60e01b8152600481018490523060248201526001600160a01b0390911690638c4c1b2f906044015b600060405180830381865afa158015611592573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261136f9190810190615472565b6000600c80546115c9906154e8565b9050111561160357600c6115dc836130ec565b6040516020016115ed929190615522565b6040516020818303038152906040529050919050565b600e54604051638c4c1b2f60e01b8152600481018490523060248201526001600160a01b0390911690638c4c1b2f90604401611575565b919050565b6060600a805461164e906154e8565b80601f016020809104026020016040519081016040528092919081815260200182805461167a906154e8565b80156116c75780601f1061169c576101008083540402835291602001916116c7565b820191906000526020600020905b8154815290600101906020018083116116aa57829003601f168201915b5050505050905090565b60006116de33848461317e565b50600192915050565b6116ef6131e0565b678ac7230489e8000081111561176a5760405162461bcd60e51b815260206004820152603560248201527f436f6c6c656374696f6e496d706c3a2063726561746f72206665652070657263604482015274656e74616765206d757374206265203c3d2031302560581b60648201526084015b60405180910390fd5b601554610100900460ff16156117e4576016548111156117e45760405162461bcd60e51b815260206004820152602f60248201527f436f6c6c656374696f6e496d706c3a2063726561746f72206665652063616e2060448201526e1bdb9b1e481899481b1bddd95c9959608a1b6064820152608401611761565b60168190556040518181527fd6df3a3a25b6e5e3b73e2423ea8f25c123fa5a25323c0710b1cf5a53d4989999906020015b60405180910390a150565b6001546000906001600160a01b0316331461184d5760405162461bcd60e51b8152600401611761906155a9565b604051637cb8cb3160e11b81523060048201526001600160a01b0384166024820152604481018390526002604360981b019063f9719662906064015b6020604051808303816000875af11580156118a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118cc91906155fd565b9392505050565b336028908152601484905268a20d6e21d0e525530f600090815260488120918190528154909190600019811461192a5780841115611924576040516313be252b60e01b815260040160405180910390fd5b83810382555b61193586868661323b565b50600195945050505050565b600080546001600160a01b0316331461196c5760405162461bcd60e51b815260040161176190615616565b604051634aa7d2f760e11b81523060048201526001600160a01b03831660248201526002604360981b019063954fa5ee906044015b6020604051808303816000875af11580156119c0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061136f91906155fd565b6001600160a01b038116600090815268a20d6e21d0e5255313602052604081208054600160581b9004600116611a1a5750503b90565b54600160581b9004600216151592915050565b6000611a393383613a78565b506001919050565b611a496131e0565b60105460ff16611a6b5760405162461bcd60e51b815260040161176190615668565b60128190556040518181527fc08f23ef53ad3e7cd7e650449b137cb29989ae6c48a726335a0bb752386c1fc790602001611815565b6003546001600160a01b03163314611aca5760405162461bcd60e51b8152600401611761906156b0565b600280546001600160a01b0319166001600160a01b038381169182179092556003546040516336b91f2b60e01b815292166004830152906336b91f2b90602401600060405180830381600087803b158015611b2457600080fd5b505af1158015611b38573d6000803e3d6000fd5b5050505050565b6003546001600160a01b03163314611b695760405162461bcd60e51b8152600401611761906156b0565b600380546001600160a01b0319166001600160a01b0392909216919091179055565b6000546001600160a01b03163314611bb55760405162461bcd60e51b815260040161176190615616565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b611bdf6131e0565b6015805461ff0019166101001790556040517f8a429ef5a9bbe6aea08f490ef3e4b48370e9d27b5e24750cacf12257fa4ea9ce90600090a1565b60145460009081611c33611c2d858461571c565b85611ca2565b90506000670de0b6b3a7640000611c48611d1a565b611c52908461572f565b611c5c919061575c565b90506000670de0b6b3a764000060165484611c77919061572f565b611c81919061575c565b905080611c8e838561571c565b611c98919061571c565b9695505050505050565b6005546000908190611cb590600161577e565b600654909150611cc690849061572f565b6004548290611cd58288615875565b84611ce0888a61577e565b611cea9190615875565b611cf4919061571c565b611cfe919061572f565b611d08919061575c565b611d12919061577e565b949350505050565b6000611d24613af6565b51919050565b611d326131e0565b611d3c6000613b7a565b565b611d466131e0565b60105460ff16611d685760405162461bcd60e51b815260040161176190615668565b60005b8251811015611dc5578160116000858481518110611d8b57611d8b615881565b6020908102919091018101516001600160a01b03168252810191909152604001600020805460ff1916911515919091179055600101611d6b565b507f472ed91ec2873cb9136458d49b242345999ac352f37d8709666738d9cba32ac18282604051611df7929190615897565b60405180910390a15050565b6060600b805461164e906154e8565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b0316600081158015611e575750825b90506000826001600160401b03166001148015611e735750303b155b905081158015611e81575080155b15611e9f5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315611ec957845460ff60401b1916600160401b1785555b855151600090611ed890613beb565b9050600081118015611eeb5750601e8111155b611f2d5760405162461bcd60e51b8152602060048201526013602482015272092dcecc2d8d2c840dcc2daca40d8cadccee8d606b1b6044820152606401611761565b6000611f40886000015160200151613beb565b9050600081118015611f535750600a8111155b611f975760405162461bcd60e51b8152602060048201526015602482015274092dcecc2d8d2c840e6f2dac4ded840d8cadccee8d605b1b6044820152606401611761565b875151600a90611fa79082615931565b50875160200151600b90611fbb9082615931565b5087516040015115801590611fdf575087516040015169152d02c7e14af680000010155b61202b5760405162461bcd60e51b815260206004820152601d60248201527f436f6c6c656374696f6e496d706c3a20696e76616c696420756e6974730000006044820152606401611761565b875160400151600955608088015161204290613d28565b6120608860c0015189608001518a61012001518b6101000151613d39565b612068613ea2565b6120796000808a6101400151613eb2565b875160600151600c9061208c9082615931565b5087516080810151600d80546001600160a01b03199081166001600160a01b03938416179091556020808c0151600e805484169185169190911790556040808d0151805160045591820151600555015160065560608b015160078054831691841691909117905560a0808c015160088054909316931692909217905566b1a2bc2ec5000060165501511561219957875160a0015163fffffffe101561217e5760405162461bcd60e51b815260206004820152602260248201527f436f6c6c656374696f6e496d706c3a206d6178537570706c7920746f6f2068696044820152610ced60f31b6064820152608401611761565b600954885160a00151612191919061572f565b600f556121ae565b6009546121aa9063fffffffe61572f565b600f555b5050855160c08101516010805491151560ff1992831617905560e0820151601255610100909101516017805491151591909216179055831561222a57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050565b600c805461223f906154e8565b80601f016020809104026020016040519081016040528092919081815260200182805461226b906154e8565b80156122b85780601f1061228d576101008083540402835291602001916122b8565b820191906000526020600020905b81548152906001019060200180831161229b57829003601f168201915b505050505081565b6122c86131e0565b60155460ff161561231b5760405162461bcd60e51b815260206004820152601f60248201527f436f6c6c656374696f6e496d706c3a206d6574616461746120667265657a65006044820152606401611761565b600c6123278282615931565b507f87cdeaffd8e70903d6ce7cc983fac3b09ca79e83818124c98e47a1d70f8027d6816040516118159190614d68565b600080546001600160a01b031633146123825760405162461bcd60e51b815260040161176190615616565b604051637d7e71cf60e11b81523060048201526001600160a01b038516602482015260448101849052606481018390526002604360981b019063fafce39e906084016020604051808303816000875af11580156123e3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d1291906155fd565b60006116de33848461323b565b60145460009081611d128285611ca2565b600080546001600160a01b031633146124505760405162461bcd60e51b815260040161176190615616565b604051630951888f60e01b81523060048201526001600160a01b0384166024820152604481018390526002604360981b0190630951888f90606401611889565b6001546000906001600160a01b031633146124bd5760405162461bcd60e51b8152600401611761906155a9565b60405163430021db60e11b81523060048201526001600160a01b03831660248201526002604360981b019063860043b6906044016119a1565b601454600090816125078285611ca2565b90506000670de0b6b3a764000061251c611d1a565b612526908461572f565b612530919061575c565b90506000670de0b6b3a76400006016548461254b919061572f565b612555919061575c565b905080612562838561577e565b611c98919061577e565b612574614070565b60175460ff166125c65760405162461bcd60e51b815260206004820181905260248201527f436f6c6c656374696f6e496d706c3a206e6f74206465666c6174696f6e6172796044820152606401611761565b8051806126155760405162461bcd60e51b815260206004820152601b60248201527f436f6c6c656374696f6e496d706c3a206e6f20746f6b656e49647300000000006044820152606401611761565b60648111156126665760405162461bcd60e51b815260206004820152601e60248201527f436f6c6c656374696f6e496d706c3a206d61782031303020746f6b656e7300006044820152606401611761565b600061267460185483611ca2565b90506000612680613af6565b90506000670de0b6b3a764000082600001518461269d919061572f565b6126a7919061575c565b90506000670de0b6b3a7640000601654856126c2919061572f565b6126cc919061575c565b90506126d7866140a8565b8551601860008282546126ea919061577e565b909155505060208301516126fe9083614138565b600754612714906001600160a01b031682614138565b6127323382612723858861571c565b61272d919061571c565b614138565b60408051868152602081018690529081018390526060810182905233907fbceaeba7c52fa63737171b74007ed07a5e53917edfb2c775793e5c4480d4de149060800160405180910390a250505050506127986001600080516020615a7f83398151915255565b50565b600080546001600160a01b031633146127c65760405162461bcd60e51b815260040161176190615616565b60405163662aa11d60e01b81523060048201526001600160a01b03831660248201526002604360981b019063662aa11d906044016119a1565b6128076131e0565b6015805460ff191660011790556040517feef043febddf4e1d1cf1f72ff1407b84e036e805aa0934418cb82095da8d716490600090a1565b612847614070565b8251806128965760405162461bcd60e51b815260206004820152601b60248201527f436f6c6c656374696f6e496d706c3a206e6f20746f6b656e49647300000000006044820152606401611761565b60648111156128e75760405162461bcd60e51b815260206004820152601e60248201527f436f6c6c656374696f6e496d706c3a206d61782031303020746f6b656e7300006044820152606401611761565b814211156129375760405162461bcd60e51b815260206004820152601f60248201527f436f6c6c656374696f6e496d706c3a20646561646c696e6520706173736564006044820152606401611761565b60006129508260145461294a919061571c565b83611ca2565b9050600061295c613af6565b90506000670de0b6b3a7640000826000015184612979919061572f565b612983919061575c565b90506000670de0b6b3a76400006016548561299e919061572f565b6129a8919061575c565b905086816129b6848761571c565b6129c0919061571c565b1015612a0e5760405162461bcd60e51b815260206004820152601d60248201527f436f6c6c656374696f6e496d706c3a20707269636520746f6f206c6f770000006044820152606401611761565b8460146000828254612a20919061571c565b90915550612a2f9050886140a8565b612a3d836020015183614138565b600754612a53906001600160a01b031682614138565b612a623382612723858861571c565b60408051868152602081018690529081018390526060810182905233907f483f8aec0fd892ac72ad1ba8d0e9c9e73db59c12d263fd71de480b5b3deeae3c9060800160405180910390a25050505050612ac86001600080516020615a7f83398151915255565b505050565b60145460009081611d12611c2d858461571c565b612ae96131e0565b6010805460ff191690556040517f212c6e1d3045c9581ef0adf2504dbb1d137f52f38162ccf77a16c69d14eba5c390600090a1565b612b26614070565b60008211612b815760405162461bcd60e51b815260206004820152602260248201527f436f6c6c656374696f6e496d706c3a20616d6f756e74206d757374206265203e604482015261020360f41b6064820152608401611761565b60105460ff1615612c99573360009081526011602052604090205460ff16612beb5760405162461bcd60e51b815260206004820152601f60248201527f436f6c6c656374696f6e496d706c3a206e6f742077686974656c6973746564006044820152606401611761565b60125415612c995760125433600090815260136020526040902054612c1190849061577e565b1115612c745760405162461bcd60e51b815260206004820152602c60248201527f436f6c6c656374696f6e496d706c3a2077686974656c697374206d696e74206c60448201526b1a5b5a5d081c995858da195960a21b6064820152608401611761565b3360009081526013602052604081208054849290612c9390849061577e565b90915550505b80421115612ce95760405162461bcd60e51b815260206004820152601f60248201527f436f6c6c656374696f6e496d706c3a20646561646c696e6520706173736564006044820152606401611761565b6000612cf760145484611ca2565b90506000612d03613af6565b90506000670de0b6b3a7640000826000015184612d20919061572f565b612d2a919061575c565b90506000670de0b6b3a764000060165485612d45919061572f565b612d4f919061575c565b905080612d5c838661577e565b612d66919061577e565b341015612dc15760405162461bcd60e51b815260206004820152602360248201527f436f6c6c656374696f6e496d706c3a206e6f7420656e6f756768206574682073604482015262195b9d60ea1b6064820152608401611761565b8560146000828254612dd3919061577e565b90915550612de3905033876141e3565b612df1836020015183614138565b600754612e07906001600160a01b031682614138565b60008183612e15873461571c565b612e1f919061571c565b612e29919061571c565b90508015612e3b57612e3b3382614138565b60408051888152602081018790529081018490526060810183905233907f064fb1933e186be0b289a87e98518dc18cc9856ecbc9f1353d1a138ddf733ec59060800160405180910390a25050505050612ea16001600080516020615a7f83398151915255565b5050565b6001546001600160a01b03163314612ecf5760405162461bcd60e51b8152600401611761906155a9565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b612ef96131e0565b60155460ff1615612f4c5760405162461bcd60e51b815260206004820152601f60248201527f436f6c6c656374696f6e496d706c3a206d6574616461746120667265657a65006044820152606401611761565b600d80546001600160a01b0319166001600160a01b0383169081179091556040519081527fcb93ebca008843e5202d69a674d3212675c9de55e9446c71da481f6b4f76d46490602001611815565b612fa26131e0565b6001600160a01b038116612fcc57604051631e4fbdf760e01b815260006004820152602401611761565b61279881613b7a565b8160031c8360601b016007831660051b815480821c841863ffffffff16821b81188355505050505050565b8154600160601b900463ffffffff1668a20d6e21d0e52553088161308657805463ffffffff198116600163ffffffff928316019182169081178355855463ffffffff60601b1916600160601b82021786556000818152600284016020526040902080546001600160a01b0319166001600160a01b03871617905590925061308657600080fd5b5092915050565b600183038060021c8560601b0180546003831660061b92508463ffffffff168460201b178082851c186001600160401b0316841b821883555050505050505050565b6000806130db836113a2565b6001600160a01b0316141592915050565b606060006130f9836142a6565b60010190506000816001600160401b0381111561311857613118614eab565b6040519080825280601f01601f191660200182016040528015613142576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a850494508461314c57509392505050565b6028828152601484905268a20d6e21d0e525530f600090815260488120915281905560008181526001600160a01b0380841691908516907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590602090a3505050565b336132127f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b031614611d3c5760405163118cdaa760e01b8152336004820152602401611761565b6001600160a01b03821661326257604051633a954ecd60e21b815260040160405180910390fd5b6001600160a01b038316600090815268a20d6e21d0e52553136020526040812068a20d6e21d0e5255308916132968561437e565b60018401549091506001600160a01b03166132c45760405163040739bf60e41b815260040160405180910390fd5b61332660405180610140016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600063ffffffff16815260200160008152602001600063ffffffff1681525090565b825463ffffffff600160801b808304821660408501528454041660608301526001600160601b03600160a01b909104168086111561337757604051631e9acf1760e31b815260040160405180910390fd5b83546001600160601b0391879003828116600160a01b9081026001600160a01b03938416178755855481810485168a01948516909102921691909117845560408301519091906133e2906133ca60095490565b84816133d8576133d8615746565b0480821191030290565b83528354600160581b900460021661344a57876001600160a01b0316896001600160a01b03160361341b57825160408401510360608401525b61344461342760095490565b828161343557613435615746565b04846060015180821191030290565b60208401525b5050613454600190565b1561362d576000613486826040015161347b84600001518560200151808218908211021890565b808218908211021890565b905080613493575061362d565b8151819003825260208201805182900390526001600160a01b03808816908916036134c857606082018051909101905261362d565b60006134d5828a8a6143e7565b6001600160a01b03808b166000908152600889016020526040808220928c168252902091925090613506868b613000565b63ffffffff1660e08601526060850151938401935b6040860180516000190190819052600381901c606085901b015460009160051b60e0161c63ffffffff1663ffffffff169050613558838383612fd5565b61356b8a600a01828960e001518561308d565b84518181526020018552600881901c60058b0160601b015460ff82161c600116156135c95760058a0160601b600882901c018054600160ff84161b19169055600081815260048b016020526040902080546001600160a01b03191690555b5084816001019150810361351b5760608601819052865463ffffffff808316600160801b90810263ffffffff60801b19938416178a5560408901518b54921602911617885560018901546136279085906001600160a01b0316614444565b50505050505b80516020820151855463ffffffff600160801b80830482168401859003608087018190529091160263ffffffff60801b19909116178655600a8601916000916136769101614473565b8654600160601b900463ffffffff1661012085015283519091501561380757606089901b6001176020828101919091526001600160a01b038a166000908152600888019091526040808220908501518551810360a08701819052885463ffffffff909116600160801b0263ffffffff60801b199091161788556101208601515b60001991909101600381901c606084901b0154909190600583901b60e0161c63ffffffff16613728878260008061308d565b8551602080880151600884901b17825201865260068b0160601b600882901c018054600160ff84161b191690558415613775576137758b6009018380600101945063ffffffff1683612fd5565b600881901c60058c0160601b015460ff82161c600116156137c95760058b0160601b600882901c018054600160ff84161b19169055600081815260048c016020526040902080546001600160a01b03191690555b508660a0015182036136f65783156138025763ffffffff811661012088018190528a5463ffffffff60601b1916600160601b909102178a555b505050505b602083015115613a2357606088901b60208201526001600160a01b0388166000908152600887016020526040902061383f858a613000565b63ffffffff1660e0850152600061385560095490565b8854600160a01b90046001600160601b03168161387457613874615746565b89549190049150600160201b900463ffffffff1681811180159091021761010086015260608501516020860151810160c08701819052875463ffffffff60801b1916600160801b63ffffffff928316021788558954600160401b9004165b600087610120015163ffffffff168263ffffffff16146139225760098b0160601b631fffffff600384901c160154600183019260e060059190911b161c63ffffffff1663ffffffff169050613981565b506101008701515b61393b87600019830160011b61103f565b63ffffffff161561396d5761396661395a8c60060183600101876144ba565b85811180159091021790565b905061392a565b600181018481118015909102176101008901525b600881901c60068c0160601b018054600160ff84161b80199091161790556139aa858483612fd5565b6139c087828a60e001518680600101975061308d565b8551602080880151600884901b178252018652508660c0015182036138d257895461010088015163ffffffff908116600160201b0267ffffffff000000001991909316600160401b02166bffffffffffffffff0000000019909116171789555050505b60408101515115613a47576001860154613a479082906001600160a01b031661459f565b5050846000528560601b60601c8760601b60601c600080516020615a5f83398151915260206000a350505050505050565b6000613a838361437e565b8054909150600160581b9004600216151582151514613abe57805460ff600160581b80830482166002189091160260ff60581b199091161781555b8115156000528260601b60601c7fb5a1de456fff688115a4f75380060c23c8532d14ff85f687cc871456d642039360206000a2505050565b604080518082019091526000808252602082015260085460408051639b86528560e01b815281516001600160a01b0390931692639b865285926004808401939192918290030181865afa158015613b51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b7591906159f0565b905090565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b600080825b8051821015613d21576007818381518110613c0d57613c0d615881565b01602001516001600160f81b031990811690911c16600003613c3b57613c3460018361577e565b9150613d0f565b8051600360f91b90600590839085908110613c5857613c58615881565b01602001516001600160f81b031990811690911c1603613c7d57613c3460028361577e565b8051600760f91b90600490839085908110613c9a57613c9a615881565b01602001516001600160f81b031990811690911c1603613cbf57613c3460038361577e565b8051600f60f91b90600390839085908110613cdc57613cdc615881565b01602001516001600160f81b031990811690911c1603613d0157613c3460048361577e565b613d0c60018361577e565b91505b82613d1981615a45565b935050613bf0565b5050919050565b613d306145db565b61279881614624565b613d416145db565b600080546001600160a01b038087166001600160a01b0319928316178355600180549187169190921617905560408051634e606c4760e01b815290516002604360981b0192634e606c47926004808201939182900301818387803b158015613da857600080fd5b505af1158015613dbc573d6000803e3d6000fd5b505050506002604360981b016001600160a01b031663f098767a6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015613e0257600080fd5b505af1158015613e16573d6000803e3d6000fd5b5050600280546001600160a01b03199081166001600160a01b03878116918217909355600380549092169286169283179091556040516336b91f2b60e01b8152600481019290925292506336b91f2b9150602401600060405180830381600087803b158015613e8457600080fd5b505af1158015613e98573d6000803e3d6000fd5b5050505050505050565b613eaa6145db565b611d3c61462c565b60095468a20d6e21d0e5255308906001600160601b03906001900310613eeb5760405163265f13bd60e21b815260040160405180910390fd5b60018101546001600160a01b031615613f1757604051633ab534b960e21b815260040160405180910390fd5b6001600160a01b038216613f3e576040516339a84a7b60e01b815260040160405180910390fd5b630f4599e560005233602052602060006024601c6000865af160016000511416613f705763d125259c6000526004601cfd5b805467ffffffff000000001916600160201b1781556001810180546001600160a01b0384166001600160a01b0319909116179055831561406a576001600160a01b038316613fd157604051633a954ecd60e21b815260040160405180910390fd5b613fda84614634565b15613ff85760405163e5cfe95760e01b815260040160405180910390fd5b80546001600160a01b0316600160a01b6001600160601b0386160217815560006140218461437e565b80546001600160601b038716600160a01b026001600160a01b039182161782556000878152919250851690600080516020615a5f833981519152602082a3611b38846001613a78565b50505050565b600080516020615a7f8339815191528054600119016140a257604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b60005b81518110156141195760008282815181106140c8576140c8615881565b6020026020010151905060006140f68484815181106140e9576140e9615881565b6020026020010151611375565b905061410481308433614659565b5050808061411190615a45565b9150506140ab565b506127983061412760095490565b8351614133919061572f565b6146a2565b8047101561415b5760405163cd78605960e01b8152306004820152602401611761565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146141a8576040519150601f19603f3d011682016040523d82523d6000602084013e6141ad565b606091505b5050905080612ac857604051630a12f52160e11b815260040160405180910390fd5b6001600080516020615a7f83398151915255565b60006141ee60095490565b6141f8908361572f565b9050600f548161420760095490565b601854614214919061572f565b68a20d6e21d0e525530854600160a01b90046001600160601b0316614239919061577e565b614243919061577e565b111561429c5760405162461bcd60e51b815260206004820152602260248201527f436f6c6c656374696f6e496d706c3a206d617820737570706c79207265616368604482015261195960f21b6064820152608401611761565b612ac8838261496a565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b83106142e55772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef81000000008310614311576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061432f57662386f26fc10000830492506010015b6305f5e1008310614347576305f5e100830492506008015b612710831061435b57612710830492506004015b6064831061436d576064830492506002015b600a831061136f5760010192915050565b6001600160a01b038116600090815268a20d6e21d0e5255313602052604090208054600160581b900460011661163a5760006002833b15156143c0565b151590565b83546001929091029190911760ff16600160581b0260ff60581b1990911617825550919050565b6040805180820182526000815260606020808301828152845163144027d381526001600160a01b03978816928101929092529490951685840152848101526080840185815260a060059690961b8501860190925291529101815290565b6020820151805160051b60840160808203915060208282601c85016000875af160018351141661406a57600082fd5b61449760405180606001604052806000815260200160008152602001606081525090565b604051828152806020018360051b81016040528183604001528083525050919050565b6000801990508360601b8360081c81018054198560ff161c8560ff161b8061450f578460081c83015b60018301925082541991508083118217156144e3578083111561450d5760ff86191691821b90911c905b505b80156145955782820360081b7e1f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405821960010183166fffffffffffffffffffffffffffffffff811160071b81811c6001600160401b031060061b1781811c63ffffffff1060051b1790811c63d76453e004601f169190911a1717858111878210176000031793505b5050509392505050565b60408201516040810363263c69d68152602080820152815160051b604401915060208183601c84016000875af160018251141661406a57600081fd5b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff16611d3c57604051631afcd79f60e31b815260040160405180910390fd5b612fa26145db565b6141cf6145db565b60008061464060095490565b606084901c930463fffffffe1092909217151592915050565b61466584848484610fb7565b68a20d6e21d0e5255308600061467d600187876143e7565b805185815260200181529050600182015461222a9082906001600160a01b0316614444565b68a20d6e21d0e52553095468a20d6e21d0e5255308906001600160a01b03166146de5760405163040739bf60e41b815260040160405180910390fd5b6001600160a01b0383166000908152600b8201602052604090208054600160a01b90046001600160601b03168084111561472b57604051631e9acf1760e31b815260040160405180910390fd5b81546001600160601b0391859003828116600160a01b9081026001600160a01b0393841617855585548181048516889003948516909102908316178555908616600090815260088501602052604081208454929392909163ffffffff600160801b90920491909116906147af826147a160095490565b87816133d8576133d8615746565b9050801561493c5760006147c282614473565b600160608c901b176020820152885463ffffffff600160801b80830482168690038216810263ffffffff60801b19938416178c558a54909216858703808316909302178a558a54929350600092600a8c019291600160601b909104165b60001996909601600381901c606089901b0154909690600588901b60e0161c63ffffffff16614851848260008061308d565b8551602080880151600884901b17825201865260068d0160601b600882901c018054600160ff84161b19169055841561489e5761489e8d6009018380600101945063ffffffff1683612fd5565b600881901c60058e0160601b015460ff82161c600116156148f25760058d0160601b600882901c018054600160ff84161b19169055600081815260048e016020526040902080546001600160a01b03191690555b5081870361481f57831561491d578b5463ffffffff60601b1916600160601b63ffffffff831602178c555b60018c01546149369086906001600160a01b031661459f565b50505050505b505050600085815290506001600160a01b038616600080516020615a5f833981519152602083a35050505050565b6001600160a01b03821661499157604051633a954ecd60e21b815260040160405180910390fd5b600061499c8361437e565b68a20d6e21d0e52553095490915068a20d6e21d0e5255308906001600160a01b03166149db5760405163040739bf60e41b815260040160405180910390fd5b60408051608081018252600080825260208201819052918101829052606081019190915282546001600160a01b038116600160a01b918290046001600160601b0390811687019081169092021784556009548181614a3b57614a3b615746565b0460408301525081546001600160601b03600160a01b80830482168701918216026001600160a01b0390921691909117835560009081614a7d6143bb83614634565b9050868210811715614aa25760405163e5cfe95760e01b815260040160405180910390fd5b6009548281614ab357614ab3615746565b87549190049350614acf9250600160581b900460021690501590565b15614d19576001600160a01b03861660009081526008840160205260408082208654918501519092600a870192600160801b900463ffffffff1691614b1a9083810390841002614473565b9050806040015151600014614d145760608a901b602082015260408082015151885463ffffffff600160801b80830482169093018116830263ffffffff60801b19928316178b55928901518b5493169091029116178855614b7b888b613000565b63ffffffff90811660608801528754600160601b810482166020890152600160201b810482168781118015909102178852600160401b9004165b6000876020015163ffffffff168263ffffffff1614614c04576009890160601b631fffffff600384901c160154600183019260e060059190911b161c63ffffffff1663ffffffff169050614c5b565b5086515b614c1985600019830160011b61103f565b63ffffffff1615614c4b57614c44614c388a600601836001018a6144ba565b88811180159091021790565b9050614c08565b6001810187811180159091021788525b600881901c60068a0160601b018054600160ff84161b8019909116179055614c84868583612fd5565b614c9a85828a606001518780600101985061308d565b8251602080850151600884901b1782520183525086604001518303614bb5578651885463ffffffff838116600160401b026bffffffff00000000000000001991909316600160201b02166bffffffffffffffff0000000019909116171788556001880154614d129083906001600160a01b031661459f565b505b505050505b60008581526001600160a01b03871690600080516020615a5f833981519152602082a3505050505050565b60005b83811015614d5f578181015183820152602001614d47565b50506000910152565b6020815260008251806020840152614d87816040850160208701614d44565b601f01601f19169190910160400192915050565b6001600160a01b038116811461279857600080fd5b803561163a81614d9b565b60008060408385031215614dce57600080fd5b8235614dd981614d9b565b946020939093013593505050565b600060208284031215614df957600080fd5b5035919050565b600080600060608486031215614e1557600080fd5b8335614e2081614d9b565b92506020840135614e3081614d9b565b929592945050506040919091013590565b600060208284031215614e5357600080fd5b81356118cc81614d9b565b8035801515811461163a57600080fd5b600060208284031215614e8057600080fd5b6118cc82614e5e565b60008060408385031215614e9c57600080fd5b50508035926020909101359150565b634e487b7160e01b600052604160045260246000fd5b60405161012081016001600160401b0381118282101715614ee457614ee4614eab565b60405290565b60405161016081016001600160401b0381118282101715614ee457614ee4614eab565b604051601f8201601f191681016001600160401b0381118282101715614f3557614f35614eab565b604052919050565b60006001600160401b03821115614f5657614f56614eab565b5060051b60200190565b60008060408385031215614f7357600080fd5b82356001600160401b03811115614f8957600080fd5b8301601f81018513614f9a57600080fd5b80356020614faf614faa83614f3d565b614f0d565b82815260059290921b83018101918181019088841115614fce57600080fd5b938201935b83851015614ff5578435614fe681614d9b565b82529382019390820190614fd3565b95506150049050868201614e5e565b93505050509250929050565b60006001600160401b0382111561502957615029614eab565b50601f01601f191660200190565b600082601f83011261504857600080fd5b8135615056614faa82615010565b81815284602083860101111561506b57600080fd5b816020850160208301376000918101602001919091529392505050565b6000610120828403121561509b57600080fd5b6150a3614ec1565b905081356001600160401b03808211156150bc57600080fd5b6150c885838601615037565b835260208401359150808211156150de57600080fd5b6150ea85838601615037565b602084015260408401356040840152606084013591508082111561510d57600080fd5b5061511a84828501615037565b60608301525061512c60808301614db0565b608082015260a082013560a082015261514760c08301614e5e565b60c082015260e082013560e0820152610100615164818401614e5e565b9082015292915050565b60006060828403121561518057600080fd5b604051606081018181106001600160401b03821117156151a2576151a2614eab565b80604052508091508235815260208301356020820152604083013560408201525092915050565b6000602082840312156151db57600080fd5b81356001600160401b03808211156151f257600080fd5b908301906101a0828603121561520757600080fd5b61520f614eea565b82358281111561521e57600080fd5b61522a87828601615088565b82525061523960208401614db0565b602082015261524b866040850161516e565b604082015261525c60a08401614db0565b606082015261526d60c08401614db0565b608082015261527e60e08401614db0565b60a08201526101009150615293828401614db0565b60c08201526101206152a6818501614db0565b60e08301526101406152b9818601614db0565b848401526152ca6101608601614db0565b828401526152db6101808601614db0565b908301525095945050505050565b6000602082840312156152fb57600080fd5b81356001600160401b0381111561531157600080fd5b611d1284828501615037565b60008060006060848603121561533257600080fd5b833561533d81614d9b565b95602085013595506040909401359392505050565b600082601f83011261536357600080fd5b81356020615373614faa83614f3d565b82815260059290921b8401810191818101908684111561539257600080fd5b8286015b848110156153ad5780358352918301918301615396565b509695505050505050565b6000602082840312156153ca57600080fd5b81356001600160401b038111156153e057600080fd5b611d1284828501615352565b60008060006060848603121561540157600080fd5b83356001600160401b0381111561541757600080fd5b61542386828701615352565b9660208601359650604090950135949350505050565b6000806040838503121561544c57600080fd5b823561545781614d9b565b9150602083013561546781614d9b565b809150509250929050565b60006020828403121561548457600080fd5b81516001600160401b0381111561549a57600080fd5b8201601f810184136154ab57600080fd5b80516154b9614faa82615010565b8181528560208385010111156154ce57600080fd5b6154df826020830160208601614d44565b95945050505050565b600181811c908216806154fc57607f821691505b60208210810361551c57634e487b7160e01b600052602260045260246000fd5b50919050565b6000808454615530816154e8565b60018281168015615548576001811461555d5761558c565b60ff198416875282151583028701945061558c565b8860005260208060002060005b858110156155835781548a82015290840190820161556a565b50505082870194505b5050505083516155a0818360208801614d44565b01949350505050565b60208082526034908201527f426c617374426173655570677261646561626c653a2063616c6c6572206973206040820152733737ba103a3432903cb4b2b63221b630b4b6b2b960611b606082015260800190565b60006020828403121561560f57600080fd5b5051919050565b60208082526032908201527f426c617374426173655570677261646561626c653a2063616c6c6572206973206040820152713737ba103a34329033b0b9a1b630b4b6b2b960711b606082015260800190565b60208082526028908201527f436f6c6c656374696f6e496d706c3a2077686974656c697374206973206e6f7460408201526708195b98589b195960c21b606082015260800190565b60208082526036908201527f426c617374426173655570677261646561626c653a2063616c6c6572206973206040820152753737ba103a3432903837b4b73a39a7b832b930ba37b960511b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b8181038181111561136f5761136f615706565b808202811582820484141761136f5761136f615706565b634e487b7160e01b600052601260045260246000fd5b60008261577957634e487b7160e01b600052601260045260246000fd5b500490565b8082018082111561136f5761136f615706565b600181815b808511156157cc5781600019048211156157b2576157b2615706565b808516156157bf57918102915b93841c9390800290615796565b509250929050565b6000826157e35750600161136f565b816157f05750600061136f565b816001811461580657600281146158105761582c565b600191505061136f565b60ff84111561582157615821615706565b50506001821b61136f565b5060208310610133831016604e8410600b841016171561584f575081810a61136f565b6158598383615791565b806000190482111561586d5761586d615706565b029392505050565b60006118cc83836157d4565b634e487b7160e01b600052603260045260246000fd5b604080825283519082018190526000906020906060840190828701845b828110156158d95781516001600160a01b0316845292840192908401906001016158b4565b50505093151592019190915250919050565b601f821115612ac857600081815260208120601f850160051c810160208610156159125750805b601f850160051c820191505b8181101561222a5782815560010161591e565b81516001600160401b0381111561594a5761594a614eab565b61595e8161595884546154e8565b846158eb565b602080601f831160018114615993576000841561597b5750858301515b600019600386901b1c1916600185901b17855561222a565b600085815260208120601f198616915b828110156159c2578886015182559484019460019091019084016159a3565b50858210156159e05787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600060408284031215615a0257600080fd5b604051604081018181106001600160401b0382111715615a2457615a24614eab565b604052825181526020830151615a3981614d9b565b60208201529392505050565b600060018201615a5757615a57615706565b506001019056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a2646970667358221220752f6f169109859cc903af2e5eaaa9e9f9c0bfd66c8bb4ec73eedeb87a0abc2f64736f6c63430008140033
Deployed Bytecode
0x6080604052600436106104095760003560e01c806395d89b4111610213578063c693b9b811610123578063d6febde8116100ab578063e3d891711161007a578063e3d8917114610f28578063f210ace414610f3d578063f2fde38b14610f5d578063f887ea4014610f7d578063fe889e0e14610f9d57610448565b8063d6febde814610ea0578063d8b5138a14610eb3578063dd62ed3e14610ed3578063ddd1783a14610f1257610448565b8063d111515d116100f2578063d111515d14610e20578063d1e8308b14610e35578063d570deff14610e55578063d5abeb0114610e75578063d6b0f48414610e8b57610448565b8063c693b9b814610daa578063c6d2cc9414610dca578063c8cc517514610de0578063cd2002f514610e0057610448565b8063a1c32725116101a6578063b2bd6b5011610175578063b2bd6b5014610d12578063b5077f4414610d32578063bcd2bf2514610d4a578063c2d94aec14610d6a578063c3c5658414610d8a57610448565b8063a1c3272514610c9c578063a4cfbe4d14610cbc578063a9059cbb14610cd2578063b1f63c3814610cf257610448565b806398a8cffe116101e257806398a8cffe14610c0a5780639abc832014610c375780639b19251a14610c4c578063a0bcfc7f14610c7c57610448565b806395d89b4114610ba4578063976a843514610bb957806397d7577614610bcf5780639869e07214610bea57610448565b80633d24557111610319578063706d9f78116102a157806384bfcb621161027057806384bfcb6214610af85780638607498514610b18578063870b08ac14610b2d5780638da5cb5b14610b4d5780638e8c10a214610b8a57610448565b8063706d9f7814610a6057806370a0823114610a75578063715018a614610ac357806378e808d514610ad857610448565b806349ad7e0b116102e857806349ad7e0b146109c55780634ac5a1e6146109e55780634babc79b146109fa5780634ef41efc14610a1a5780635cf4ee9114610a4057610448565b80633d2455711461092c5780634077ec301461094b57806344048e3d1461098557806347fb4553146109a557610448565b806323b872dd1161039c5780632a6a935d1161036b5780632a6a935d1461089a5780632b8a30d2146108ba578063313ce567146108d0578063381b965c146108ec5780633c0ae2c71461090c57610448565b806323b872dd1461081a578063272b13231461083a578063274e430b1461085a578063292691441461087a57610448565b806318160ddd116103d857806318160ddd1461078b578063184d69ab146107c25780631869ebda146107dc5780632163837f146107fc57610448565b806306fdde03146106d6578063095ea7b3146107015780630dd16fd5146107315780631760d1cd1461075357610448565b3661044857604080513381523460208201527f6ef95f06320e7a25a04a175ca677b7052bdd97131872c2192525a629f51be770910160405180910390a1005b68a20d6e21d0e525530860003560e01c63e5eb36c88190036104af5760018201546001600160a01b031633146104915760405163ce5a776b60e01b815260040160405180910390fd5b6104a5600435602435604435606435610fb7565b6104af6001611342565b8063813500fc0361051b5760018201546001600160a01b031633146104e75760405163ce5a776b60e01b815260040160405180910390fd5b600435602890815260443560145268a20d6e21d0e525530b60009081526048812091526024351515905561051b6001611342565b8063e985e9c50361054557600061053660043560243561134c565b9050610543811515611342565b505b80636352211e0361056c5761056c61055e600435611375565b6001600160a01b0316611342565b806324359879036105855761058561055e6004356113a2565b8063d10b6e0c036105e65760018201546001600160a01b031633146105bd5760405163ce5a776b60e01b815260040160405180910390fd5b60006105d06004356024356044356113fe565b90506105e4816001600160a01b0316611342565b505b8063081812fc036105ff576105ff61055e6004356114e2565b8063f5b100ea03610643576001600160a01b0360043516600090815268a20d6e21d0e5255313602052604090205461064390600160801b900463ffffffff16611342565b8063e2c792810361066f5768a20d6e21d0e52553085461066f90600160801b900463ffffffff16611342565b8063c87b56dd036106a857602060405101604052600061069661069160043590565b61152e565b90506020810360208152815160600181f35b8063b7a94eb8036106bd576106bd6001611342565b604051631e085ca760e11b815260040160405180910390fd5b3480156106e257600080fd5b506106eb61163f565b6040516106f89190614d68565b60405180910390f35b34801561070d57600080fd5b5061072161071c366004614dbb565b6116d1565b60405190151581526020016106f8565b34801561073d57600080fd5b5061075161074c366004614de7565b6116e7565b005b34801561075f57600080fd5b50600054610773906001600160a01b031681565b6040516001600160a01b0390911681526020016106f8565b34801561079757600080fd5b5068a20d6e21d0e525530854600160a01b90046001600160601b03165b6040519081526020016106f8565b3480156107ce57600080fd5b506010546107219060ff1681565b3480156107e857600080fd5b506107b46107f7366004614dbb565b611820565b34801561080857600080fd5b506107b469152d02c7e14af680000081565b34801561082657600080fd5b50610721610835366004614e00565b6118d3565b34801561084657600080fd5b506107b4610855366004614e41565b611941565b34801561086657600080fd5b50610721610875366004614e41565b6119e4565b34801561088657600080fd5b50600754610773906001600160a01b031681565b3480156108a657600080fd5b506107216108b5366004614e6e565b611a2d565b3480156108c657600080fd5b506107b460145481565b3480156108dc57600080fd5b50604051601281526020016106f8565b3480156108f857600080fd5b50610751610907366004614de7565b611a41565b34801561091857600080fd5b50610751610927366004614e41565b611aa0565b34801561093857600080fd5b5060155461072190610100900460ff1681565b34801561095757600080fd5b5060045460055460065461096a92919083565b604080519384526020840192909252908201526060016106f8565b34801561099157600080fd5b506107516109a0366004614e41565b611b3f565b3480156109b157600080fd5b50600354610773906001600160a01b031681565b3480156109d157600080fd5b506107516109e0366004614e41565b611b8b565b3480156109f157600080fd5b50610751611bd7565b348015610a0657600080fd5b506107b4610a15366004614de7565b611c19565b348015610a2657600080fd5b5068a20d6e21d0e5255309546001600160a01b0316610773565b348015610a4c57600080fd5b506107b4610a5b366004614e89565b611ca2565b348015610a6c57600080fd5b506107b4611d1a565b348015610a8157600080fd5b506107b4610a90366004614e41565b6001600160a01b0316600090815268a20d6e21d0e52553136020526040902054600160a01b90046001600160601b031690565b348015610acf57600080fd5b50610751611d2a565b348015610ae457600080fd5b50600154610773906001600160a01b031681565b348015610b0457600080fd5b50600e54610773906001600160a01b031681565b348015610b2457600080fd5b506107b4601e81565b348015610b3957600080fd5b50610751610b48366004614f60565b611d3e565b348015610b5957600080fd5b507f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b0316610773565b348015610b9657600080fd5b506017546107219060ff1681565b348015610bb057600080fd5b506106eb611e03565b348015610bc557600080fd5b506107b460095481565b348015610bdb57600080fd5b506107736002604360981b0181565b348015610bf657600080fd5b50610751610c053660046151c9565b611e12565b348015610c1657600080fd5b506107b4610c25366004614e41565b60136020526000908152604090205481565b348015610c4357600080fd5b506106eb612232565b348015610c5857600080fd5b50610721610c67366004614e41565b60116020526000908152604090205460ff1681565b348015610c8857600080fd5b50610751610c973660046152e9565b6122c0565b348015610ca857600080fd5b506107b4610cb736600461531d565b612357565b348015610cc857600080fd5b506107b460165481565b348015610cde57600080fd5b50610721610ced366004614dbb565b612407565b348015610cfe57600080fd5b506107b4610d0d366004614de7565b612414565b348015610d1e57600080fd5b50600254610773906001600160a01b031681565b348015610d3e57600080fd5b506107b463fffffffe81565b348015610d5657600080fd5b506107b4610d65366004614dbb565b612425565b348015610d7657600080fd5b506107b4610d85366004614e41565b612490565b348015610d9657600080fd5b506107b4610da5366004614de7565b6124f6565b348015610db657600080fd5b50610751610dc53660046153b8565b61256c565b348015610dd657600080fd5b506107b460185481565b348015610dec57600080fd5b50600d54610773906001600160a01b031681565b348015610e0c57600080fd5b506107b4610e1b366004614e41565b61279b565b348015610e2c57600080fd5b506107516127ff565b348015610e4157600080fd5b50610751610e503660046153ec565b61283f565b348015610e6157600080fd5b506107b4610e70366004614de7565b612acd565b348015610e8157600080fd5b506107b4600f5481565b348015610e9757600080fd5b50610751612ae1565b610751610eae366004614e89565b612b1e565b348015610ebf57600080fd5b50610751610ece366004614e41565b612ea5565b348015610edf57600080fd5b506107b4610eee366004615439565b602890815260149190915268a20d6e21d0e525530f60009081526048812091525490565b348015610f1e57600080fd5b506107b460125481565b348015610f3457600080fd5b506107b4600a81565b348015610f4957600080fd5b50610751610f58366004614e41565b612ef1565b348015610f6957600080fd5b50610751610f78366004614e41565b612f9a565b348015610f8957600080fd5b50600854610773906001600160a01b031681565b348015610fa957600080fd5b506015546107219060ff1681565b6001600160a01b038316610fde57604051633a954ecd60e21b815260040160405180910390fd5b68a20d6e21d0e52553095468a20d6e21d0e5255308906001600160a01b031661101a5760405163040739bf60e41b815260040160405180910390fd5b600a81016002820160006110638361103f600160201b891089025b6000190160011b90565b60008160031c8360601b0180546007841660051b1c63ffffffff1691505092915050565b63ffffffff1681526020810191909152604001600020546001600160a01b038781169116146110a45760405162a1148160e81b815260040160405180910390fd5b856001600160a01b0316836001600160a01b031614611105576110c7868461134c565b61110557826001600160a01b03166110de856114e2565b6001600160a01b03161461110557604051632ce44b5f60e11b815260040160405180910390fd5b6001600160a01b038087166000908152600b84016020526040808220928816825281209061113260095490565b83549091506008860190600160a01b90046001600160601b03168083111561116d57604051631e9acf1760e31b815260040160405180910390fd5b84546001600160601b03918490038216600160a01b9081026001600160a01b039283161787558554818104841686019093160291161783556005860160601b600889901c015460ff89161c600116156111f9576005860160601b600889901c018054600160ff8b161b191690556000888152600487016020526040902080546001600160a01b03191690555b6001600160a01b038a166000908152602082905260408120855460001963ffffffff600160801b80840482169290920180821690920263ffffffff60801b19909316929092178855600381901c631fffffff16606084901b015492939260059190911b60e0161c1690506000611279886000198d01600190811b0161103f565b905061128c838263ffffffff1684612fd5565b6112a78860001963ffffffff851601600190811b0183612fd5565b5050835463ffffffff60801b198116600160801b9182900463ffffffff908116600181019091169092021785556001600160a01b038b1660009081526020849052604090209091506112fa90828b612fd5565b61130f868a611309878e613000565b8461308d565b50816000528860601b60601c8a60601b60601c600080516020615a5f83398151915260206000a350505050505050505050565b8060005260206000f35b6028818152601483905268a20d6e21d0e525530b60009081526048812091525415155b92915050565b6000611380826130cf565b61139d5760405163677510db60e11b815260040160405180910390fd5b61136f825b600068a20d6e21d0e525530868a20d6e21d0e525530a826113d768a20d6e21d0e525531261103f600160201b88108802611035565b63ffffffff1681526020810191909152604001600020546001600160a01b03169392505050565b600068a20d6e21d0e525530868a20d6e21d0e525530a8261143368a20d6e21d0e525531261103f600160201b89108902611035565b63ffffffff1681526020810191909152604001600020546001600160a01b0390811692508316821461148657611469828461134c565b611486576040516367d9dca160e11b815260040160405180910390fd5b6000848152600482016020526040902080546001600160a01b0319166001600160a01b0387169081179091556005820160601b600886901c018054600160ff881690811b1991909116921515901b919091179055509392505050565b60006114ed826130cf565b61150a5760405163677510db60e11b815260040160405180910390fd5b50600090815268a20d6e21d0e525530c60205260409020546001600160a01b031690565b600d546060906001600160a01b0316156115ba57600d54604051638c4c1b2f60e01b8152600481018490523060248201526001600160a01b0390911690638c4c1b2f906044015b600060405180830381865afa158015611592573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261136f9190810190615472565b6000600c80546115c9906154e8565b9050111561160357600c6115dc836130ec565b6040516020016115ed929190615522565b6040516020818303038152906040529050919050565b600e54604051638c4c1b2f60e01b8152600481018490523060248201526001600160a01b0390911690638c4c1b2f90604401611575565b919050565b6060600a805461164e906154e8565b80601f016020809104026020016040519081016040528092919081815260200182805461167a906154e8565b80156116c75780601f1061169c576101008083540402835291602001916116c7565b820191906000526020600020905b8154815290600101906020018083116116aa57829003601f168201915b5050505050905090565b60006116de33848461317e565b50600192915050565b6116ef6131e0565b678ac7230489e8000081111561176a5760405162461bcd60e51b815260206004820152603560248201527f436f6c6c656374696f6e496d706c3a2063726561746f72206665652070657263604482015274656e74616765206d757374206265203c3d2031302560581b60648201526084015b60405180910390fd5b601554610100900460ff16156117e4576016548111156117e45760405162461bcd60e51b815260206004820152602f60248201527f436f6c6c656374696f6e496d706c3a2063726561746f72206665652063616e2060448201526e1bdb9b1e481899481b1bddd95c9959608a1b6064820152608401611761565b60168190556040518181527fd6df3a3a25b6e5e3b73e2423ea8f25c123fa5a25323c0710b1cf5a53d4989999906020015b60405180910390a150565b6001546000906001600160a01b0316331461184d5760405162461bcd60e51b8152600401611761906155a9565b604051637cb8cb3160e11b81523060048201526001600160a01b0384166024820152604481018390526002604360981b019063f9719662906064015b6020604051808303816000875af11580156118a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118cc91906155fd565b9392505050565b336028908152601484905268a20d6e21d0e525530f600090815260488120918190528154909190600019811461192a5780841115611924576040516313be252b60e01b815260040160405180910390fd5b83810382555b61193586868661323b565b50600195945050505050565b600080546001600160a01b0316331461196c5760405162461bcd60e51b815260040161176190615616565b604051634aa7d2f760e11b81523060048201526001600160a01b03831660248201526002604360981b019063954fa5ee906044015b6020604051808303816000875af11580156119c0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061136f91906155fd565b6001600160a01b038116600090815268a20d6e21d0e5255313602052604081208054600160581b9004600116611a1a5750503b90565b54600160581b9004600216151592915050565b6000611a393383613a78565b506001919050565b611a496131e0565b60105460ff16611a6b5760405162461bcd60e51b815260040161176190615668565b60128190556040518181527fc08f23ef53ad3e7cd7e650449b137cb29989ae6c48a726335a0bb752386c1fc790602001611815565b6003546001600160a01b03163314611aca5760405162461bcd60e51b8152600401611761906156b0565b600280546001600160a01b0319166001600160a01b038381169182179092556003546040516336b91f2b60e01b815292166004830152906336b91f2b90602401600060405180830381600087803b158015611b2457600080fd5b505af1158015611b38573d6000803e3d6000fd5b5050505050565b6003546001600160a01b03163314611b695760405162461bcd60e51b8152600401611761906156b0565b600380546001600160a01b0319166001600160a01b0392909216919091179055565b6000546001600160a01b03163314611bb55760405162461bcd60e51b815260040161176190615616565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b611bdf6131e0565b6015805461ff0019166101001790556040517f8a429ef5a9bbe6aea08f490ef3e4b48370e9d27b5e24750cacf12257fa4ea9ce90600090a1565b60145460009081611c33611c2d858461571c565b85611ca2565b90506000670de0b6b3a7640000611c48611d1a565b611c52908461572f565b611c5c919061575c565b90506000670de0b6b3a764000060165484611c77919061572f565b611c81919061575c565b905080611c8e838561571c565b611c98919061571c565b9695505050505050565b6005546000908190611cb590600161577e565b600654909150611cc690849061572f565b6004548290611cd58288615875565b84611ce0888a61577e565b611cea9190615875565b611cf4919061571c565b611cfe919061572f565b611d08919061575c565b611d12919061577e565b949350505050565b6000611d24613af6565b51919050565b611d326131e0565b611d3c6000613b7a565b565b611d466131e0565b60105460ff16611d685760405162461bcd60e51b815260040161176190615668565b60005b8251811015611dc5578160116000858481518110611d8b57611d8b615881565b6020908102919091018101516001600160a01b03168252810191909152604001600020805460ff1916911515919091179055600101611d6b565b507f472ed91ec2873cb9136458d49b242345999ac352f37d8709666738d9cba32ac18282604051611df7929190615897565b60405180910390a15050565b6060600b805461164e906154e8565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b0316600081158015611e575750825b90506000826001600160401b03166001148015611e735750303b155b905081158015611e81575080155b15611e9f5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315611ec957845460ff60401b1916600160401b1785555b855151600090611ed890613beb565b9050600081118015611eeb5750601e8111155b611f2d5760405162461bcd60e51b8152602060048201526013602482015272092dcecc2d8d2c840dcc2daca40d8cadccee8d606b1b6044820152606401611761565b6000611f40886000015160200151613beb565b9050600081118015611f535750600a8111155b611f975760405162461bcd60e51b8152602060048201526015602482015274092dcecc2d8d2c840e6f2dac4ded840d8cadccee8d605b1b6044820152606401611761565b875151600a90611fa79082615931565b50875160200151600b90611fbb9082615931565b5087516040015115801590611fdf575087516040015169152d02c7e14af680000010155b61202b5760405162461bcd60e51b815260206004820152601d60248201527f436f6c6c656374696f6e496d706c3a20696e76616c696420756e6974730000006044820152606401611761565b875160400151600955608088015161204290613d28565b6120608860c0015189608001518a61012001518b6101000151613d39565b612068613ea2565b6120796000808a6101400151613eb2565b875160600151600c9061208c9082615931565b5087516080810151600d80546001600160a01b03199081166001600160a01b03938416179091556020808c0151600e805484169185169190911790556040808d0151805160045591820151600555015160065560608b015160078054831691841691909117905560a0808c015160088054909316931692909217905566b1a2bc2ec5000060165501511561219957875160a0015163fffffffe101561217e5760405162461bcd60e51b815260206004820152602260248201527f436f6c6c656374696f6e496d706c3a206d6178537570706c7920746f6f2068696044820152610ced60f31b6064820152608401611761565b600954885160a00151612191919061572f565b600f556121ae565b6009546121aa9063fffffffe61572f565b600f555b5050855160c08101516010805491151560ff1992831617905560e0820151601255610100909101516017805491151591909216179055831561222a57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050565b600c805461223f906154e8565b80601f016020809104026020016040519081016040528092919081815260200182805461226b906154e8565b80156122b85780601f1061228d576101008083540402835291602001916122b8565b820191906000526020600020905b81548152906001019060200180831161229b57829003601f168201915b505050505081565b6122c86131e0565b60155460ff161561231b5760405162461bcd60e51b815260206004820152601f60248201527f436f6c6c656374696f6e496d706c3a206d6574616461746120667265657a65006044820152606401611761565b600c6123278282615931565b507f87cdeaffd8e70903d6ce7cc983fac3b09ca79e83818124c98e47a1d70f8027d6816040516118159190614d68565b600080546001600160a01b031633146123825760405162461bcd60e51b815260040161176190615616565b604051637d7e71cf60e11b81523060048201526001600160a01b038516602482015260448101849052606481018390526002604360981b019063fafce39e906084016020604051808303816000875af11580156123e3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d1291906155fd565b60006116de33848461323b565b60145460009081611d128285611ca2565b600080546001600160a01b031633146124505760405162461bcd60e51b815260040161176190615616565b604051630951888f60e01b81523060048201526001600160a01b0384166024820152604481018390526002604360981b0190630951888f90606401611889565b6001546000906001600160a01b031633146124bd5760405162461bcd60e51b8152600401611761906155a9565b60405163430021db60e11b81523060048201526001600160a01b03831660248201526002604360981b019063860043b6906044016119a1565b601454600090816125078285611ca2565b90506000670de0b6b3a764000061251c611d1a565b612526908461572f565b612530919061575c565b90506000670de0b6b3a76400006016548461254b919061572f565b612555919061575c565b905080612562838561577e565b611c98919061577e565b612574614070565b60175460ff166125c65760405162461bcd60e51b815260206004820181905260248201527f436f6c6c656374696f6e496d706c3a206e6f74206465666c6174696f6e6172796044820152606401611761565b8051806126155760405162461bcd60e51b815260206004820152601b60248201527f436f6c6c656374696f6e496d706c3a206e6f20746f6b656e49647300000000006044820152606401611761565b60648111156126665760405162461bcd60e51b815260206004820152601e60248201527f436f6c6c656374696f6e496d706c3a206d61782031303020746f6b656e7300006044820152606401611761565b600061267460185483611ca2565b90506000612680613af6565b90506000670de0b6b3a764000082600001518461269d919061572f565b6126a7919061575c565b90506000670de0b6b3a7640000601654856126c2919061572f565b6126cc919061575c565b90506126d7866140a8565b8551601860008282546126ea919061577e565b909155505060208301516126fe9083614138565b600754612714906001600160a01b031682614138565b6127323382612723858861571c565b61272d919061571c565b614138565b60408051868152602081018690529081018390526060810182905233907fbceaeba7c52fa63737171b74007ed07a5e53917edfb2c775793e5c4480d4de149060800160405180910390a250505050506127986001600080516020615a7f83398151915255565b50565b600080546001600160a01b031633146127c65760405162461bcd60e51b815260040161176190615616565b60405163662aa11d60e01b81523060048201526001600160a01b03831660248201526002604360981b019063662aa11d906044016119a1565b6128076131e0565b6015805460ff191660011790556040517feef043febddf4e1d1cf1f72ff1407b84e036e805aa0934418cb82095da8d716490600090a1565b612847614070565b8251806128965760405162461bcd60e51b815260206004820152601b60248201527f436f6c6c656374696f6e496d706c3a206e6f20746f6b656e49647300000000006044820152606401611761565b60648111156128e75760405162461bcd60e51b815260206004820152601e60248201527f436f6c6c656374696f6e496d706c3a206d61782031303020746f6b656e7300006044820152606401611761565b814211156129375760405162461bcd60e51b815260206004820152601f60248201527f436f6c6c656374696f6e496d706c3a20646561646c696e6520706173736564006044820152606401611761565b60006129508260145461294a919061571c565b83611ca2565b9050600061295c613af6565b90506000670de0b6b3a7640000826000015184612979919061572f565b612983919061575c565b90506000670de0b6b3a76400006016548561299e919061572f565b6129a8919061575c565b905086816129b6848761571c565b6129c0919061571c565b1015612a0e5760405162461bcd60e51b815260206004820152601d60248201527f436f6c6c656374696f6e496d706c3a20707269636520746f6f206c6f770000006044820152606401611761565b8460146000828254612a20919061571c565b90915550612a2f9050886140a8565b612a3d836020015183614138565b600754612a53906001600160a01b031682614138565b612a623382612723858861571c565b60408051868152602081018690529081018390526060810182905233907f483f8aec0fd892ac72ad1ba8d0e9c9e73db59c12d263fd71de480b5b3deeae3c9060800160405180910390a25050505050612ac86001600080516020615a7f83398151915255565b505050565b60145460009081611d12611c2d858461571c565b612ae96131e0565b6010805460ff191690556040517f212c6e1d3045c9581ef0adf2504dbb1d137f52f38162ccf77a16c69d14eba5c390600090a1565b612b26614070565b60008211612b815760405162461bcd60e51b815260206004820152602260248201527f436f6c6c656374696f6e496d706c3a20616d6f756e74206d757374206265203e604482015261020360f41b6064820152608401611761565b60105460ff1615612c99573360009081526011602052604090205460ff16612beb5760405162461bcd60e51b815260206004820152601f60248201527f436f6c6c656374696f6e496d706c3a206e6f742077686974656c6973746564006044820152606401611761565b60125415612c995760125433600090815260136020526040902054612c1190849061577e565b1115612c745760405162461bcd60e51b815260206004820152602c60248201527f436f6c6c656374696f6e496d706c3a2077686974656c697374206d696e74206c60448201526b1a5b5a5d081c995858da195960a21b6064820152608401611761565b3360009081526013602052604081208054849290612c9390849061577e565b90915550505b80421115612ce95760405162461bcd60e51b815260206004820152601f60248201527f436f6c6c656374696f6e496d706c3a20646561646c696e6520706173736564006044820152606401611761565b6000612cf760145484611ca2565b90506000612d03613af6565b90506000670de0b6b3a7640000826000015184612d20919061572f565b612d2a919061575c565b90506000670de0b6b3a764000060165485612d45919061572f565b612d4f919061575c565b905080612d5c838661577e565b612d66919061577e565b341015612dc15760405162461bcd60e51b815260206004820152602360248201527f436f6c6c656374696f6e496d706c3a206e6f7420656e6f756768206574682073604482015262195b9d60ea1b6064820152608401611761565b8560146000828254612dd3919061577e565b90915550612de3905033876141e3565b612df1836020015183614138565b600754612e07906001600160a01b031682614138565b60008183612e15873461571c565b612e1f919061571c565b612e29919061571c565b90508015612e3b57612e3b3382614138565b60408051888152602081018790529081018490526060810183905233907f064fb1933e186be0b289a87e98518dc18cc9856ecbc9f1353d1a138ddf733ec59060800160405180910390a25050505050612ea16001600080516020615a7f83398151915255565b5050565b6001546001600160a01b03163314612ecf5760405162461bcd60e51b8152600401611761906155a9565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b612ef96131e0565b60155460ff1615612f4c5760405162461bcd60e51b815260206004820152601f60248201527f436f6c6c656374696f6e496d706c3a206d6574616461746120667265657a65006044820152606401611761565b600d80546001600160a01b0319166001600160a01b0383169081179091556040519081527fcb93ebca008843e5202d69a674d3212675c9de55e9446c71da481f6b4f76d46490602001611815565b612fa26131e0565b6001600160a01b038116612fcc57604051631e4fbdf760e01b815260006004820152602401611761565b61279881613b7a565b8160031c8360601b016007831660051b815480821c841863ffffffff16821b81188355505050505050565b8154600160601b900463ffffffff1668a20d6e21d0e52553088161308657805463ffffffff198116600163ffffffff928316019182169081178355855463ffffffff60601b1916600160601b82021786556000818152600284016020526040902080546001600160a01b0319166001600160a01b03871617905590925061308657600080fd5b5092915050565b600183038060021c8560601b0180546003831660061b92508463ffffffff168460201b178082851c186001600160401b0316841b821883555050505050505050565b6000806130db836113a2565b6001600160a01b0316141592915050565b606060006130f9836142a6565b60010190506000816001600160401b0381111561311857613118614eab565b6040519080825280601f01601f191660200182016040528015613142576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a850494508461314c57509392505050565b6028828152601484905268a20d6e21d0e525530f600090815260488120915281905560008181526001600160a01b0380841691908516907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590602090a3505050565b336132127f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b031614611d3c5760405163118cdaa760e01b8152336004820152602401611761565b6001600160a01b03821661326257604051633a954ecd60e21b815260040160405180910390fd5b6001600160a01b038316600090815268a20d6e21d0e52553136020526040812068a20d6e21d0e5255308916132968561437e565b60018401549091506001600160a01b03166132c45760405163040739bf60e41b815260040160405180910390fd5b61332660405180610140016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600063ffffffff16815260200160008152602001600063ffffffff1681525090565b825463ffffffff600160801b808304821660408501528454041660608301526001600160601b03600160a01b909104168086111561337757604051631e9acf1760e31b815260040160405180910390fd5b83546001600160601b0391879003828116600160a01b9081026001600160a01b03938416178755855481810485168a01948516909102921691909117845560408301519091906133e2906133ca60095490565b84816133d8576133d8615746565b0480821191030290565b83528354600160581b900460021661344a57876001600160a01b0316896001600160a01b03160361341b57825160408401510360608401525b61344461342760095490565b828161343557613435615746565b04846060015180821191030290565b60208401525b5050613454600190565b1561362d576000613486826040015161347b84600001518560200151808218908211021890565b808218908211021890565b905080613493575061362d565b8151819003825260208201805182900390526001600160a01b03808816908916036134c857606082018051909101905261362d565b60006134d5828a8a6143e7565b6001600160a01b03808b166000908152600889016020526040808220928c168252902091925090613506868b613000565b63ffffffff1660e08601526060850151938401935b6040860180516000190190819052600381901c606085901b015460009160051b60e0161c63ffffffff1663ffffffff169050613558838383612fd5565b61356b8a600a01828960e001518561308d565b84518181526020018552600881901c60058b0160601b015460ff82161c600116156135c95760058a0160601b600882901c018054600160ff84161b19169055600081815260048b016020526040902080546001600160a01b03191690555b5084816001019150810361351b5760608601819052865463ffffffff808316600160801b90810263ffffffff60801b19938416178a5560408901518b54921602911617885560018901546136279085906001600160a01b0316614444565b50505050505b80516020820151855463ffffffff600160801b80830482168401859003608087018190529091160263ffffffff60801b19909116178655600a8601916000916136769101614473565b8654600160601b900463ffffffff1661012085015283519091501561380757606089901b6001176020828101919091526001600160a01b038a166000908152600888019091526040808220908501518551810360a08701819052885463ffffffff909116600160801b0263ffffffff60801b199091161788556101208601515b60001991909101600381901c606084901b0154909190600583901b60e0161c63ffffffff16613728878260008061308d565b8551602080880151600884901b17825201865260068b0160601b600882901c018054600160ff84161b191690558415613775576137758b6009018380600101945063ffffffff1683612fd5565b600881901c60058c0160601b015460ff82161c600116156137c95760058b0160601b600882901c018054600160ff84161b19169055600081815260048c016020526040902080546001600160a01b03191690555b508660a0015182036136f65783156138025763ffffffff811661012088018190528a5463ffffffff60601b1916600160601b909102178a555b505050505b602083015115613a2357606088901b60208201526001600160a01b0388166000908152600887016020526040902061383f858a613000565b63ffffffff1660e0850152600061385560095490565b8854600160a01b90046001600160601b03168161387457613874615746565b89549190049150600160201b900463ffffffff1681811180159091021761010086015260608501516020860151810160c08701819052875463ffffffff60801b1916600160801b63ffffffff928316021788558954600160401b9004165b600087610120015163ffffffff168263ffffffff16146139225760098b0160601b631fffffff600384901c160154600183019260e060059190911b161c63ffffffff1663ffffffff169050613981565b506101008701515b61393b87600019830160011b61103f565b63ffffffff161561396d5761396661395a8c60060183600101876144ba565b85811180159091021790565b905061392a565b600181018481118015909102176101008901525b600881901c60068c0160601b018054600160ff84161b80199091161790556139aa858483612fd5565b6139c087828a60e001518680600101975061308d565b8551602080880151600884901b178252018652508660c0015182036138d257895461010088015163ffffffff908116600160201b0267ffffffff000000001991909316600160401b02166bffffffffffffffff0000000019909116171789555050505b60408101515115613a47576001860154613a479082906001600160a01b031661459f565b5050846000528560601b60601c8760601b60601c600080516020615a5f83398151915260206000a350505050505050565b6000613a838361437e565b8054909150600160581b9004600216151582151514613abe57805460ff600160581b80830482166002189091160260ff60581b199091161781555b8115156000528260601b60601c7fb5a1de456fff688115a4f75380060c23c8532d14ff85f687cc871456d642039360206000a2505050565b604080518082019091526000808252602082015260085460408051639b86528560e01b815281516001600160a01b0390931692639b865285926004808401939192918290030181865afa158015613b51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b7591906159f0565b905090565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b600080825b8051821015613d21576007818381518110613c0d57613c0d615881565b01602001516001600160f81b031990811690911c16600003613c3b57613c3460018361577e565b9150613d0f565b8051600360f91b90600590839085908110613c5857613c58615881565b01602001516001600160f81b031990811690911c1603613c7d57613c3460028361577e565b8051600760f91b90600490839085908110613c9a57613c9a615881565b01602001516001600160f81b031990811690911c1603613cbf57613c3460038361577e565b8051600f60f91b90600390839085908110613cdc57613cdc615881565b01602001516001600160f81b031990811690911c1603613d0157613c3460048361577e565b613d0c60018361577e565b91505b82613d1981615a45565b935050613bf0565b5050919050565b613d306145db565b61279881614624565b613d416145db565b600080546001600160a01b038087166001600160a01b0319928316178355600180549187169190921617905560408051634e606c4760e01b815290516002604360981b0192634e606c47926004808201939182900301818387803b158015613da857600080fd5b505af1158015613dbc573d6000803e3d6000fd5b505050506002604360981b016001600160a01b031663f098767a6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015613e0257600080fd5b505af1158015613e16573d6000803e3d6000fd5b5050600280546001600160a01b03199081166001600160a01b03878116918217909355600380549092169286169283179091556040516336b91f2b60e01b8152600481019290925292506336b91f2b9150602401600060405180830381600087803b158015613e8457600080fd5b505af1158015613e98573d6000803e3d6000fd5b5050505050505050565b613eaa6145db565b611d3c61462c565b60095468a20d6e21d0e5255308906001600160601b03906001900310613eeb5760405163265f13bd60e21b815260040160405180910390fd5b60018101546001600160a01b031615613f1757604051633ab534b960e21b815260040160405180910390fd5b6001600160a01b038216613f3e576040516339a84a7b60e01b815260040160405180910390fd5b630f4599e560005233602052602060006024601c6000865af160016000511416613f705763d125259c6000526004601cfd5b805467ffffffff000000001916600160201b1781556001810180546001600160a01b0384166001600160a01b0319909116179055831561406a576001600160a01b038316613fd157604051633a954ecd60e21b815260040160405180910390fd5b613fda84614634565b15613ff85760405163e5cfe95760e01b815260040160405180910390fd5b80546001600160a01b0316600160a01b6001600160601b0386160217815560006140218461437e565b80546001600160601b038716600160a01b026001600160a01b039182161782556000878152919250851690600080516020615a5f833981519152602082a3611b38846001613a78565b50505050565b600080516020615a7f8339815191528054600119016140a257604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b60005b81518110156141195760008282815181106140c8576140c8615881565b6020026020010151905060006140f68484815181106140e9576140e9615881565b6020026020010151611375565b905061410481308433614659565b5050808061411190615a45565b9150506140ab565b506127983061412760095490565b8351614133919061572f565b6146a2565b8047101561415b5760405163cd78605960e01b8152306004820152602401611761565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146141a8576040519150601f19603f3d011682016040523d82523d6000602084013e6141ad565b606091505b5050905080612ac857604051630a12f52160e11b815260040160405180910390fd5b6001600080516020615a7f83398151915255565b60006141ee60095490565b6141f8908361572f565b9050600f548161420760095490565b601854614214919061572f565b68a20d6e21d0e525530854600160a01b90046001600160601b0316614239919061577e565b614243919061577e565b111561429c5760405162461bcd60e51b815260206004820152602260248201527f436f6c6c656374696f6e496d706c3a206d617820737570706c79207265616368604482015261195960f21b6064820152608401611761565b612ac8838261496a565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b83106142e55772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef81000000008310614311576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061432f57662386f26fc10000830492506010015b6305f5e1008310614347576305f5e100830492506008015b612710831061435b57612710830492506004015b6064831061436d576064830492506002015b600a831061136f5760010192915050565b6001600160a01b038116600090815268a20d6e21d0e5255313602052604090208054600160581b900460011661163a5760006002833b15156143c0565b151590565b83546001929091029190911760ff16600160581b0260ff60581b1990911617825550919050565b6040805180820182526000815260606020808301828152845163144027d381526001600160a01b03978816928101929092529490951685840152848101526080840185815260a060059690961b8501860190925291529101815290565b6020820151805160051b60840160808203915060208282601c85016000875af160018351141661406a57600082fd5b61449760405180606001604052806000815260200160008152602001606081525090565b604051828152806020018360051b81016040528183604001528083525050919050565b6000801990508360601b8360081c81018054198560ff161c8560ff161b8061450f578460081c83015b60018301925082541991508083118217156144e3578083111561450d5760ff86191691821b90911c905b505b80156145955782820360081b7e1f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405821960010183166fffffffffffffffffffffffffffffffff811160071b81811c6001600160401b031060061b1781811c63ffffffff1060051b1790811c63d76453e004601f169190911a1717858111878210176000031793505b5050509392505050565b60408201516040810363263c69d68152602080820152815160051b604401915060208183601c84016000875af160018251141661406a57600081fd5b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff16611d3c57604051631afcd79f60e31b815260040160405180910390fd5b612fa26145db565b6141cf6145db565b60008061464060095490565b606084901c930463fffffffe1092909217151592915050565b61466584848484610fb7565b68a20d6e21d0e5255308600061467d600187876143e7565b805185815260200181529050600182015461222a9082906001600160a01b0316614444565b68a20d6e21d0e52553095468a20d6e21d0e5255308906001600160a01b03166146de5760405163040739bf60e41b815260040160405180910390fd5b6001600160a01b0383166000908152600b8201602052604090208054600160a01b90046001600160601b03168084111561472b57604051631e9acf1760e31b815260040160405180910390fd5b81546001600160601b0391859003828116600160a01b9081026001600160a01b0393841617855585548181048516889003948516909102908316178555908616600090815260088501602052604081208454929392909163ffffffff600160801b90920491909116906147af826147a160095490565b87816133d8576133d8615746565b9050801561493c5760006147c282614473565b600160608c901b176020820152885463ffffffff600160801b80830482168690038216810263ffffffff60801b19938416178c558a54909216858703808316909302178a558a54929350600092600a8c019291600160601b909104165b60001996909601600381901c606089901b0154909690600588901b60e0161c63ffffffff16614851848260008061308d565b8551602080880151600884901b17825201865260068d0160601b600882901c018054600160ff84161b19169055841561489e5761489e8d6009018380600101945063ffffffff1683612fd5565b600881901c60058e0160601b015460ff82161c600116156148f25760058d0160601b600882901c018054600160ff84161b19169055600081815260048e016020526040902080546001600160a01b03191690555b5081870361481f57831561491d578b5463ffffffff60601b1916600160601b63ffffffff831602178c555b60018c01546149369086906001600160a01b031661459f565b50505050505b505050600085815290506001600160a01b038616600080516020615a5f833981519152602083a35050505050565b6001600160a01b03821661499157604051633a954ecd60e21b815260040160405180910390fd5b600061499c8361437e565b68a20d6e21d0e52553095490915068a20d6e21d0e5255308906001600160a01b03166149db5760405163040739bf60e41b815260040160405180910390fd5b60408051608081018252600080825260208201819052918101829052606081019190915282546001600160a01b038116600160a01b918290046001600160601b0390811687019081169092021784556009548181614a3b57614a3b615746565b0460408301525081546001600160601b03600160a01b80830482168701918216026001600160a01b0390921691909117835560009081614a7d6143bb83614634565b9050868210811715614aa25760405163e5cfe95760e01b815260040160405180910390fd5b6009548281614ab357614ab3615746565b87549190049350614acf9250600160581b900460021690501590565b15614d19576001600160a01b03861660009081526008840160205260408082208654918501519092600a870192600160801b900463ffffffff1691614b1a9083810390841002614473565b9050806040015151600014614d145760608a901b602082015260408082015151885463ffffffff600160801b80830482169093018116830263ffffffff60801b19928316178b55928901518b5493169091029116178855614b7b888b613000565b63ffffffff90811660608801528754600160601b810482166020890152600160201b810482168781118015909102178852600160401b9004165b6000876020015163ffffffff168263ffffffff1614614c04576009890160601b631fffffff600384901c160154600183019260e060059190911b161c63ffffffff1663ffffffff169050614c5b565b5086515b614c1985600019830160011b61103f565b63ffffffff1615614c4b57614c44614c388a600601836001018a6144ba565b88811180159091021790565b9050614c08565b6001810187811180159091021788525b600881901c60068a0160601b018054600160ff84161b8019909116179055614c84868583612fd5565b614c9a85828a606001518780600101985061308d565b8251602080850151600884901b1782520183525086604001518303614bb5578651885463ffffffff838116600160401b026bffffffff00000000000000001991909316600160201b02166bffffffffffffffff0000000019909116171788556001880154614d129083906001600160a01b031661459f565b505b505050505b60008581526001600160a01b03871690600080516020615a5f833981519152602082a3505050505050565b60005b83811015614d5f578181015183820152602001614d47565b50506000910152565b6020815260008251806020840152614d87816040850160208701614d44565b601f01601f19169190910160400192915050565b6001600160a01b038116811461279857600080fd5b803561163a81614d9b565b60008060408385031215614dce57600080fd5b8235614dd981614d9b565b946020939093013593505050565b600060208284031215614df957600080fd5b5035919050565b600080600060608486031215614e1557600080fd5b8335614e2081614d9b565b92506020840135614e3081614d9b565b929592945050506040919091013590565b600060208284031215614e5357600080fd5b81356118cc81614d9b565b8035801515811461163a57600080fd5b600060208284031215614e8057600080fd5b6118cc82614e5e565b60008060408385031215614e9c57600080fd5b50508035926020909101359150565b634e487b7160e01b600052604160045260246000fd5b60405161012081016001600160401b0381118282101715614ee457614ee4614eab565b60405290565b60405161016081016001600160401b0381118282101715614ee457614ee4614eab565b604051601f8201601f191681016001600160401b0381118282101715614f3557614f35614eab565b604052919050565b60006001600160401b03821115614f5657614f56614eab565b5060051b60200190565b60008060408385031215614f7357600080fd5b82356001600160401b03811115614f8957600080fd5b8301601f81018513614f9a57600080fd5b80356020614faf614faa83614f3d565b614f0d565b82815260059290921b83018101918181019088841115614fce57600080fd5b938201935b83851015614ff5578435614fe681614d9b565b82529382019390820190614fd3565b95506150049050868201614e5e565b93505050509250929050565b60006001600160401b0382111561502957615029614eab565b50601f01601f191660200190565b600082601f83011261504857600080fd5b8135615056614faa82615010565b81815284602083860101111561506b57600080fd5b816020850160208301376000918101602001919091529392505050565b6000610120828403121561509b57600080fd5b6150a3614ec1565b905081356001600160401b03808211156150bc57600080fd5b6150c885838601615037565b835260208401359150808211156150de57600080fd5b6150ea85838601615037565b602084015260408401356040840152606084013591508082111561510d57600080fd5b5061511a84828501615037565b60608301525061512c60808301614db0565b608082015260a082013560a082015261514760c08301614e5e565b60c082015260e082013560e0820152610100615164818401614e5e565b9082015292915050565b60006060828403121561518057600080fd5b604051606081018181106001600160401b03821117156151a2576151a2614eab565b80604052508091508235815260208301356020820152604083013560408201525092915050565b6000602082840312156151db57600080fd5b81356001600160401b03808211156151f257600080fd5b908301906101a0828603121561520757600080fd5b61520f614eea565b82358281111561521e57600080fd5b61522a87828601615088565b82525061523960208401614db0565b602082015261524b866040850161516e565b604082015261525c60a08401614db0565b606082015261526d60c08401614db0565b608082015261527e60e08401614db0565b60a08201526101009150615293828401614db0565b60c08201526101206152a6818501614db0565b60e08301526101406152b9818601614db0565b848401526152ca6101608601614db0565b828401526152db6101808601614db0565b908301525095945050505050565b6000602082840312156152fb57600080fd5b81356001600160401b0381111561531157600080fd5b611d1284828501615037565b60008060006060848603121561533257600080fd5b833561533d81614d9b565b95602085013595506040909401359392505050565b600082601f83011261536357600080fd5b81356020615373614faa83614f3d565b82815260059290921b8401810191818101908684111561539257600080fd5b8286015b848110156153ad5780358352918301918301615396565b509695505050505050565b6000602082840312156153ca57600080fd5b81356001600160401b038111156153e057600080fd5b611d1284828501615352565b60008060006060848603121561540157600080fd5b83356001600160401b0381111561541757600080fd5b61542386828701615352565b9660208601359650604090950135949350505050565b6000806040838503121561544c57600080fd5b823561545781614d9b565b9150602083013561546781614d9b565b809150509250929050565b60006020828403121561548457600080fd5b81516001600160401b0381111561549a57600080fd5b8201601f810184136154ab57600080fd5b80516154b9614faa82615010565b8181528560208385010111156154ce57600080fd5b6154df826020830160208601614d44565b95945050505050565b600181811c908216806154fc57607f821691505b60208210810361551c57634e487b7160e01b600052602260045260246000fd5b50919050565b6000808454615530816154e8565b60018281168015615548576001811461555d5761558c565b60ff198416875282151583028701945061558c565b8860005260208060002060005b858110156155835781548a82015290840190820161556a565b50505082870194505b5050505083516155a0818360208801614d44565b01949350505050565b60208082526034908201527f426c617374426173655570677261646561626c653a2063616c6c6572206973206040820152733737ba103a3432903cb4b2b63221b630b4b6b2b960611b606082015260800190565b60006020828403121561560f57600080fd5b5051919050565b60208082526032908201527f426c617374426173655570677261646561626c653a2063616c6c6572206973206040820152713737ba103a34329033b0b9a1b630b4b6b2b960711b606082015260800190565b60208082526028908201527f436f6c6c656374696f6e496d706c3a2077686974656c697374206973206e6f7460408201526708195b98589b195960c21b606082015260800190565b60208082526036908201527f426c617374426173655570677261646561626c653a2063616c6c6572206973206040820152753737ba103a3432903837b4b73a39a7b832b930ba37b960511b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b8181038181111561136f5761136f615706565b808202811582820484141761136f5761136f615706565b634e487b7160e01b600052601260045260246000fd5b60008261577957634e487b7160e01b600052601260045260246000fd5b500490565b8082018082111561136f5761136f615706565b600181815b808511156157cc5781600019048211156157b2576157b2615706565b808516156157bf57918102915b93841c9390800290615796565b509250929050565b6000826157e35750600161136f565b816157f05750600061136f565b816001811461580657600281146158105761582c565b600191505061136f565b60ff84111561582157615821615706565b50506001821b61136f565b5060208310610133831016604e8410600b841016171561584f575081810a61136f565b6158598383615791565b806000190482111561586d5761586d615706565b029392505050565b60006118cc83836157d4565b634e487b7160e01b600052603260045260246000fd5b604080825283519082018190526000906020906060840190828701845b828110156158d95781516001600160a01b0316845292840192908401906001016158b4565b50505093151592019190915250919050565b601f821115612ac857600081815260208120601f850160051c810160208610156159125750805b601f850160051c820191505b8181101561222a5782815560010161591e565b81516001600160401b0381111561594a5761594a614eab565b61595e8161595884546154e8565b846158eb565b602080601f831160018114615993576000841561597b5750858301515b600019600386901b1c1916600185901b17855561222a565b600085815260208120601f198616915b828110156159c2578886015182559484019460019091019084016159a3565b50858210156159e05787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600060408284031215615a0257600080fd5b604051604081018181106001600160401b0382111715615a2457615a24614eab565b604052825181526020830151615a3981614d9b565b60208201529392505050565b600060018201615a5757615a57615706565b506001019056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a2646970667358221220752f6f169109859cc903af2e5eaaa9e9f9c0bfd66c8bb4ec73eedeb87a0abc2f64736f6c63430008140033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.