Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Method | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|---|
0x60c06040 | 2824391 | 177 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
OfferFactory
Compiler Version
v0.8.26+commit.8a97fa7a
Optimization Enabled:
Yes with 1000000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.26; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {Offer} from "./Offer.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; error InfluencerExists(); error NoProfit(); error NotCorrectPercent(); error NullAddress(); error NotForYourself(); error TransferFailed(); contract OfferFactory is Ownable { /********************************************************* ========================= Events ========================= *********************************************************/ /** * @param offerAddress Address of created offer contract * @param owner Owner of created contract * @param _name Name of collection (contract) * @param _symbol Symbol of collection (contract) * @param _commissionPercent Commission percent of created contract * @param offer_id Id for backend */ event OfferCreated( address offerAddress, address owner, string _name, string _symbol, uint256 _commissionPercent, string offer_id ); /** * @dev Emitted after becoming refferal * @param refferal Address of refferal * @param influencer Address of influencer */ event BecameRefferal(address refferal, address influencer); /********************************************************** ======================== Variables ======================== **********************************************************/ /// @notice Amount representing percent amount uint16 constant MULTIPLIER = 1000; uint8 constant MAX_DISCOUNT_PERCENT = 50; uint8 constant MAX_PROFIT_PERCENT = 100; /// @notice amount of discount percent for refferals uint16 public discountPercent; /*********************************************************** ========================= Mappings ========================= ***********************************************************/ /// @notice address of refferal => address of influencer mapping(address => address) public refferalToInfluencer; /// @notice address of influencer => influencer profit percent mapping(address => uint16) public influencerProfitPercents; /************************************************************ ========================= Functions ========================= ************************************************************/ constructor( address initialOwner, uint16 _discountPercent ) payable Ownable(initialOwner) { discountPercent = _discountPercent; } function createOfferSC( string calldata offer_id, address _initialOwner, string memory _name, string memory _symbol, uint16 _commissionPercent, uint16 _penaltyPercent, string memory _uriLink, address _marketplaceAddress ) public onlyOwner { if (_initialOwner == address(0)) revert NullAddress(); if (_marketplaceAddress == address(0)) revert NullAddress(); if (_commissionPercent > MULTIPLIER) revert NotCorrectPercent(); if (_penaltyPercent > MULTIPLIER) revert NotCorrectPercent(); Offer offerContract = new Offer( address(this), _initialOwner, _name, _symbol, _commissionPercent, _penaltyPercent, _uriLink, _marketplaceAddress ); emit OfferCreated( address(offerContract), msg.sender, _name, _symbol, _commissionPercent, offer_id ); } function setProfitPercent( uint8 _profitPercent, address _user ) external onlyOwner { if (_profitPercent > MAX_PROFIT_PERCENT) revert NotCorrectPercent(); influencerProfitPercents[_user] = _profitPercent; } /** * @notice Function for setting discount value * @param _discountPercent Amount of commission discount */ function setDiscountValue(uint16 _discountPercent) external onlyOwner { if (_discountPercent > MAX_DISCOUNT_PERCENT) revert NotCorrectPercent(); discountPercent = _discountPercent; } /** * @notice Function for becoming refferal and getting commission discount * @param influencer Address of user that gives refferal */ function becomeRefferal(address influencer) external { if (msg.sender == influencer) revert NotForYourself(); if (refferalToInfluencer[msg.sender] != address(0)) revert InfluencerExists(); refferalToInfluencer[msg.sender] = influencer; emit BecameRefferal(msg.sender, influencer); } /** * @notice Function for withdrawing commission * @param _to Address of withdrawer */ function withdraw(address _to) public payable onlyOwner { (bool ok, ) = payable(_to).call{value: address(this).balance}(""); if (!ok) revert TransferFailed(); } function withdrawERC20( address erc20Address, uint256 value ) public onlyOwner { IERC20(erc20Address).transferFrom(address(this), msg.sender, value); } receive() external payable {} }
// 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.0.0) (interfaces/draft-IERC6093.sol) pragma solidity ^0.8.20; /** * @dev Standard ERC20 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 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 ERC721 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens. */ interface IERC721Errors { /** * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-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 ERC1155 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 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.0.0) (interfaces/IERC165.sol) pragma solidity ^0.8.20; import {IERC165} from "../utils/introspection/IERC165.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4906.sol) pragma solidity ^0.8.20; import {IERC165} from "./IERC165.sol"; import {IERC721} from "./IERC721.sol"; /// @title EIP-721 Metadata Update Extension interface IERC4906 is IERC165, IERC721 { /// @dev This event emits when the metadata of a token is changed. /// So that the third-party platforms such as NFT market could /// timely update the images and related attributes of the NFT. event MetadataUpdate(uint256 _tokenId); /// @dev This event emits when the metadata of a range of tokens is changed. /// So that the third-party platforms such as NFT market could /// timely update the images and related attributes of the NFTs. event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC721.sol) pragma solidity ^0.8.20; import {IERC721} from "../token/ERC721/IERC721.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.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 ERC20 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.0.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. * * CAUTION: See Security Considerations above. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ 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/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC20Permit} from "../extensions/IERC20Permit.sol"; import {Address} from "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev An operation with an ERC20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data); if (returndata.length != 0 && !abi.decode(returndata, (bool))) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/ERC721.sol) pragma solidity ^0.8.20; import {IERC721} from "./IERC721.sol"; import {IERC721Receiver} from "./IERC721Receiver.sol"; import {IERC721Metadata} from "./extensions/IERC721Metadata.sol"; import {Context} from "../../utils/Context.sol"; import {Strings} from "../../utils/Strings.sol"; import {IERC165, ERC165} from "../../utils/introspection/ERC165.sol"; import {IERC721Errors} from "../../interfaces/draft-IERC6093.sol"; /** * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including * the Metadata extension, but not including the Enumerable extension, which is available separately as * {ERC721Enumerable}. */ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Errors { using Strings for uint256; // Token name string private _name; // Token symbol string private _symbol; mapping(uint256 tokenId => address) private _owners; mapping(address owner => uint256) private _balances; mapping(uint256 tokenId => address) private _tokenApprovals; mapping(address owner => mapping(address operator => bool)) private _operatorApprovals; /** * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { return interfaceId == type(IERC721).interfaceId || interfaceId == type(IERC721Metadata).interfaceId || super.supportsInterface(interfaceId); } /** * @dev See {IERC721-balanceOf}. */ function balanceOf(address owner) public view virtual returns (uint256) { if (owner == address(0)) { revert ERC721InvalidOwner(address(0)); } return _balances[owner]; } /** * @dev See {IERC721-ownerOf}. */ function ownerOf(uint256 tokenId) public view virtual returns (address) { return _requireOwned(tokenId); } /** * @dev See {IERC721Metadata-name}. */ function name() public view virtual returns (string memory) { return _name; } /** * @dev See {IERC721Metadata-symbol}. */ function symbol() public view virtual returns (string memory) { return _symbol; } /** * @dev See {IERC721Metadata-tokenURI}. */ function tokenURI(uint256 tokenId) public view virtual returns (string memory) { _requireOwned(tokenId); string memory baseURI = _baseURI(); return bytes(baseURI).length > 0 ? string.concat(baseURI, tokenId.toString()) : ""; } /** * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each * token will be the concatenation of the `baseURI` and the `tokenId`. Empty * by default, can be overridden in child contracts. */ function _baseURI() internal view virtual returns (string memory) { return ""; } /** * @dev See {IERC721-approve}. */ function approve(address to, uint256 tokenId) public virtual { _approve(to, tokenId, _msgSender()); } /** * @dev See {IERC721-getApproved}. */ function getApproved(uint256 tokenId) public view virtual returns (address) { _requireOwned(tokenId); return _getApproved(tokenId); } /** * @dev See {IERC721-setApprovalForAll}. */ function setApprovalForAll(address operator, bool approved) public virtual { _setApprovalForAll(_msgSender(), operator, approved); } /** * @dev See {IERC721-isApprovedForAll}. */ function isApprovedForAll(address owner, address operator) public view virtual returns (bool) { return _operatorApprovals[owner][operator]; } /** * @dev See {IERC721-transferFrom}. */ function transferFrom(address from, address to, uint256 tokenId) public virtual { if (to == address(0)) { revert ERC721InvalidReceiver(address(0)); } // Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists // (from != 0). Therefore, it is not needed to verify that the return value is not 0 here. address previousOwner = _update(to, tokenId, _msgSender()); if (previousOwner != from) { revert ERC721IncorrectOwner(from, tokenId, previousOwner); } } /** * @dev See {IERC721-safeTransferFrom}. */ function safeTransferFrom(address from, address to, uint256 tokenId) public { safeTransferFrom(from, to, tokenId, ""); } /** * @dev See {IERC721-safeTransferFrom}. */ function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual { transferFrom(from, to, tokenId); _checkOnERC721Received(from, to, tokenId, data); } /** * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist * * IMPORTANT: Any overrides to this function that add ownership of tokens not tracked by the * core ERC721 logic MUST be matched with the use of {_increaseBalance} to keep balances * consistent with ownership. The invariant to preserve is that for any address `a` the value returned by * `balanceOf(a)` must be equal to the number of tokens such that `_ownerOf(tokenId)` is `a`. */ function _ownerOf(uint256 tokenId) internal view virtual returns (address) { return _owners[tokenId]; } /** * @dev Returns the approved address for `tokenId`. Returns 0 if `tokenId` is not minted. */ function _getApproved(uint256 tokenId) internal view virtual returns (address) { return _tokenApprovals[tokenId]; } /** * @dev Returns whether `spender` is allowed to manage `owner`'s tokens, or `tokenId` in * particular (ignoring whether it is owned by `owner`). * * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this * assumption. */ function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool) { return spender != address(0) && (owner == spender || isApprovedForAll(owner, spender) || _getApproved(tokenId) == spender); } /** * @dev Checks if `spender` can operate on `tokenId`, assuming the provided `owner` is the actual owner. * Reverts if `spender` does not have approval from the provided `owner` for the given token or for all its assets * the `spender` for the specific `tokenId`. * * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this * assumption. */ function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual { if (!_isAuthorized(owner, spender, tokenId)) { if (owner == address(0)) { revert ERC721NonexistentToken(tokenId); } else { revert ERC721InsufficientApproval(spender, tokenId); } } } /** * @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override. * * NOTE: the value is limited to type(uint128).max. This protect against _balance overflow. It is unrealistic that * a uint256 would ever overflow from increments when these increments are bounded to uint128 values. * * WARNING: Increasing an account's balance using this function tends to be paired with an override of the * {_ownerOf} function to resolve the ownership of the corresponding tokens so that balances and ownership * remain consistent with one another. */ function _increaseBalance(address account, uint128 value) internal virtual { unchecked { _balances[account] += value; } } /** * @dev Transfers `tokenId` from its current owner to `to`, or alternatively mints (or burns) if the current owner * (or `to`) is the zero address. Returns the owner of the `tokenId` before the update. * * The `auth` argument is optional. If the value passed is non 0, then this function will check that * `auth` is either the owner of the token, or approved to operate on the token (by the owner). * * Emits a {Transfer} event. * * NOTE: If overriding this function in a way that tracks balances, see also {_increaseBalance}. */ function _update(address to, uint256 tokenId, address auth) internal virtual returns (address) { address from = _ownerOf(tokenId); // Perform (optional) operator check if (auth != address(0)) { _checkAuthorized(from, auth, tokenId); } // Execute the update if (from != address(0)) { // Clear approval. No need to re-authorize or emit the Approval event _approve(address(0), tokenId, address(0), false); unchecked { _balances[from] -= 1; } } if (to != address(0)) { unchecked { _balances[to] += 1; } } _owners[tokenId] = to; emit Transfer(from, to, tokenId); return from; } /** * @dev Mints `tokenId` and transfers it to `to`. * * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible * * Requirements: * * - `tokenId` must not exist. * - `to` cannot be the zero address. * * Emits a {Transfer} event. */ function _mint(address to, uint256 tokenId) internal { if (to == address(0)) { revert ERC721InvalidReceiver(address(0)); } address previousOwner = _update(to, tokenId, address(0)); if (previousOwner != address(0)) { revert ERC721InvalidSender(address(0)); } } /** * @dev Mints `tokenId`, transfers it to `to` and checks for `to` acceptance. * * Requirements: * * - `tokenId` must not exist. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function _safeMint(address to, uint256 tokenId) internal { _safeMint(to, tokenId, ""); } /** * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. */ function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual { _mint(to, tokenId); _checkOnERC721Received(address(0), to, tokenId, data); } /** * @dev Destroys `tokenId`. * The approval is cleared when the token is burned. * This is an internal function that does not check if the sender is authorized to operate on the token. * * Requirements: * * - `tokenId` must exist. * * Emits a {Transfer} event. */ function _burn(uint256 tokenId) internal { address previousOwner = _update(address(0), tokenId, address(0)); if (previousOwner == address(0)) { revert ERC721NonexistentToken(tokenId); } } /** * @dev Transfers `tokenId` from `from` to `to`. * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. * * Requirements: * * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * * Emits a {Transfer} event. */ function _transfer(address from, address to, uint256 tokenId) internal { if (to == address(0)) { revert ERC721InvalidReceiver(address(0)); } address previousOwner = _update(to, tokenId, address(0)); if (previousOwner == address(0)) { revert ERC721NonexistentToken(tokenId); } else if (previousOwner != from) { revert ERC721IncorrectOwner(from, tokenId, previousOwner); } } /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking that contract recipients * are aware of the ERC721 standard to prevent tokens from being forever locked. * * `data` is additional data, it has no specified format and it is sent in call to `to`. * * This internal function is like {safeTransferFrom} in the sense that it invokes * {IERC721Receiver-onERC721Received} on the receiver, and can be used to e.g. * implement alternative mechanisms to perform token transfer, such as signature-based. * * Requirements: * * - `tokenId` token must exist and be owned by `from`. * - `to` cannot be the zero address. * - `from` cannot be the zero address. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function _safeTransfer(address from, address to, uint256 tokenId) internal { _safeTransfer(from, to, tokenId, ""); } /** * @dev Same as {xref-ERC721-_safeTransfer-address-address-uint256-}[`_safeTransfer`], with an additional `data` parameter which is * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. */ function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual { _transfer(from, to, tokenId); _checkOnERC721Received(from, to, tokenId, data); } /** * @dev Approve `to` to operate on `tokenId` * * The `auth` argument is optional. If the value passed is non 0, then this function will check that `auth` is * either the owner of the token, or approved to operate on all tokens held by this owner. * * Emits an {Approval} event. * * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument. */ function _approve(address to, uint256 tokenId, address auth) internal { _approve(to, tokenId, auth, true); } /** * @dev Variant of `_approve` with an optional flag to enable or disable the {Approval} event. The event is not * emitted in the context of transfers. */ function _approve(address to, uint256 tokenId, address auth, bool emitEvent) internal virtual { // Avoid reading the owner unless necessary if (emitEvent || auth != address(0)) { address owner = _requireOwned(tokenId); // We do not use _isAuthorized because single-token approvals should not be able to call approve if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) { revert ERC721InvalidApprover(auth); } if (emitEvent) { emit Approval(owner, to, tokenId); } } _tokenApprovals[tokenId] = to; } /** * @dev Approve `operator` to operate on all of `owner` tokens * * Requirements: * - operator can't be the address zero. * * Emits an {ApprovalForAll} event. */ function _setApprovalForAll(address owner, address operator, bool approved) internal virtual { if (operator == address(0)) { revert ERC721InvalidOperator(operator); } _operatorApprovals[owner][operator] = approved; emit ApprovalForAll(owner, operator, approved); } /** * @dev Reverts if the `tokenId` doesn't have a current owner (it hasn't been minted, or it has been burned). * Returns the owner. * * Overrides to ownership logic should be done to {_ownerOf}. */ function _requireOwned(uint256 tokenId) internal view returns (address) { address owner = _ownerOf(tokenId); if (owner == address(0)) { revert ERC721NonexistentToken(tokenId); } return owner; } /** * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target address. This will revert if the * recipient doesn't accept the token transfer. The call is not executed if the target address is not a contract. * * @param from address representing the previous owner of the given token ID * @param to target address that will receive the tokens * @param tokenId uint256 ID of the token to be transferred * @param data bytes optional data to send along with the call */ function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private { if (to.code.length > 0) { try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) { if (retval != IERC721Receiver.onERC721Received.selector) { revert ERC721InvalidReceiver(to); } } catch (bytes memory reason) { if (reason.length == 0) { revert ERC721InvalidReceiver(to); } else { /// @solidity memory-safe-assembly assembly { revert(add(32, reason), mload(reason)) } } } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/ERC721URIStorage.sol) pragma solidity ^0.8.20; import {ERC721} from "../ERC721.sol"; import {Strings} from "../../../utils/Strings.sol"; import {IERC4906} from "../../../interfaces/IERC4906.sol"; import {IERC165} from "../../../interfaces/IERC165.sol"; /** * @dev ERC721 token with storage based token URI management. */ abstract contract ERC721URIStorage is IERC4906, ERC721 { using Strings for uint256; // Interface ID as defined in ERC-4906. This does not correspond to a traditional interface ID as ERC-4906 only // defines events and does not include any external function. bytes4 private constant ERC4906_INTERFACE_ID = bytes4(0x49064906); // Optional mapping for token URIs mapping(uint256 tokenId => string) private _tokenURIs; /** * @dev See {IERC165-supportsInterface} */ function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, IERC165) returns (bool) { return interfaceId == ERC4906_INTERFACE_ID || super.supportsInterface(interfaceId); } /** * @dev See {IERC721Metadata-tokenURI}. */ function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { _requireOwned(tokenId); string memory _tokenURI = _tokenURIs[tokenId]; string memory base = _baseURI(); // If there is no base URI, return the token URI. if (bytes(base).length == 0) { return _tokenURI; } // If both are set, concatenate the baseURI and tokenURI (via string.concat). if (bytes(_tokenURI).length > 0) { return string.concat(base, _tokenURI); } return super.tokenURI(tokenId); } /** * @dev Sets `_tokenURI` as the tokenURI of `tokenId`. * * Emits {MetadataUpdate}. */ function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual { _tokenURIs[tokenId] = _tokenURI; emit MetadataUpdate(tokenId); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Metadata.sol) pragma solidity ^0.8.20; import {IERC721} from "../IERC721.sol"; /** * @title ERC-721 Non-Fungible Token Standard, optional metadata extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ interface IERC721Metadata is IERC721 { /** * @dev Returns the token collection name. */ function name() external view returns (string memory); /** * @dev Returns the token collection symbol. */ function symbol() external view returns (string memory); /** * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. */ function tokenURI(uint256 tokenId) external view returns (string memory); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.20; import {IERC165} from "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 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 ERC721 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 ERC721 * 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.0.0) (token/ERC721/IERC721Receiver.sol) pragma solidity ^0.8.20; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ interface IERC721Receiver { /** * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} * by `operator` from `from`, this function is called. * * It must return its Solidity selector to confirm the token transfer. * If any other value is returned or the interface is not implemented by the recipient, the transfer will be * reverted. * * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`. */ function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol) pragma solidity ^0.8.20; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error AddressInsufficientBalance(address account); /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedInnerCall(); /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert AddressInsufficientBalance(address(this)); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert FailedInnerCall(); } } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {FailedInnerCall} error. * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert AddressInsufficientBalance(address(this)); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an * unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {FailedInnerCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}. */ function _revert(bytes memory returndata) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert FailedInnerCall(); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.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.0.0) (utils/introspection/ERC165.sol) pragma solidity ^0.8.20; import {IERC165} from "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * 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[EIP 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/math/Math.sol) pragma solidity ^0.8.20; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Muldiv operation overflow. */ error MathOverflowedMulDiv(); enum Rounding { Floor, // Toward negative infinity Ceil, // Toward positive infinity Trunc, // Toward zero Expand // Away from zero } /** * @dev Returns the addition of two unsigned integers, with an overflow flag. */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the subtraction of two unsigned integers, with an overflow flag. */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds towards infinity instead * of rounding towards zero. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { if (b == 0) { // Guarantee the same behavior as in a regular Solidity division. return a / b; } // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or * denominator == 0. * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by * Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0 = x * y; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. if (denominator <= prod1) { revert MathOverflowedMulDiv(); } /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. // Always >= 1. See https://cs.stackexchange.com/q/138556/92363. uint256 twos = denominator & (0 - denominator); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also // works in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded * towards zero. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256 of a positive value rounded towards zero. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0); } } /** * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers. */ function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) { return uint8(rounding) % 2 == 1; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.20; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol) pragma solidity ^0.8.20; import {Math} from "./math/Math.sol"; import {SignedMath} from "./math/SignedMath.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant HEX_DIGITS = "0123456789abcdef"; uint8 private constant ADDRESS_LENGTH = 20; /** * @dev The `value` string doesn't fit in the specified `length`. */ error StringsInsufficientHexLength(uint256 value, uint256 length); /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), HEX_DIGITS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `int256` to its ASCII `string` decimal representation. */ function toStringSigned(int256 value) internal pure returns (string memory) { return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value))); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { uint256 localValue = value; bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = HEX_DIGITS[localValue & 0xf]; localValue >>= 4; } if (localValue != 0) { revert StringsInsufficientHexLength(value, length); } return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal * representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH); } /** * @dev Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b)); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.26; interface IOfferFactory { function refferalToInfluencer(address _refferal) external view returns (address); function influencerProfitPercents(address _influencer) external view returns (uint16); function discountPercent() external view returns (uint16); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.26; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {ERC721URIStorage, ERC721} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import {OfferStorage} from "./OfferStorage.sol"; import {IOfferFactory} from "./interface/IOfferFactory.sol"; contract Offer is OfferStorage, ERC721URIStorage, Ownable { using SafeERC20 for IERC20Metadata; using Math for uint256; /// @notice Add payable modificator for reducing deployment gas cost constructor( address _withdrawalSCAddress, address _initialOwner, string memory _name, string memory _symbol, uint16 _commissionPercent, uint16 _penaltyPercent, string memory _uriLink, address _marketplaceAddress ) payable Ownable(_initialOwner) ERC721(_name, _symbol) { uriLink = _uriLink; commissionPercent = _commissionPercent; penaltyPercent = _penaltyPercent; marketplaceAddress = _marketplaceAddress; withdrawalSCAddress = _withdrawalSCAddress; } /************************************************************ ========================= Functions ========================= ************************************************************/ /** * @notice Function for adding seller lot * @param _amount Amount of erc20 tokens for future selling * @param _price Price of all amount of tokens in wei */ function addTokenTradeLot( uint256 _amount, uint256 _price ) external payable beforeLaunch { if (_amount > MAX_TOKEN_AMOUNT) revert MoreThanMaxAmountOfTokens(MAX_TOKEN_AMOUNT); (uint256 commission, uint256 discount) = getCommissionAmount(_price); ( uint256 refferalsProfit, uint16 profitPercent, address influencer ) = getRefferalsInfo(commission); // checking checkValueWithCommission(_price); (bool ok, ) = payable(withdrawalSCAddress).call{ value: commission - discount - refferalsProfit }(""); if (!ok) revert TransferFailed(); if (profitPercent > 0 && refferalsProfit > 0) { (ok, ) = payable(influencer).call{value: refferalsProfit}(""); if (!ok) revert TransferFailed(); } tradeLots[tradeLotId] = TradeLotInfo( _amount, _price, msg.sender, LotStatus.Created ); emit LotCreated( tradeLotId, _amount, _price, msg.sender, address(this), refferalsProfit ); ++tradeLotId; } /** * @notice Function for donating ERC20 tokens * @notice Returns to seller his `deposit - commision` and reward of purchase * @notice Needs preliminary `Approve` of user from `tokenAddress` * @param lotId Id of token lot */ function donateERC20Tokens(uint256 lotId) external payable { TradeLotInfo storage tradeLotInfo = tradeLots[lotId]; // checking if (isDeadline) revert DeadlineError(); if (tradeLotInfo.amount == 0) revert LotNotExists(lotId); if (tradeLotInfo.status != LotStatus.Bought) revert BuyerNotExists(); IERC20Metadata erc20Token = IERC20Metadata(tokenAddress); (uint256 commission, uint256 discount) = getCommissionAmount( tradeLotInfo.price ); ( uint256 refferalsProfit, uint16 profitPercent, address influencer ) = getRefferalsInfo(commission); tradeLotInfo.status = LotStatus.Donated; uint8 decimals = IERC20Metadata(tokenAddress).decimals(); erc20Token.safeTransferFrom( msg.sender, address(this), tradeLotInfo.amount * 10 ** decimals // tradeLotInfo.amount ); uint256 totalCommission = commission - discount; (bool ok, ) = payable(msg.sender).call{ value: tradeLotInfo.price + tradeLotInfo.price - totalCommission }(""); if (!ok) revert TransferFailed(); (ok, ) = payable(withdrawalSCAddress).call{ value: totalCommission - refferalsProfit }(""); if (!ok) revert TransferFailed(); if (profitPercent > 0 && refferalsProfit > 0) { (ok, ) = payable(influencer).call{value: refferalsProfit}(""); if (!ok) revert TransferFailed(); } emit TokensDonated( lotId, tradeLotInfo.amount, tradeLotInfo.price, msg.sender, address(this), refferalsProfit ); } /** * @notice Function for returning deposit * @param lotId Id of lot */ function sellerDepositReturning(uint256 lotId) external payable { TradeLotInfo memory tradeLotInfo = tradeLots[lotId]; (uint256 commission, uint256 discount) = getCommissionAmount( tradeLotInfo.price ); ( uint256 refferalsProfit, uint16 profitPercent, address influencer ) = getRefferalsInfo(commission); if (msg.sender != tradeLotInfo.seller) revert NotSeller(); if (tradeLotInfo.status == LotStatus.Withdrawed) revert AlreadyWithdrawed(); if (tradeLotInfo.status == LotStatus.Bought) revert DeadlineError(); if (tradeLotInfo.status == LotStatus.Canceled) revert Canceled(); if ( tradeLotInfo.status == LotStatus.Donated && msg.value != commission - discount ) revert NotCorrectValue( msg.value, tradeLotInfo.price, commission, discount ); tradeLots[lotId].status = LotStatus.Withdrawed; (bool ok, ) = payable(withdrawalSCAddress).call{ value: commission - discount - refferalsProfit }(""); if (!ok) revert TransferFailed(); if (profitPercent > 0 && refferalsProfit > 0) { (ok, ) = payable(influencer).call{value: refferalsProfit}(""); if (!ok) revert TransferFailed(); } totalProfit += commission - discount - refferalsProfit; uint256 sellerValue = tradeLotInfo.status == LotStatus.Donated ? (tradeLotInfo.price * 2) : tradeLotInfo.price; (ok, ) = payable(msg.sender).call{value: sellerValue}(""); if (!ok) revert TransferFailed(); emit DepReturnedBySeller(lotId, address(this), refferalsProfit); } /** * @notice Function for cancelling lot * @param lotId Id of lot */ function sellerCancelLot(uint256 lotId) external { TradeLotInfo memory tradeLotInfo = tradeLots[lotId]; if (msg.sender != tradeLotInfo.seller) revert NotSeller(); if (tradeLotInfo.status == LotStatus.Withdrawed) revert AlreadyWithdrawed(); if (tradeLotInfo.status == LotStatus.Canceled) revert Canceled(); if (tradeLotInfo.status != LotStatus.Bought) revert BuyerNotExists(); tradeLots[lotId].status = LotStatus.Canceled; emit LotCanceled(lotId, address(this)); } /** * @notice Function for purchase future ERC20 tokens and transferring NFT to buyer * @param lotId Id of lot */ function buy(uint256 lotId) external payable beforeLaunch { TradeLotInfo memory tradeLotInfo = tradeLots[lotId]; checkValueWithCommission(tradeLotInfo.price); if (tradeLots[lotId].status == LotStatus.Bought) revert AlreadyBought(); if (tradeLotInfo.status == LotStatus.Canceled) revert Canceled(); tradeLots[lotId].status = LotStatus.Bought; (uint256 commission, uint256 discount) = getCommissionAmount( tradeLotInfo.price ); ( uint256 refferalsProfit, uint16 profitPercent, address influencer ) = getRefferalsInfo(commission); (bool ok, ) = payable(withdrawalSCAddress).call{ value: commission - discount - refferalsProfit }(""); if (!ok) revert TransferFailed(); if (profitPercent > 0 && refferalsProfit > 0) { (ok, ) = payable(influencer).call{value: refferalsProfit}(""); if (!ok) revert TransferFailed(); } totalProfit += commission - discount - refferalsProfit; uint256 nftIdBeforeIncr = mintNFT( lotId, msg.sender, tradeLotInfo.amount ); emit NFTBoughtFromOffer( lotId, msg.sender, address(this), nftIdBeforeIncr, refferalsProfit ); } /** * @notice Function for buyer's claiming erc20 tokens * @param lotId Id of lot */ function buyerClaimsTokens(uint256 lotId, uint256 _nftId) external payable { address nftOwner = ownerOf(_nftId); TradeLotInfo memory tradeLotInfo = tradeLots[lotId]; PurchaseInfo memory purchaseInfo = purchases[lotId][_nftId]; // Checking if (tradeLotInfo.amount == 0) revert LotNotExists(lotId); if (nftOwner != msg.sender) revert NotNFTOwner(_nftId); if (tradeLotInfo.status == LotStatus.Bought && !isDeadline) revert NotAvailableForClaim(); _burn(_nftId); purchases[lotId][_nftId].isClaimed = true; uint256 percentPart = (purchaseInfo.tokensAmount * PERCENT_DIVIDER) / tradeLotInfo.amount; uint256 priceOfPart = (tradeLotInfo.price * percentPart) / PERCENT_DIVIDER; uint256 _refferalsProfit; if (tradeLotInfo.status == LotStatus.Donated) { // Seller donates tokens (uint256 commission, uint256 discount) = getCommissionAmount( priceOfPart ); ( uint256 refferalsProfit, uint16 profitPercent, address influencer ) = getRefferalsInfo(commission); _refferalsProfit = refferalsProfit; if (msg.value != commission - discount) revert NotCorrectValue( msg.value, priceOfPart, commission, discount ); (bool ok, ) = payable(withdrawalSCAddress).call{ value: commission - discount - refferalsProfit }(""); if (!ok) revert TransferFailed(); if (profitPercent > 0 && refferalsProfit > 0) { (ok, ) = payable(influencer).call{value: refferalsProfit}(""); if (!ok) revert TransferFailed(); } uint8 decimals = IERC20Metadata(tokenAddress).decimals(); bool success = IERC20Metadata(tokenAddress).transfer( nftOwner, purchaseInfo.tokensAmount * 10 ** decimals // purchaseInfo.tokensAmount ); if (!success) revert TransferFailed(); } else { // Seller doesn't donate tokens uint256 valueForSC = (priceOfPart * penaltyPercent) / MULTIPLIER; ( uint256 refferalsProfit, uint16 profitPercent, address influencer ) = getRefferalsInfo(valueForSC); _refferalsProfit = refferalsProfit; // Commision for withdrawal SC (bool ok, ) = payable(withdrawalSCAddress).call{ value: valueForSC - refferalsProfit }(""); if (!ok) revert TransferFailed(); // Compensation for buyer (ok, ) = payable(nftOwner).call{ value: priceOfPart + priceOfPart - valueForSC }(""); if (!ok) revert TransferFailed(); } emit NFTReturnedFromOffer( lotId, purchaseInfo.tokensAmount, _nftId, nftOwner, address(this), _refferalsProfit ); } /** * @notice Function for splitting NFT * @notice This function burns original NFT and mints 2 new instead * @param _nftId Id of NFT * @param amountForBuyer Percent amount of ERC20 tokens that buyer receives * @param nftBuyer Address of NFT buyer * @param nftSeller Address of NFT seller */ function splitNFT( uint256 _nftId, uint256 amountForBuyer, address nftBuyer, address nftSeller ) public returns (uint256, uint256) { if (msg.sender != marketplaceAddress) revert OnlyForMarketplace(); uint256 _tradeLotId = nftIdToLotId[_nftId]; uint256 tokensAmount = nftIdToTokensAmount[_nftId]; if (amountForBuyer >= tokensAmount) revert MoreThanAvailableAmount(); // minting NFT and writting to mapping uint256 buyerNFTId = mintNFT(_tradeLotId, nftBuyer, amountForBuyer); uint256 sellerNFTId = mintNFT( _tradeLotId, nftSeller, tokensAmount - amountForBuyer ); // burning prev nft with removing purchase from mapping _burn(_nftId); delete purchases[_tradeLotId][_nftId]; delete nftIdToTokensAmount[_nftId]; return (buyerNFTId, sellerNFTId); } /** * @notice Function for minting NFT and writting to mapping * @param lotId Id of lot * @param buyer Address of buyer * @param tokensAmount Amount of ERC20 tokens */ function mintNFT( uint256 lotId, address buyer, uint256 tokensAmount ) private returns (uint256) { uint256 currNftId = nftId; purchases[lotId][currNftId] = PurchaseInfo(currNftId, tokensAmount, false); nftIdToLotId[currNftId] = lotId; nftIdToTokensAmount[currNftId] = tokensAmount; string memory fileName = string.concat( uriLink, Strings.toString(currNftId), ".json" ); // minting NFT and setting URI _mint(buyer, currNftId); _setTokenURI(currNftId, fileName); // id of current NFT uint256 idBeforeIncr = currNftId; ++nftId; return idBeforeIncr; } /** * @notice Function for setting percent of commission * @param _commissionPercent percent of commission */ function setCommission(uint16 _commissionPercent) external onlyOwner { if (_commissionPercent >= MULTIPLIER) revert NotCorrectPercent(); commissionPercent = _commissionPercent; } function checkValueWithCommission(uint256 _price) private { (uint256 commission, uint256 discount) = getCommissionAmount(_price); if (msg.value != _price + (commission - discount)) revert NotCorrectValue(msg.value, _price, commission, discount); } /** * @notice Function for getting amount of commission * @param price Price of tokens */ function getCommissionAmount( uint256 price ) private view returns (uint256, uint256) { uint256 commission = (price * commissionPercent) / MULTIPLIER; uint256 discountValueFromFactory = IOfferFactory(withdrawalSCAddress) .discountPercent(); address influencer = IOfferFactory(withdrawalSCAddress) .refferalToInfluencer(msg.sender); uint256 discount = influencer != address(0) ? (commission * discountValueFromFactory) / PERCENT_DIVIDER : 0; return (commission, discount); } /** * @notice Function for calculating profit from refferal and getting influencer * @param commission Value of commission */ function getRefferalsInfo( uint256 commission ) internal view returns (uint256, uint16, address) { IOfferFactory factory = IOfferFactory(withdrawalSCAddress); address influencer = factory.refferalToInfluencer(msg.sender); uint16 influencerProfitPercent = factory.influencerProfitPercents( influencer ); uint256 profit = commission.mulDiv(influencerProfitPercent, MULTIPLIER); return (profit, influencerProfitPercent, influencer); } /** * @notice Function for setting token launch * @param _tokenAddress Address of ERC20 token */ function setTokenLaunch( address _tokenAddress, uint256 _decimalsMultiplier ) external onlyOwner { tokenAddress = _tokenAddress; decimalsMultiplier = _decimalsMultiplier; isTokenLaunched = true; emit TokenLaunchedOnOffer(address(this)); } /** * @notice Function for setting percent of deposit amount that platform * @notice takes if seller don't donate ERC20 tokens in time * @param _penaltyPercent Amount of percent */ function setPenaltyPercent(uint16 _penaltyPercent) external onlyOwner { if (_penaltyPercent >= MULTIPLIER) revert NotCorrectPercent(); penaltyPercent = _penaltyPercent; } /// @notice Function for setting deadline function setDeadline() external onlyOwner { if (!isTokenLaunched) revert TokenNotLaunched(); isDeadline = true; emit TokenDeadlineOnOffer(address(this)); } /// @notice Function for manual minting function setURILink(string calldata link) external onlyOwner { uriLink = link; } function transferToWithdrawalSC(uint256 _value) internal { (bool ok, ) = payable(withdrawalSCAddress).call{value: _value}(""); if (!ok) revert TransferFailed(); totalProfit += _value; } receive() external payable {} fallback() external payable {} }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.26; contract OfferStorage { error NotCorrectValue( uint256 value, uint256 price, uint256 commission, uint256 discount ); error NotDonated(); error NotSelled(uint256 id); error NotSeller(); error BuyerNotExists(); error BuyerExists(); error AlreadyBought(); error AlreadyClaimed(); error AlreadyWithdrawed(); error TokenNotLaunched(); error TokenLaunchedError(); error DeadlineError(); error NotCorrectURIsAmount(); error NotCorrectPercent(); error LotNotExists(uint256 lotId); error NotNFTOwner(uint256 nftId); error MoreThanMaxAmountOfTokens(uint32 maxAmount); error OnlyForMarketplace(); error TransferFailed(); error NotAvailableForClaim(); error Canceled(); error MoreThanAvailableAmount(); /********************************************************* ========================= Events ========================= *********************************************************/ /** * @dev Emitted after creating trade lot * @param tradeLotId Id of trade lot * @param amount Amount of tokens for sell * @param price Price of all amount ot tokens for sell * @param seller Address of seller */ event LotCreated( uint256 tradeLotId, uint256 amount, uint256 price, address seller, address offer_address, uint256 refferalsProfit ); /** * @dev Emitted after donation tokens on contract * @param tradeLotId Id of trade lot * @param amount Amount of tokens for sell * @param price Price of all amount ot tokens for sell * @param seller Address of seller */ event TokensDonated( uint256 tradeLotId, uint256 amount, uint256 price, address seller, address offer_address, uint256 refferalsProfit ); /** * @dev Emitted after returning deposit to seller * @param tradeLotId Id of trade lot * @param offer_address Address of this contract */ event DepReturnedBySeller( uint256 tradeLotId, address offer_address, uint256 refferalsProfit ); /** * @dev Emitted after buying nft(lot) * @param tradeLotId Id of trade lot * @param buyer Address of buyer * @param offer_address Address of this contract * @param nftId Id of NFT */ event NFTBoughtFromOffer( uint256 tradeLotId, address buyer, address offer_address, uint256 nftId, uint256 refferalsProfit ); /** * @dev Emitted after cancelling lot * @param tradeLotId Id of trade lot * @param offer_address Address of this contract */ event LotCanceled(uint256 tradeLotId, address offer_address); /** * @dev Emitted after returning NFT * @param tradeLotId Id of trade lot * @param tokensAmount Amount of ERC20 tokens for claim * @param nftId Id of NFT * @param buyer Address of buyer * @param offer_address Address of this contract */ event NFTReturnedFromOffer( uint256 tradeLotId, uint256 tokensAmount, uint256 nftId, address buyer, address offer_address, uint256 refferalsProfit ); /** * @dev Emitted after token launching * @param offer_address Address of this contract */ event TokenLaunchedOnOffer(address offer_address); /** * @dev Emitted after ending of deadline * @param offer_address Address of this contract */ event TokenDeadlineOnOffer(address offer_address); /********************************************************** ========================= Structs ========================= **********************************************************/ /** * @param amount Amount of tokens for sell * @param price Price of all amount ot tokens * @param seller Address of seller * @param isDonated Flag indicates is tokens donated * @param isDepWithdrawed Flag indicates is deposit withdrawed */ struct TradeLotInfo { uint256 amount; uint256 price; address seller; LotStatus status; } /** * @param nftId Id of NFT * @param amount Amount of tokens for sell * @param isClaimed Flag indicates is buyer claimed erc20 tokens */ struct PurchaseInfo { uint256 nftId; uint256 tokensAmount; bool isClaimed; } /************************************************************ ========================= Constants ========================= ************************************************************/ /// @notice Max possible amount of ERC20 tokens for lot uint32 constant MAX_TOKEN_AMOUNT = 1_000_000_000; /// @notice Amount representing percent amount uint16 constant MULTIPLIER = 1000; uint8 constant PERCENT_DIVIDER = 100; /************************************************************ ========================= Variables ========================= ************************************************************/ /// @notice Id of current trade lot uint256 internal tradeLotId = 1; /// @notice Id of current NFT uint256 internal nftId = 1; /// @notice Total profit from commission uint256 public totalProfit = 0; /// @notice Address of token for sell address public tokenAddress; /// @notice Address of NFT marketplace address internal marketplaceAddress; /// @notice Percent amount of commission uint16 public commissionPercent = 1; /// @notice Flag for indication is token launched bool public isTokenLaunched = false; /// @notice Flag for indication is deadline end bool public isDeadline = false; /// @notice Address for commssion transfer address public immutable withdrawalSCAddress; /// @notice Percent amount that contract remains to himself uint16 public penaltyPercent = 1; /// @notice URI link with path to json metadata string uriLink = ""; /// @notice Multiplier for amount of ERC20 tokens uint256 internal decimalsMultiplier = 1; address internal immutable factoryAddress; /// @notice Status of trade lot enum LotStatus { Created, Bought, Donated, Withdrawed, Canceled } /*********************************************************** ========================= Mappings ========================= ***********************************************************/ /** * @notice List of trade lots of tokens * @notice lotId => TradeLotInfo */ mapping(uint256 => TradeLotInfo) public tradeLots; /** * @notice List of purchases * @notice lotId => nftId => PurchaseInfo */ mapping(uint256 => mapping(uint256 => PurchaseInfo)) public purchases; /// @notice List of lot ids for finding lot via nftId mapping(uint256 => uint256) public nftIdToLotId; /// @notice List of ERC20 tokens amount mapping(uint256 => uint256) public nftIdToTokensAmount; /************************************************************ ========================= Modifiers ========================= ************************************************************/ modifier beforeLaunch() { if (isTokenLaunched) revert TokenLaunchedError(); _; } }
{ "viaIR": true, "optimizer": { "enabled": true, "runs": 1000000 }, "evmVersion": "paris", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract ABI
API[{"inputs":[{"internalType":"address","name":"initialOwner","type":"address"},{"internalType":"uint16","name":"_discountPercent","type":"uint16"}],"stateMutability":"payable","type":"constructor"},{"inputs":[],"name":"InfluencerExists","type":"error"},{"inputs":[],"name":"NotCorrectPercent","type":"error"},{"inputs":[],"name":"NotForYourself","type":"error"},{"inputs":[],"name":"NullAddress","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":"TransferFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"refferal","type":"address"},{"indexed":false,"internalType":"address","name":"influencer","type":"address"}],"name":"BecameRefferal","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"offerAddress","type":"address"},{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"string","name":"_name","type":"string"},{"indexed":false,"internalType":"string","name":"_symbol","type":"string"},{"indexed":false,"internalType":"uint256","name":"_commissionPercent","type":"uint256"},{"indexed":false,"internalType":"string","name":"offer_id","type":"string"}],"name":"OfferCreated","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"},{"inputs":[{"internalType":"address","name":"influencer","type":"address"}],"name":"becomeRefferal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"offer_id","type":"string"},{"internalType":"address","name":"_initialOwner","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint16","name":"_commissionPercent","type":"uint16"},{"internalType":"uint16","name":"_penaltyPercent","type":"uint16"},{"internalType":"string","name":"_uriLink","type":"string"},{"internalType":"address","name":"_marketplaceAddress","type":"address"}],"name":"createOfferSC","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"discountPercent","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"influencerProfitPercents","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"refferalToInfluencer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_discountPercent","type":"uint16"}],"name":"setDiscountValue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"_profitPercent","type":"uint8"},{"internalType":"address","name":"_user","type":"address"}],"name":"setProfitPercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"}],"name":"withdraw","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"erc20Address","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"withdrawERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
6080601f61580f38819003918201601f19168301916001600160401b0383118484101760da57808492604094855283398101031260d55780516001600160a01b038116919082900360d557602001519061ffff8216820360d557801560bf5760005490604051928160018060a01b0384167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a36001600160b01b03199092161760a09190911b61ffff60a01b161760005561571e90816100f18239f35b631e4fbdf760e01b600052600060045260246000fd5b600080fd5b634e487b7160e01b600052604160045260246000fdfe608080604052600436101561001d575b50361561001b57600080fd5b005b60003560e01c90816323f6f96814610a6e5750806329ef191914610a2b57806351cff8d9146109735780635d8162b01461090a578063715018a61461086e5780638da5cb5b1461081c578063a1db978214610740578063b915d1dd146106a7578063e04d70bc146103ac578063ec906f7614610274578063ef57e0b4146101a55763f2fde38b146100ae573861000f565b346101a05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a05773ffffffffffffffffffffffffffffffffffffffff6100fa610ae5565b610102610c4c565b1680156101715773ffffffffffffffffffffffffffffffffffffffff600054827fffffffffffffffffffffffff0000000000000000000000000000000000000000821617600055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b7f1e4fbdf700000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b600080fd5b346101a05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a05760043560ff81168091036101a0576101ea610b08565b6101f2610c4c565b6064821161024a5773ffffffffffffffffffffffffffffffffffffffff1660005260026020526040600020907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000825416179055600080f35b7f3cb42c8a0000000000000000000000000000000000000000000000000000000060005260046000fd5b346101a05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a05773ffffffffffffffffffffffffffffffffffffffff6102c0610ae5565b168033146103825733600052600160205273ffffffffffffffffffffffffffffffffffffffff604060002054166103585760407ffe894b43a2eb942442ee61921094be387ddcdc3a20b3ef07188febf719a9eea69133600052600160205281600020817fffffffffffffffffffffffff00000000000000000000000000000000000000008254161790558151903382526020820152a1005b7f336b2c1b0000000000000000000000000000000000000000000000000000000060005260046000fd5b7f8141c6040000000000000000000000000000000000000000000000000000000060005260046000fd5b346101a0576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a05760043567ffffffffffffffff81116101a057366023820112156101a057806004013567ffffffffffffffff81116101a05736602482840101116101a057610421610b08565b60443567ffffffffffffffff81116101a057610441903690600401610ba6565b9060643567ffffffffffffffff81116101a057610462903690600401610ba6565b906084359061ffff82168092036101a05760a43561ffff81168091036101a05760c43567ffffffffffffffff81116101a0576104a2903690600401610ba6565b9060e4359273ffffffffffffffffffffffffffffffffffffffff84168094036101a05773ffffffffffffffffffffffffffffffffffffffff906104e3610c4c565b1692831561067d57801561067d576103e8851161024a576103e8821161024a5760405193614a4d8086019386851067ffffffffffffffff86111761064e57869560e09361057793610c9c89393087526020870152610100604087015261055e61055061010088018d610bed565b87810360608901528b610bed565b9189608088015260a087015285820360c0870152610bed565b92015203906000f091821561064257601f8573ffffffffffffffffffffffffffffffffffffffff9660247faad9ed14e5a3a1445c95acc441b3e0db97c4458cbaf66f3836aa43898be962e89961061b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09661060d60209b6040519d8e9c168c52338d8d015260c060408d015260c08c0190610bed565b908a820360608c0152610bed565b96608089015287870360a089015282875201878601376000868286010152011601030190a1005b6040513d6000823e3d90fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7fe99d5ac50000000000000000000000000000000000000000000000000000000060005260046000fd5b346101a05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a05760043561ffff81168082036101a0576032906106f0610c4c565b1161024a577fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff75ffff00000000000000000000000000000000000000006000549260a01b16911617600055600080f35b346101a05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a0576000602073ffffffffffffffffffffffffffffffffffffffff6064610792610ae5565b61079a610c4c565b60405194859384927f23b872dd0000000000000000000000000000000000000000000000000000000084523060048501523360248501526024356044850152165af18015610642576107e857005b6020813d602011610814575b8161080160209383610b2b565b810103126101a05751801515036101a057005b3d91506107f4565b346101a05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a057602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b346101a05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a0576108a5610c4c565b600073ffffffffffffffffffffffffffffffffffffffff81547fffffffffffffffffffffffff000000000000000000000000000000000000000081168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346101a05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a05773ffffffffffffffffffffffffffffffffffffffff610956610ae5565b166000526002602052602061ffff60406000205416604051908152f35b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a05760008080806109aa610ae5565b6109b2610c4c565b73ffffffffffffffffffffffffffffffffffffffff4791165af13d15610a26573d6109dc81610b6c565b906109ea6040519283610b2b565b8152600060203d92013e5b156109fc57005b7f90b8ec180000000000000000000000000000000000000000000000000000000060005260046000fd5b6109f5565b346101a05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a057602061ffff60005460a01c16604051908152f35b346101a05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a05760209073ffffffffffffffffffffffffffffffffffffffff610abd610ae5565b166000526001825273ffffffffffffffffffffffffffffffffffffffff604060002054168152f35b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101a057565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036101a057565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761064e57604052565b67ffffffffffffffff811161064e57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b81601f820112156101a057803590610bbd82610b6c565b92610bcb6040519485610b2b565b828452602083830101116101a057816000926020809301838601378301015290565b919082519283825260005b848110610c375750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b80602080928401015182828601015201610bf8565b73ffffffffffffffffffffffffffffffffffffffff600054163303610c6d57565b7f118cdaa7000000000000000000000000000000000000000000000000000000006000523360045260246000fdfe60c0604052614a4d8038038061001481610595565b928339810190610100818303126105905761002e816105ba565b9161003b602083016105ba565b60408301519092906001600160401b038111610590578261005d9183016105ce565b60608201519091906001600160401b038111610590578361007f9183016105ce565b9361008c60808301610639565b9461009960a08401610639565b60c084015190956001600160401b038211610590576100bf60e0916100c69387016105ce565b94016105ba565b600160008181559080556002556004805465ffffffffffff60a01b1916780100000001000000000000000000000000000000000000000017905560055490949061010f90610648565b601f811161056d575b50600060055560016006558051906001600160401b0382116103bd578190610141600b54610648565b601f811161051e575b50602090601f83116001146104b6576000926104ab575b50508160011b916000199060031b1c191617600b555b8051906001600160401b0382116103bd578190610195600c54610648565b601f811161045c575b50602090601f83116001146103f4576000926103e9575b50508160011b916000199060031b1c191617600c555b6001600160a01b031680156103d357601280546001600160a01b0319811683179091556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a38051906001600160401b0382116103bd57819061023a600554610648565b601f811161037f575b50602090601f83116001146103175760009261030c575b50508160011b916000199060031b1c1916176005555b6004805460c09390931b61ffff60c01b16600163ffff000160b01b031990931660a09490941b61ffff60a01b1693909317919091176001600160a01b039091161790556080526040516143b3908161069a823960805181818161044701528181610be301528181611ac701528181611daf01528181611ff70152818161227e015281816127d4015281816137210152613890015260a051815050f35b01519050388061025a565b600560009081528281209350601f198516905b818110610367575090846001959493921061034e575b505050811b01600555610270565b015160001960f88460031b161c19169055388080610340565b9293602060018192878601518155019501930161032a565b6103ad9060056000526020600020601f850160051c810191602086106103b3575b601f0160051c0190610682565b38610243565b90915081906103a0565b634e487b7160e01b600052604160045260246000fd5b631e4fbdf760e01b600052600060045260246000fd5b0151905038806101b5565b600c60009081528281209350601f198516905b818110610444575090846001959493921061042b575b505050811b01600c556101cb565b015160001960f88460031b161c1916905538808061041d565b92936020600181928786015181550195019301610407565b600c6000526104a5907fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7601f850160051c810191602086106103b357601f0160051c0190610682565b3861019e565b015190503880610161565b600b60009081528281209350601f198516905b81811061050657509084600195949392106104ed575b505050811b01600b55610177565b015160001960f88460031b161c191690553880806104df565b929360206001819287860151815501950193016104c9565b600b600052610567907f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9601f850160051c810191602086106103b357601f0160051c0190610682565b3861014a565b61058a906005600052601f6020600020910160051c810190610682565b38610118565b600080fd5b6040519190601f01601f191682016001600160401b038111838210176103bd57604052565b51906001600160a01b038216820361059057565b81601f82011215610590578051906001600160401b0382116103bd576105fd601f8301601f1916602001610595565b92828452602083830101116105905760005b82811061062457505060206000918301015290565b8060208092840101518282870101520161060f565b519061ffff8216820361059057565b90600182811c92168015610678575b602083101461066257565b634e487b7160e01b600052602260045260246000fd5b91607f1691610657565b81811061068d575050565b6000815560010161068256fe608080604052600436101561001a575b50361561001857005b005b60003560e01c90816301ffc9a714612c4f5750806306fdde0314612b88578063081812fc14612b1e578063095ea7b314612998578063125ff95d146125bb578063161ba46a146125785780632372243b1461253457806323b872dd1461251d5780632953e714146124915780632cbf8fb9146122a25780633ced31711461223357806342842e0e1461220957806350bd305c14611f8f578063528a28b714611f4b578063629b6307146118ea5780636352211e146118905780636cd3e8301461179f57806370a0823114611705578063715018a61461166757806377d3550b1461162457806386001519146115e85780638da5cb5b1461159657806390e6871a1461154c57806395d89b411461144257806399707f9c146113665780639a06b0b11461131c5780639d76ea58146112ca578063a22cb465146111bd578063abc9104314610f7a578063b88d4fde14610ed3578063bf96f31714610e84578063c594cc6514610de9578063c87b56dd14610d94578063d96a094a14610a75578063e5e03c84146108fa578063e7fad70a14610831578063e985e9c51461079c578063f0e4d60e146102cb5763f2fde38b146101d4573861000f565b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65773ffffffffffffffffffffffffffffffffffffffff610220612dd5565b610228613998565b1680156102975773ffffffffffffffffffffffffffffffffffffffff601254827fffffffffffffffffffffffff0000000000000000000000000000000000000000821617601255167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b7f1e4fbdf700000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b600080fd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65760043580600052600760205260406000209073ffffffffffffffffffffffffffffffffffffffff6040519261032984612ec1565b80548452600260018201549160208601928352015461035b60ff606060408801978685168952019260a01c1682613351565b61036582516136f1565b9061036f81613861565b91969098511633036107725783519760058910156107435760036000991461071b578451600581101561069e576001146106f3578451600581101561069e576004146106cb578451600581101561069e576002148061068b575b61063e578789526007602052600260408a2001740300000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff825416179055888080806104308b61042b8a8a613062565b613062565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af161047161307c565b50156106165786929161ffff8a921615158061060d575b6105b0575b50509061042b6104a8936104a093613062565b60025461306f565b6002555160058110156105835784906002036105795750518060011b908082046002149015171561054c5783808080935b335af16104e461307c565b5015610524577fafe5eb92bc7335536bafdb5ec3cc9545b5c1f06f5f3886690a8d6640084855a9916060916040519182523060208301526040820152a180f35b6004837f90b8ec18000000000000000000000000000000000000000000000000000000008152fd5b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b80808093516104d9565b6024857f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b8180939473ffffffffffffffffffffffffffffffffffffffff8293165af16105d661307c565b50156105e5578490878961048d565b6004877f90b8ec18000000000000000000000000000000000000000000000000000000008152fd5b50831515610488565b6004897f90b8ec18000000000000000000000000000000000000000000000000000000008152fd5b85516040517f42d45f7800000000000000000000000000000000000000000000000000000000815234600482015260248101919091526044810184905260648101859052608490fd5b0390fd5b506106968484613062565b3414156103c9565b60248a7f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b6004897f1afb0ae5000000000000000000000000000000000000000000000000000000008152fd5b6004897fb21ba927000000000000000000000000000000000000000000000000000000008152fd5b6004897f219055cc000000000000000000000000000000000000000000000000000000008152fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7f5ec823510000000000000000000000000000000000000000000000000000000060005260046000fd5b346102c65760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c6576107d3612dd5565b73ffffffffffffffffffffffffffffffffffffffff6107f0612df8565b9116600052601060205273ffffffffffffffffffffffffffffffffffffffff60406000209116600052602052602060ff604060002054166040519015158152f35b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65760043561ffff81168082036102c6576103e89061087b613998565b10156108d0577fffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffff79ffff0000000000000000000000000000000000000000000000006004549260c01b16911617600455600080f35b7f3cb42c8a0000000000000000000000000000000000000000000000000000000060005260046000fd5b346102c65760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65760443560043560243573ffffffffffffffffffffffffffffffffffffffff831683036102c6576064359173ffffffffffffffffffffffffffffffffffffffff831683036102c65773ffffffffffffffffffffffffffffffffffffffff600454163303610a4b578060005260096020526040600020549181600052600a6020526040600020549384821015610a21576109d1826109cb6109d89460409988613b5d565b96613062565b9084613b5d565b916109e282613a5e565b60005260086020528360002081600052602052600060028582208281558260018201550155600052600a60205260008381205582519182526020820152f35b7f9603b4aa0000000000000000000000000000000000000000000000000000000060005260046000fd5b7fcf6b8f780000000000000000000000000000000000000000000000000000000060005260046000fd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65760043560ff60045460b01c16610d6a57806000526007602052604060002090604051610aca81612ec1565b82548152600260018401549360208301948552015473ffffffffffffffffffffffffffffffffffffffff81166040830152610b0f60ff606084019260a01c1682613351565b610b1984516139e7565b82600052600760205260ff60026040600020015460a01c16600581101561074357600114610d40575192600584101561074357600460009414610d1857610bac90838552600760205260026040862001740100000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff825416179055516136f1565b610bb7829392613861565b9087808080610bcc8761042b8a8d9a9d613062565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1610c0d61307c565b5015610cf0579061ffff889216151580610ce7575b610c8d575b5093610c6d7fb72f862d94450c8b8e1ee32c6630970bee0e51cbfaffb8f401e6e312d6ece18094610c626104a08461042b60a0988c98613062565b600255513384613b5d565b60405192835233602084015230604084015260608301526080820152a180f35b81808773ffffffffffffffffffffffffffffffffffffffff8294165af1610cb261307c565b5015610cbf578587610c27565b6004867f90b8ec18000000000000000000000000000000000000000000000000000000008152fd5b50851515610c22565b6004887f90b8ec18000000000000000000000000000000000000000000000000000000008152fd5b6004847f1afb0ae5000000000000000000000000000000000000000000000000000000008152fd5b7f0b3465c20000000000000000000000000000000000000000000000000000000060005260046000fd5b7fd989c4ae0000000000000000000000000000000000000000000000000000000060005260046000fd5b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657610de5610dd1600435613564565b604051918291602083526020830190612d92565b0390f35b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65760043561ffff81168082036102c6576103e890610e33613998565b10156108d0577fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff75ffff00000000000000000000000000000000000000006004549260a01b16911617600455600080f35b346102c657610e9236612e8d565b9060005260086020526040600020906000526020526060604060002080549060ff600260018301549201541690604051928352602083015215156040820152f35b346102c65760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657610f0a612dd5565b610f12612df8565b6064359167ffffffffffffffff83116102c657366023840112156102c657826004013591610f3f83612f69565b92610f4d6040519485612f28565b80845236602482870101116102c657602081600092602461001898018388013785010152604435916133a2565b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65760043580600052600760205260406000209073ffffffffffffffffffffffffffffffffffffffff600260405193610fdf85612ec1565b805485526001810154602086015201549261100d60ff606060408401938588168552019560a01c1685613351565b511633036107725781519160058310156107435760036000931461119557805160058110156111685760041461114057516005811015611113577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff016110eb576040817f9eb7657bef9907ba1467bb58e6f3b70349f0ee413302ab895acbc8bc225e4b219284526007602052600282852001740400000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff8254161790558151908152306020820152a180f35b6004827fdd980a31000000000000000000000000000000000000000000000000000000008152fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b6004837f1afb0ae5000000000000000000000000000000000000000000000000000000008152fd5b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b6004837f219055cc000000000000000000000000000000000000000000000000000000008152fd5b346102c65760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c6576111f4612dd5565b60243590811515908183036102c65773ffffffffffffffffffffffffffffffffffffffff1691821561129c5761126e9033600052601060205260406000208460005260205260406000209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b6040519081527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b827f5b08ba180000000000000000000000000000000000000000000000000000000060005260045260246000fd5b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657602073ffffffffffffffffffffffffffffffffffffffff60035416604051908152f35b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657600435600052600a6020526020604060002054604051908152f35b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65761139d613998565b60045460ff8160b01c1615611418577fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff770100000000000000000000000000000000000000000000009116176004557f206f7aa88b4936d5a9b98bed26f5f0d5e12c04b26ae217ebdb3a623a86a55f596020604051308152a1005b7fc79a5b5b0000000000000000000000000000000000000000000000000000000060005260046000fd5b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c6576040516000600c5461148281612fa3565b808452906001811690811561150a57506001146114aa575b610de583610dd181850382612f28565b919050600c6000527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7916000905b8082106114f057509091508101602001610dd161149a565b9192600181602092548385880101520191019092916114d8565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208086019190915291151560051b84019091019150610dd1905061149a565b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65760043560005260096020526020604060002054604051908152f35b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657602073ffffffffffffffffffffffffffffffffffffffff60125416604051908152f35b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c6576020600254604051908152f35b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657602061ffff60045460a01c16604051908152f35b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65761169e613998565b600073ffffffffffffffffffffffffffffffffffffffff6012547fffffffffffffffffffffffff00000000000000000000000000000000000000008116601255167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65773ffffffffffffffffffffffffffffffffffffffff611751612dd5565b16801561177057600052600e6020526020604060002054604051908152f35b7f89c62b6400000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b346102c65760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65773ffffffffffffffffffffffffffffffffffffffff6117eb612dd5565b6117f3613998565b167fffffffffffffffffffffffff000000000000000000000000000000000000000060035416176003556024356006557601000000000000000000000000000000000000000000007fffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffff60045416176004557f3b11d3e657d8d514329702c63d255f4697baf18e6410e1f9072c93c731eb70326020604051308152a1005b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65760206118cc60043561364e565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b6118f336612e8d565b906118fd8261364e565b918160005260076020526040600020926040519261191a84612ec1565b8454845260026001860154956020860196875201549473ffffffffffffffffffffffffffffffffffffffff8616604086015261196060ff606087019760a01c1687613351565b81600052600860205260406000208460005260205260406000209060405161198781612f0c565b82548152604060ff6002600186015495602085019687520154161515910152855115611f1d5773ffffffffffffffffffffffffffffffffffffffff841690338203611eef5787519660058810156107435760016000981480611edf575b611eb7576119f187613a5e565b8488526008602052604088208789526020526002604089200160017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055835190606482029180830460641490151715611e8a5751908115611e5d5791611a619160649304905161304f565b0496516005811015611e3057600203611d6757611a7d876136f1565b90611a8781613861565b93909192839b611a978383613062565b3403611d1f57508a80611ab08661042b83968496613062565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1611af161307c565b5015610616579061ffff89939216151580611d16575b611ce2575b50505073ffffffffffffffffffffffffffffffffffffffff60035416906040517f313ce567000000000000000000000000000000000000000000000000000000008152602081600481865afa908115611cd757611b82602093926044928b91611caa575b50611b7c87519161300f565b9061304f565b918960405195869485937fa9059cbb000000000000000000000000000000000000000000000000000000008552600485015260248401525af1908115611c9f578691611c70575b5015611c485794611c42917f7368bfddf54437295c38d71448fd337c832ab763b8d669fa154f59a111c5640a95965b5160408051928352602083019190915281019490945273ffffffffffffffffffffffffffffffffffffffff909216606084015230608084015260a0830191909152819060c0820190565b0390a180f35b6004857f90b8ec18000000000000000000000000000000000000000000000000000000008152fd5b611c92915060203d602011611c98575b611c8a8183612f28565b81019061338a565b87611bc9565b503d611c80565b6040513d88823e3d90fd5b611cca9150853d8711611cd0575b611cc28183612f28565b810190612ff6565b8c611b70565b503d611cb8565b6040513d8a823e3d90fd5b8280929173ffffffffffffffffffffffffffffffffffffffff8293165af1611d0861307c565b5015610cbf57858880611b0c565b50801515611b07565b6040517f42d45f780000000000000000000000000000000000000000000000000000000081523460048201526024810191909152604481019190915260648101829052608490fd5b6103e8611d7d61ffff60045460c01c168961304f565b04611d8781613861565b50509787808080611d988d87613062565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1611dd961307c565b5015610cf057878093611df2829461042b85859661306f565b905af1611dfd61307c565b5015611c485794611c42917f7368bfddf54437295c38d71448fd337c832ab763b8d669fa154f59a111c5640a9596611bf8565b6024877f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b6024897f4e487b710000000000000000000000000000000000000000000000000000000081526012600452fd5b6024897f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b6004887f7361fc0c000000000000000000000000000000000000000000000000000000008152fd5b5060ff60045460b81c16156119e4565b857fd64dfb010000000000000000000000000000000000000000000000000000000060005260045260246000fd5b827fc87c16410000000000000000000000000000000000000000000000000000000060005260045260246000fd5b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657602060ff60045460b81c166040519015158152f35b611f9836612e8d565b9060ff60045460b01c16610d6a57633b9aca0081116121d757611fe08261042b6000808080611fc6866136f1565b979061042b611fd482613861565b99829c9294919b6139e7565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af161202161307c565b50156121a45761ffff8392161515806121ce575b612170575b505060405161204881612ec1565b8281526020810184815273ffffffffffffffffffffffffffffffffffffffff8060026040850194338652606081019460008652600054600052600760205260406000209151825551600182015501935116167fffffffffffffffffffffffff0000000000000000000000000000000000000000835416178255519360058510156107435781547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1660a095861b74ff00000000000000000000000000000000000000001617909155600080546040805182815260208101969096528501929092523360608501523060808501529383019190915261216b917f8fd77df48aedff028372940389f8d7b6c07528858c2f7cc94e3c5ccd9fe2b2239060c090a161335d565b815580f35b600080809373ffffffffffffffffffffffffffffffffffffffff8294165af161219761307c565b50156121a457808461203a565b7f90b8ec180000000000000000000000000000000000000000000000000000000060005260046000fd5b50811515612035565b7f4e42c81400000000000000000000000000000000000000000000000000000000600052633b9aca0060045260246000fd5b346102c65761001861221a36612e1b565b906040519261222a602085612f28565b600084526133a2565b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65760043567ffffffffffffffff81116102c657366023820112156102c657806004013567ffffffffffffffff81116102c65736602482840101116102c657612316613998565b600090612324600554612fa3565b601f811161244b575b5081601f821160011461238e578190839461237a9492612380575b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8260011b9260031b1c19161790565b60055580f35b602492500101358480612348565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08216937f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db091845b86811061243057508360019596106123f5575b505050811b0160055580f35b01602401357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600384901b60f8161c191690558380806123e9565b909260206001819260248787010135815501940191016123d6565b6005835260208320601f830160051c81019160208410612487575b601f0160051c01905b81811061247c575061232d565b83815560010161246f565b9091508190612466565b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65760043560005260076020526040600020805473ffffffffffffffffffffffffffffffffffffffff6002600184015493015460ff8160a01c169360405193845260208401521660408201526005821015610743576080916060820152f35b346102c65761001861252e36612e1b565b916130ac565b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657602060ff60045460b01c166040519015158152f35b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657602061ffff60045460c01c16604051908152f35b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65760043580600052600760205260406000209060ff60045460b81c1661296e57815415612941576002820160ff815460a01c166005811015610743576001036129175773ffffffffffffffffffffffffffffffffffffffff6003541692600181019161265083546136f1565b61265b829392613861565b9491989093740200000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff8254161790556004602073ffffffffffffffffffffffffffffffffffffffff60035416604051928380927f313ce5670000000000000000000000000000000000000000000000000000000082525afa90811561290b57600061270b61276b93829383916128ec575b50611b7c8b549161300f565b60405160208101917f23b872dd000000000000000000000000000000000000000000000000000000008352336024830152306044830152606482015260648152612756608482612f28565b519082865af161276461307c565b90836142e0565b80519081151591826128d1575b50506128a457509061278991613062565b600080808061279d8561042b8b548061306f565b335af16127a861307c565b50156121a457600080806127bd8a8295613062565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af16127fe61307c565b50156121a45761ffff1615158061289b575b612868575b505490546040805193845260208401929092529082015233606082015230608082015260a08101919091527ffa9aad700273c94161651bdca5c167b4d924f83f2e832d88a861debed12438619060c090a1005b600080808773ffffffffffffffffffffffffffffffffffffffff8295165af161288f61307c565b50156121a45784612815565b50841515612810565b7f5274afe70000000000000000000000000000000000000000000000000000000060005260045260246000fd5b6128e4925060208091830101910161338a565b158a80612778565b612905915060203d602011611cd057611cc28183612f28565b8e6126ff565b6040513d6000823e3d90fd5b7fdd980a310000000000000000000000000000000000000000000000000000000060005260046000fd5b7fc87c16410000000000000000000000000000000000000000000000000000000060005260045260246000fd5b7fb21ba9270000000000000000000000000000000000000000000000000000000060005260046000fd5b346102c65760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c6576129cf612dd5565b6024356129db8161364e565b33151580612afe575b80612aad575b612a7f57819073ffffffffffffffffffffffffffffffffffffffff80851691167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600080a4600052600f60205273ffffffffffffffffffffffffffffffffffffffff604060002091167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055600080f35b7fa9fbf51f000000000000000000000000000000000000000000000000000000006000523360045260246000fd5b5073ffffffffffffffffffffffffffffffffffffffff81166000526010602052604060002073ffffffffffffffffffffffffffffffffffffffff331660005260205260ff60406000205416156129ea565b503373ffffffffffffffffffffffffffffffffffffffff821614156129e4565b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657600435612b598161364e565b50600052600f602052602073ffffffffffffffffffffffffffffffffffffffff60406000205416604051908152f35b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c6576040516000600b54612bc881612fa3565b808452906001811690811561150a5750600114612bef57610de583610dd181850382612f28565b919050600b6000527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9916000905b808210612c3557509091508101602001610dd161149a565b919260018160209254838588010152019101909291612c1d565b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657600435907fffffffff0000000000000000000000000000000000000000000000000000000082168092036102c657817f490649060000000000000000000000000000000000000000000000000000000060209314908115612ce1575b5015158152f35b7f80ac58cd00000000000000000000000000000000000000000000000000000000811491508115612d45575b8115612d1b575b5083612cda565b7f01ffc9a70000000000000000000000000000000000000000000000000000000091501483612d14565b7f5b5e139f0000000000000000000000000000000000000000000000000000000081149150612d0d565b60005b838110612d825750506000910152565b8181015183820152602001612d72565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093612dce81518092818752878088019101612d6f565b0116010190565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036102c657565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036102c657565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126102c65760043573ffffffffffffffffffffffffffffffffffffffff811681036102c6579060243573ffffffffffffffffffffffffffffffffffffffff811681036102c6579060443590565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60409101126102c6576004359060243590565b6080810190811067ffffffffffffffff821117612edd57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6060810190811067ffffffffffffffff821117612edd57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117612edd57604052565b67ffffffffffffffff8111612edd57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b90600182811c92168015612fec575b6020831014612fbd57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691612fb2565b908160209103126102c6575160ff811681036102c65790565b60ff16604d811161302057600a0a90565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181029291811591840414171561302057565b9190820391821161302057565b9190820180921161302057565b3d156130a7573d9061308d82612f69565b9161309b6040519384612f28565b82523d6000602084013e565b606090565b919073ffffffffffffffffffffffffffffffffffffffff1691821561332257600090828252600d6020528273ffffffffffffffffffffffffffffffffffffffff604084205416948533151580613234575b507fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef73ffffffffffffffffffffffffffffffffffffffff95826131c4575b838152600e6020526040812060018154019055848152600d60205260408120847fffffffffffffffffffffffff000000000000000000000000000000000000000082541617905580a41680830361319157505050565b7f64283d7b0000000000000000000000000000000000000000000000000000000060005260045260245260445260646000fd5b6131fd85600052600f60205260406000207fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b828152600e602052604081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff815401905561313b565b919394925050806132b1575b15613250579190839085386130fd565b839085613282576024917f7e273289000000000000000000000000000000000000000000000000000000008252600452fd5b6044917f177e802f00000000000000000000000000000000000000000000000000000000825233600452602452fd5b5033851480156132ec575b806132405750838152600f6020523373ffffffffffffffffffffffffffffffffffffffff60408320541614613240565b5084815260106020526040812073ffffffffffffffffffffffffffffffffffffffff3316825260205260ff6040822054166132bc565b7f64a0ae9200000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b60058210156107435752565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146130205760010190565b908160209103126102c6575180151581036102c65790565b906133ae8382846130ac565b803b6133bb575b50505050565b60209161342773ffffffffffffffffffffffffffffffffffffffff8093169560405195869485947f150b7a020000000000000000000000000000000000000000000000000000000086523360048701521660248501526044840152608060648401526084830190612d92565b03816000865af18091600091613502575b5090613482575061344761307c565b8051908161347d57827f64a0ae920000000000000000000000000000000000000000000000000000000060005260045260246000fd5b602001fd5b7fffffffff000000000000000000000000000000000000000000000000000000007f150b7a02000000000000000000000000000000000000000000000000000000009116036134d55750388080806133b5565b7f64a0ae920000000000000000000000000000000000000000000000000000000060005260045260246000fd5b6020813d60201161355c575b8161351b60209383612f28565b810103126135585751907fffffffff0000000000000000000000000000000000000000000000000000000082168203613555575038613438565b80fd5b5080fd5b3d915061350e565b61356d8161364e565b50600052601160205260406000206040519081600082549261358e84612fa3565b808452936001811690811561360e57506001146135c7575b506135b392500382612f28565b60006040516135c3602082612f28565b5290565b90506000929192526020600020906000915b8183106135f25750509060206135b392820101386135a6565b60209193508060019154838588010152019101909183926135d9565b602093506135b39592507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0091501682840152151560051b820101386135a6565b80600052600d60205273ffffffffffffffffffffffffffffffffffffffff6040600020541690811561367e575090565b7f7e2732890000000000000000000000000000000000000000000000000000000060005260045260246000fd5b908160209103126102c6575161ffff811681036102c65790565b908160209103126102c6575173ffffffffffffffffffffffffffffffffffffffff811681036102c65790565b6137086103e89161ffff60045460a01c169061304f565b049073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016604051907f29ef1919000000000000000000000000000000000000000000000000000000008252602082600481845afa91821561290b5760009261382c575b506020602491604051928380927f23f6f9680000000000000000000000000000000000000000000000000000000082523360048301525afa801561290b5773ffffffffffffffffffffffffffffffffffffffff916000916137fd575b5016156137f7576137f361ffff606492168461304f565b0490565b50600090565b61381f915060203d602011613825575b6138178183612f28565b8101906136c5565b386137dc565b503d61380d565b6024919250613852602091823d841161385a575b61384a8183612f28565b8101906136ab565b929150613780565b503d613840565b6040517f23f6f968000000000000000000000000000000000000000000000000000000008152336004820152917f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1691602084602481865afa92831561290b57602494600094613976575b50602090604051958680927f5d8162b000000000000000000000000000000000000000000000000000000000825273ffffffffffffffffffffffffffffffffffffffff881660048301525afa93841561290b57600094613951575b5061394c9061ffff851690614227565b929190565b61394c91945061396f9060203d60201161385a5761384a8183612f28565b939061393c565b602091945061399190823d8411613825576138178183612f28565b93906138e1565b73ffffffffffffffffffffffffffffffffffffffff6012541633036139b957565b7f118cdaa7000000000000000000000000000000000000000000000000000000006000523360045260246000fd5b6139f0816136f1565b613a056139ff82849594613062565b8361306f565b3403613a1057505050565b610687906040519384937f42d45f7800000000000000000000000000000000000000000000000000000000855234600486019094939260609260808301968352602083015260408201520152565b6000818152600d60205273ffffffffffffffffffffffffffffffffffffffff604082205416828115928315613aed575b818152600d602052604081207fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8280a45061367e5750565b613b2682600052600f60205260406000207fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b828152600e602052604081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8154019055613a8e565b90929160015491613be1604051613b7381612f0c565b84815260026020820185815260408301906000825285600052600860205260406000208860005260205260406000209351845551600184015551151591019060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b82600052600960205260406000205581600052600a6020526040600020558092816000907a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008410156141ff575b806d04ee2d6d415b85acef8100000000600a9210156141e4575b662386f26fc100008110156141d0575b6305f5e1008110156141bf575b6127108110156141b0575b60648110156141a2575b101561419a575b6001810194600a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6021613cc8613cb28a612f69565b99613cc06040519b8c612f28565b808b52612f69565b947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe060208b0196013687378901015b01917f30313233343536373839616263646566000000000000000000000000000000008282061a8353048015613d51577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600a9192613cf7565b5050604051948591600091600554613d6881612fa3565b906001811690811561415957506001146140f8575b509282613dfb93613daa60059473ffffffffffffffffffffffffffffffffffffffff989751938491612d6f565b017f2e6a736f6e0000000000000000000000000000000000000000000000000000008152037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe5810188520186612f28565b16801561332257600090828252600d6020528273ffffffffffffffffffffffffffffffffffffffff60408420541691827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8115159586614088575b838152600e6020526040812060018154019055848152600d60205260408120847fffffffffffffffffffffffff000000000000000000000000000000000000000082541617905580a450614059578060005260116020526040600020835167ffffffffffffffff8111612edd57613ecd8254612fa3565b601f8111614011575b50602094601f8211600114613f7057613f27929394958291600092613f655750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8260011b9260031b1c19161790565b90555b7ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce76020604051838152a1613f5f60015461335d565b60015590565b015190503880612348565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082169583600052806000209160005b888110613ff957508360019596979810613fc2575b505050811b019055613f2a565b01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88460031b161c19169055388080613fb5565b91926020600181928685015181550194019201613fa0565b826000526020600020601f830160051c8101916020841061404f575b601f0160051c01905b8181106140435750613ed6565b60008155600101614036565b909150819061402d565b7f73c6ac6e00000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b6140c185600052600f60205260406000207fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b828152600e602052604081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8154019055613e56565b9091925060056000527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db06000905b82821061413d575050830160200191906005613d7d565b80546020838c018101919091528a965090910190600101614126565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166020808801919091528215159092028601909101935060059050613d7d565b600101613c7c565b606460029104920191613c75565b61271060049104920191613c6b565b6305f5e10060089104920191613c60565b662386f26fc1000060109104920191613c53565b6d04ee2d6d415b85acef810000000060209104920191613c43565b50604090507a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008304613c29565b9190916000838202917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff858209918380841093039280840393146142d357826103e811156142ab57507fac083126e978d4fdf3b645a1cac083126e978d4fdf3b645a1cac083126e978d593946103e8910990828211900360fd1b910360031c170290565b807f227bc1530000000000000000000000000000000000000000000000000000000060049252fd5b50506103e8909104925050565b9061431f57508051156142f557805190602001fd5b7f1425ea420000000000000000000000000000000000000000000000000000000060005260046000fd5b81511580614374575b614330575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000006000521660045260246000fd5b50803b1561432856fea2646970667358221220846a832f01e3f6a64a95c3c2ca4c72b99c07fec2b1e16017e0d7aa18c8122b8164736f6c634300081a0033a2646970667358221220f611a8b3f3d0e24e98988602ae968dabd85284e81b86e3695c47b26ed36099bb64736f6c634300081a0033000000000000000000000000c946cb236481c159f460b212b34ab246dac37fcd000000000000000000000000000000000000000000000000000000000000000a
Deployed Bytecode
0x608080604052600436101561001d575b50361561001b57600080fd5b005b60003560e01c90816323f6f96814610a6e5750806329ef191914610a2b57806351cff8d9146109735780635d8162b01461090a578063715018a61461086e5780638da5cb5b1461081c578063a1db978214610740578063b915d1dd146106a7578063e04d70bc146103ac578063ec906f7614610274578063ef57e0b4146101a55763f2fde38b146100ae573861000f565b346101a05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a05773ffffffffffffffffffffffffffffffffffffffff6100fa610ae5565b610102610c4c565b1680156101715773ffffffffffffffffffffffffffffffffffffffff600054827fffffffffffffffffffffffff0000000000000000000000000000000000000000821617600055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b7f1e4fbdf700000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b600080fd5b346101a05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a05760043560ff81168091036101a0576101ea610b08565b6101f2610c4c565b6064821161024a5773ffffffffffffffffffffffffffffffffffffffff1660005260026020526040600020907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000825416179055600080f35b7f3cb42c8a0000000000000000000000000000000000000000000000000000000060005260046000fd5b346101a05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a05773ffffffffffffffffffffffffffffffffffffffff6102c0610ae5565b168033146103825733600052600160205273ffffffffffffffffffffffffffffffffffffffff604060002054166103585760407ffe894b43a2eb942442ee61921094be387ddcdc3a20b3ef07188febf719a9eea69133600052600160205281600020817fffffffffffffffffffffffff00000000000000000000000000000000000000008254161790558151903382526020820152a1005b7f336b2c1b0000000000000000000000000000000000000000000000000000000060005260046000fd5b7f8141c6040000000000000000000000000000000000000000000000000000000060005260046000fd5b346101a0576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a05760043567ffffffffffffffff81116101a057366023820112156101a057806004013567ffffffffffffffff81116101a05736602482840101116101a057610421610b08565b60443567ffffffffffffffff81116101a057610441903690600401610ba6565b9060643567ffffffffffffffff81116101a057610462903690600401610ba6565b906084359061ffff82168092036101a05760a43561ffff81168091036101a05760c43567ffffffffffffffff81116101a0576104a2903690600401610ba6565b9060e4359273ffffffffffffffffffffffffffffffffffffffff84168094036101a05773ffffffffffffffffffffffffffffffffffffffff906104e3610c4c565b1692831561067d57801561067d576103e8851161024a576103e8821161024a5760405193614a4d8086019386851067ffffffffffffffff86111761064e57869560e09361057793610c9c89393087526020870152610100604087015261055e61055061010088018d610bed565b87810360608901528b610bed565b9189608088015260a087015285820360c0870152610bed565b92015203906000f091821561064257601f8573ffffffffffffffffffffffffffffffffffffffff9660247faad9ed14e5a3a1445c95acc441b3e0db97c4458cbaf66f3836aa43898be962e89961061b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09661060d60209b6040519d8e9c168c52338d8d015260c060408d015260c08c0190610bed565b908a820360608c0152610bed565b96608089015287870360a089015282875201878601376000868286010152011601030190a1005b6040513d6000823e3d90fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7fe99d5ac50000000000000000000000000000000000000000000000000000000060005260046000fd5b346101a05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a05760043561ffff81168082036101a0576032906106f0610c4c565b1161024a577fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff75ffff00000000000000000000000000000000000000006000549260a01b16911617600055600080f35b346101a05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a0576000602073ffffffffffffffffffffffffffffffffffffffff6064610792610ae5565b61079a610c4c565b60405194859384927f23b872dd0000000000000000000000000000000000000000000000000000000084523060048501523360248501526024356044850152165af18015610642576107e857005b6020813d602011610814575b8161080160209383610b2b565b810103126101a05751801515036101a057005b3d91506107f4565b346101a05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a057602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b346101a05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a0576108a5610c4c565b600073ffffffffffffffffffffffffffffffffffffffff81547fffffffffffffffffffffffff000000000000000000000000000000000000000081168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346101a05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a05773ffffffffffffffffffffffffffffffffffffffff610956610ae5565b166000526002602052602061ffff60406000205416604051908152f35b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a05760008080806109aa610ae5565b6109b2610c4c565b73ffffffffffffffffffffffffffffffffffffffff4791165af13d15610a26573d6109dc81610b6c565b906109ea6040519283610b2b565b8152600060203d92013e5b156109fc57005b7f90b8ec180000000000000000000000000000000000000000000000000000000060005260046000fd5b6109f5565b346101a05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a057602061ffff60005460a01c16604051908152f35b346101a05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a05760209073ffffffffffffffffffffffffffffffffffffffff610abd610ae5565b166000526001825273ffffffffffffffffffffffffffffffffffffffff604060002054168152f35b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101a057565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036101a057565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761064e57604052565b67ffffffffffffffff811161064e57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b81601f820112156101a057803590610bbd82610b6c565b92610bcb6040519485610b2b565b828452602083830101116101a057816000926020809301838601378301015290565b919082519283825260005b848110610c375750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b80602080928401015182828601015201610bf8565b73ffffffffffffffffffffffffffffffffffffffff600054163303610c6d57565b7f118cdaa7000000000000000000000000000000000000000000000000000000006000523360045260246000fdfe60c0604052614a4d8038038061001481610595565b928339810190610100818303126105905761002e816105ba565b9161003b602083016105ba565b60408301519092906001600160401b038111610590578261005d9183016105ce565b60608201519091906001600160401b038111610590578361007f9183016105ce565b9361008c60808301610639565b9461009960a08401610639565b60c084015190956001600160401b038211610590576100bf60e0916100c69387016105ce565b94016105ba565b600160008181559080556002556004805465ffffffffffff60a01b1916780100000001000000000000000000000000000000000000000017905560055490949061010f90610648565b601f811161056d575b50600060055560016006558051906001600160401b0382116103bd578190610141600b54610648565b601f811161051e575b50602090601f83116001146104b6576000926104ab575b50508160011b916000199060031b1c191617600b555b8051906001600160401b0382116103bd578190610195600c54610648565b601f811161045c575b50602090601f83116001146103f4576000926103e9575b50508160011b916000199060031b1c191617600c555b6001600160a01b031680156103d357601280546001600160a01b0319811683179091556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a38051906001600160401b0382116103bd57819061023a600554610648565b601f811161037f575b50602090601f83116001146103175760009261030c575b50508160011b916000199060031b1c1916176005555b6004805460c09390931b61ffff60c01b16600163ffff000160b01b031990931660a09490941b61ffff60a01b1693909317919091176001600160a01b039091161790556080526040516143b3908161069a823960805181818161044701528181610be301528181611ac701528181611daf01528181611ff70152818161227e015281816127d4015281816137210152613890015260a051815050f35b01519050388061025a565b600560009081528281209350601f198516905b818110610367575090846001959493921061034e575b505050811b01600555610270565b015160001960f88460031b161c19169055388080610340565b9293602060018192878601518155019501930161032a565b6103ad9060056000526020600020601f850160051c810191602086106103b3575b601f0160051c0190610682565b38610243565b90915081906103a0565b634e487b7160e01b600052604160045260246000fd5b631e4fbdf760e01b600052600060045260246000fd5b0151905038806101b5565b600c60009081528281209350601f198516905b818110610444575090846001959493921061042b575b505050811b01600c556101cb565b015160001960f88460031b161c1916905538808061041d565b92936020600181928786015181550195019301610407565b600c6000526104a5907fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7601f850160051c810191602086106103b357601f0160051c0190610682565b3861019e565b015190503880610161565b600b60009081528281209350601f198516905b81811061050657509084600195949392106104ed575b505050811b01600b55610177565b015160001960f88460031b161c191690553880806104df565b929360206001819287860151815501950193016104c9565b600b600052610567907f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9601f850160051c810191602086106103b357601f0160051c0190610682565b3861014a565b61058a906005600052601f6020600020910160051c810190610682565b38610118565b600080fd5b6040519190601f01601f191682016001600160401b038111838210176103bd57604052565b51906001600160a01b038216820361059057565b81601f82011215610590578051906001600160401b0382116103bd576105fd601f8301601f1916602001610595565b92828452602083830101116105905760005b82811061062457505060206000918301015290565b8060208092840101518282870101520161060f565b519061ffff8216820361059057565b90600182811c92168015610678575b602083101461066257565b634e487b7160e01b600052602260045260246000fd5b91607f1691610657565b81811061068d575050565b6000815560010161068256fe608080604052600436101561001a575b50361561001857005b005b60003560e01c90816301ffc9a714612c4f5750806306fdde0314612b88578063081812fc14612b1e578063095ea7b314612998578063125ff95d146125bb578063161ba46a146125785780632372243b1461253457806323b872dd1461251d5780632953e714146124915780632cbf8fb9146122a25780633ced31711461223357806342842e0e1461220957806350bd305c14611f8f578063528a28b714611f4b578063629b6307146118ea5780636352211e146118905780636cd3e8301461179f57806370a0823114611705578063715018a61461166757806377d3550b1461162457806386001519146115e85780638da5cb5b1461159657806390e6871a1461154c57806395d89b411461144257806399707f9c146113665780639a06b0b11461131c5780639d76ea58146112ca578063a22cb465146111bd578063abc9104314610f7a578063b88d4fde14610ed3578063bf96f31714610e84578063c594cc6514610de9578063c87b56dd14610d94578063d96a094a14610a75578063e5e03c84146108fa578063e7fad70a14610831578063e985e9c51461079c578063f0e4d60e146102cb5763f2fde38b146101d4573861000f565b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65773ffffffffffffffffffffffffffffffffffffffff610220612dd5565b610228613998565b1680156102975773ffffffffffffffffffffffffffffffffffffffff601254827fffffffffffffffffffffffff0000000000000000000000000000000000000000821617601255167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b7f1e4fbdf700000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b600080fd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65760043580600052600760205260406000209073ffffffffffffffffffffffffffffffffffffffff6040519261032984612ec1565b80548452600260018201549160208601928352015461035b60ff606060408801978685168952019260a01c1682613351565b61036582516136f1565b9061036f81613861565b91969098511633036107725783519760058910156107435760036000991461071b578451600581101561069e576001146106f3578451600581101561069e576004146106cb578451600581101561069e576002148061068b575b61063e578789526007602052600260408a2001740300000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff825416179055888080806104308b61042b8a8a613062565b613062565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af161047161307c565b50156106165786929161ffff8a921615158061060d575b6105b0575b50509061042b6104a8936104a093613062565b60025461306f565b6002555160058110156105835784906002036105795750518060011b908082046002149015171561054c5783808080935b335af16104e461307c565b5015610524577fafe5eb92bc7335536bafdb5ec3cc9545b5c1f06f5f3886690a8d6640084855a9916060916040519182523060208301526040820152a180f35b6004837f90b8ec18000000000000000000000000000000000000000000000000000000008152fd5b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b80808093516104d9565b6024857f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b8180939473ffffffffffffffffffffffffffffffffffffffff8293165af16105d661307c565b50156105e5578490878961048d565b6004877f90b8ec18000000000000000000000000000000000000000000000000000000008152fd5b50831515610488565b6004897f90b8ec18000000000000000000000000000000000000000000000000000000008152fd5b85516040517f42d45f7800000000000000000000000000000000000000000000000000000000815234600482015260248101919091526044810184905260648101859052608490fd5b0390fd5b506106968484613062565b3414156103c9565b60248a7f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b6004897f1afb0ae5000000000000000000000000000000000000000000000000000000008152fd5b6004897fb21ba927000000000000000000000000000000000000000000000000000000008152fd5b6004897f219055cc000000000000000000000000000000000000000000000000000000008152fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7f5ec823510000000000000000000000000000000000000000000000000000000060005260046000fd5b346102c65760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c6576107d3612dd5565b73ffffffffffffffffffffffffffffffffffffffff6107f0612df8565b9116600052601060205273ffffffffffffffffffffffffffffffffffffffff60406000209116600052602052602060ff604060002054166040519015158152f35b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65760043561ffff81168082036102c6576103e89061087b613998565b10156108d0577fffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffff79ffff0000000000000000000000000000000000000000000000006004549260c01b16911617600455600080f35b7f3cb42c8a0000000000000000000000000000000000000000000000000000000060005260046000fd5b346102c65760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65760443560043560243573ffffffffffffffffffffffffffffffffffffffff831683036102c6576064359173ffffffffffffffffffffffffffffffffffffffff831683036102c65773ffffffffffffffffffffffffffffffffffffffff600454163303610a4b578060005260096020526040600020549181600052600a6020526040600020549384821015610a21576109d1826109cb6109d89460409988613b5d565b96613062565b9084613b5d565b916109e282613a5e565b60005260086020528360002081600052602052600060028582208281558260018201550155600052600a60205260008381205582519182526020820152f35b7f9603b4aa0000000000000000000000000000000000000000000000000000000060005260046000fd5b7fcf6b8f780000000000000000000000000000000000000000000000000000000060005260046000fd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65760043560ff60045460b01c16610d6a57806000526007602052604060002090604051610aca81612ec1565b82548152600260018401549360208301948552015473ffffffffffffffffffffffffffffffffffffffff81166040830152610b0f60ff606084019260a01c1682613351565b610b1984516139e7565b82600052600760205260ff60026040600020015460a01c16600581101561074357600114610d40575192600584101561074357600460009414610d1857610bac90838552600760205260026040862001740100000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff825416179055516136f1565b610bb7829392613861565b9087808080610bcc8761042b8a8d9a9d613062565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1610c0d61307c565b5015610cf0579061ffff889216151580610ce7575b610c8d575b5093610c6d7fb72f862d94450c8b8e1ee32c6630970bee0e51cbfaffb8f401e6e312d6ece18094610c626104a08461042b60a0988c98613062565b600255513384613b5d565b60405192835233602084015230604084015260608301526080820152a180f35b81808773ffffffffffffffffffffffffffffffffffffffff8294165af1610cb261307c565b5015610cbf578587610c27565b6004867f90b8ec18000000000000000000000000000000000000000000000000000000008152fd5b50851515610c22565b6004887f90b8ec18000000000000000000000000000000000000000000000000000000008152fd5b6004847f1afb0ae5000000000000000000000000000000000000000000000000000000008152fd5b7f0b3465c20000000000000000000000000000000000000000000000000000000060005260046000fd5b7fd989c4ae0000000000000000000000000000000000000000000000000000000060005260046000fd5b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657610de5610dd1600435613564565b604051918291602083526020830190612d92565b0390f35b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65760043561ffff81168082036102c6576103e890610e33613998565b10156108d0577fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff75ffff00000000000000000000000000000000000000006004549260a01b16911617600455600080f35b346102c657610e9236612e8d565b9060005260086020526040600020906000526020526060604060002080549060ff600260018301549201541690604051928352602083015215156040820152f35b346102c65760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657610f0a612dd5565b610f12612df8565b6064359167ffffffffffffffff83116102c657366023840112156102c657826004013591610f3f83612f69565b92610f4d6040519485612f28565b80845236602482870101116102c657602081600092602461001898018388013785010152604435916133a2565b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65760043580600052600760205260406000209073ffffffffffffffffffffffffffffffffffffffff600260405193610fdf85612ec1565b805485526001810154602086015201549261100d60ff606060408401938588168552019560a01c1685613351565b511633036107725781519160058310156107435760036000931461119557805160058110156111685760041461114057516005811015611113577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff016110eb576040817f9eb7657bef9907ba1467bb58e6f3b70349f0ee413302ab895acbc8bc225e4b219284526007602052600282852001740400000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff8254161790558151908152306020820152a180f35b6004827fdd980a31000000000000000000000000000000000000000000000000000000008152fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b6004837f1afb0ae5000000000000000000000000000000000000000000000000000000008152fd5b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b6004837f219055cc000000000000000000000000000000000000000000000000000000008152fd5b346102c65760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c6576111f4612dd5565b60243590811515908183036102c65773ffffffffffffffffffffffffffffffffffffffff1691821561129c5761126e9033600052601060205260406000208460005260205260406000209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b6040519081527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b827f5b08ba180000000000000000000000000000000000000000000000000000000060005260045260246000fd5b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657602073ffffffffffffffffffffffffffffffffffffffff60035416604051908152f35b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657600435600052600a6020526020604060002054604051908152f35b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65761139d613998565b60045460ff8160b01c1615611418577fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff770100000000000000000000000000000000000000000000009116176004557f206f7aa88b4936d5a9b98bed26f5f0d5e12c04b26ae217ebdb3a623a86a55f596020604051308152a1005b7fc79a5b5b0000000000000000000000000000000000000000000000000000000060005260046000fd5b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c6576040516000600c5461148281612fa3565b808452906001811690811561150a57506001146114aa575b610de583610dd181850382612f28565b919050600c6000527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7916000905b8082106114f057509091508101602001610dd161149a565b9192600181602092548385880101520191019092916114d8565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208086019190915291151560051b84019091019150610dd1905061149a565b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65760043560005260096020526020604060002054604051908152f35b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657602073ffffffffffffffffffffffffffffffffffffffff60125416604051908152f35b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c6576020600254604051908152f35b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657602061ffff60045460a01c16604051908152f35b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65761169e613998565b600073ffffffffffffffffffffffffffffffffffffffff6012547fffffffffffffffffffffffff00000000000000000000000000000000000000008116601255167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65773ffffffffffffffffffffffffffffffffffffffff611751612dd5565b16801561177057600052600e6020526020604060002054604051908152f35b7f89c62b6400000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b346102c65760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65773ffffffffffffffffffffffffffffffffffffffff6117eb612dd5565b6117f3613998565b167fffffffffffffffffffffffff000000000000000000000000000000000000000060035416176003556024356006557601000000000000000000000000000000000000000000007fffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffff60045416176004557f3b11d3e657d8d514329702c63d255f4697baf18e6410e1f9072c93c731eb70326020604051308152a1005b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65760206118cc60043561364e565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b6118f336612e8d565b906118fd8261364e565b918160005260076020526040600020926040519261191a84612ec1565b8454845260026001860154956020860196875201549473ffffffffffffffffffffffffffffffffffffffff8616604086015261196060ff606087019760a01c1687613351565b81600052600860205260406000208460005260205260406000209060405161198781612f0c565b82548152604060ff6002600186015495602085019687520154161515910152855115611f1d5773ffffffffffffffffffffffffffffffffffffffff841690338203611eef5787519660058810156107435760016000981480611edf575b611eb7576119f187613a5e565b8488526008602052604088208789526020526002604089200160017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055835190606482029180830460641490151715611e8a5751908115611e5d5791611a619160649304905161304f565b0496516005811015611e3057600203611d6757611a7d876136f1565b90611a8781613861565b93909192839b611a978383613062565b3403611d1f57508a80611ab08661042b83968496613062565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1611af161307c565b5015610616579061ffff89939216151580611d16575b611ce2575b50505073ffffffffffffffffffffffffffffffffffffffff60035416906040517f313ce567000000000000000000000000000000000000000000000000000000008152602081600481865afa908115611cd757611b82602093926044928b91611caa575b50611b7c87519161300f565b9061304f565b918960405195869485937fa9059cbb000000000000000000000000000000000000000000000000000000008552600485015260248401525af1908115611c9f578691611c70575b5015611c485794611c42917f7368bfddf54437295c38d71448fd337c832ab763b8d669fa154f59a111c5640a95965b5160408051928352602083019190915281019490945273ffffffffffffffffffffffffffffffffffffffff909216606084015230608084015260a0830191909152819060c0820190565b0390a180f35b6004857f90b8ec18000000000000000000000000000000000000000000000000000000008152fd5b611c92915060203d602011611c98575b611c8a8183612f28565b81019061338a565b87611bc9565b503d611c80565b6040513d88823e3d90fd5b611cca9150853d8711611cd0575b611cc28183612f28565b810190612ff6565b8c611b70565b503d611cb8565b6040513d8a823e3d90fd5b8280929173ffffffffffffffffffffffffffffffffffffffff8293165af1611d0861307c565b5015610cbf57858880611b0c565b50801515611b07565b6040517f42d45f780000000000000000000000000000000000000000000000000000000081523460048201526024810191909152604481019190915260648101829052608490fd5b6103e8611d7d61ffff60045460c01c168961304f565b04611d8781613861565b50509787808080611d988d87613062565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1611dd961307c565b5015610cf057878093611df2829461042b85859661306f565b905af1611dfd61307c565b5015611c485794611c42917f7368bfddf54437295c38d71448fd337c832ab763b8d669fa154f59a111c5640a9596611bf8565b6024877f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b6024897f4e487b710000000000000000000000000000000000000000000000000000000081526012600452fd5b6024897f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b6004887f7361fc0c000000000000000000000000000000000000000000000000000000008152fd5b5060ff60045460b81c16156119e4565b857fd64dfb010000000000000000000000000000000000000000000000000000000060005260045260246000fd5b827fc87c16410000000000000000000000000000000000000000000000000000000060005260045260246000fd5b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657602060ff60045460b81c166040519015158152f35b611f9836612e8d565b9060ff60045460b01c16610d6a57633b9aca0081116121d757611fe08261042b6000808080611fc6866136f1565b979061042b611fd482613861565b99829c9294919b6139e7565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af161202161307c565b50156121a45761ffff8392161515806121ce575b612170575b505060405161204881612ec1565b8281526020810184815273ffffffffffffffffffffffffffffffffffffffff8060026040850194338652606081019460008652600054600052600760205260406000209151825551600182015501935116167fffffffffffffffffffffffff0000000000000000000000000000000000000000835416178255519360058510156107435781547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1660a095861b74ff00000000000000000000000000000000000000001617909155600080546040805182815260208101969096528501929092523360608501523060808501529383019190915261216b917f8fd77df48aedff028372940389f8d7b6c07528858c2f7cc94e3c5ccd9fe2b2239060c090a161335d565b815580f35b600080809373ffffffffffffffffffffffffffffffffffffffff8294165af161219761307c565b50156121a457808461203a565b7f90b8ec180000000000000000000000000000000000000000000000000000000060005260046000fd5b50811515612035565b7f4e42c81400000000000000000000000000000000000000000000000000000000600052633b9aca0060045260246000fd5b346102c65761001861221a36612e1b565b906040519261222a602085612f28565b600084526133a2565b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65760043567ffffffffffffffff81116102c657366023820112156102c657806004013567ffffffffffffffff81116102c65736602482840101116102c657612316613998565b600090612324600554612fa3565b601f811161244b575b5081601f821160011461238e578190839461237a9492612380575b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8260011b9260031b1c19161790565b60055580f35b602492500101358480612348565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08216937f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db091845b86811061243057508360019596106123f5575b505050811b0160055580f35b01602401357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600384901b60f8161c191690558380806123e9565b909260206001819260248787010135815501940191016123d6565b6005835260208320601f830160051c81019160208410612487575b601f0160051c01905b81811061247c575061232d565b83815560010161246f565b9091508190612466565b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65760043560005260076020526040600020805473ffffffffffffffffffffffffffffffffffffffff6002600184015493015460ff8160a01c169360405193845260208401521660408201526005821015610743576080916060820152f35b346102c65761001861252e36612e1b565b916130ac565b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657602060ff60045460b01c166040519015158152f35b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657602061ffff60045460c01c16604051908152f35b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c65760043580600052600760205260406000209060ff60045460b81c1661296e57815415612941576002820160ff815460a01c166005811015610743576001036129175773ffffffffffffffffffffffffffffffffffffffff6003541692600181019161265083546136f1565b61265b829392613861565b9491989093740200000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff8254161790556004602073ffffffffffffffffffffffffffffffffffffffff60035416604051928380927f313ce5670000000000000000000000000000000000000000000000000000000082525afa90811561290b57600061270b61276b93829383916128ec575b50611b7c8b549161300f565b60405160208101917f23b872dd000000000000000000000000000000000000000000000000000000008352336024830152306044830152606482015260648152612756608482612f28565b519082865af161276461307c565b90836142e0565b80519081151591826128d1575b50506128a457509061278991613062565b600080808061279d8561042b8b548061306f565b335af16127a861307c565b50156121a457600080806127bd8a8295613062565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af16127fe61307c565b50156121a45761ffff1615158061289b575b612868575b505490546040805193845260208401929092529082015233606082015230608082015260a08101919091527ffa9aad700273c94161651bdca5c167b4d924f83f2e832d88a861debed12438619060c090a1005b600080808773ffffffffffffffffffffffffffffffffffffffff8295165af161288f61307c565b50156121a45784612815565b50841515612810565b7f5274afe70000000000000000000000000000000000000000000000000000000060005260045260246000fd5b6128e4925060208091830101910161338a565b158a80612778565b612905915060203d602011611cd057611cc28183612f28565b8e6126ff565b6040513d6000823e3d90fd5b7fdd980a310000000000000000000000000000000000000000000000000000000060005260046000fd5b7fc87c16410000000000000000000000000000000000000000000000000000000060005260045260246000fd5b7fb21ba9270000000000000000000000000000000000000000000000000000000060005260046000fd5b346102c65760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c6576129cf612dd5565b6024356129db8161364e565b33151580612afe575b80612aad575b612a7f57819073ffffffffffffffffffffffffffffffffffffffff80851691167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600080a4600052600f60205273ffffffffffffffffffffffffffffffffffffffff604060002091167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055600080f35b7fa9fbf51f000000000000000000000000000000000000000000000000000000006000523360045260246000fd5b5073ffffffffffffffffffffffffffffffffffffffff81166000526010602052604060002073ffffffffffffffffffffffffffffffffffffffff331660005260205260ff60406000205416156129ea565b503373ffffffffffffffffffffffffffffffffffffffff821614156129e4565b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657600435612b598161364e565b50600052600f602052602073ffffffffffffffffffffffffffffffffffffffff60406000205416604051908152f35b346102c65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c6576040516000600b54612bc881612fa3565b808452906001811690811561150a5750600114612bef57610de583610dd181850382612f28565b919050600b6000527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9916000905b808210612c3557509091508101602001610dd161149a565b919260018160209254838588010152019101909291612c1d565b346102c65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c657600435907fffffffff0000000000000000000000000000000000000000000000000000000082168092036102c657817f490649060000000000000000000000000000000000000000000000000000000060209314908115612ce1575b5015158152f35b7f80ac58cd00000000000000000000000000000000000000000000000000000000811491508115612d45575b8115612d1b575b5083612cda565b7f01ffc9a70000000000000000000000000000000000000000000000000000000091501483612d14565b7f5b5e139f0000000000000000000000000000000000000000000000000000000081149150612d0d565b60005b838110612d825750506000910152565b8181015183820152602001612d72565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093612dce81518092818752878088019101612d6f565b0116010190565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036102c657565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036102c657565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126102c65760043573ffffffffffffffffffffffffffffffffffffffff811681036102c6579060243573ffffffffffffffffffffffffffffffffffffffff811681036102c6579060443590565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60409101126102c6576004359060243590565b6080810190811067ffffffffffffffff821117612edd57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6060810190811067ffffffffffffffff821117612edd57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117612edd57604052565b67ffffffffffffffff8111612edd57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b90600182811c92168015612fec575b6020831014612fbd57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691612fb2565b908160209103126102c6575160ff811681036102c65790565b60ff16604d811161302057600a0a90565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181029291811591840414171561302057565b9190820391821161302057565b9190820180921161302057565b3d156130a7573d9061308d82612f69565b9161309b6040519384612f28565b82523d6000602084013e565b606090565b919073ffffffffffffffffffffffffffffffffffffffff1691821561332257600090828252600d6020528273ffffffffffffffffffffffffffffffffffffffff604084205416948533151580613234575b507fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef73ffffffffffffffffffffffffffffffffffffffff95826131c4575b838152600e6020526040812060018154019055848152600d60205260408120847fffffffffffffffffffffffff000000000000000000000000000000000000000082541617905580a41680830361319157505050565b7f64283d7b0000000000000000000000000000000000000000000000000000000060005260045260245260445260646000fd5b6131fd85600052600f60205260406000207fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b828152600e602052604081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff815401905561313b565b919394925050806132b1575b15613250579190839085386130fd565b839085613282576024917f7e273289000000000000000000000000000000000000000000000000000000008252600452fd5b6044917f177e802f00000000000000000000000000000000000000000000000000000000825233600452602452fd5b5033851480156132ec575b806132405750838152600f6020523373ffffffffffffffffffffffffffffffffffffffff60408320541614613240565b5084815260106020526040812073ffffffffffffffffffffffffffffffffffffffff3316825260205260ff6040822054166132bc565b7f64a0ae9200000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b60058210156107435752565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146130205760010190565b908160209103126102c6575180151581036102c65790565b906133ae8382846130ac565b803b6133bb575b50505050565b60209161342773ffffffffffffffffffffffffffffffffffffffff8093169560405195869485947f150b7a020000000000000000000000000000000000000000000000000000000086523360048701521660248501526044840152608060648401526084830190612d92565b03816000865af18091600091613502575b5090613482575061344761307c565b8051908161347d57827f64a0ae920000000000000000000000000000000000000000000000000000000060005260045260246000fd5b602001fd5b7fffffffff000000000000000000000000000000000000000000000000000000007f150b7a02000000000000000000000000000000000000000000000000000000009116036134d55750388080806133b5565b7f64a0ae920000000000000000000000000000000000000000000000000000000060005260045260246000fd5b6020813d60201161355c575b8161351b60209383612f28565b810103126135585751907fffffffff0000000000000000000000000000000000000000000000000000000082168203613555575038613438565b80fd5b5080fd5b3d915061350e565b61356d8161364e565b50600052601160205260406000206040519081600082549261358e84612fa3565b808452936001811690811561360e57506001146135c7575b506135b392500382612f28565b60006040516135c3602082612f28565b5290565b90506000929192526020600020906000915b8183106135f25750509060206135b392820101386135a6565b60209193508060019154838588010152019101909183926135d9565b602093506135b39592507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0091501682840152151560051b820101386135a6565b80600052600d60205273ffffffffffffffffffffffffffffffffffffffff6040600020541690811561367e575090565b7f7e2732890000000000000000000000000000000000000000000000000000000060005260045260246000fd5b908160209103126102c6575161ffff811681036102c65790565b908160209103126102c6575173ffffffffffffffffffffffffffffffffffffffff811681036102c65790565b6137086103e89161ffff60045460a01c169061304f565b049073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016604051907f29ef1919000000000000000000000000000000000000000000000000000000008252602082600481845afa91821561290b5760009261382c575b506020602491604051928380927f23f6f9680000000000000000000000000000000000000000000000000000000082523360048301525afa801561290b5773ffffffffffffffffffffffffffffffffffffffff916000916137fd575b5016156137f7576137f361ffff606492168461304f565b0490565b50600090565b61381f915060203d602011613825575b6138178183612f28565b8101906136c5565b386137dc565b503d61380d565b6024919250613852602091823d841161385a575b61384a8183612f28565b8101906136ab565b929150613780565b503d613840565b6040517f23f6f968000000000000000000000000000000000000000000000000000000008152336004820152917f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1691602084602481865afa92831561290b57602494600094613976575b50602090604051958680927f5d8162b000000000000000000000000000000000000000000000000000000000825273ffffffffffffffffffffffffffffffffffffffff881660048301525afa93841561290b57600094613951575b5061394c9061ffff851690614227565b929190565b61394c91945061396f9060203d60201161385a5761384a8183612f28565b939061393c565b602091945061399190823d8411613825576138178183612f28565b93906138e1565b73ffffffffffffffffffffffffffffffffffffffff6012541633036139b957565b7f118cdaa7000000000000000000000000000000000000000000000000000000006000523360045260246000fd5b6139f0816136f1565b613a056139ff82849594613062565b8361306f565b3403613a1057505050565b610687906040519384937f42d45f7800000000000000000000000000000000000000000000000000000000855234600486019094939260609260808301968352602083015260408201520152565b6000818152600d60205273ffffffffffffffffffffffffffffffffffffffff604082205416828115928315613aed575b818152600d602052604081207fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8280a45061367e5750565b613b2682600052600f60205260406000207fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b828152600e602052604081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8154019055613a8e565b90929160015491613be1604051613b7381612f0c565b84815260026020820185815260408301906000825285600052600860205260406000208860005260205260406000209351845551600184015551151591019060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b82600052600960205260406000205581600052600a6020526040600020558092816000907a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008410156141ff575b806d04ee2d6d415b85acef8100000000600a9210156141e4575b662386f26fc100008110156141d0575b6305f5e1008110156141bf575b6127108110156141b0575b60648110156141a2575b101561419a575b6001810194600a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6021613cc8613cb28a612f69565b99613cc06040519b8c612f28565b808b52612f69565b947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe060208b0196013687378901015b01917f30313233343536373839616263646566000000000000000000000000000000008282061a8353048015613d51577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600a9192613cf7565b5050604051948591600091600554613d6881612fa3565b906001811690811561415957506001146140f8575b509282613dfb93613daa60059473ffffffffffffffffffffffffffffffffffffffff989751938491612d6f565b017f2e6a736f6e0000000000000000000000000000000000000000000000000000008152037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe5810188520186612f28565b16801561332257600090828252600d6020528273ffffffffffffffffffffffffffffffffffffffff60408420541691827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8115159586614088575b838152600e6020526040812060018154019055848152600d60205260408120847fffffffffffffffffffffffff000000000000000000000000000000000000000082541617905580a450614059578060005260116020526040600020835167ffffffffffffffff8111612edd57613ecd8254612fa3565b601f8111614011575b50602094601f8211600114613f7057613f27929394958291600092613f655750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8260011b9260031b1c19161790565b90555b7ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce76020604051838152a1613f5f60015461335d565b60015590565b015190503880612348565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082169583600052806000209160005b888110613ff957508360019596979810613fc2575b505050811b019055613f2a565b01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88460031b161c19169055388080613fb5565b91926020600181928685015181550194019201613fa0565b826000526020600020601f830160051c8101916020841061404f575b601f0160051c01905b8181106140435750613ed6565b60008155600101614036565b909150819061402d565b7f73c6ac6e00000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b6140c185600052600f60205260406000207fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b828152600e602052604081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8154019055613e56565b9091925060056000527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db06000905b82821061413d575050830160200191906005613d7d565b80546020838c018101919091528a965090910190600101614126565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166020808801919091528215159092028601909101935060059050613d7d565b600101613c7c565b606460029104920191613c75565b61271060049104920191613c6b565b6305f5e10060089104920191613c60565b662386f26fc1000060109104920191613c53565b6d04ee2d6d415b85acef810000000060209104920191613c43565b50604090507a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008304613c29565b9190916000838202917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff858209918380841093039280840393146142d357826103e811156142ab57507fac083126e978d4fdf3b645a1cac083126e978d4fdf3b645a1cac083126e978d593946103e8910990828211900360fd1b910360031c170290565b807f227bc1530000000000000000000000000000000000000000000000000000000060049252fd5b50506103e8909104925050565b9061431f57508051156142f557805190602001fd5b7f1425ea420000000000000000000000000000000000000000000000000000000060005260046000fd5b81511580614374575b614330575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000006000521660045260246000fd5b50803b1561432856fea2646970667358221220846a832f01e3f6a64a95c3c2ca4c72b99c07fec2b1e16017e0d7aa18c8122b8164736f6c634300081a0033a2646970667358221220f611a8b3f3d0e24e98988602ae968dabd85284e81b86e3695c47b26ed36099bb64736f6c634300081a0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000c946cb236481c159f460b212b34ab246dac37fcd000000000000000000000000000000000000000000000000000000000000000a
-----Decoded View---------------
Arg [0] : initialOwner (address): 0xC946cB236481C159F460b212b34AB246daC37FcD
Arg [1] : _discountPercent (uint16): 10
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000c946cb236481c159f460b212b34ab246dac37fcd
Arg [1] : 000000000000000000000000000000000000000000000000000000000000000a
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.