Source Code
Overview
S Balance
S Value
$0.00Latest 13 from a total of 13 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Create | 26903965 | 256 days ago | IN | 0 S | 0.13227536 | ||||
| Update Token Inf... | 23481039 | 270 days ago | IN | 0 S | 0.00204915 | ||||
| Create | 22824634 | 273 days ago | IN | 0 S | 0.11049977 | ||||
| Update Token Inf... | 22514476 | 275 days ago | IN | 0 S | 0.00204915 | ||||
| Create | 22008456 | 277 days ago | IN | 0 S | 0.10940566 | ||||
| Create | 21572890 | 279 days ago | IN | 0 S | 0.11836874 | ||||
| Create | 21568341 | 279 days ago | IN | 0 S | 0.13660762 | ||||
| Create | 21315089 | 280 days ago | IN | 0 S | 0.10941106 | ||||
| Create | 21290649 | 280 days ago | IN | 0 S | 0.13513774 | ||||
| Create | 21260443 | 281 days ago | IN | 0 S | 0.11050997 | ||||
| Create | 21197171 | 281 days ago | IN | 0 S | 0.10940746 | ||||
| Set Buy Token Am... | 21191703 | 281 days ago | IN | 0 S | 0.00261294 | ||||
| Set Buy Token Ad... | 21191699 | 281 days ago | IN | 0 S | 0.00264181 |
Latest 10 internal transactions
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 26903965 | 256 days ago | Contract Creation | 0 S | |||
| 22824634 | 273 days ago | Contract Creation | 0 S | |||
| 22008456 | 277 days ago | Contract Creation | 0 S | |||
| 21572890 | 279 days ago | Contract Creation | 0 S | |||
| 21568341 | 279 days ago | Contract Creation | 0 S | |||
| 21315089 | 280 days ago | Contract Creation | 0 S | |||
| 21290649 | 280 days ago | Contract Creation | 0 S | |||
| 21260443 | 281 days ago | Contract Creation | 0 S | |||
| 21197171 | 281 days ago | Contract Creation | 0 S | |||
| 21191685 | 281 days ago | Contract Creation | 0 S |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
Factory
Compiler Version
v0.8.26+commit.8a97fa7a
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.26;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "./interfaces/IFactory.sol";
import "./MintMemeToken.sol";
import "./LPLocker.sol";
import "./libraries/FeeMRegister.sol";
/// @title Factory Contract for MintMemeToken and LPLocker
/// @notice This contract creates MintMemeToken instances and manages LPLocker
contract Factory is Ownable, Pausable, FeeMRegister {
string public constant VERSION = "V2.1.0";
uint256 public constant MAX_CREATOR_FEE_PERCENT = 5;
// Immutable state variables
address public immutable uniswapV3Factory;
address public immutable nonfungiblePositionManager;
address public immutable weth;
address public immutable lpLocker;
// Mutable state variables
address public feeAddress;
uint256 public minTotalEther;
uint256 public creatorFeePercent;
// new: buy token related variables
address public buyTokenAddress; // the address of the token to buy
uint256 public buyTokenAmount; // the amount of ETH to use for buying tokens
address public constant BURN_ADDRESS =
address(0x000000000000000000000000000000000000dEaD); // the burn address
// Mappings
mapping(address => address) public tokenCreators;
mapping(address => uint256) public tokenPools;
mapping(address => string) public tokenInfoHashes;
// Events
event FeeAddressUpdated(
address indexed oldFeeAddress,
address indexed newFeeAddress
);
event TokenCreated(
address indexed tokenAddress,
address indexed creator,
string infoHash,
uint256 totalSupply,
uint256 totalMinter,
uint256 totalEther,
uint256 liquidityPercent,
uint256 startTime
);
event LPTokenSet(
uint256 indexed tokenId,
address indexed tokenAddress,
address indexed creator
);
event MinTotalEtherUpdated(
uint256 oldMinTotalEther,
uint256 newMinTotalEther
);
event CreatorFeePercentUpdated(
uint256 oldCreatorFeePercent,
uint256 newCreatorFeePercent
);
event TokenInfoUpdated(address indexed tokenAddress, string newInfoHash);
// new: buy token related events
event BuyTokenAddressUpdated(
address indexed oldBuyTokenAddress,
address indexed newBuyTokenAddress
);
event BuyTokenAmountUpdated(
uint256 oldBuyTokenAmount,
uint256 newBuyTokenAmount
);
// Custom errors
error ZeroAddress();
error InvalidFeeAddress();
error TokenNotCreatedByFactory();
error InsufficientTotalEther(uint256 provided, uint256 required);
error OnlyTokenCreatorCanUpdate();
error InvalidCreatorFeePercent(uint256 creatorFeePercent);
/// @notice Constructor to initialize the Factory contract
/// @param _minTotalEther The minimum total Ether for each MintMemeToken
/// @param _feeAddress The address to receive fees
/// @param _uniswapV3Factory The address of Uniswap V3 Factory
/// @param _nonfungiblePositionManager The address of Nonfungible Position Manager
/// @param _weth The address of WETH token
constructor(
uint256 _minTotalEther,
address _feeAddress,
address _uniswapV3Factory,
address _nonfungiblePositionManager,
address _weth,
uint256 _creatorFeePercent
) Ownable(msg.sender) {
if (
_feeAddress == address(0) ||
_uniswapV3Factory == address(0) ||
_nonfungiblePositionManager == address(0) ||
_weth == address(0)
) {
revert ZeroAddress();
}
uniswapV3Factory = _uniswapV3Factory;
nonfungiblePositionManager = _nonfungiblePositionManager;
weth = _weth;
_setFeeAddress(_feeAddress);
_setMinTotalEther(_minTotalEther);
_setCreatorFeePercent(_creatorFeePercent);
lpLocker = address(
new LPLocker(address(this), nonfungiblePositionManager)
);
registerMe();
}
/// @notice Creates a new MintMemeToken
/// @param name The name of the token
/// @param symbol The symbol of the token
/// @param totalSupply The total supply of the token
/// @param totalMinter The total number of minters
/// @param totalEther The total amount of Ether for minting
/// @param liquidityPercent The percentage of liquidity to be added
/// @param startTime The start time for minting
/// @param infoHash The IPFS hash of the stored token information
/// @return TokenAddress The address of the newly created MintMemeToken
/// @dev Contracts are not allowed to create
function create(
string memory name,
string memory symbol,
uint256 totalSupply,
uint256 totalMinter,
uint256 totalEther,
uint256 liquidityPercent,
uint256 startTime,
string memory infoHash
) external whenNotPaused returns (address) {
return
_create(
name,
symbol,
totalSupply,
totalMinter,
totalEther,
liquidityPercent,
startTime,
infoHash
);
}
/// @notice Update the token information hash
/// @param tokenAddress The address of the token
/// @param infoHash The IPFS hash of the stored token information
function updateTokenInfo(
address tokenAddress,
string memory infoHash
) external {
if (tokenCreators[tokenAddress] != msg.sender) {
revert OnlyTokenCreatorCanUpdate();
}
_updateTokenInfo(tokenAddress, infoHash);
}
/// @notice Sets the LP token for a given tokenId
/// @param tokenId The ID of the LP token
function setLPToken(uint256 tokenId) external {
address creator = tokenCreators[msg.sender];
if (creator == address(0)) {
revert TokenNotCreatedByFactory();
}
tokenPools[msg.sender] = tokenId;
emit LPTokenSet(tokenId, msg.sender, creator);
}
/// @notice Sets the fee address
/// @param _feeAddress The new fee address
function setFeeAddress(address payable _feeAddress) public onlyOwner {
_setFeeAddress(_feeAddress);
}
/// @notice Sets the minimum total Ether for each MintMemeToken
/// @param _minTotalEther The new minimum total Ether
function setMinTotalEther(uint256 _minTotalEther) public onlyOwner {
_setMinTotalEther(_minTotalEther);
}
/// @notice Sets the creator fee percentage
/// @param _creatorFeePercent The new creator fee percentage (0-100)
function setCreatorFeePercent(uint256 _creatorFeePercent) public onlyOwner {
_setCreatorFeePercent(_creatorFeePercent);
}
/// @notice Sets the token address to buy
/// @param _buyTokenAddress The new token address to buy
function setBuyTokenAddress(address _buyTokenAddress) public onlyOwner {
_setBuyTokenAddress(_buyTokenAddress);
}
/// @notice Sets the amount of ETH to use for buying tokens
/// @param _buyTokenAmount The new amount of ETH to use
function setBuyTokenAmount(uint256 _buyTokenAmount) public onlyOwner {
_setBuyTokenAmount(_buyTokenAmount);
}
/// @notice Pauses the contract
function pause() external onlyOwner {
_pause();
}
/// @notice Unpauses the contract
function unpause() external onlyOwner {
_unpause();
}
/// @notice Internal function to create a new MintMemeToken
/// @param name The name of the token
/// @param symbol The symbol of the token
/// @param totalSupply The total supply of the token
/// @param totalMinter The total number of minters
/// @param totalEther The total amount of Ether for minting
/// @param liquidityPercent The percentage of liquidity to be added
function _create(
string memory name,
string memory symbol,
uint256 totalSupply,
uint256 totalMinter,
uint256 totalEther,
uint256 liquidityPercent,
uint256 startTime,
string memory infoHash
) internal returns (address) {
if (totalEther > 0 && totalEther < minTotalEther) {
revert InsufficientTotalEther(totalEther, minTotalEther);
}
MintMemeToken newToken = new MintMemeToken(
name,
symbol,
totalSupply,
totalMinter,
totalEther,
liquidityPercent,
startTime,
address(this)
);
address tokenAddress = address(newToken);
tokenCreators[tokenAddress] = msg.sender;
_updateTokenInfo(tokenAddress, infoHash);
emit TokenCreated(
tokenAddress,
msg.sender,
infoHash,
totalSupply,
totalMinter,
totalEther,
liquidityPercent,
startTime
);
return tokenAddress;
}
/// @notice Internal function to set the fee address
/// @param _feeAddress The new fee address
function _setFeeAddress(address _feeAddress) internal {
if (_feeAddress == address(0)) {
revert InvalidFeeAddress();
}
address oldFeeAddress = feeAddress;
feeAddress = _feeAddress;
emit FeeAddressUpdated(oldFeeAddress, _feeAddress);
}
/// @notice Sets the minimum total Ether for each MintMemeToken
/// @param _minTotalEther The new minimum total Ether
function _setMinTotalEther(uint256 _minTotalEther) internal {
uint256 oldMinTotalEther = minTotalEther;
minTotalEther = _minTotalEther;
emit MinTotalEtherUpdated(oldMinTotalEther, _minTotalEther);
}
/// @notice Internal function to set the creator fee percentage
/// @param _creatorFeePercent The new creator fee percentage
/// @dev Emits CreatorFeePercentUpdated event
function _setCreatorFeePercent(uint256 _creatorFeePercent) internal {
if (_creatorFeePercent > MAX_CREATOR_FEE_PERCENT) {
revert InvalidCreatorFeePercent(_creatorFeePercent);
}
uint256 oldCreatorFeePercent = creatorFeePercent;
creatorFeePercent = _creatorFeePercent;
emit CreatorFeePercentUpdated(oldCreatorFeePercent, _creatorFeePercent);
}
/// @notice Internal function to set the token address to buy
/// @param _buyTokenAddress The new token address to buy
function _setBuyTokenAddress(address _buyTokenAddress) internal {
address oldBuyTokenAddress = buyTokenAddress;
buyTokenAddress = _buyTokenAddress;
emit BuyTokenAddressUpdated(oldBuyTokenAddress, _buyTokenAddress);
}
/// @notice Internal function to set the amount of ETH to use for buying tokens
/// @param _buyTokenAmount The new amount of ETH to use
function _setBuyTokenAmount(uint256 _buyTokenAmount) internal {
uint256 oldBuyTokenAmount = buyTokenAmount;
buyTokenAmount = _buyTokenAmount;
emit BuyTokenAmountUpdated(oldBuyTokenAmount, _buyTokenAmount);
}
/// @notice Internal function to update the token information hash
/// @param tokenAddress The address of the token
/// @param infoHash The IPFS hash of the stored token information
function _updateTokenInfo(
address tokenAddress,
string memory infoHash
) internal {
if (bytes(infoHash).length > 0) {
tokenInfoHashes[tokenAddress] = infoHash;
emit TokenInfoUpdated(tokenAddress, infoHash);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.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 Ownable is Context {
address private _owner;
/**
* @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.
*/
constructor(address initialOwner) {
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) {
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 {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC-20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC-721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC-1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC-20
* applications.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Skips emitting an {Approval} event indicating an allowance update. This is not
* required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
*
* ```solidity
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance < type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC-20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Enumerable.sol)
pragma solidity ^0.8.20;
import {IERC721} from "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Enumerable is IERC721 {
/**
* @dev Returns the total amount of tokens stored by the contract.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns a token ID owned by `owner` at a given `index` of its token list.
* Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
/**
* @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
* Use along with {totalSupply} to enumerate all tokens.
*/
function tokenByIndex(uint256 index) external view returns (uint256);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC-721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC-721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
* {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (utils/Address.sol)
pragma solidity ^0.8.20;
import {Errors} from "./Errors.sol";
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @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 Errors.InsufficientBalance(address(this).balance, amount);
}
(bool success, bytes memory returndata) = recipient.call{value: amount}("");
if (!success) {
_revert(returndata);
}
}
/**
* @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
* {Errors.FailedCall} 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 Errors.InsufficientBalance(address(this).balance, value);
}
(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 {Errors.FailedCall}) 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 {Errors.FailedCall} 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 {Errors.FailedCall}.
*/
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
assembly ("memory-safe") {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert Errors.FailedCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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 Context {
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.1.0) (utils/Errors.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of common custom errors used in multiple contracts
*
* IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
* It is recommended to avoid relying on the error API for critical functionality.
*
* _Available since v5.1._
*/
library Errors {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error InsufficientBalance(uint256 balance, uint256 needed);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedCall();
/**
* @dev The deployment failed.
*/
error FailedDeployment();
/**
* @dev A necessary precompile is missing.
*/
error MissingPrecompile(address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
bool private _paused;
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @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 EIP-1153 (transient storage) is available on the chain you're deploying at,
* consider using {ReentrancyGuardTransient} instead.
*
* 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 ReentrancyGuard {
// 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;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_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 {
// 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 {
// 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) {
return _status == ENTERED;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
interface IFactory {
function uniswapV3Factory() external view returns (address);
function nonfungiblePositionManager() external view returns (address);
function weth() external view returns (address);
function lpLocker() external view returns (address);
function feeAddress() external view returns (address);
function minTotalEther() external view returns (uint256);
function creatorFeePercent() external view returns (uint256);
function tokenCreators(address) external view returns (address);
function tokenPools(address) external view returns (uint256);
function tokenInfoHashes(address) external view returns (string memory);
function buyTokenAddress() external view returns (address);
function buyTokenAmount() external view returns (uint256);
function BURN_ADDRESS() external view returns (address);
function create(
string memory name,
string memory symbol,
uint256 totalSupply,
uint256 totalMinter,
uint256 totalEther,
uint256 liquidityPercent,
uint256 startTime,
string memory infoHash
) external returns (address);
function setLPToken(uint256 tokenId) external;
function setFeeAddress(address payable _feeAddress) external;
function setMinTotalEther(uint256 _minTotalEther) external;
function setCreatorFeePercent(uint256 _creatorFeePercent) external;
function setBuyTokenAddress(address _buyTokenAddress) external;
function setBuyTokenAmount(uint256 _buyTokenAmount) external;
function pause() external;
function unpause() external;
function updateTokenInfo(
address tokenAddress,
string memory infoHash
) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.26;
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
/// @title Non-fungible token for positions
/// @notice Wraps Uniswap V3 positions in a non-fungible token interface which allows for them to be transferred
/// and authorized.
interface INonfungiblePositionManager is IERC721Enumerable {
/// @notice Emitted when liquidity is increased for a position NFT
/// @dev Also emitted when a token is minted
/// @param tokenId The ID of the token for which liquidity was increased
/// @param liquidity The amount by which liquidity for the NFT position was increased
/// @param amount0 The amount of token0 that was paid for the increase in liquidity
/// @param amount1 The amount of token1 that was paid for the increase in liquidity
event IncreaseLiquidity(
uint256 indexed tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
/// @notice Emitted when liquidity is decreased for a position NFT
/// @param tokenId The ID of the token for which liquidity was decreased
/// @param liquidity The amount by which liquidity for the NFT position was decreased
/// @param amount0 The amount of token0 that was accounted for the decrease in liquidity
/// @param amount1 The amount of token1 that was accounted for the decrease in liquidity
event DecreaseLiquidity(
uint256 indexed tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
/// @notice Emitted when tokens are collected for a position NFT
/// @dev The amounts reported may not be exactly equivalent to the amounts transferred, due to rounding behavior
/// @param tokenId The ID of the token for which underlying tokens were collected
/// @param recipient The address of the account that received the collected tokens
/// @param amount0 The amount of token0 owed to the position that was collected
/// @param amount1 The amount of token1 owed to the position that was collected
event Collect(
uint256 indexed tokenId,
address recipient,
uint256 amount0,
uint256 amount1
);
/// @notice Returns the position information associated with a given token ID.
/// @dev Throws if the token ID is not valid.
/// @param tokenId The ID of the token that represents the position
/// @return token0 The address of the token0 for a specific pool
/// @return token1 The address of the token1 for a specific pool
/// @return tickSpacing The fee associated with the pool
/// @return tickLower The lower end of the tick range for the position
/// @return tickUpper The higher end of the tick range for the position
/// @return liquidity The liquidity of the position
/// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position
/// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position
/// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation
/// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation
function positions(
uint256 tokenId
)
external
view
returns (
address token0,
address token1,
int24 tickSpacing,
int24 tickLower,
int24 tickUpper,
uint128 liquidity,
uint256 feeGrowthInside0LastX128,
uint256 feeGrowthInside1LastX128,
uint128 tokensOwed0,
uint128 tokensOwed1
);
struct MintParams {
address token0;
address token1;
int24 tickSpacing;
int24 tickLower;
int24 tickUpper;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
address recipient;
uint256 deadline;
}
/// @notice Creates a new position wrapped in a NFT
/// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized
/// a method does not exist, i.e. the pool is assumed to be initialized.
/// @param params The params necessary to mint a position, encoded as `MintParams` in calldata
/// @return tokenId The ID of the token that represents the minted position
/// @return liquidity The amount of liquidity for this position
/// @return amount0 The amount of token0
/// @return amount1 The amount of token1
function mint(
MintParams calldata params
)
external
payable
returns (
uint256 tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
struct IncreaseLiquidityParams {
uint256 tokenId;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
uint256 deadline;
}
/// @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender`
/// @param params tokenId The ID of the token for which liquidity is being increased,
/// amount0Desired The desired amount of token0 to be spent,
/// amount1Desired The desired amount of token1 to be spent,
/// amount0Min The minimum amount of token0 to spend, which serves as a slippage check,
/// amount1Min The minimum amount of token1 to spend, which serves as a slippage check,
/// deadline The time by which the transaction must be included to effect the change
/// @return liquidity The new liquidity amount as a result of the increase
/// @return amount0 The amount of token0 to acheive resulting liquidity
/// @return amount1 The amount of token1 to acheive resulting liquidity
function increaseLiquidity(
IncreaseLiquidityParams calldata params
)
external
payable
returns (uint128 liquidity, uint256 amount0, uint256 amount1);
struct DecreaseLiquidityParams {
uint256 tokenId;
uint128 liquidity;
uint256 amount0Min;
uint256 amount1Min;
uint256 deadline;
}
/// @notice Decreases the amount of liquidity in a position and accounts it to the position
/// @param params tokenId The ID of the token for which liquidity is being decreased,
/// amount The amount by which liquidity will be decreased,
/// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,
/// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,
/// deadline The time by which the transaction must be included to effect the change
/// @return amount0 The amount of token0 accounted to the position's tokens owed
/// @return amount1 The amount of token1 accounted to the position's tokens owed
function decreaseLiquidity(
DecreaseLiquidityParams calldata params
) external payable returns (uint256 amount0, uint256 amount1);
struct CollectParams {
uint256 tokenId;
address recipient;
uint128 amount0Max;
uint128 amount1Max;
}
/// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient
/// @param params tokenId The ID of the NFT for which tokens are being collected,
/// recipient The account that should receive the tokens,
/// amount0Max The maximum amount of token0 to collect,
/// amount1Max The maximum amount of token1 to collect
/// @return amount0 The amount of fees collected in token0
/// @return amount1 The amount of fees collected in token1
function collect(
CollectParams calldata params
) external payable returns (uint256 amount0, uint256 amount1);
/// @notice Burns a token ID, which deletes it from the NFT contract. The token must have 0 liquidity and all tokens
/// must be collected first.
/// @param tokenId The ID of the token that is being burned
function burn(uint256 tokenId) external payable;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.26;
/// @title The interface for the Uniswap V3 Factory
interface IUniswapV3Factory {
/// @notice Creates a pool for the given two tokens and fee
/// @param tokenA One of the two tokens in the desired pool
/// @param tokenB The other of the two tokens in the desired pool
/// @param tickSpacing The desired fee for the pool
/// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved
/// from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments
/// are invalid.
/// @return pool The address of the newly created pool
function createPool(
address tokenA,
address tokenB,
int24 tickSpacing,
uint160 sqrtPriceLimitX96
) external returns (address pool);
/// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist
/// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
/// @param tokenA The contract address of either token0 or token1
/// @param tokenB The contract address of the other token
/// @param tickSpacing The fee collected upon every swap in the pool, denominated in hundredths of a bip
/// @return pool The pool address
function getPool(
address tokenA,
address tokenB,
int24 tickSpacing
) external view returns (address pool);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.26;
/// @title The interface for a Uniswap V3 Pool
interface IUniswapV3Pool {
function initialize(uint160 sqrtPriceX96) external;
function token0() external view returns (address);
function token1() external view returns (address);
function fee() external view returns (uint24);
function slot0()
external
view
returns (
uint160 sqrtPriceX96,
int24 tick,
uint16 observationIndex,
uint16 observationCardinality,
uint16 observationCardinalityNext,
uint8 feeProtocol,
bool unlocked
);
function ticks(
int24 tick
)
external
view
returns (
uint128 liquidityGross,
int128 liquidityNet,
uint256 feeGrowthOutside0X128,
uint256 feeGrowthOutside1X128,
int56 tickCumulativeOutside,
uint160 secondsPerLiquidityOutsideX128,
uint32 secondsOutside,
bool initialized
);
function feeGrowthGlobal0X128() external view returns (uint256);
function feeGrowthGlobal1X128() external view returns (uint256);
function swap(
address recipient,
bool zeroForOne,
int256 amountSpecified,
uint160 sqrtPriceLimitX96,
bytes memory data
) external returns (int256 amount0, int256 amount1);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.26;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IWETH is IERC20 {
function deposit() external payable;
function withdraw(uint256) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
contract FeeMRegister {
/// @dev Register my contract on Sonic FeeM
function registerMe() internal {
(bool _success, ) = address(0xDC2B0D2Dd2b7759D97D50db4eabDC36973110830)
.call(abi.encodeWithSignature("selfRegister(uint256)", 105));
require(_success, "FeeM registration failed");
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.26;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
library TransferHelper {
/// @notice Transfers tokens from the targeted address to the given destination
/// @notice Errors with 'STF' if transfer fails
/// @param token The contract address of the token to be transferred
/// @param from The originating address from which the tokens will be transferred
/// @param to The destination address of the transfer
/// @param value The amount to be transferred
function safeTransferFrom(
address token,
address from,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(
abi.encodeWithSelector(
IERC20.transferFrom.selector,
from,
to,
value
)
);
require(
success && (data.length == 0 || abi.decode(data, (bool))),
"STF"
);
}
/// @notice Transfers tokens from msg.sender to a recipient
/// @dev Errors with ST if transfer fails
/// @param token The contract address of the token which will be transferred
/// @param to The recipient of the transfer
/// @param value The value of the transfer
function safeTransfer(address token, address to, uint256 value) internal {
(bool success, bytes memory data) = token.call(
abi.encodeWithSelector(IERC20.transfer.selector, to, value)
);
require(
success && (data.length == 0 || abi.decode(data, (bool))),
"ST"
);
}
/// @notice Approves the stipulated contract to spend the given allowance in the given token
/// @dev Errors with 'SA' if transfer fails
/// @param token The contract address of the token to be approved
/// @param to The target of the approval
/// @param value The amount of the given token the target will be allowed to spend
function safeApprove(address token, address to, uint256 value) internal {
(bool success, bytes memory data) = token.call(
abi.encodeWithSelector(IERC20.approve.selector, to, value)
);
require(
success && (data.length == 0 || abi.decode(data, (bool))),
"SA"
);
}
/// @notice Transfers ETH to the recipient address
/// @dev Fails with `STE`
/// @param to The destination of the transfer
/// @param value The value to be transferred
function safeTransferETH(address to, uint256 value) internal {
(bool success, ) = to.call{value: value}(new bytes(0));
require(success, "STE");
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./interfaces/INonfungiblePositionManager.sol";
import "./interfaces/IWETH.sol";
import "./interfaces/IFactory.sol";
import "./libraries/FeeMRegister.sol";
/// @title LPLocker Contract
/// @notice This contract locks Uniswap V3 LP tokens and manages fee collection
/// @dev Implements ReentrancyGuard for protection against reentrancy attacks
contract LPLocker is ReentrancyGuard, FeeMRegister {
using Address for address payable;
// Constants
string public constant VERSION = "V2.1.0";
// Custom errors
error TokenNotCreatedByFactory();
error NotFromPositionManager();
error PositionNotLocked();
/// @notice Reference to the Factory contract
IFactory public immutable factory;
/// @notice Reference to the Uniswap V3 NonfungiblePositionManager contract
INonfungiblePositionManager public immutable nonfungiblePositionManager;
/// @notice Mapping to track locked positions
/// @dev Maps tokenId to a boolean indicating if the position is locked
mapping(uint256 => bool) public lockedPositions;
/// @notice Emitted when a position is locked
/// @param tokenId The ID of the locked position
/// @param tokenAddress The address of the token
event PositionLocked(uint256 indexed tokenId, address indexed tokenAddress);
/// @notice Emitted when fees are collected
/// @param tokenId The ID of the position from which fees were collected
/// @param token0Amount The amount of token0 collected
/// @param token1Amount The amount of token1 collected
event FeesCollected(
uint256 indexed tokenId,
uint256 token0Amount,
uint256 token1Amount
);
/// @notice Contract constructor
/// @param _factory Address of the Factory contract
/// @param _nonfungiblePositionManager Address of the NonfungiblePositionManager contract
constructor(address _factory, address _nonfungiblePositionManager) {
factory = IFactory(_factory);
nonfungiblePositionManager = INonfungiblePositionManager(
_nonfungiblePositionManager
);
registerMe();
}
/// @notice Allows the contract to receive ETH
receive() external payable {}
/// @notice Handles the receipt of an ERC721 token
/// @dev Implements IERC721Receiver
/// @param from The address from which the token is being transferred
/// @param tokenId The NFT identifier which is being transferred
/// @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
function onERC721Received(
address,
address from,
uint256 tokenId,
bytes memory
) public returns (bytes4) {
if (msg.sender != address(nonfungiblePositionManager)) {
revert NotFromPositionManager();
}
if (factory.tokenCreators(from) == address(0)) {
revert TokenNotCreatedByFactory();
}
lockedPositions[tokenId] = true;
emit PositionLocked(tokenId, from);
return this.onERC721Received.selector;
}
/// @notice Collects fees from a locked position
/// @dev Can only be called for locked positions
/// @param tokenId The ID of the position to collect fees from
function collectFees(uint256 tokenId) external nonReentrant {
if (!lockedPositions[tokenId]) {
revert PositionNotLocked();
}
_collectFeesFromPosition(tokenId);
}
/// @notice Collects fees from a position
/// @param tokenId The ID of the position
function _collectFeesFromPosition(uint256 tokenId) private {
(
uint256 token0Amount,
uint256 token1Amount
) = nonfungiblePositionManager.collect(
INonfungiblePositionManager.CollectParams({
tokenId: tokenId,
recipient: factory.feeAddress(),
amount0Max: type(uint128).max,
amount1Max: type(uint128).max
})
);
emit FeesCollected(tokenId, token0Amount, token1Amount);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "./libraries/TransferHelper.sol";
import "./interfaces/INonfungiblePositionManager.sol";
import "./interfaces/IWETH.sol";
import "./interfaces/IUniswapV3Factory.sol";
import "./interfaces/IUniswapV3Pool.sol";
import "./interfaces/IFactory.sol";
import "./libraries/FeeMRegister.sol";
/// @title MintMemeToken Contract
/// @notice This contract implements a fair token distribution mechanism with Uniswap V3 liquidity provision
/// @dev Inherits from OpenZeppelin's ERC20 contract
contract MintMemeToken is ERC20, FeeMRegister {
using Address for address payable;
// Constants
string public constant VERSION = "V2.1.0";
int24 private constant TICK_LOWER = -887200;
int24 private constant TICK_UPPER = 887200;
int24 private constant POOL_FEE = 200;
uint256 private constant BASE_DENOMINATOR = 100;
uint256 private constant TOKEN_PRECISION = 1e18;
uint256 private constant DEFAULT_CALL_GAS_LIMIT = 8000;
// Immutable state variables
uint256 public immutable maxMintAmount;
uint256 public immutable mintAmountPerAddress;
uint256 public immutable mintStartTime;
uint256 public immutable etherPerMint;
uint256 public immutable liquidityAmount;
address public immutable lpLocker;
IFactory public immutable factory;
INonfungiblePositionManager public immutable nonfungiblePositionManager;
// Mutable state variables
bool public transfersLocked = true;
address public poolAddress;
// Events
event TransfersUnlocked();
event LiquidityAddedV3(
address poolAddress,
uint256 tokenAmount,
uint256 ethAmount,
uint128 liquidity,
uint256 tokenId
);
event CreatorFeePaid(address indexed creator, uint256 amount);
event CreatorFeeTransferFailed(address indexed creator, uint256 amount);
event Refunded(
address indexed user,
uint256 tokenAmount,
uint256 etherAmount
);
// new: buy token related events
event TokenBought(
address indexed tokenAddress,
uint256 ethAmount,
uint256 tokenAmount
);
event TokenBurned(address indexed tokenAddress, uint256 amount);
/// @notice Constructs the MintMemeToken contract
/// @param name The name of the token
/// @param symbol The symbol of the token
/// @param totalSupply The total supply of the token
/// @param totalMinter The total number of minters
/// @param totalEther The total amount of Ether for minting
/// @param liquidityPercent The percentage of tokens to be used for liquidity
/// @param startTime The start time for minting
/// @param factoryAddress The address of the factory contract
constructor(
string memory name,
string memory symbol,
uint256 totalSupply,
uint256 totalMinter,
uint256 totalEther,
uint256 liquidityPercent,
uint256 startTime,
address factoryAddress
) ERC20(name, symbol) {
factory = IFactory(factoryAddress);
lpLocker = factory.lpLocker();
nonfungiblePositionManager = INonfungiblePositionManager(
factory.nonfungiblePositionManager()
);
_validateConstructorParams(
totalSupply,
totalMinter,
liquidityPercent,
totalEther,
startTime
);
(
maxMintAmount,
mintAmountPerAddress,
liquidityAmount
) = _calculateTokenAmounts(totalSupply, totalMinter, liquidityPercent);
etherPerMint = _calculateEtherPerMint(totalEther, totalMinter);
mintStartTime = _setMintStartTime(startTime);
if (totalEther == 0) {
_unlockTransfers();
}
registerMe();
}
/// @notice Allows the contract to receive Ether
receive() external payable {}
/// @notice Handles the receipt of an ERC721 token
/// @dev Implements IERC721Receiver
function onERC721Received(
address,
address,
uint256,
bytes memory
) public virtual returns (bytes4) {
require(
msg.sender == address(nonfungiblePositionManager),
"Not from position manager"
);
return this.onERC721Received.selector;
}
/// @notice Overrides the ERC20 transfer function to implement transfer locking
/// @param recipient The address to transfer tokens to
/// @param amount The amount of tokens to transfer
function transfer(
address recipient,
uint256 amount
) public virtual override returns (bool) {
require(!transfersLocked, "Transfers are locked");
return super.transfer(recipient, amount);
}
/// @notice Overrides the ERC20 transferFrom function to implement transfer locking
/// @param sender The address to transfer tokens from
/// @param recipient The address to transfer tokens to
/// @param amount The amount of tokens to transfer
function transferFrom(
address sender,
address recipient,
uint256 amount
) public virtual override returns (bool) {
require(!transfersLocked, "Transfers are locked");
return super.transferFrom(sender, recipient, amount);
}
/// @notice Allows users to mint tokens
function mint() public payable {
_validateMint();
_processMint();
_buyAndBurnToken();
}
/// @notice Allows users to refund their tokens before transfers are unlocked
function refund() public {
require(transfersLocked, "Transfers are not locked");
require(
balanceOf(msg.sender) >= mintAmountPerAddress,
"Insufficient token balance"
);
_refund();
}
/// @notice Allows users to add liquidity
function addLiquidity() public {
_checkAndAddLiquidity();
_processAddLiquidity();
}
/// @notice Validates the constructor parameters
function _validateConstructorParams(
uint256 totalSupply,
uint256 totalMinter,
uint256 liquidityPercent,
uint256 totalEther,
uint256 startTime
) private view {
require(totalSupply > 0, "totalSupply must be greater than 0");
require(totalMinter > 0, "totalMinter must be greater than 0");
if (totalEther > 0) {
require(
totalEther >= factory.minTotalEther(),
"totalEther must be greater than minTotalEther"
);
require(
liquidityPercent > 0 && liquidityPercent < BASE_DENOMINATOR,
"liquidityPercent must be greater than 0 and less than BASE_DENOMINATOR"
);
} else {
require(
liquidityPercent == 0,
"liquidityPercent must be 0 when totalEther is 0"
);
}
if (startTime != 0) {
require(
startTime > block.timestamp,
"startTime must be in the future"
);
}
require(
totalEther % totalMinter == 0,
"totalEther must evenly divide totalMinter"
);
}
/// @notice Calculates token amounts
function _calculateTokenAmounts(
uint256 totalSupply,
uint256 totalMinter,
uint256 liquidityPercent
) private pure returns (uint256, uint256, uint256) {
uint256 maxSupply = totalSupply * TOKEN_PRECISION;
uint256 _liquidityAmount = (maxSupply * liquidityPercent) /
BASE_DENOMINATOR;
uint256 _maxMintAmount = maxSupply - _liquidityAmount;
require(
_maxMintAmount % totalMinter == 0,
"maxMintAmount must evenly divide totalMinter"
);
uint256 _mintAmountPerAddress = _maxMintAmount / totalMinter;
return (_maxMintAmount, _mintAmountPerAddress, _liquidityAmount);
}
/// @notice Calculates Ether per mint
function _calculateEtherPerMint(
uint256 totalEther,
uint256 totalMinter
) private pure returns (uint256) {
return totalEther / totalMinter;
}
/// @notice Sets the mint start time
function _setMintStartTime(
uint256 startTime
) private view returns (uint256) {
return startTime == 0 ? block.timestamp : startTime;
}
/// @notice Unlocks token transfers
function _unlockTransfers() private {
transfersLocked = false;
emit TransfersUnlocked();
}
/// @notice Validates the mint operation
function _validateMint() private view {
require(
block.timestamp >= mintStartTime,
"minting has not started yet"
);
require(
totalSupply() + mintAmountPerAddress <= maxMintAmount,
"minting would exceed max supply"
);
require(
msg.value == etherPerMint + factory.buyTokenAmount(),
"invalid mint value"
);
}
/// @notice Processes the mint operation
function _processMint() private {
_mint(msg.sender, mintAmountPerAddress);
}
/// @notice new: buy token and burn it
function _buyAndBurnToken() private {
address buyTokenAddress = factory.buyTokenAddress();
uint256 buyTokenAmount = factory.buyTokenAmount();
if (buyTokenAddress == address(0) || buyTokenAmount == 0) {
return;
}
IWETH weth = IWETH(factory.weth());
weth.deposit{value: buyTokenAmount}();
address uniswapV3Factory = factory.uniswapV3Factory();
address tokenPoolAddress = IUniswapV3Factory(uniswapV3Factory).getPool(
address(weth),
buyTokenAddress,
POOL_FEE
);
if (tokenPoolAddress == address(0)) {
return;
}
IUniswapV3Pool pool = IUniswapV3Pool(tokenPoolAddress);
bool isWethToken0 = address(weth) < buyTokenAddress;
pool.swap(
address(this),
isWethToken0,
int256(buyTokenAmount),
isWethToken0
? uint160(4295128740)
: uint160(1461446703485210103287273052203988822378723970341),
abi.encode(address(weth), buyTokenAddress)
);
uint256 tokenBalance = IERC20(buyTokenAddress).balanceOf(address(this));
// transfer token to burn address
if (tokenBalance > 0) {
TransferHelper.safeTransfer(
buyTokenAddress,
factory.BURN_ADDRESS(),
tokenBalance
);
emit TokenBurned(buyTokenAddress, tokenBalance);
}
emit TokenBought(buyTokenAddress, buyTokenAmount, tokenBalance);
}
/// @notice Checks if all tokens are minted and adds liquidity if necessary
function _checkAndAddLiquidity() private view {
require(poolAddress == address(0), "liquidity already added");
require(totalSupply() == maxMintAmount, "minting is not complete");
require(liquidityAmount > 0, "liquidityAmount is 0");
require(address(this).balance > 0, "no ether to add liquidity");
}
/// @notice Processes the liquidity addition workflow
/// @dev Unlocks transfers, adds liquidity, and transfers LP token to locker
function _processAddLiquidity() private {
_unlockTransfers();
uint256 tokenId = _addLiquidityToUniswapV3();
factory.setLPToken(tokenId);
nonfungiblePositionManager.safeTransferFrom(
address(this),
lpLocker,
tokenId
);
}
/// @notice Adds liquidity to Uniswap V3
function _addLiquidityToUniswapV3() private returns (uint256) {
address weth = factory.weth();
(
uint256 creatorFee,
uint256 liquidityEthAmount
) = _calculateCreatorFee();
_setTokenAllowance(weth, liquidityEthAmount);
IUniswapV3Pool pool = _createPool(
address(this),
weth,
liquidityAmount,
liquidityEthAmount
);
(
uint256 tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
) = _mintLiquidity(
pool,
address(this),
weth,
liquidityAmount,
liquidityEthAmount
);
_processCreatorFee(creatorFee);
emit LiquidityAddedV3(
poolAddress,
amount0,
amount1,
liquidity,
tokenId
);
return tokenId;
}
/// @notice Calculates the creator fee and remaining ETH for liquidity
/// @dev Splits the contract's ETH balance according to the creator fee percentage
/// @return creatorFee The amount of ETH to be sent to the creator
/// @return liquidityEthAmount The remaining ETH amount for liquidity provision
function _calculateCreatorFee()
private
view
returns (uint256 creatorFee, uint256 liquidityEthAmount)
{
creatorFee =
(address(this).balance * factory.creatorFeePercent()) /
BASE_DENOMINATOR;
liquidityEthAmount = address(this).balance - creatorFee;
}
/// @notice Processes the creator fee payment
/// @dev Attempts to send fee to creator, falls back to fee address if failed
/// @param creatorFee The amount of ETH to be sent as creator fee
function _processCreatorFee(uint256 creatorFee) private {
address creator = factory.tokenCreators(address(this));
(bool success, ) = payable(creator).call{
value: creatorFee,
gas: DEFAULT_CALL_GAS_LIMIT
}("");
if (success) {
emit CreatorFeePaid(creator, creatorFee);
return;
}
address feeAddress = factory.feeAddress();
(success, ) = payable(feeAddress).call{
value: creatorFee,
gas: DEFAULT_CALL_GAS_LIMIT
}("");
if (success) {
emit CreatorFeePaid(feeAddress, creatorFee);
return;
}
emit CreatorFeeTransferFailed(creator, creatorFee);
}
/// @notice Sets token allowances for liquidity provision
function _setTokenAllowance(
address weth,
uint256 liquidityEthAmount
) private {
_mint(address(this), liquidityAmount);
_approve(
address(this),
address(nonfungiblePositionManager),
liquidityAmount
);
IWETH(weth).deposit{value: liquidityEthAmount}();
TransferHelper.safeApprove(
weth,
address(nonfungiblePositionManager),
liquidityEthAmount
);
}
/// @notice Creates a Uniswap V3 pool
function _createPool(
address token0,
address token1,
uint256 token0Amount,
uint256 token1Amount
) private returns (IUniswapV3Pool) {
poolAddress = IUniswapV3Factory(factory.uniswapV3Factory()).createPool(
token0,
token1,
POOL_FEE,
0
);
IUniswapV3Pool pool = IUniswapV3Pool(poolAddress);
uint256 amount0 = token0 == pool.token0() ? token0Amount : token1Amount;
uint256 amount1 = token1 == pool.token1() ? token1Amount : token0Amount;
uint160 sqrtPriceX96 = uint160(
(_sqrt((amount1 * 1e18) / amount0) * (2 ** 96)) / 1e9
);
pool.initialize(sqrtPriceX96);
return pool;
}
/// @notice Mints liquidity in Uniswap V3
function _mintLiquidity(
IUniswapV3Pool pool,
address token0,
address token1,
uint256 token0Amount,
uint256 token1Amount
) private returns (uint256, uint128, uint256, uint256) {
INonfungiblePositionManager.MintParams
memory params = INonfungiblePositionManager.MintParams({
token0: pool.token0(),
token1: pool.token1(),
tickSpacing: POOL_FEE,
tickLower: TICK_LOWER,
tickUpper: TICK_UPPER,
amount0Desired: token0 == pool.token0()
? token0Amount
: token1Amount,
amount1Desired: token1 == pool.token1()
? token1Amount
: token0Amount,
amount0Min: 0,
amount1Min: 0,
recipient: address(this),
deadline: block.timestamp
});
return nonfungiblePositionManager.mint(params);
}
/// @notice Calculates the square root of a number
function _sqrt(uint256 y) private pure returns (uint256 z) {
if (y > 3) {
z = y;
uint256 x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
/// @notice Allows users to get a refund while transfers are locked
/// @dev Burns user's tokens and returns their ETH
function _refund() internal {
_burn(msg.sender, mintAmountPerAddress);
(bool success, ) = payable(msg.sender).call{
value: etherPerMint,
gas: DEFAULT_CALL_GAS_LIMIT
}("");
require(success, "ETH transfer failed");
emit Refunded(msg.sender, mintAmountPerAddress, etherPerMint);
}
/// @notice new: Uniswap V3 callback function, for handling swaps
function uniswapV3SwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes memory data
) external {
(address tokenIn, address tokenOut) = abi.decode(
data,
(address, address)
);
address tokenPoolAddress = IUniswapV3Factory(factory.uniswapV3Factory())
.getPool(tokenIn, tokenOut, POOL_FEE);
require(msg.sender == tokenPoolAddress, "Invalid caller");
if (amount0Delta > 0) {
TransferHelper.safeTransfer(
tokenIn,
tokenPoolAddress,
uint256(amount0Delta)
);
}
if (amount1Delta > 0) {
TransferHelper.safeTransfer(
tokenIn,
tokenPoolAddress,
uint256(amount1Delta)
);
}
}
function totalEtherPerMint() public view returns (uint256) {
return etherPerMint + factory.buyTokenAmount();
}
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"viaIR": true,
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"uint256","name":"_minTotalEther","type":"uint256"},{"internalType":"address","name":"_feeAddress","type":"address"},{"internalType":"address","name":"_uniswapV3Factory","type":"address"},{"internalType":"address","name":"_nonfungiblePositionManager","type":"address"},{"internalType":"address","name":"_weth","type":"address"},{"internalType":"uint256","name":"_creatorFeePercent","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[{"internalType":"uint256","name":"provided","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"InsufficientTotalEther","type":"error"},{"inputs":[{"internalType":"uint256","name":"creatorFeePercent","type":"uint256"}],"name":"InvalidCreatorFeePercent","type":"error"},{"inputs":[],"name":"InvalidFeeAddress","type":"error"},{"inputs":[],"name":"OnlyTokenCreatorCanUpdate","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":"TokenNotCreatedByFactory","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldBuyTokenAddress","type":"address"},{"indexed":true,"internalType":"address","name":"newBuyTokenAddress","type":"address"}],"name":"BuyTokenAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldBuyTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBuyTokenAmount","type":"uint256"}],"name":"BuyTokenAmountUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldCreatorFeePercent","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newCreatorFeePercent","type":"uint256"}],"name":"CreatorFeePercentUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldFeeAddress","type":"address"},{"indexed":true,"internalType":"address","name":"newFeeAddress","type":"address"}],"name":"FeeAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":true,"internalType":"address","name":"creator","type":"address"}],"name":"LPTokenSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldMinTotalEther","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newMinTotalEther","type":"uint256"}],"name":"MinTotalEtherUpdated","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":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":false,"internalType":"string","name":"infoHash","type":"string"},{"indexed":false,"internalType":"uint256","name":"totalSupply","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalMinter","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalEther","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"liquidityPercent","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startTime","type":"uint256"}],"name":"TokenCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"string","name":"newInfoHash","type":"string"}],"name":"TokenInfoUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"BURN_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_CREATOR_FEE_PERCENT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"buyTokenAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"buyTokenAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint256","name":"totalSupply","type":"uint256"},{"internalType":"uint256","name":"totalMinter","type":"uint256"},{"internalType":"uint256","name":"totalEther","type":"uint256"},{"internalType":"uint256","name":"liquidityPercent","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"string","name":"infoHash","type":"string"}],"name":"create","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"creatorFeePercent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lpLocker","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minTotalEther","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nonfungiblePositionManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_buyTokenAddress","type":"address"}],"name":"setBuyTokenAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_buyTokenAmount","type":"uint256"}],"name":"setBuyTokenAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_creatorFeePercent","type":"uint256"}],"name":"setCreatorFeePercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"_feeAddress","type":"address"}],"name":"setFeeAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"setLPToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minTotalEther","type":"uint256"}],"name":"setMinTotalEther","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokenCreators","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokenInfoHashes","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokenPools","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"uniswapV3Factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"string","name":"infoHash","type":"string"}],"name":"updateTokenInfo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
61010080604052346103965760c081614b7a8038038091610020828561039b565b833981010312610396578051610038602083016103be565b610044604084016103be565b90610051606085016103be565b9160a0610060608087016103be565b9501519433156103805760008054604051949133906001600160a01b038316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a36001600160a81b0319163360ff60a01b1916176000556001600160a01b03169384159283801561036f575b801561035e575b801561034d575b61033c5760805260a05260c05261032b57600180546001600160a01b0319811684179091557fb420a40084726ae29557ee58fb1af901767127a23aeec4e5e5daeaec97c3d2a39360409390916001600160a01b03167f11f35a22548bcd4c3788ab4a7e4fba427a2014f02e5d5e2da9af62212c03183f600080a36002548160025582526020820152a1600581116103175760407f1f440356ff29d242b1f0e2afdae6b4be38d5cce2db5fbb241d3fdc363b94210091600354908060035582519182526020820152a160a051604051906001600160a01b03166107f58083016001600160401b038111848210176102f0576040928492614385843930825260208201520301906000f0801561030b5760018060a01b031660e05260008060405160208101906307983f4560e21b8252606960248201526024815261022160448261039b565b51908273dc2b0d2dd2b7759d97d50db4eabdc369731108305af13d15610306573d6001600160401b0381116102f05760405190610268601f8201601f19166020018361039b565b8152600060203d92013e5b156102ab57604051613fb290816103d3823960805181610651015260a051816103a5015260c05181610753015260e05181610bd40152f35b60405162461bcd60e51b815260206004820152601860248201527f4665654d20726567697374726174696f6e206661696c656400000000000000006044820152606490fd5b634e487b7160e01b600052604160045260246000fd5b610273565b6040513d6000823e3d90fd5b6303b0bacf60e01b60005260045260246000fd5b63ae33c5cd60e01b60005260046000fd5b63d92e233d60e01b60005260046000fd5b506001600160a01b038316156100dd565b506001600160a01b038216156100d6565b506001600160a01b038116156100cf565b631e4fbdf760e01b600052600060045260246000fd5b600080fd5b601f909101601f19168101906001600160401b038211908210176102f057604052565b51906001600160a01b03821682036103965756fe608080604052600436101561001357600080fd5b60003560e01c90816303fc201314610bc157508063076999ee14610ba35780630b761a3614610abd5780630fcbf894146108c4578063157b4719146108475780633e4dddaf146107f35780633f4ba83a146107825780633fc8cef31461073d57806341275358146107145780634eb40dc41461069e57806353613dd3146106805780635b5491821461063b5780635c975abb1461061557806360ebbf81146105ec5780636ff6c4b81461057b578063715018a6146105225780638456cb59146104c05780638705fcd4146104375780638868362a146104195780638da5cb5b146103f0578063b0c684cc146103d4578063b44a27221461038f578063c3d2c3c114610355578063ce7e8de814610313578063d5c3585d146102bf578063dda6bb9a14610255578063f2fde38b146101cc578063fccc2813146101af5763ffa1ad741461015e57600080fd5b346101aa5760003660031901126101aa576101a660408051906101818183610c53565b6006825265056322e312e360d41b602083015251918291602083526020830190610c75565b0390f35b600080fd5b346101aa5760003660031901126101aa57602060405161dead8152f35b346101aa5760203660031901126101aa576101e5610c03565b6101ed610d2d565b6001600160a01b0316801561023f57600080546001600160a01b03198116831782556001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3005b631e4fbdf760e01b600052600060045260246000fd5b346101aa5760203660031901126101aa5761026e610c03565b610276610d2d565b600480546001600160a01b039283166001600160a01b0319821681179092559091167f6d295b6b04456e9e1bccaf6872cf6d3a16af45c740c285c4f403124efc123f84600080a3005b346101aa5760203660031901126101aa577f5232111f2be9cca3a7b943fe9abbbe42eafc43bacd7cdb14eb8cb1eb08f322a960406004356102fe610d2d565b600554908060055582519182526020820152a1005b346101aa5760203660031901126101aa576001600160a01b03610334610c03565b166000526006602052602060018060a01b0360406000205416604051908152f35b346101aa5760203660031901126101aa576001600160a01b03610376610c03565b1660005260076020526020604060002054604051908152f35b346101aa5760003660031901126101aa576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346101aa5760003660031901126101aa57602060405160058152f35b346101aa5760003660031901126101aa576000546040516001600160a01b039091168152602090f35b346101aa5760003660031901126101aa576020600554604051908152f35b346101aa5760203660031901126101aa576004356001600160a01b038116908190036101aa57610465610d2d565b80156104af57600180546001600160a01b0319811683179091556001600160a01b03167f11f35a22548bcd4c3788ab4a7e4fba427a2014f02e5d5e2da9af62212c03183f600080a3005b63ae33c5cd60e01b60005260046000fd5b346101aa5760003660031901126101aa576104d9610d2d565b6104e1610d0d565b6000805460ff60a01b1916600160a01b1790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25890602090a1005b346101aa5760003660031901126101aa5761053b610d2d565b600080546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346101aa5760203660031901126101aa57600435610597610d2d565b600581116105d85760407f1f440356ff29d242b1f0e2afdae6b4be38d5cce2db5fbb241d3fdc363b94210091600354908060035582519182526020820152a1005b6303b0bacf60e01b60005260045260246000fd5b346101aa5760003660031901126101aa576004546040516001600160a01b039091168152602090f35b346101aa5760003660031901126101aa57602060ff60005460a01c166040519015158152f35b346101aa5760003660031901126101aa576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346101aa5760003660031901126101aa576020600354604051908152f35b346101aa5760403660031901126101aa576106b7610c03565b60243567ffffffffffffffff81116101aa576106d7903690600401610cb6565b6001600160a01b038281166000908152600660205260409020541633036107035761070191610d56565b005b6322b8548560e01b60005260046000fd5b346101aa5760003660031901126101aa576001546040516001600160a01b039091168152602090f35b346101aa5760003660031901126101aa576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346101aa5760003660031901126101aa5761079b610d2d565b60005460ff8160a01c16156107e25760ff60a01b19166000556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa90602090a1005b638dfc202b60e01b60005260046000fd5b346101aa5760203660031901126101aa577fb420a40084726ae29557ee58fb1af901767127a23aeec4e5e5daeaec97c3d2a36040600435610832610d2d565b600254908060025582519182526020820152a1005b346101aa5760203660031901126101aa57336000908152600660205260409020546001600160a01b031660043581156108b3573360005260076020528060406000205533907fe28aa6fa88b8801606101ea7f7c54a658990ccba2cb2a8ce4db740f47910baa7600080a4005b6324f0519960e21b60005260046000fd5b346101aa576101003660031901126101aa5760043567ffffffffffffffff81116101aa576108f6903690600401610cb6565b60243567ffffffffffffffff81116101aa57610916903690600401610cb6565b6044356064356084359260a4359460c4359160e43567ffffffffffffffff81116101aa57610948903690600401610cb6565b91610951610d0d565b86151580610ab2575b610a965760405191613096918284019184831067ffffffffffffffff841117610a805761099b6109a9928695610ee787396101008552610100850190610c75565b908382036020850152610c75565b908760408201528660608201528860808201528960a08201528560c082015260e03091015203906000f0918215610a74576020967f2adc8c3149d8c35efa9b0ea8f1b2a037c12b5fb0e704620701a12440a0b926249360018060a01b03169687958660005260068a526040600020336bffffffffffffffffffffffff60a01b825416179055610a388588610d56565b610a4d6040519560c0875260c0870190610c75565b978a86015260408501526060840152608083015260a08201528033940390a3604051908152f35b6040513d6000823e3d90fd5b634e487b7160e01b600052604160045260246000fd5b86600254906301849e9b60e11b60005260045260245260446000fd5b50600254871061095a565b346101aa5760203660031901126101aa576001600160a01b03610ade610c03565b166000526008602052604060002060405190600090805490610aff82610c19565b8085529160018116908115610b7c5750600114610b3b575b6101a684610b2781860382610c53565b604051918291602083526020830190610c75565b600090815260208120939250905b808210610b6257509091508101602001610b2782610b17565b919260018160209254838588010152019101909291610b49565b60ff191660208087019190915292151560051b85019092019250610b279150839050610b17565b346101aa5760003660031901126101aa576020600254604051908152f35b346101aa5760003660031901126101aa577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b600435906001600160a01b03821682036101aa57565b90600182811c92168015610c49575b6020831014610c3357565b634e487b7160e01b600052602260045260246000fd5b91607f1691610c28565b90601f8019910116810190811067ffffffffffffffff821117610a8057604052565b919082519283825260005b848110610ca1575050826000602080949584010152601f8019910116010190565b80602080928401015182828601015201610c80565b81601f820112156101aa5780359067ffffffffffffffff8211610a805760405192610ceb601f8401601f191660200185610c53565b828452602083830101116101aa57816000926020809301838601378301015290565b60ff60005460a01c16610d1c57565b63d93c066560e01b60005260046000fd5b6000546001600160a01b03163303610d4157565b63118cdaa760e01b6000523360045260246000fd5b8151610d60575050565b6001600160a01b03166000818152600860205260409020825191929167ffffffffffffffff8111610a8057610d958254610c19565b601f8111610e9e575b506020601f8211600114610e145791817f25c9905798f2c8f6e4cb2f05a20e5b661db0023ec859177bb6374800a39515ba9492610e0494600091610e09575b508160011b916000199060031b1c1916179055604051918291602083526020830190610c75565b0390a2565b905083015138610ddd565b601f1982169083600052806000209160005b818110610e86575092610e049492600192827f25c9905798f2c8f6e4cb2f05a20e5b661db0023ec859177bb6374800a39515ba989610610e6d575b5050811b019055610b27565b85015160001960f88460031b161c191690553880610e61565b9192602060018192868a015181550194019201610e26565b826000526020600020601f830160051c81019160208410610edc575b601f0160051c01905b818110610ed05750610d9e565b60008155600101610ec3565b9091508190610eba56fe61018080604052346106ca57613096803803809161001d8285610a60565b8339810190610100818303126106ca5780516001600160401b0381116106ca5782610049918301610a9e565b602082015190926001600160401b0382116106ca57610069918301610a9e565b9060408101519160608201519260808301519360a08401519261009360e060c08701519601610afe565b875190976001600160401b03821161095d5760035490600182811c92168015610a56575b602083101461093d5781601f8493116109e6575b50602090601f831160011461097e57600092610973575b50508160011b916000199060031b1c1916176003555b8051906001600160401b03821161095d5760045490600182811c92168015610953575b602083101461093d5781601f8493116108cd575b50602090601f83116001146108655760009261085a575b50508160011b916000199060031b1c1916176004555b6004602060ff1960055416976001891760055560018060a01b03168061014052604051928380926303fc201360e01b82525afa9081156106d757600091610820575b506101205261014051604051635a25139160e11b815290602090829060049082906001600160a01b03165afa9081156106d7576000916107e6575b506001600160a01b031661016052811561079657801561074657841594856106e357610140516040516303b4ccf760e11b815290602090829060049082906001600160a01b03165afa9081156106d7576000916106a0575b508110610645578315158061063b575b156105c1575b8415938415610575575b61025b8383610b12565b61051e57670de0b6b3a7640000840293808504670de0b6b3a7640000036105085781670de0b6b3a7640000910202908482041484151715610508576064900492838103908111610508576102af8382610b12565b6104ae576102d1936102c18483610b32565b906101005260a052608052610b32565b60e052156104a95750425b60c05261047b575b5060008060405160208101906307983f4560e21b82526069602482015260248152610310604482610a60565b51908273dc2b0d2dd2b7759d97d50db4eabdc369731108305af13d15610476573d61033a81610a83565b906103486040519283610a60565b8152600060203d92013e5b15610431576040516125599081610b3d82396080518181816103b70152818161156101526117d8015260a051818181610ff30152818161125a01526117b0015260c05181818161113b0152611783015260e05181818161120c015281816112e0015281816115ea015261184f0152610100518181816103dd01526111990152610120518181816109ef0152611a820152610140518181816102180152818161044301528181610f26015281816115aa0152818161181101528181611c6601526123ac0152610160518181816104d501528181610f6b01526116ec0152f35b60405162461bcd60e51b815260206004820152601860248201527f4665654d20726567697374726174696f6e206661696c656400000000000000006044820152606490fd5b610353565b6005557f1d8b2f61c84f331c359476b447a0ddc4fd75f10d265a30e609526e440cdc3a47600080a1386102e4565b6102dc565b60405162461bcd60e51b815260206004820152602c60248201527f6d61784d696e74416d6f756e74206d757374206576656e6c792064697669646560448201526b103a37ba30b626b4b73a32b960a11b6064820152608490fd5b634e487b7160e01b600052601160045260246000fd5b60405162461bcd60e51b815260206004820152602960248201527f746f74616c4574686572206d757374206576656e6c792064697669646520746f6044820152683a30b626b4b73a32b960b91b6064820152608490fd5b4286116102515760405162461bcd60e51b815260206004820152601f60248201527f737461727454696d65206d75737420626520696e2074686520667574757265006044820152606490fd5b60405162461bcd60e51b815260206004820152604660248201527f6c697175696469747950657263656e74206d757374206265206772656174657260448201527f207468616e203020616e64206c657373207468616e20424153455f44454e4f4d60648201526524a720aa27a960d11b608482015260a490fd5b5060648410610241565b60405162461bcd60e51b815260206004820152602d60248201527f746f74616c4574686572206d7573742062652067726561746572207468616e2060448201526c36b4b72a37ba30b622ba3432b960991b6064820152608490fd5b90506020813d6020116106cf575b816106bb60209383610a60565b810103126106ca575138610231565b600080fd5b3d91506106ae565b6040513d6000823e3d90fd5b83156102475760405162461bcd60e51b815260206004820152602f60248201527f6c697175696469747950657263656e74206d7573742062652030207768656e2060448201526e0746f74616c4574686572206973203608c1b6064820152608490fd5b60405162461bcd60e51b815260206004820152602260248201527f746f74616c4d696e746572206d7573742062652067726561746572207468616e604482015261020360f41b6064820152608490fd5b60405162461bcd60e51b815260206004820152602260248201527f746f74616c537570706c79206d7573742062652067726561746572207468616e604482015261020360f41b6064820152608490fd5b90506020813d602011610818575b8161080160209383610a60565b810103126106ca5761081290610afe565b386101d9565b3d91506107f4565b90506020813d602011610852575b8161083b60209383610a60565b810103126106ca5761084c90610afe565b3861019e565b3d915061082e565b015190503880610146565b600460009081528281209350601f198516905b8181106108b5575090846001959493921061089c575b505050811b0160045561015c565b015160001960f88460031b161c1916905538808061088e565b92936020600181928786015181550195019301610878565b60046000529091507f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b601f840160051c81019160208510610933575b90601f859493920160051c01905b818110610924575061012f565b60008155849350600101610917565b9091508190610909565b634e487b7160e01b600052602260045260246000fd5b91607f169161011b565b634e487b7160e01b600052604160045260246000fd5b0151905038806100e2565b600360009081528281209350601f198516905b8181106109ce57509084600195949392106109b5575b505050811b016003556100f8565b015160001960f88460031b161c191690553880806109a7565b92936020600181928786015181550195019301610991565b60036000529091507fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b601f840160051c81019160208510610a4c575b90601f859493920160051c01905b818110610a3d57506100cb565b60008155849350600101610a30565b9091508190610a22565b91607f16916100b7565b601f909101601f19168101906001600160401b0382119082101761095d57604052565b6001600160401b03811161095d57601f01601f191660200190565b81601f820112156106ca57805190610ab582610a83565b92610ac36040519485610a60565b828452602083830101116106ca5760005b828110610ae957505060206000918301015290565b80602080928401015182828701015201610ad4565b51906001600160a01b03821682036106ca57565b8115610b1c570690565b634e487b7160e01b600052601260045260246000fd5b8115610b1c57049056fe608080604052600436101561001d575b50361561001b57600080fd5b005b600090813560e01c90816303fc201314611a6e5750806306fdde03146119b1578063095ea7b31461198a5780631249c58b14611775578063150b7a02146116a55780631755ff211461167857806318160ddd1461165a5780631a93e56714611584578063239c70ae1461154957806323b872dd14611459578063313ce5671461143d578063590e1ae31461122f5780636bc5ec8f146111f457806370a08231146111bc57806370baed431461118157806383f1211b1461115e578063931e2e491461112357806395d89b4114611016578063a2fb130014610fdb578063a9059cbb14610f9a578063b44a272214610f55578063c45a015514610f10578063dd62ed3e14610ec5578063e8078d941461038b578063fa461e33146101945763ffa1ad740361000f57346101915780600319360112610191575061018d604051610166604082611b23565b6006815265056322e312e360d41b6020820152604051918291602083526020830190611ab1565b0390f35b80fd5b50346101915760603660031901126101915760043560243560443567ffffffffffffffff8111610387576101cc903690600401611b77565b604081805181010312610387576101f160406101ea60208401611c24565b9201611c24565b604051632daa48c160e11b81526001600160a01b03928316949192602090829060049082907f0000000000000000000000000000000000000000000000000000000000000000165afa92831561037c5761028c93602092889161035f575b506040516328af8d0b60e01b81526001600160a01b038881166004830152909216602483015260c860448301529093849190829081906064820190565b03916001600160a01b03165afa918215610354578592610323575b506001600160a01b03821633036102ed578481136102dc575b508382136102cc578380f35b6102d59261210f565b3880808380f35b6102e790828561210f565b386102c0565b60405162461bcd60e51b815260206004820152600e60248201526d24b73b30b634b21031b0b63632b960911b6044820152606490fd5b61034691925060203d60201161034d575b61033e8183611b23565b810190611c38565b90386102a7565b503d610334565b6040513d87823e3d90fd5b6103769150833d851161034d5761033e8183611b23565b3861024f565b6040513d88823e3d90fd5b8380fd5b503461019157806003193601126101915760055490600882901c6001600160a01b0316610e80576002547f000000000000000000000000000000000000000000000000000000000000000003610e3b577f0000000000000000000000000000000000000000000000000000000000000000918215610dff574715610dba5760ff19166005556040517f1d8b2f61c84f331c359476b447a0ddc4fd75f10d265a30e609526e440cdc3a478280a1633fc8cef360e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316602082600481845afa918215610daf578392610d8e575b506040516353613dd360e01b815247602082600481865afa918215610354578592610d5a575b50818102918183041490151715610d4657606490049047828103908111610d32576104d38630612254565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316936105098786306121c1565b6001600160a01b03811690813b15610ada57604051630d0e30db60e41b8152878160048187875af18015610ba757610d1e575b5060405163095ea7b360e01b602082019081526001600160a01b03881660248301526044820185905288928392909190839061058581606481015b03601f198101835282611b23565b51925af16105916120c7565b81610cef575b5015610cc557604051632daa48c160e11b8152602081600481875afa908115610c9d5787916020918391610ca8575b506040516308caa96b60e21b81523060048201526024810185905260c860448201526064810184905292839160849183916001600160a01b03165af1908115610c9d578791610c7e575b5060058054610100600160a81b031916600892831b610100600160a81b03161790819055604051630dfe168160e01b8152911c6001600160a01b03169290602081600481875afa908115610ba7578891610c5f575b50306001600160a01b039190911603610c5957875b60405163d21220a760e01b8152602081600481885afa908115610b7b578991610c3a575b506001600160a01b03168303610c3457815b670de0b6b3a7640000810290808204670de0b6b3a76400001490151715610c2057906106db91612377565b876003821115610c125750808060011c60018101809111610bfe57905b828210610bdf5750505b8060601b90808204600160601b1490151715610bcb57833b15610bc75760405163f637731d60e01b8152633b9aca009091046001600160a01b03166004820152878160248183885af18015610ba757908891610bb2575b5050604051630dfe168160e01b815297602089600481875afa988915610ba7578899610b86575b5060405163d21220a760e01b815292602084600481885afa938415610b7b578994610b5a575b50604051630dfe168160e01b8152602081600481895afa908115610b25578a91610b3b575b50306001600160a01b039190911603610b30576004602083965b60405163d21220a760e01b815292839182905afa908115610b25578a91610b06575b506001600160a01b031603610afe5750915b60405192610160840198848a1067ffffffffffffffff8b1117610aea57889960405260018060a01b03168452602084019260018060a01b03168352604084019160c883526060850191620d899f1983526080860191620d89a0835260a0870190815260c0870191825260e08701928b84526101008801948c86526101208901963088526101408a0198428a526040519a636d70c41560e01b8c5260018060a01b0390511660048c015260018060a01b0390511660248b01525160020b60448a01525160020b60648901525160020b60848801525160a48701525160c48601525160e48501525161010484015260018060a01b03905116610124830152516101448201526080816101648188885af1928315610354578593869187938892610a84575b5060a0927f15b45263b60f046a8d4ea51dc645f820b07169e923b52fc98e109b09c69bfeb194926109736001600160801b0393612397565b600180861b0360055460081c169360405194855260208501526040840152166060820152846080820152a1803b15610a7f5783809160246040518094819363157b471960e01b83528760048401525af1908115610a74578491610a5f575b5050813b15610a5b57604051632142170760e11b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316602482015260448101919091529082908290606490829084905af18015610a5057610a3f5750f35b81610a4991611b23565b6101915780f35b6040513d84823e3d90fd5b5050fd5b81610a6991611b23565b610a5b5782386109d1565b6040513d86823e3d90fd5b505050fd5b9550925050506080833d608011610ae2575b81610aa360809383611b23565b81010312610ade578251926020810151936001600160801b0385168503610ada5760408201516060909201519094919260a061093b565b8680fd5b8480fd5b3d9150610a96565b634e487b7160e01b89526041600452602489fd5b905091610819565b610b1f915060203d60201161034d5761033e8183611b23565b38610807565b6040513d8c823e3d90fd5b6004602084966107e5565b610b54915060203d60201161034d5761033e8183611b23565b386107cb565b610b7491945060203d60201161034d5761033e8183611b23565b92386107a6565b6040513d8b823e3d90fd5b610ba091995060203d60201161034d5761033e8183611b23565b9738610780565b6040513d8a823e3d90fd5b81610bbc91611b23565b610ada578638610759565b8780fd5b634e487b7160e01b88526011600452602488fd5b909150610bf582610bf08184612377565b611bbe565b60011c906106f8565b634e487b7160e01b8a52601160045260248afd5b901561070257506001610702565b634e487b7160e01b89526011600452602489fd5b886106b0565b610c53915060203d60201161034d5761033e8183611b23565b3861069e565b8061067a565b610c78915060203d60201161034d5761033e8183611b23565b38610665565b610c97915060203d60201161034d5761033e8183611b23565b38610610565b6040513d89823e3d90fd5b610cbf9150823d841161034d5761033e8183611b23565b386105c6565b60405162461bcd60e51b8152602060048201526002602482015261534160f01b6044820152606490fd5b8051801592508215610d04575b505038610597565b610d1792506020809183010191016120f7565b3880610cfc565b87610d2b91989298611b23565b953861053c565b634e487b7160e01b85526011600452602485fd5b634e487b7160e01b84526011600452602484fd5b9091506020813d602011610d86575b81610d7660209383611b23565b81010312610ade575190386104a8565b3d9150610d69565b610da891925060203d60201161034d5761033e8183611b23565b9038610482565b6040513d85823e3d90fd5b60405162461bcd60e51b815260206004820152601960248201527f6e6f20657468657220746f20616464206c6971756964697479000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601460248201527306c6971756964697479416d6f756e7420697320360641b6044820152606490fd5b60405162461bcd60e51b815260206004820152601760248201527f6d696e74696e67206973206e6f7420636f6d706c6574650000000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601760248201527f6c697175696469747920616c72656164792061646465640000000000000000006044820152606490fd5b5034610191576040366003190112610191576040602091610ee4611af2565b610eec611b0d565b6001600160a01b039182168352600185528383209116825283522054604051908152f35b50346101915780600319360112610191576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50346101915780600319360112610191576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461019157604036600319011261019157610fd0610fb7611af2565b610fc660ff6005541615611be1565b60243590336122c8565b602060405160018152f35b503461019157806003193601126101915760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b50346101915780600319360112610191576040519080600454908160011c91600181168015611119575b602084108114611105578386529081156110de5750600114611081575b61018d8461106d81860382611b23565b604051918291602083526020830190611ab1565b600481527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b939250905b8082106110c45750909150810160200161106d8261105d565b9192600181602092548385880101520191019092916110ab565b60ff191660208087019190915292151560051b8501909201925061106d915083905061105d565b634e487b7160e01b83526022600452602483fd5b92607f1692611040565b503461019157806003193601126101915760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b5034610191578060031936011261019157602060ff600554166040519015158152f35b503461019157806003193601126101915760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b5034610191576020366003190112610191576020906040906001600160a01b036111e4611af2565b1681528083522054604051908152f35b503461019157806003193601126101915760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461019157806003193601126101915760ff60055416156113f857338152806020526040812054907f00000000000000000000000000000000000000000000000000000000000000008092106113b357331561139f5733815280602052604081205491808310611386578083338452836020520360408320558060025403600255816040518281527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60203392a37f0000000000000000000000000000000000000000000000000000000000000000828080808433611f40f16113116120c7565b501561134b5760405191825260208201527f2dc8e290002f06fc0085bbca9dfb8b415cf4d1178950c72ff9ee8f4d8878ee6660403392a280f35b60405162461bcd60e51b8152602060048201526013602482015272115512081d1c985b9cd9995c8819985a5b1959606a1b6044820152606490fd5b60649263391434e360e21b835233600452602452604452fd5b634b637e8f60e11b81526004819052602490fd5b60405162461bcd60e51b815260206004820152601a60248201527f496e73756666696369656e7420746f6b656e2062616c616e63650000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601860248201527f5472616e736665727320617265206e6f74206c6f636b656400000000000000006044820152606490fd5b5034610191578060031936011261019157602060405160128152f35b503461019157606036600319011261019157611473611af2565b61147b611b0d565b6044359161148e60ff6005541615611be1565b6001600160a01b0381168085526001602090815260408087203388529091528520549060001982106114c7575b5050610fd093506122c8565b84821061152e57801561151a573315611506576040868692610fd09852600160205281812060018060a01b0333168252602052209103905538806114bb565b634a1406b160e11b86526004869052602486fd5b63e602df0560e01b86526004869052602486fd5b6064868684637dc7a0d960e11b835233600452602452604452fd5b503461019157806003193601126101915760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b50346101915780600319360112610191576040516344341b1560e11b81526020816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa91821561164e578092611616575b602061160e847f0000000000000000000000000000000000000000000000000000000000000000611bbe565b604051908152f35b9091506020823d602011611646575b8161163260209383611b23565b8101031261019157505161160e60206115e2565b3d9150611625565b604051903d90823e3d90fd5b50346101915780600319360112610191576020600254604051908152f35b503461019157806003193601126101915760055460405160089190911c6001600160a01b03168152602090f35b5034610191576080366003190112610191576116bf611af2565b506116c8611b0d565b5060643567ffffffffffffffff8111611771576116e9903690600401611b77565b507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361172c57604051630a85bd0160e11b8152602090f35b60405162461bcd60e51b815260206004820152601960248201527f4e6f742066726f6d20706f736974696f6e206d616e61676572000000000000006044820152606490fd5b5080fd5b5080600319360112610191577f00000000000000000000000000000000000000000000000000000000000000004210611945576002546117d67f00000000000000000000000000000000000000000000000000000000000000008092611bbe565b7f000000000000000000000000000000000000000000000000000000000000000010611900576040516344341b1560e11b81526020816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa8015610daf5783906118c8575b61187391507f0000000000000000000000000000000000000000000000000000000000000000611bbe565b340361188e576118839033612254565b61188b611c57565b80f35b60405162461bcd60e51b8152602060048201526012602482015271696e76616c6964206d696e742076616c756560701b6044820152606490fd5b506020813d6020116118f8575b816118e260209383611b23565b810103126118f4576118739051611848565b8280fd5b3d91506118d5565b60405162461bcd60e51b815260206004820152601f60248201527f6d696e74696e6720776f756c6420657863656564206d617820737570706c79006044820152606490fd5b60405162461bcd60e51b815260206004820152601b60248201527f6d696e74696e6720686173206e6f7420737461727465642079657400000000006044820152606490fd5b503461019157604036600319011261019157610fd06119a7611af2565b60243590336121c1565b50346101915780600319360112610191576040519080600354908160011c91600181168015611a64575b602084108114611105578386529081156110de5750600114611a075761018d8461106d81860382611b23565b600381527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b939250905b808210611a4a5750909150810160200161106d8261105d565b919260018160209254838588010152019101909291611a31565b92607f16926119db565b9050346117715781600319360112611771577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b919082519283825260005b848110611add575050826000602080949584010152601f8019910116010190565b80602080928401015182828601015201611abc565b600435906001600160a01b0382168203611b0857565b600080fd5b602435906001600160a01b0382168203611b0857565b90601f8019910116810190811067ffffffffffffffff821117611b4557604052565b634e487b7160e01b600052604160045260246000fd5b67ffffffffffffffff8111611b4557601f01601f191660200190565b81601f82011215611b0857803590611b8e82611b5b565b92611b9c6040519485611b23565b82845260208383010111611b0857816000926020809301838601378301015290565b91908201809211611bcb57565b634e487b7160e01b600052601160045260246000fd5b15611be857565b60405162461bcd60e51b8152602060048201526014602482015273151c985b9cd9995c9cc8185c99481b1bd8dad95960621b6044820152606490fd5b51906001600160a01b0382168203611b0857565b90816020910312611b0857516001600160a01b0381168103611b085790565b6040516360ebbf8160e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690602081600481855afa9182156120bb57600091829361209a575b506040516344341b1560e11b815291602083600481855afa92831561208d578193612059575b506001600160a01b0384169384158015612051575b61204a57604051633fc8cef360e01b8152602081600481875afa908115610daf57839161202b575b506001600160a01b0316803b156118f457604051630d0e30db60e41b8152838160048189865af18015610a7457612017575b50604051632daa48c160e11b8152602081600481885afa908115610a74578491611ff8575b506040516328af8d0b60e01b81526001600160a01b038381166004830152848116602483015260c86044830152909160209183916064918391165afa908115610a74578491611fd9575b506001600160a01b03168015611fd057611e33916040918882108015611fb457866401000276a4915b85519460208601528b86860152858552611df1606086611b23565b855196879586948593630251596160e31b855230600486015260248501528d604485015260018060a01b0316606484015260a0608484015260a4830190611ab1565b03925af18015610daf57611f89575b506040516370a0823160e01b815230600482015292602084602481895afa938415610daf578394611f55575b5083611eaa575b5050507f28cab0d660ed8aedd61a8c9db00b97f6a2d67e07d87795994f440b18bc5f1aa39160409182519182526020820152a2565b60206004916040519283809263fccc281360e01b82525afa92831561164e57937f28cab0d660ed8aedd61a8c9db00b97f6a2d67e07d87795994f440b18bc5f1aa395938193611f029360409792611f34575b5061210f565b847f1af5163f80e79b5e554f61e1d052084d3a3fe1166e42a265798c4e2ddce8ffa260208551848152a2918193611e75565b611f4e91925060203d60201161034d5761033e8183611b23565b9038611efc565b9093506020813d602011611f81575b81611f7160209383611b23565b81010312611b0857519238611e6e565b3d9150611f64565b604090813d8311611fad575b611f9f8183611b23565b810103126117715738611e42565b503d611f95565b8673fffd8963efd1fc6a506488495d951d5263988d2591611dd6565b50505050505050565b611ff2915060203d60201161034d5761033e8183611b23565b38611dad565b612011915060203d60201161034d5761033e8183611b23565b38611d63565b8361202491949294611b23565b9138611d3e565b612044915060203d60201161034d5761033e8183611b23565b38611d0c565b5050505050565b508315611ce4565b9092506020813d602011612085575b8161207560209383611b23565b810103126118f457519138611ccf565b3d9150612068565b50604051903d90823e3d90fd5b6120b491935060203d60201161034d5761033e8183611b23565b9138611ca9565b6040513d6000823e3d90fd5b3d156120f2573d906120d882611b5b565b916120e66040519384611b23565b82523d6000602084013e565b606090565b90816020910312611b0857518015158103611b085790565b60405163a9059cbb60e01b602082019081526001600160a01b0390931660248201526044810193909352600092839290839061214e8160648101610577565b51925af161215a6120c7565b81612192575b501561216857565b60405162461bcd60e51b815260206004820152600260248201526114d560f21b6044820152606490fd5b80518015925082156121a7575b505038612160565b6121ba92506020809183010191016120f7565b388061219f565b6001600160a01b031690811561223e576001600160a01b03169182156122285760207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925918360005260018252604060002085600052825280604060002055604051908152a3565b634a1406b160e11b600052600060045260246000fd5b63e602df0560e01b600052600060045260246000fd5b6001600160a01b03169081156122b25761227081600254611bbe565b6002557fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602060009284845283825260408420818154019055604051908152a3565b63ec442f0560e01b600052600060045260246000fd5b6001600160a01b0316908115612361576001600160a01b03169182156122b25760008281528060205260408120548281106123475791604082827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef958760209652828652038282205586815280845220818154019055604051908152a3565b916064928463391434e360e21b8452600452602452604452fd5b634b637e8f60e11b600052600060045260246000fd5b8115612381570490565b634e487b7160e01b600052601260045260246000fd5b6040516319cfd1bd60e31b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169190602081602481865afa9081156120bb57600091612504575b506001600160a01b03169160008080808587611f40f161240c6120c7565b506124d657602060049160405192838092630824ea6b60e31b82525afa9081156120bb576000916124b7575b506001600160a01b031660008080808585611f40f16124556120c7565b50612488575060207f7bde4cee589fca1cd293184d2ed4b334511e42beb53077bbacee55b51db497b991604051908152a2565b915060207f83bad9dc00e441f3f6bb4546287f288189a4ab296585f5d7fa2bd686242ca8ed91604051908152a2565b6124d0915060203d60201161034d5761033e8183611b23565b38612438565b5060207f83bad9dc00e441f3f6bb4546287f288189a4ab296585f5d7fa2bd686242ca8ed91604051908152a2565b61251d915060203d60201161034d5761033e8183611b23565b386123ee56fea2646970667358221220bb15a6768914643e986511414c9ba9855983e7b98ea9540208901ed63b8a094264736f6c634300081a0033a2646970667358221220b680aad399786271b4ff6aa7e255b5a53fc848e7a0abfb0784af53297833e7c564736f6c634300081a003360c08060405234610178576040816107f5803803809161001f828561017d565b8339810103126101785761003e6020610037836101a0565b92016101a0565b600160009081556001600160a01b03928316608052911660a0526040516307983f4560e21b6020820190815260696024808401919091528252829161008460448261017d565b51908273dc2b0d2dd2b7759d97d50db4eabdc369731108305af13d15610173573d6001600160401b03811161015d57604051906100cb601f8201601f19166020018361017d565b8152600060203d92013e5b156101185760405161064090816101b58239608051818181610104015281816101c701526104ad015260a0518181816101490152818161028d01526104630152f35b60405162461bcd60e51b815260206004820152601860248201527f4665654d20726567697374726174696f6e206661696c656400000000000000006044820152606490fd5b634e487b7160e01b600052604160045260246000fd5b6100d6565b600080fd5b601f909101601f19168101906001600160401b0382119082101761015d57604052565b51906001600160a01b03821682036101785756fe608080604052600436101561001d575b50361561001b57600080fd5b005b60003560e01c90816301a5e1631461058657508063150b7a02146103c5578063b17acdcd14610178578063b44a272214610133578063c45a0155146100ee5763ffa1ad741461006c573861000f565b346100e95760003660031901126100e957604080519061008c81836105c9565b6006825265056322e312e360d41b6020830152805180926020825280519081602084015260005b8281106100d25750506000828201840152601f01601f19168101030190f35b6020828201810151878301870152869450016100b3565b600080fd5b346100e95760003660031901126100e9576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100e95760003660031901126100e9576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100e95760203660031901126100e9576004356002600054146103b457600260005580600052600160205260ff60406000205416156103a357604051630824ea6b60e31b81526020816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa90811561035257600091610374575b5060405190608082019082821067ffffffffffffffff83111761035e5760409182528383526001600160a01b03908116602084019081526fffffffffffffffffffffffffffffffff84840181815260608601828152855163fc6f786560e01b81529651600488015292518416602487015251811660448601529051166064840152829060849082906000907f0000000000000000000000000000000000000000000000000000000000000000165af18015610352576000916000916102f8575b507fa54e6c2fc0861aa9c991b26891d71059d517983b9e84b91020f42609c452eb9d9160409182519182526020820152a26001600055005b9150506040813d60401161034a575b81610314604093836105c9565b810103126100e95780516020909101517fa54e6c2fc0861aa9c991b26891d71059d517983b9e84b91020f42609c452eb9d6102c0565b3d9150610307565b6040513d6000823e3d90fd5b634e487b7160e01b600052604160045260246000fd5b610396915060203d60201161039c575b61038e81836105c9565b8101906105eb565b82610200565b503d610384565b636ac7cb4360e11b60005260046000fd5b633ee5aeb560e01b60005260046000fd5b346100e95760803660031901126100e9576103de6105b3565b506024356001600160a01b038116908190036100e95760443560643567ffffffffffffffff81116100e957366023820112156100e95780600401359067ffffffffffffffff821161035e5760405191610441601f8201601f1916602001846105c9565b80835236602482840101116100e95760009281602460209401848301370101527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303610575576040516319cfd1bd60e31b8152600481018390526020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa90811561035257600091610556575b506001600160a01b0316156105455760209181600052600183526040600020600160ff19825416179055604051917fdd67f326f26468245c1778c9e66467a1e59e8ed2f3aa040511c03fe0c07daaa1600080a3630a85bd0160e11b8152f35b6324f0519960e21b60005260046000fd5b61056f915060203d60201161039c5761038e81836105c9565b836104e6565b63f06b2ad360e01b60005260046000fd5b346100e95760203660031901126100e9576020906004356000526001825260ff6040600020541615158152f35b600435906001600160a01b03821682036100e957565b90601f8019910116810190811067ffffffffffffffff82111761035e57604052565b908160209103126100e957516001600160a01b03811681036100e9579056fea2646970667358221220beb3278a590399b82fd6b37cabe2952b34235ac0bd930e816716adf7990e513e64736f6c634300081a00330000000000000000000000000000000000000000000000a2a15d09519be0000000000000000000000000000014802de1221f41ccc1101cfc95e9161f2717631e000000000000000000000000cd2d0637c94fe77c2896bbcbb174ceffb08de6d700000000000000000000000012e66c8f215ddd5d48d150c8f46ad0c6fb0f4406000000000000000000000000039e2fb66102314ce7b64ce5ce3e5183bc94ad380000000000000000000000000000000000000000000000000000000000000001
Deployed Bytecode
0x608080604052600436101561001357600080fd5b60003560e01c90816303fc201314610bc157508063076999ee14610ba35780630b761a3614610abd5780630fcbf894146108c4578063157b4719146108475780633e4dddaf146107f35780633f4ba83a146107825780633fc8cef31461073d57806341275358146107145780634eb40dc41461069e57806353613dd3146106805780635b5491821461063b5780635c975abb1461061557806360ebbf81146105ec5780636ff6c4b81461057b578063715018a6146105225780638456cb59146104c05780638705fcd4146104375780638868362a146104195780638da5cb5b146103f0578063b0c684cc146103d4578063b44a27221461038f578063c3d2c3c114610355578063ce7e8de814610313578063d5c3585d146102bf578063dda6bb9a14610255578063f2fde38b146101cc578063fccc2813146101af5763ffa1ad741461015e57600080fd5b346101aa5760003660031901126101aa576101a660408051906101818183610c53565b6006825265056322e312e360d41b602083015251918291602083526020830190610c75565b0390f35b600080fd5b346101aa5760003660031901126101aa57602060405161dead8152f35b346101aa5760203660031901126101aa576101e5610c03565b6101ed610d2d565b6001600160a01b0316801561023f57600080546001600160a01b03198116831782556001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3005b631e4fbdf760e01b600052600060045260246000fd5b346101aa5760203660031901126101aa5761026e610c03565b610276610d2d565b600480546001600160a01b039283166001600160a01b0319821681179092559091167f6d295b6b04456e9e1bccaf6872cf6d3a16af45c740c285c4f403124efc123f84600080a3005b346101aa5760203660031901126101aa577f5232111f2be9cca3a7b943fe9abbbe42eafc43bacd7cdb14eb8cb1eb08f322a960406004356102fe610d2d565b600554908060055582519182526020820152a1005b346101aa5760203660031901126101aa576001600160a01b03610334610c03565b166000526006602052602060018060a01b0360406000205416604051908152f35b346101aa5760203660031901126101aa576001600160a01b03610376610c03565b1660005260076020526020604060002054604051908152f35b346101aa5760003660031901126101aa576040517f00000000000000000000000012e66c8f215ddd5d48d150c8f46ad0c6fb0f44066001600160a01b03168152602090f35b346101aa5760003660031901126101aa57602060405160058152f35b346101aa5760003660031901126101aa576000546040516001600160a01b039091168152602090f35b346101aa5760003660031901126101aa576020600554604051908152f35b346101aa5760203660031901126101aa576004356001600160a01b038116908190036101aa57610465610d2d565b80156104af57600180546001600160a01b0319811683179091556001600160a01b03167f11f35a22548bcd4c3788ab4a7e4fba427a2014f02e5d5e2da9af62212c03183f600080a3005b63ae33c5cd60e01b60005260046000fd5b346101aa5760003660031901126101aa576104d9610d2d565b6104e1610d0d565b6000805460ff60a01b1916600160a01b1790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25890602090a1005b346101aa5760003660031901126101aa5761053b610d2d565b600080546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346101aa5760203660031901126101aa57600435610597610d2d565b600581116105d85760407f1f440356ff29d242b1f0e2afdae6b4be38d5cce2db5fbb241d3fdc363b94210091600354908060035582519182526020820152a1005b6303b0bacf60e01b60005260045260246000fd5b346101aa5760003660031901126101aa576004546040516001600160a01b039091168152602090f35b346101aa5760003660031901126101aa57602060ff60005460a01c166040519015158152f35b346101aa5760003660031901126101aa576040517f000000000000000000000000cd2d0637c94fe77c2896bbcbb174ceffb08de6d76001600160a01b03168152602090f35b346101aa5760003660031901126101aa576020600354604051908152f35b346101aa5760403660031901126101aa576106b7610c03565b60243567ffffffffffffffff81116101aa576106d7903690600401610cb6565b6001600160a01b038281166000908152600660205260409020541633036107035761070191610d56565b005b6322b8548560e01b60005260046000fd5b346101aa5760003660031901126101aa576001546040516001600160a01b039091168152602090f35b346101aa5760003660031901126101aa576040517f000000000000000000000000039e2fb66102314ce7b64ce5ce3e5183bc94ad386001600160a01b03168152602090f35b346101aa5760003660031901126101aa5761079b610d2d565b60005460ff8160a01c16156107e25760ff60a01b19166000556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa90602090a1005b638dfc202b60e01b60005260046000fd5b346101aa5760203660031901126101aa577fb420a40084726ae29557ee58fb1af901767127a23aeec4e5e5daeaec97c3d2a36040600435610832610d2d565b600254908060025582519182526020820152a1005b346101aa5760203660031901126101aa57336000908152600660205260409020546001600160a01b031660043581156108b3573360005260076020528060406000205533907fe28aa6fa88b8801606101ea7f7c54a658990ccba2cb2a8ce4db740f47910baa7600080a4005b6324f0519960e21b60005260046000fd5b346101aa576101003660031901126101aa5760043567ffffffffffffffff81116101aa576108f6903690600401610cb6565b60243567ffffffffffffffff81116101aa57610916903690600401610cb6565b6044356064356084359260a4359460c4359160e43567ffffffffffffffff81116101aa57610948903690600401610cb6565b91610951610d0d565b86151580610ab2575b610a965760405191613096918284019184831067ffffffffffffffff841117610a805761099b6109a9928695610ee787396101008552610100850190610c75565b908382036020850152610c75565b908760408201528660608201528860808201528960a08201528560c082015260e03091015203906000f0918215610a74576020967f2adc8c3149d8c35efa9b0ea8f1b2a037c12b5fb0e704620701a12440a0b926249360018060a01b03169687958660005260068a526040600020336bffffffffffffffffffffffff60a01b825416179055610a388588610d56565b610a4d6040519560c0875260c0870190610c75565b978a86015260408501526060840152608083015260a08201528033940390a3604051908152f35b6040513d6000823e3d90fd5b634e487b7160e01b600052604160045260246000fd5b86600254906301849e9b60e11b60005260045260245260446000fd5b50600254871061095a565b346101aa5760203660031901126101aa576001600160a01b03610ade610c03565b166000526008602052604060002060405190600090805490610aff82610c19565b8085529160018116908115610b7c5750600114610b3b575b6101a684610b2781860382610c53565b604051918291602083526020830190610c75565b600090815260208120939250905b808210610b6257509091508101602001610b2782610b17565b919260018160209254838588010152019101909291610b49565b60ff191660208087019190915292151560051b85019092019250610b279150839050610b17565b346101aa5760003660031901126101aa576020600254604051908152f35b346101aa5760003660031901126101aa577f000000000000000000000000d91ac7df00b715ed207ad6408bd034510afcc09d6001600160a01b03168152602090f35b600435906001600160a01b03821682036101aa57565b90600182811c92168015610c49575b6020831014610c3357565b634e487b7160e01b600052602260045260246000fd5b91607f1691610c28565b90601f8019910116810190811067ffffffffffffffff821117610a8057604052565b919082519283825260005b848110610ca1575050826000602080949584010152601f8019910116010190565b80602080928401015182828601015201610c80565b81601f820112156101aa5780359067ffffffffffffffff8211610a805760405192610ceb601f8401601f191660200185610c53565b828452602083830101116101aa57816000926020809301838601378301015290565b60ff60005460a01c16610d1c57565b63d93c066560e01b60005260046000fd5b6000546001600160a01b03163303610d4157565b63118cdaa760e01b6000523360045260246000fd5b8151610d60575050565b6001600160a01b03166000818152600860205260409020825191929167ffffffffffffffff8111610a8057610d958254610c19565b601f8111610e9e575b506020601f8211600114610e145791817f25c9905798f2c8f6e4cb2f05a20e5b661db0023ec859177bb6374800a39515ba9492610e0494600091610e09575b508160011b916000199060031b1c1916179055604051918291602083526020830190610c75565b0390a2565b905083015138610ddd565b601f1982169083600052806000209160005b818110610e86575092610e049492600192827f25c9905798f2c8f6e4cb2f05a20e5b661db0023ec859177bb6374800a39515ba989610610e6d575b5050811b019055610b27565b85015160001960f88460031b161c191690553880610e61565b9192602060018192868a015181550194019201610e26565b826000526020600020601f830160051c81019160208410610edc575b601f0160051c01905b818110610ed05750610d9e565b60008155600101610ec3565b9091508190610eba56fe61018080604052346106ca57613096803803809161001d8285610a60565b8339810190610100818303126106ca5780516001600160401b0381116106ca5782610049918301610a9e565b602082015190926001600160401b0382116106ca57610069918301610a9e565b9060408101519160608201519260808301519360a08401519261009360e060c08701519601610afe565b875190976001600160401b03821161095d5760035490600182811c92168015610a56575b602083101461093d5781601f8493116109e6575b50602090601f831160011461097e57600092610973575b50508160011b916000199060031b1c1916176003555b8051906001600160401b03821161095d5760045490600182811c92168015610953575b602083101461093d5781601f8493116108cd575b50602090601f83116001146108655760009261085a575b50508160011b916000199060031b1c1916176004555b6004602060ff1960055416976001891760055560018060a01b03168061014052604051928380926303fc201360e01b82525afa9081156106d757600091610820575b506101205261014051604051635a25139160e11b815290602090829060049082906001600160a01b03165afa9081156106d7576000916107e6575b506001600160a01b031661016052811561079657801561074657841594856106e357610140516040516303b4ccf760e11b815290602090829060049082906001600160a01b03165afa9081156106d7576000916106a0575b508110610645578315158061063b575b156105c1575b8415938415610575575b61025b8383610b12565b61051e57670de0b6b3a7640000840293808504670de0b6b3a7640000036105085781670de0b6b3a7640000910202908482041484151715610508576064900492838103908111610508576102af8382610b12565b6104ae576102d1936102c18483610b32565b906101005260a052608052610b32565b60e052156104a95750425b60c05261047b575b5060008060405160208101906307983f4560e21b82526069602482015260248152610310604482610a60565b51908273dc2b0d2dd2b7759d97d50db4eabdc369731108305af13d15610476573d61033a81610a83565b906103486040519283610a60565b8152600060203d92013e5b15610431576040516125599081610b3d82396080518181816103b70152818161156101526117d8015260a051818181610ff30152818161125a01526117b0015260c05181818161113b0152611783015260e05181818161120c015281816112e0015281816115ea015261184f0152610100518181816103dd01526111990152610120518181816109ef0152611a820152610140518181816102180152818161044301528181610f26015281816115aa0152818161181101528181611c6601526123ac0152610160518181816104d501528181610f6b01526116ec0152f35b60405162461bcd60e51b815260206004820152601860248201527f4665654d20726567697374726174696f6e206661696c656400000000000000006044820152606490fd5b610353565b6005557f1d8b2f61c84f331c359476b447a0ddc4fd75f10d265a30e609526e440cdc3a47600080a1386102e4565b6102dc565b60405162461bcd60e51b815260206004820152602c60248201527f6d61784d696e74416d6f756e74206d757374206576656e6c792064697669646560448201526b103a37ba30b626b4b73a32b960a11b6064820152608490fd5b634e487b7160e01b600052601160045260246000fd5b60405162461bcd60e51b815260206004820152602960248201527f746f74616c4574686572206d757374206576656e6c792064697669646520746f6044820152683a30b626b4b73a32b960b91b6064820152608490fd5b4286116102515760405162461bcd60e51b815260206004820152601f60248201527f737461727454696d65206d75737420626520696e2074686520667574757265006044820152606490fd5b60405162461bcd60e51b815260206004820152604660248201527f6c697175696469747950657263656e74206d757374206265206772656174657260448201527f207468616e203020616e64206c657373207468616e20424153455f44454e4f4d60648201526524a720aa27a960d11b608482015260a490fd5b5060648410610241565b60405162461bcd60e51b815260206004820152602d60248201527f746f74616c4574686572206d7573742062652067726561746572207468616e2060448201526c36b4b72a37ba30b622ba3432b960991b6064820152608490fd5b90506020813d6020116106cf575b816106bb60209383610a60565b810103126106ca575138610231565b600080fd5b3d91506106ae565b6040513d6000823e3d90fd5b83156102475760405162461bcd60e51b815260206004820152602f60248201527f6c697175696469747950657263656e74206d7573742062652030207768656e2060448201526e0746f74616c4574686572206973203608c1b6064820152608490fd5b60405162461bcd60e51b815260206004820152602260248201527f746f74616c4d696e746572206d7573742062652067726561746572207468616e604482015261020360f41b6064820152608490fd5b60405162461bcd60e51b815260206004820152602260248201527f746f74616c537570706c79206d7573742062652067726561746572207468616e604482015261020360f41b6064820152608490fd5b90506020813d602011610818575b8161080160209383610a60565b810103126106ca5761081290610afe565b386101d9565b3d91506107f4565b90506020813d602011610852575b8161083b60209383610a60565b810103126106ca5761084c90610afe565b3861019e565b3d915061082e565b015190503880610146565b600460009081528281209350601f198516905b8181106108b5575090846001959493921061089c575b505050811b0160045561015c565b015160001960f88460031b161c1916905538808061088e565b92936020600181928786015181550195019301610878565b60046000529091507f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b601f840160051c81019160208510610933575b90601f859493920160051c01905b818110610924575061012f565b60008155849350600101610917565b9091508190610909565b634e487b7160e01b600052602260045260246000fd5b91607f169161011b565b634e487b7160e01b600052604160045260246000fd5b0151905038806100e2565b600360009081528281209350601f198516905b8181106109ce57509084600195949392106109b5575b505050811b016003556100f8565b015160001960f88460031b161c191690553880806109a7565b92936020600181928786015181550195019301610991565b60036000529091507fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b601f840160051c81019160208510610a4c575b90601f859493920160051c01905b818110610a3d57506100cb565b60008155849350600101610a30565b9091508190610a22565b91607f16916100b7565b601f909101601f19168101906001600160401b0382119082101761095d57604052565b6001600160401b03811161095d57601f01601f191660200190565b81601f820112156106ca57805190610ab582610a83565b92610ac36040519485610a60565b828452602083830101116106ca5760005b828110610ae957505060206000918301015290565b80602080928401015182828701015201610ad4565b51906001600160a01b03821682036106ca57565b8115610b1c570690565b634e487b7160e01b600052601260045260246000fd5b8115610b1c57049056fe608080604052600436101561001d575b50361561001b57600080fd5b005b600090813560e01c90816303fc201314611a6e5750806306fdde03146119b1578063095ea7b31461198a5780631249c58b14611775578063150b7a02146116a55780631755ff211461167857806318160ddd1461165a5780631a93e56714611584578063239c70ae1461154957806323b872dd14611459578063313ce5671461143d578063590e1ae31461122f5780636bc5ec8f146111f457806370a08231146111bc57806370baed431461118157806383f1211b1461115e578063931e2e491461112357806395d89b4114611016578063a2fb130014610fdb578063a9059cbb14610f9a578063b44a272214610f55578063c45a015514610f10578063dd62ed3e14610ec5578063e8078d941461038b578063fa461e33146101945763ffa1ad740361000f57346101915780600319360112610191575061018d604051610166604082611b23565b6006815265056322e312e360d41b6020820152604051918291602083526020830190611ab1565b0390f35b80fd5b50346101915760603660031901126101915760043560243560443567ffffffffffffffff8111610387576101cc903690600401611b77565b604081805181010312610387576101f160406101ea60208401611c24565b9201611c24565b604051632daa48c160e11b81526001600160a01b03928316949192602090829060049082907f0000000000000000000000000000000000000000000000000000000000000000165afa92831561037c5761028c93602092889161035f575b506040516328af8d0b60e01b81526001600160a01b038881166004830152909216602483015260c860448301529093849190829081906064820190565b03916001600160a01b03165afa918215610354578592610323575b506001600160a01b03821633036102ed578481136102dc575b508382136102cc578380f35b6102d59261210f565b3880808380f35b6102e790828561210f565b386102c0565b60405162461bcd60e51b815260206004820152600e60248201526d24b73b30b634b21031b0b63632b960911b6044820152606490fd5b61034691925060203d60201161034d575b61033e8183611b23565b810190611c38565b90386102a7565b503d610334565b6040513d87823e3d90fd5b6103769150833d851161034d5761033e8183611b23565b3861024f565b6040513d88823e3d90fd5b8380fd5b503461019157806003193601126101915760055490600882901c6001600160a01b0316610e80576002547f000000000000000000000000000000000000000000000000000000000000000003610e3b577f0000000000000000000000000000000000000000000000000000000000000000918215610dff574715610dba5760ff19166005556040517f1d8b2f61c84f331c359476b447a0ddc4fd75f10d265a30e609526e440cdc3a478280a1633fc8cef360e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316602082600481845afa918215610daf578392610d8e575b506040516353613dd360e01b815247602082600481865afa918215610354578592610d5a575b50818102918183041490151715610d4657606490049047828103908111610d32576104d38630612254565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316936105098786306121c1565b6001600160a01b03811690813b15610ada57604051630d0e30db60e41b8152878160048187875af18015610ba757610d1e575b5060405163095ea7b360e01b602082019081526001600160a01b03881660248301526044820185905288928392909190839061058581606481015b03601f198101835282611b23565b51925af16105916120c7565b81610cef575b5015610cc557604051632daa48c160e11b8152602081600481875afa908115610c9d5787916020918391610ca8575b506040516308caa96b60e21b81523060048201526024810185905260c860448201526064810184905292839160849183916001600160a01b03165af1908115610c9d578791610c7e575b5060058054610100600160a81b031916600892831b610100600160a81b03161790819055604051630dfe168160e01b8152911c6001600160a01b03169290602081600481875afa908115610ba7578891610c5f575b50306001600160a01b039190911603610c5957875b60405163d21220a760e01b8152602081600481885afa908115610b7b578991610c3a575b506001600160a01b03168303610c3457815b670de0b6b3a7640000810290808204670de0b6b3a76400001490151715610c2057906106db91612377565b876003821115610c125750808060011c60018101809111610bfe57905b828210610bdf5750505b8060601b90808204600160601b1490151715610bcb57833b15610bc75760405163f637731d60e01b8152633b9aca009091046001600160a01b03166004820152878160248183885af18015610ba757908891610bb2575b5050604051630dfe168160e01b815297602089600481875afa988915610ba7578899610b86575b5060405163d21220a760e01b815292602084600481885afa938415610b7b578994610b5a575b50604051630dfe168160e01b8152602081600481895afa908115610b25578a91610b3b575b50306001600160a01b039190911603610b30576004602083965b60405163d21220a760e01b815292839182905afa908115610b25578a91610b06575b506001600160a01b031603610afe5750915b60405192610160840198848a1067ffffffffffffffff8b1117610aea57889960405260018060a01b03168452602084019260018060a01b03168352604084019160c883526060850191620d899f1983526080860191620d89a0835260a0870190815260c0870191825260e08701928b84526101008801948c86526101208901963088526101408a0198428a526040519a636d70c41560e01b8c5260018060a01b0390511660048c015260018060a01b0390511660248b01525160020b60448a01525160020b60648901525160020b60848801525160a48701525160c48601525160e48501525161010484015260018060a01b03905116610124830152516101448201526080816101648188885af1928315610354578593869187938892610a84575b5060a0927f15b45263b60f046a8d4ea51dc645f820b07169e923b52fc98e109b09c69bfeb194926109736001600160801b0393612397565b600180861b0360055460081c169360405194855260208501526040840152166060820152846080820152a1803b15610a7f5783809160246040518094819363157b471960e01b83528760048401525af1908115610a74578491610a5f575b5050813b15610a5b57604051632142170760e11b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316602482015260448101919091529082908290606490829084905af18015610a5057610a3f5750f35b81610a4991611b23565b6101915780f35b6040513d84823e3d90fd5b5050fd5b81610a6991611b23565b610a5b5782386109d1565b6040513d86823e3d90fd5b505050fd5b9550925050506080833d608011610ae2575b81610aa360809383611b23565b81010312610ade578251926020810151936001600160801b0385168503610ada5760408201516060909201519094919260a061093b565b8680fd5b8480fd5b3d9150610a96565b634e487b7160e01b89526041600452602489fd5b905091610819565b610b1f915060203d60201161034d5761033e8183611b23565b38610807565b6040513d8c823e3d90fd5b6004602084966107e5565b610b54915060203d60201161034d5761033e8183611b23565b386107cb565b610b7491945060203d60201161034d5761033e8183611b23565b92386107a6565b6040513d8b823e3d90fd5b610ba091995060203d60201161034d5761033e8183611b23565b9738610780565b6040513d8a823e3d90fd5b81610bbc91611b23565b610ada578638610759565b8780fd5b634e487b7160e01b88526011600452602488fd5b909150610bf582610bf08184612377565b611bbe565b60011c906106f8565b634e487b7160e01b8a52601160045260248afd5b901561070257506001610702565b634e487b7160e01b89526011600452602489fd5b886106b0565b610c53915060203d60201161034d5761033e8183611b23565b3861069e565b8061067a565b610c78915060203d60201161034d5761033e8183611b23565b38610665565b610c97915060203d60201161034d5761033e8183611b23565b38610610565b6040513d89823e3d90fd5b610cbf9150823d841161034d5761033e8183611b23565b386105c6565b60405162461bcd60e51b8152602060048201526002602482015261534160f01b6044820152606490fd5b8051801592508215610d04575b505038610597565b610d1792506020809183010191016120f7565b3880610cfc565b87610d2b91989298611b23565b953861053c565b634e487b7160e01b85526011600452602485fd5b634e487b7160e01b84526011600452602484fd5b9091506020813d602011610d86575b81610d7660209383611b23565b81010312610ade575190386104a8565b3d9150610d69565b610da891925060203d60201161034d5761033e8183611b23565b9038610482565b6040513d85823e3d90fd5b60405162461bcd60e51b815260206004820152601960248201527f6e6f20657468657220746f20616464206c6971756964697479000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601460248201527306c6971756964697479416d6f756e7420697320360641b6044820152606490fd5b60405162461bcd60e51b815260206004820152601760248201527f6d696e74696e67206973206e6f7420636f6d706c6574650000000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601760248201527f6c697175696469747920616c72656164792061646465640000000000000000006044820152606490fd5b5034610191576040366003190112610191576040602091610ee4611af2565b610eec611b0d565b6001600160a01b039182168352600185528383209116825283522054604051908152f35b50346101915780600319360112610191576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50346101915780600319360112610191576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461019157604036600319011261019157610fd0610fb7611af2565b610fc660ff6005541615611be1565b60243590336122c8565b602060405160018152f35b503461019157806003193601126101915760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b50346101915780600319360112610191576040519080600454908160011c91600181168015611119575b602084108114611105578386529081156110de5750600114611081575b61018d8461106d81860382611b23565b604051918291602083526020830190611ab1565b600481527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b939250905b8082106110c45750909150810160200161106d8261105d565b9192600181602092548385880101520191019092916110ab565b60ff191660208087019190915292151560051b8501909201925061106d915083905061105d565b634e487b7160e01b83526022600452602483fd5b92607f1692611040565b503461019157806003193601126101915760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b5034610191578060031936011261019157602060ff600554166040519015158152f35b503461019157806003193601126101915760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b5034610191576020366003190112610191576020906040906001600160a01b036111e4611af2565b1681528083522054604051908152f35b503461019157806003193601126101915760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461019157806003193601126101915760ff60055416156113f857338152806020526040812054907f00000000000000000000000000000000000000000000000000000000000000008092106113b357331561139f5733815280602052604081205491808310611386578083338452836020520360408320558060025403600255816040518281527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60203392a37f0000000000000000000000000000000000000000000000000000000000000000828080808433611f40f16113116120c7565b501561134b5760405191825260208201527f2dc8e290002f06fc0085bbca9dfb8b415cf4d1178950c72ff9ee8f4d8878ee6660403392a280f35b60405162461bcd60e51b8152602060048201526013602482015272115512081d1c985b9cd9995c8819985a5b1959606a1b6044820152606490fd5b60649263391434e360e21b835233600452602452604452fd5b634b637e8f60e11b81526004819052602490fd5b60405162461bcd60e51b815260206004820152601a60248201527f496e73756666696369656e7420746f6b656e2062616c616e63650000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601860248201527f5472616e736665727320617265206e6f74206c6f636b656400000000000000006044820152606490fd5b5034610191578060031936011261019157602060405160128152f35b503461019157606036600319011261019157611473611af2565b61147b611b0d565b6044359161148e60ff6005541615611be1565b6001600160a01b0381168085526001602090815260408087203388529091528520549060001982106114c7575b5050610fd093506122c8565b84821061152e57801561151a573315611506576040868692610fd09852600160205281812060018060a01b0333168252602052209103905538806114bb565b634a1406b160e11b86526004869052602486fd5b63e602df0560e01b86526004869052602486fd5b6064868684637dc7a0d960e11b835233600452602452604452fd5b503461019157806003193601126101915760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b50346101915780600319360112610191576040516344341b1560e11b81526020816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa91821561164e578092611616575b602061160e847f0000000000000000000000000000000000000000000000000000000000000000611bbe565b604051908152f35b9091506020823d602011611646575b8161163260209383611b23565b8101031261019157505161160e60206115e2565b3d9150611625565b604051903d90823e3d90fd5b50346101915780600319360112610191576020600254604051908152f35b503461019157806003193601126101915760055460405160089190911c6001600160a01b03168152602090f35b5034610191576080366003190112610191576116bf611af2565b506116c8611b0d565b5060643567ffffffffffffffff8111611771576116e9903690600401611b77565b507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361172c57604051630a85bd0160e11b8152602090f35b60405162461bcd60e51b815260206004820152601960248201527f4e6f742066726f6d20706f736974696f6e206d616e61676572000000000000006044820152606490fd5b5080fd5b5080600319360112610191577f00000000000000000000000000000000000000000000000000000000000000004210611945576002546117d67f00000000000000000000000000000000000000000000000000000000000000008092611bbe565b7f000000000000000000000000000000000000000000000000000000000000000010611900576040516344341b1560e11b81526020816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa8015610daf5783906118c8575b61187391507f0000000000000000000000000000000000000000000000000000000000000000611bbe565b340361188e576118839033612254565b61188b611c57565b80f35b60405162461bcd60e51b8152602060048201526012602482015271696e76616c6964206d696e742076616c756560701b6044820152606490fd5b506020813d6020116118f8575b816118e260209383611b23565b810103126118f4576118739051611848565b8280fd5b3d91506118d5565b60405162461bcd60e51b815260206004820152601f60248201527f6d696e74696e6720776f756c6420657863656564206d617820737570706c79006044820152606490fd5b60405162461bcd60e51b815260206004820152601b60248201527f6d696e74696e6720686173206e6f7420737461727465642079657400000000006044820152606490fd5b503461019157604036600319011261019157610fd06119a7611af2565b60243590336121c1565b50346101915780600319360112610191576040519080600354908160011c91600181168015611a64575b602084108114611105578386529081156110de5750600114611a075761018d8461106d81860382611b23565b600381527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b939250905b808210611a4a5750909150810160200161106d8261105d565b919260018160209254838588010152019101909291611a31565b92607f16926119db565b9050346117715781600319360112611771577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b919082519283825260005b848110611add575050826000602080949584010152601f8019910116010190565b80602080928401015182828601015201611abc565b600435906001600160a01b0382168203611b0857565b600080fd5b602435906001600160a01b0382168203611b0857565b90601f8019910116810190811067ffffffffffffffff821117611b4557604052565b634e487b7160e01b600052604160045260246000fd5b67ffffffffffffffff8111611b4557601f01601f191660200190565b81601f82011215611b0857803590611b8e82611b5b565b92611b9c6040519485611b23565b82845260208383010111611b0857816000926020809301838601378301015290565b91908201809211611bcb57565b634e487b7160e01b600052601160045260246000fd5b15611be857565b60405162461bcd60e51b8152602060048201526014602482015273151c985b9cd9995c9cc8185c99481b1bd8dad95960621b6044820152606490fd5b51906001600160a01b0382168203611b0857565b90816020910312611b0857516001600160a01b0381168103611b085790565b6040516360ebbf8160e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690602081600481855afa9182156120bb57600091829361209a575b506040516344341b1560e11b815291602083600481855afa92831561208d578193612059575b506001600160a01b0384169384158015612051575b61204a57604051633fc8cef360e01b8152602081600481875afa908115610daf57839161202b575b506001600160a01b0316803b156118f457604051630d0e30db60e41b8152838160048189865af18015610a7457612017575b50604051632daa48c160e11b8152602081600481885afa908115610a74578491611ff8575b506040516328af8d0b60e01b81526001600160a01b038381166004830152848116602483015260c86044830152909160209183916064918391165afa908115610a74578491611fd9575b506001600160a01b03168015611fd057611e33916040918882108015611fb457866401000276a4915b85519460208601528b86860152858552611df1606086611b23565b855196879586948593630251596160e31b855230600486015260248501528d604485015260018060a01b0316606484015260a0608484015260a4830190611ab1565b03925af18015610daf57611f89575b506040516370a0823160e01b815230600482015292602084602481895afa938415610daf578394611f55575b5083611eaa575b5050507f28cab0d660ed8aedd61a8c9db00b97f6a2d67e07d87795994f440b18bc5f1aa39160409182519182526020820152a2565b60206004916040519283809263fccc281360e01b82525afa92831561164e57937f28cab0d660ed8aedd61a8c9db00b97f6a2d67e07d87795994f440b18bc5f1aa395938193611f029360409792611f34575b5061210f565b847f1af5163f80e79b5e554f61e1d052084d3a3fe1166e42a265798c4e2ddce8ffa260208551848152a2918193611e75565b611f4e91925060203d60201161034d5761033e8183611b23565b9038611efc565b9093506020813d602011611f81575b81611f7160209383611b23565b81010312611b0857519238611e6e565b3d9150611f64565b604090813d8311611fad575b611f9f8183611b23565b810103126117715738611e42565b503d611f95565b8673fffd8963efd1fc6a506488495d951d5263988d2591611dd6565b50505050505050565b611ff2915060203d60201161034d5761033e8183611b23565b38611dad565b612011915060203d60201161034d5761033e8183611b23565b38611d63565b8361202491949294611b23565b9138611d3e565b612044915060203d60201161034d5761033e8183611b23565b38611d0c565b5050505050565b508315611ce4565b9092506020813d602011612085575b8161207560209383611b23565b810103126118f457519138611ccf565b3d9150612068565b50604051903d90823e3d90fd5b6120b491935060203d60201161034d5761033e8183611b23565b9138611ca9565b6040513d6000823e3d90fd5b3d156120f2573d906120d882611b5b565b916120e66040519384611b23565b82523d6000602084013e565b606090565b90816020910312611b0857518015158103611b085790565b60405163a9059cbb60e01b602082019081526001600160a01b0390931660248201526044810193909352600092839290839061214e8160648101610577565b51925af161215a6120c7565b81612192575b501561216857565b60405162461bcd60e51b815260206004820152600260248201526114d560f21b6044820152606490fd5b80518015925082156121a7575b505038612160565b6121ba92506020809183010191016120f7565b388061219f565b6001600160a01b031690811561223e576001600160a01b03169182156122285760207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925918360005260018252604060002085600052825280604060002055604051908152a3565b634a1406b160e11b600052600060045260246000fd5b63e602df0560e01b600052600060045260246000fd5b6001600160a01b03169081156122b25761227081600254611bbe565b6002557fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602060009284845283825260408420818154019055604051908152a3565b63ec442f0560e01b600052600060045260246000fd5b6001600160a01b0316908115612361576001600160a01b03169182156122b25760008281528060205260408120548281106123475791604082827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef958760209652828652038282205586815280845220818154019055604051908152a3565b916064928463391434e360e21b8452600452602452604452fd5b634b637e8f60e11b600052600060045260246000fd5b8115612381570490565b634e487b7160e01b600052601260045260246000fd5b6040516319cfd1bd60e31b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169190602081602481865afa9081156120bb57600091612504575b506001600160a01b03169160008080808587611f40f161240c6120c7565b506124d657602060049160405192838092630824ea6b60e31b82525afa9081156120bb576000916124b7575b506001600160a01b031660008080808585611f40f16124556120c7565b50612488575060207f7bde4cee589fca1cd293184d2ed4b334511e42beb53077bbacee55b51db497b991604051908152a2565b915060207f83bad9dc00e441f3f6bb4546287f288189a4ab296585f5d7fa2bd686242ca8ed91604051908152a2565b6124d0915060203d60201161034d5761033e8183611b23565b38612438565b5060207f83bad9dc00e441f3f6bb4546287f288189a4ab296585f5d7fa2bd686242ca8ed91604051908152a2565b61251d915060203d60201161034d5761033e8183611b23565b386123ee56fea2646970667358221220bb15a6768914643e986511414c9ba9855983e7b98ea9540208901ed63b8a094264736f6c634300081a0033a2646970667358221220b680aad399786271b4ff6aa7e255b5a53fc848e7a0abfb0784af53297833e7c564736f6c634300081a0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000000000000000000000000a2a15d09519be0000000000000000000000000000014802de1221f41ccc1101cfc95e9161f2717631e000000000000000000000000cd2d0637c94fe77c2896bbcbb174ceffb08de6d700000000000000000000000012e66c8f215ddd5d48d150c8f46ad0c6fb0f4406000000000000000000000000039e2fb66102314ce7b64ce5ce3e5183bc94ad380000000000000000000000000000000000000000000000000000000000000001
-----Decoded View---------------
Arg [0] : _minTotalEther (uint256): 3000000000000000000000
Arg [1] : _feeAddress (address): 0x14802DE1221f41cCc1101cfC95E9161f2717631E
Arg [2] : _uniswapV3Factory (address): 0xcD2d0637c94fe77C2896BbCBB174cefFb08DE6d7
Arg [3] : _nonfungiblePositionManager (address): 0x12E66C8F215DdD5d48d150c8f46aD0c6fB0F4406
Arg [4] : _weth (address): 0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38
Arg [5] : _creatorFeePercent (uint256): 1
-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000a2a15d09519be00000
Arg [1] : 00000000000000000000000014802de1221f41ccc1101cfc95e9161f2717631e
Arg [2] : 000000000000000000000000cd2d0637c94fe77c2896bbcbb174ceffb08de6d7
Arg [3] : 00000000000000000000000012e66c8f215ddd5d48d150c8f46ad0c6fb0f4406
Arg [4] : 000000000000000000000000039e2fb66102314ce7b64ce5ce3e5183bc94ad38
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000001
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in S
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.