Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
This contract has been partially verified via Sourcify.
- Contract name:
- GPADFactory
- Optimization enabled
- true
- Compiler version
- v0.8.18+commit.87f61d96
- Optimization runs
- 200
- EVM Version
- london
- Verified at
- 2025-04-12T07:35:57.786311Z
Constructor Arguments
0x000000000000000000000000abccefb00528c9c792ac7c46997f0f6ee5dcdddd0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000032
Arg [0] (address) : 0xabccefb00528c9c792ac7c46997f0f6ee5dcdddd
Arg [1] (uint256) : 1000000000000000000
Arg [2] (uint256) : 50
contracts/GPAD/GPADFactory.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./GPADPool.sol";
contract GPADFactory is Ownable {
using SafeMath for uint256;
using SafeERC20 for ERC20Burnable;
using SafeERC20 for ERC20;
using SafeERC20 for IERC20;
// === State Variables ===
ERC20Burnable public feeToken;
address public feeWallet;
uint256 public feeAmount;
uint256 public burnPercent; // Use this state only if your token is ERC20Burnable and has burnFrom method
uint256 public divider;
address[] public idoPools;
// === Error Definitions ===
error InvalidAddress(string message);
// === Events ===
event IDOCreated(
address indexed owner,
address gpadPool,
address indexed idoToken,
string tokenURI
);
event IDOCreatedByHelper(
address indexed owner,
address gpadPool,
address indexed idoToken,
string tokenURI,
uint256 idoTokenAmount, // IDO token exact amount
uint256 lpTokenAmount // LP token exact amount
);
event TokenFeeUpdated(address newFeeToken);
event FeeAmountUpdated(uint256 newFeeAmount);
event BurnPercentUpdated(uint256 newBurnPercent, uint256 divider);
event FeeWalletUpdated(address newFeeWallet);
event GPADPoolInitialized(
address indexed gpadPool,
address indexed idoToken,
GPADPool.FinInfo finInfo,
GPADPool.Timestamps timestamps,
GPADPool.DEXInfo dexInfo,
string metadataURL
);
/**
* @dev Constructor
* @param _feeToken Token used for fee payment
* @param _feeAmount Amount of tokens to charge as fee
* @param _burnPercent Percentage of fee tokens to burn
*/
constructor(
ERC20Burnable _feeToken,
uint256 _feeAmount,
uint256 _burnPercent
){
feeToken = _feeToken;
feeAmount = _feeAmount;
burnPercent = _burnPercent;
divider = 100;
}
/**
* @dev Get all IDO pools created by this factory
* @return Array of pool addresses
*/
function getIdoPools() public view returns (address[] memory) {
return idoPools;
}
/**
* @dev Set new fee token
* @param _newFeeToken Address of new fee token
*/
function setFeeToken(address _newFeeToken) external onlyOwner {
_validateContractAddress(_newFeeToken, "New address is not a token");
feeToken = ERC20Burnable(_newFeeToken);
emit TokenFeeUpdated(_newFeeToken);
}
/**
* @dev Set new fee amount
* @param _newFeeAmount New fee amount to charge
*/
function setFeeAmount(uint256 _newFeeAmount) external onlyOwner {
feeAmount = _newFeeAmount;
emit FeeAmountUpdated(_newFeeAmount);
}
/**
* @dev Set new fee wallet
* @param _newFeeWallet Address of new fee wallet
*/
function setFeeWallet(address _newFeeWallet) external onlyOwner {
feeWallet = _newFeeWallet;
emit FeeWalletUpdated(_newFeeWallet);
}
/**
* @dev Set new burn percentage
* @param _newBurnPercent New percentage of fee to burn
* @param _newDivider New divider for percentage calculation
*/
function setBurnPercent(uint256 _newBurnPercent, uint256 _newDivider)
external
onlyOwner
{
require(_newBurnPercent <= _newDivider, "Burn percent must be less than divider");
burnPercent = _newBurnPercent;
divider = _newDivider;
emit BurnPercentUpdated(_newBurnPercent, _newDivider);
}
/**
* @dev Handle fee collection and burning
*/
function handleFees() internal {
if(feeAmount > 0) {
if (burnPercent > 0) {
uint256 burnAmount = feeAmount.mul(burnPercent).div(divider);
uint256 transferAmount = feeAmount.sub(burnAmount);
if (transferAmount > 0) {
feeToken.safeTransferFrom(msg.sender, feeWallet, transferAmount);
}
if (burnAmount > 0) {
feeToken.burnFrom(msg.sender, burnAmount);
}
} else {
feeToken.safeTransferFrom(msg.sender, feeWallet, feeAmount);
}
}
}
/**
* @dev Create a new GPAD Pool
* @param _idoToken Token to be distributed in the IDO
* @param _finInfo Financial parameters of the IDO (note: lpInterestRate will be ignored)
* @param _timestamps Timestamp parameters (start and end)
* @param _dexInfo DEX configuration information
* @param _metadataURL URL to IDO metadata
* @param idoTokenAmount Amount of tokens allocated for IDO
* @param lpTokenAmount Amount of tokens allocated for liquidity
* @return gpadPoolAddress Address of created pool
*/
function createIDOPool(
ERC20 _idoToken,
GPADPool.FinInfo memory _finInfo,
GPADPool.Timestamps memory _timestamps,
GPADPool.DEXInfo memory _dexInfo,
string memory _metadataURL,
uint256 idoTokenAmount,
uint256 lpTokenAmount
) external returns (address gpadPoolAddress) {
// Note: lpInterestRate in _finInfo will be ignored as all ETH goes to liquidity
GPADPool gpadPool = new GPADPool(
_idoToken,
_finInfo,
_timestamps,
_dexInfo,
_metadataURL
);
// Trigger initialized event
emit GPADPoolInitialized(
address(gpadPool),
address(_idoToken),
_finInfo,
_timestamps,
_dexInfo,
_metadataURL
);
// Use exact token amount
uint256 transferAmount = idoTokenAmount + lpTokenAmount;
// Transfer ownership
gpadPool.transferOwnership(msg.sender);
// Set pool address
gpadPoolAddress = address(gpadPool);
// Transfer token to pool
_idoToken.safeTransferFrom(
msg.sender,
gpadPoolAddress,
transferAmount
);
// Record pool address
idoPools.push(gpadPoolAddress);
// Trigger event
emit IDOCreatedByHelper(
msg.sender,
gpadPoolAddress,
address(_idoToken),
_metadataURL,
idoTokenAmount,
lpTokenAmount
);
handleFees();
return gpadPoolAddress;
}
/**
* @dev Calculate token amount based on ETH amount and token price
* @param ethAmount Amount of ETH
* @param tokenPrice Price of token in ETH
* @param decimals Token decimals
* @return Token amount
*/
function getTokenAmount(
uint256 ethAmount,
uint256 tokenPrice,
uint8 decimals
) internal pure returns (uint256) {
return (ethAmount / tokenPrice) * (10**decimals);
}
/**
* @dev Validate if address is a contract
* @param _addr Address to check
* @param errorMsg Error message to return if validation fails
*/
function _validateContractAddress(address _addr, string memory errorMsg) private view {
if (_addr == address(0)) revert InvalidAddress("Zero address");
if (!isContract(_addr)) revert InvalidAddress(errorMsg);
}
/**
* @dev Check if address contains contract code
* @param _addr Address to check
* @return Whether address is a contract
*/
function isContract(address _addr) private view returns (bool) {
uint32 size;
assembly {
size := extcodesize(_addr)
}
return (size > 0);
}
/**
* @dev Recover any tokens or ETH sent to this contract
* @param _tokenAddress Address of token to recover (zero address for ETH)
* @param _amount Amount to recover (0 for all available balance)
*/
function recoverFunds(address _tokenAddress, uint256 _amount) external onlyOwner {
if (_tokenAddress == address(0)) {
// Recover ETH
uint256 balance = address(this).balance;
uint256 amountToRecover = _amount == 0 ? balance : (_amount < balance ? _amount : balance);
if (amountToRecover > 0) {
(bool success,) = payable(owner()).call{value: amountToRecover}("");
require(success, "ETH transfer failed");
}
} else {
// Recover tokens
IERC20 token = IERC20(_tokenAddress);
uint256 balance = token.balanceOf(address(this));
uint256 amountToRecover = _amount == 0 ? balance : (_amount < balance ? _amount : balance);
if (amountToRecover > 0) {
token.safeTransfer(owner(), amountToRecover);
}
}
}
/**
* @dev Receive ETH
*/
receive() external payable {}
}
@openzeppelin/contracts/access/Ownable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../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.
*
* By default, the owner account will be the one that deploys the contract. 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;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_setOwner(_msgSender());
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_setOwner(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 {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_setOwner(newOwner);
}
function _setOwner(address newOwner) private {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
@openzeppelin/contracts/security/ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
@openzeppelin/contracts/token/ERC20/ERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* The default value of {decimals} is 18. To select a different value for
* {decimals} you should overload it.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless this function is
* overridden;
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* Requirements:
*
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for ``sender``'s tokens of at least
* `amount`.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
uint256 currentAllowance = _allowances[sender][_msgSender()];
require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
unchecked {
_approve(sender, _msgSender(), currentAllowance - amount);
}
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
uint256 currentAllowance = _allowances[_msgSender()][spender];
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(_msgSender(), spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `sender` to `recipient`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(
address sender,
address recipient,
uint256 amount
) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
uint256 senderBalance = _balances[sender];
require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[sender] = senderBalance - amount;
}
_balances[recipient] += amount;
emit Transfer(sender, recipient, amount);
_afterTokenTransfer(sender, recipient, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
_balances[account] += amount;
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
}
_totalSupply -= amount;
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
}
@openzeppelin/contracts/token/ERC20/IERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) 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 `amount` 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 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @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);
}
@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../ERC20.sol";
import "../../../utils/Context.sol";
/**
* @dev Extension of {ERC20} that allows token holders to destroy both their own
* tokens and those that they have an allowance for, in a way that can be
* recognized off-chain (via event analysis).
*/
abstract contract ERC20Burnable is Context, ERC20 {
/**
* @dev Destroys `amount` tokens from the caller.
*
* See {ERC20-_burn}.
*/
function burn(uint256 amount) public virtual {
_burn(_msgSender(), amount);
}
/**
* @dev Destroys `amount` tokens from `account`, deducting from the caller's
* allowance.
*
* See {ERC20-_burn} and {ERC20-allowance}.
*
* Requirements:
*
* - the caller must have allowance for ``accounts``'s tokens of at least
* `amount`.
*/
function burnFrom(address account, uint256 amount) public virtual {
uint256 currentAllowance = allowance(account, _msgSender());
require(currentAllowance >= amount, "ERC20: burn amount exceeds allowance");
unchecked {
_approve(account, _msgSender(), currentAllowance - amount);
}
_burn(account, amount);
}
}
@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
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);
}
@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../../../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;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
/**
* @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, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
@openzeppelin/contracts/utils/Address.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
/**
* @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://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @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, it is bubbled up by this
* function (like regular Solidity function calls).
*
* 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.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @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`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
@openzeppelin/contracts/utils/Context.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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;
}
}
@openzeppelin/contracts/utils/math/Math.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @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 up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a / b + (a % b == 0 ? 0 : 1);
}
}
@openzeppelin/contracts/utils/math/SafeMath.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.
/**
* @dev Wrappers over Solidity's arithmetic operations.
*
* NOTE: `SafeMath` is no longer needed starting with Solidity 0.8. The compiler
* now has built in overflow checking.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
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 substraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
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.
*
* _Available since v3.4._
*/
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.
*
* _Available since v3.4._
*/
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.
*
* _Available since v3.4._
*/
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 addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
return a + b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
return a * b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator.
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b <= a, errorMessage);
return a - b;
}
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b > 0, errorMessage);
return a / b;
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b > 0, errorMessage);
return a % b;
}
}
}
contracts/GPAD/GPADPool.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./IUniswapV2Router02.sol";
import "./IUniswapV2Factory.sol";
contract GPADPool is Ownable, ReentrancyGuard {
using SafeMath for uint256;
using SafeERC20 for ERC20;
using SafeERC20 for IERC20;
// Fixed burn address for LP tokens
address private constant BURN_ADDRESS = 0x000000000000000000000000000000000000dEaD;
struct FinInfo {
uint256 tokenPrice; // Price of one token in WEI for IDO
uint256 softCap; // Minimum ETH amount to consider IDO successful
uint256 hardCap; // Maximum ETH amount to raise in IDO
uint256 minEthPayment; // Minimum ETH amount per user
uint256 maxEthPayment; // Maximum ETH amount per user
uint256 listingPrice; // Price of one token in WEI for listing
}
struct Timestamps {
uint256 startTimestamp; // When IDO starts
uint256 endTimestamp; // When IDO ends
}
struct DEXInfo {
address router; // DEX router address
address factory; // DEX factory address
address weth; // WETH token address
}
struct UserInfo {
uint debt; // Tokens owed to user
uint total; // Total tokens allocated to user
uint totalInvestedETH; // Total ETH invested by user
}
ERC20 public idoToken; // Token being sold in IDO
uint256 public decimals; // Token decimals
string public metadataURL; // Project metadata URL
FinInfo public finInfo; // Financial parameters
Timestamps public timestamps; // Time parameters
DEXInfo public dexInfo; // DEX parameters
uint256 public totalInvestedETH; // Total ETH raised
uint256 public tokensForDistribution; // Total tokens for users
uint256 public distributedTokens; // Tokens already claimed
bool public distributed = false; // Whether liquidity has been added
mapping(address => UserInfo) public userInfo; // User participation info
// Events
event TokensDebt(
address indexed holder,
uint256 ethAmount,
uint256 tokenAmount
);
event TokensWithdrawn(address indexed holder, uint256 amount);
event LPBurned(
address indexed token,
address indexed pairedToken,
uint256 ethAmount,
uint256 tokenAmount,
address burnAddress,
uint256 liquidity
);
event EmergencyWithdraw(address indexed recipient, uint256 amount);
/**
* @dev Constructor - simplified parameters
* @param _idoToken Token to be distributed in the IDO
* @param _finInfo Financial parameters of the IDO
* @param _timestamps Timestamp parameters for IDO
* @param _dexInfo DEX configuration information
* @param _metadataURL URL to IDO metadata
*/
constructor(
ERC20 _idoToken,
FinInfo memory _finInfo,
Timestamps memory _timestamps,
DEXInfo memory _dexInfo,
string memory _metadataURL
) {
idoToken = _idoToken;
decimals = idoToken.decimals();
finInfo = _finInfo;
setTimestamps(_timestamps);
dexInfo = _dexInfo;
setMetadataURL(_metadataURL);
}
/**
* @dev Set timestamps for the IDO pool
* @param _timestamps Timestamp structure with start and end times
*/
function setTimestamps(Timestamps memory _timestamps) internal {
require(
_timestamps.startTimestamp < _timestamps.endTimestamp,
"Start timestamp must be less than finish timestamp"
);
require(
_timestamps.endTimestamp > block.timestamp,
"Finish timestamp must be more than current block"
);
timestamps = _timestamps;
}
/**
* @dev Set metadata URL for the IDO
* @param _metadataURL URL to IDO metadata
*/
function setMetadataURL(string memory _metadataURL) public {
metadataURL = _metadataURL;
}
/**
* @dev Allow users to invest ETH in the IDO
*/
function pay() payable external {
// Control participation state only by time
require(block.timestamp >= timestamps.startTimestamp, "Not started");
require(block.timestamp < timestamps.endTimestamp, "Ended");
require(msg.value >= finInfo.minEthPayment, "Less than min amount");
require(msg.value <= finInfo.maxEthPayment, "More than max amount");
require(totalInvestedETH.add(msg.value) <= finInfo.hardCap, "Overfilled");
UserInfo storage user = userInfo[msg.sender];
require(user.totalInvestedETH.add(msg.value) <= finInfo.maxEthPayment, "More than max amount");
uint256 tokenAmount = getTokenAmount(msg.value, finInfo.tokenPrice);
totalInvestedETH = totalInvestedETH.add(msg.value);
tokensForDistribution = tokensForDistribution.add(tokenAmount);
user.totalInvestedETH = user.totalInvestedETH.add(msg.value);
user.total = user.total.add(tokenAmount);
user.debt = user.debt.add(tokenAmount);
emit TokensDebt(msg.sender, msg.value, tokenAmount);
}
/**
* @dev Allow users to refund if soft cap not reached
*/
function refund() external nonReentrant {
require(block.timestamp > timestamps.endTimestamp, "IDO pool has not ended");
require(totalInvestedETH < finInfo.softCap, "IDO pool has reached soft cap");
UserInfo storage user = userInfo[msg.sender];
uint256 _amount = user.totalInvestedETH;
require(_amount > 0, "You have no investment");
// update global state
totalInvestedETH = totalInvestedETH.sub(_amount);
tokensForDistribution = tokensForDistribution.sub(user.total);
// update user state
user.debt = 0;
user.totalInvestedETH = 0;
user.total = 0;
// transfer ETH
(bool success, ) = msg.sender.call{value: _amount}("");
require(success, "Transfer failed");
}
/**
* @dev Allow admin to claim tokens for a user
* @param _user Address of user to process claim for
*/
function claimFor(address _user) external nonReentrant {
proccessClaim(_user);
}
/**
* @dev Allow users to claim their tokens
*/
function claim() external nonReentrant {
proccessClaim(msg.sender);
}
/**
* @dev Process token claims for a specified address
* @param _receiver Address to receive tokens
*/
function proccessClaim(address _receiver) internal {
require(block.timestamp > timestamps.endTimestamp, "IDO pool has not ended");
require(totalInvestedETH >= finInfo.softCap, "IDO pool did not reach soft cap");
require(distributed, "IDO pool has not been distributed");
UserInfo storage user = userInfo[_receiver];
uint256 _amount = user.debt;
require(_amount > 0, "You do not have debt tokens");
user.debt = 0;
distributedTokens = distributedTokens.add(_amount);
idoToken.safeTransfer(_receiver, _amount);
emit TokensWithdrawn(_receiver, _amount);
}
/**
* @dev Withdraw ETH and create liquidity - All LP tokens are burned
* All collected ETH is used for liquidity, no funds are sent to owner
*/
function withdrawETH() external payable nonReentrant {
require(block.timestamp > timestamps.endTimestamp, "IDO pool has not ended");
require(totalInvestedETH >= finInfo.softCap, "Soft cap not reached");
require(!distributed, "Already distributed");
uint256 balance = address(this).balance;
require(balance > 0, "No ETH to create liquidity");
// Use all ETH for liquidity
uint256 ethForLP = balance;
// Calculate token amount needed at listing price
uint256 tokenAmount = getTokenAmount(ethForLP, finInfo.listingPrice);
// Ensure we have enough tokens
require(idoToken.balanceOf(address(this)) >= tokenAmount, "Insufficient token balance");
// Add liquidity - all LP tokens are sent directly to burn address
IUniswapV2Router02 uniswapRouter = IUniswapV2Router02(dexInfo.router);
idoToken.approve(address(uniswapRouter), tokenAmount);
// Record balance before adding liquidity to optimize gas usage
uint256 preBalance = address(this).balance;
// Add all ETH to liquidity
(uint amountToken, uint amountETH, uint liquidity) = uniswapRouter.addLiquidityETH{value: ethForLP}(
address(idoToken),
tokenAmount,
0, // slippage is unavoidable
0, // slippage is unavoidable
BURN_ADDRESS, // Send LP tokens directly to burn address
block.timestamp + 360
);
// Emit LP burned event
emit LPBurned(
address(idoToken),
dexInfo.weth,
amountETH,
amountToken,
BURN_ADDRESS,
liquidity
);
distributed = true;
}
/**
* @dev Withdraw tokens that were not sold during the IDO
*/
function withdrawNotSoldTokens() external onlyOwner {
require(distributed, "Withdraw allowed after distributed");
uint256 balance = getNotSoldToken();
require(balance > 0, "No unsold tokens");
idoToken.safeTransfer(msg.sender, balance);
}
/**
* @dev Get the amount of tokens that were not sold during the IDO
* @return Amount of unsold tokens
*/
function getNotSoldToken() public view returns(uint256) {
uint256 balance = idoToken.balanceOf(address(this));
return balance.add(distributedTokens).sub(tokensForDistribution);
}
/**
* @dev Refund tokens to owner if soft cap not reached
*/
function refundTokens() external onlyOwner {
require(block.timestamp > timestamps.endTimestamp, "IDO pool has not ended");
require(totalInvestedETH < finInfo.softCap, "IDO pool has reached soft cap");
uint256 balance = idoToken.balanceOf(address(this));
require(balance > 0, "No tokens to refund");
idoToken.safeTransfer(msg.sender, balance);
}
/**
* @dev Calculate token amount based on ETH amount and price
* @param ethAmount Amount of ETH
* @param oneTokenInWei Price of one token in WEI
* @return Amount of tokens
*/
function getTokenAmount(uint256 ethAmount, uint256 oneTokenInWei)
internal
view
returns (uint256)
{
require(oneTokenInWei > 0, "Token price cannot be zero");
return (ethAmount / oneTokenInWei) * 10**decimals;
}
/**
* @dev Recover any tokens or ETH from this contract
* @param _tokenAddress Address of token to recover (zero address for ETH)
* @param _amount Amount to recover (0 for all available balance)
*/
function recoverFunds(address _tokenAddress, uint256 _amount) external onlyOwner nonReentrant {
if (_tokenAddress == address(0)) {
// Recover ETH
uint256 balance = address(this).balance;
uint256 amountToRecover = _amount == 0 ? balance : (_amount < balance ? _amount : balance);
require(amountToRecover > 0, "No ETH to recover");
(bool success,) = payable(owner()).call{value: amountToRecover}("");
require(success, "ETH transfer failed");
} else if (_tokenAddress == address(idoToken)) {
// Recover reward tokens with special handling
require(distributed || block.timestamp > timestamps.endTimestamp && totalInvestedETH < finInfo.softCap,
"Can only recover reward tokens after distribution or failed IDO");
uint256 balance = idoToken.balanceOf(address(this));
uint256 availableBalance = block.timestamp > timestamps.endTimestamp && totalInvestedETH < finInfo.softCap ?
balance : getNotSoldToken();
uint256 amountToRecover = _amount == 0 ? availableBalance : (_amount < availableBalance ? _amount : availableBalance);
require(amountToRecover > 0, "No tokens available to recover");
idoToken.safeTransfer(owner(), amountToRecover);
} else {
// Recover other tokens
IERC20 token = IERC20(_tokenAddress);
uint256 balance = token.balanceOf(address(this));
uint256 amountToRecover = _amount == 0 ? balance : (_amount < balance ? _amount : balance);
require(amountToRecover > 0, "No tokens to recover");
token.safeTransfer(owner(), amountToRecover);
}
}
}
contracts/GPAD/IUniswapV2Factory.sol
pragma solidity >=0.5.0;
interface IUniswapV2Factory {
event PairCreated(address indexed token0, address indexed token1, address pair, uint);
function feeTo() external view returns (address);
function feeToSetter() external view returns (address);
function getPair(address tokenA, address tokenB) external view returns (address pair);
function allPairs(uint) external view returns (address pair);
function allPairsLength() external view returns (uint);
function createPair(address tokenA, address tokenB) external returns (address pair);
function setFeeTo(address) external;
function setFeeToSetter(address) external;
}
contracts/GPAD/IUniswapV2Router01.sol
pragma solidity >=0.6.2;
interface IUniswapV2Router01 {
function factory() external pure returns (address);
function WETH() external pure returns (address);
function addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB, uint liquidity);
function addLiquidityETH(
address token,
uint amountTokenDesired,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external payable returns (uint amountToken, uint amountETH, uint liquidity);
function removeLiquidity(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB);
function removeLiquidityETH(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external returns (uint amountToken, uint amountETH);
function removeLiquidityWithPermit(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountA, uint amountB);
function removeLiquidityETHWithPermit(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountToken, uint amountETH);
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapTokensForExactTokens(
uint amountOut,
uint amountInMax,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
external
payable
returns (uint[] memory amounts);
function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
external
returns (uint[] memory amounts);
function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
external
returns (uint[] memory amounts);
function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
external
payable
returns (uint[] memory amounts);
function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}
contracts/GPAD/IUniswapV2Router02.sol
pragma solidity >=0.6.2;
import './IUniswapV2Router01.sol';
interface IUniswapV2Router02 is IUniswapV2Router01 {
function removeLiquidityETHSupportingFeeOnTransferTokens(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external returns (uint amountETH);
function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountETH);
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
function swapExactETHForTokensSupportingFeeOnTransferTokens(
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external payable;
function swapExactTokensForETHSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
}
Compiler Settings
{"viaIR":true,"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"],"":["ast"]}},"optimizer":{"runs":200,"enabled":true},"metadata":{"bytecodeHash":"none"},"libraries":{},"evmVersion":"london"}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_feeToken","internalType":"contract ERC20Burnable"},{"type":"uint256","name":"_feeAmount","internalType":"uint256"},{"type":"uint256","name":"_burnPercent","internalType":"uint256"}]},{"type":"error","name":"InvalidAddress","inputs":[{"type":"string","name":"message","internalType":"string"}]},{"type":"event","name":"BurnPercentUpdated","inputs":[{"type":"uint256","name":"newBurnPercent","internalType":"uint256","indexed":false},{"type":"uint256","name":"divider","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"FeeAmountUpdated","inputs":[{"type":"uint256","name":"newFeeAmount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"FeeWalletUpdated","inputs":[{"type":"address","name":"newFeeWallet","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"GPADPoolInitialized","inputs":[{"type":"address","name":"gpadPool","internalType":"address","indexed":true},{"type":"address","name":"idoToken","internalType":"address","indexed":true},{"type":"tuple","name":"finInfo","internalType":"struct GPADPool.FinInfo","indexed":false,"components":[{"type":"uint256","name":"tokenPrice","internalType":"uint256"},{"type":"uint256","name":"softCap","internalType":"uint256"},{"type":"uint256","name":"hardCap","internalType":"uint256"},{"type":"uint256","name":"minEthPayment","internalType":"uint256"},{"type":"uint256","name":"maxEthPayment","internalType":"uint256"},{"type":"uint256","name":"listingPrice","internalType":"uint256"}]},{"type":"tuple","name":"timestamps","internalType":"struct GPADPool.Timestamps","indexed":false,"components":[{"type":"uint256","name":"startTimestamp","internalType":"uint256"},{"type":"uint256","name":"endTimestamp","internalType":"uint256"}]},{"type":"tuple","name":"dexInfo","internalType":"struct GPADPool.DEXInfo","indexed":false,"components":[{"type":"address","name":"router","internalType":"address"},{"type":"address","name":"factory","internalType":"address"},{"type":"address","name":"weth","internalType":"address"}]},{"type":"string","name":"metadataURL","internalType":"string","indexed":false}],"anonymous":false},{"type":"event","name":"IDOCreated","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"address","name":"gpadPool","internalType":"address","indexed":false},{"type":"address","name":"idoToken","internalType":"address","indexed":true},{"type":"string","name":"tokenURI","internalType":"string","indexed":false}],"anonymous":false},{"type":"event","name":"IDOCreatedByHelper","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"address","name":"gpadPool","internalType":"address","indexed":false},{"type":"address","name":"idoToken","internalType":"address","indexed":true},{"type":"string","name":"tokenURI","internalType":"string","indexed":false},{"type":"uint256","name":"idoTokenAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"lpTokenAmount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"TokenFeeUpdated","inputs":[{"type":"address","name":"newFeeToken","internalType":"address","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"burnPercent","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"address","name":"gpadPoolAddress","internalType":"address"}],"name":"createIDOPool","inputs":[{"type":"address","name":"_idoToken","internalType":"contract ERC20"},{"type":"tuple","name":"_finInfo","internalType":"struct GPADPool.FinInfo","components":[{"type":"uint256","name":"tokenPrice","internalType":"uint256"},{"type":"uint256","name":"softCap","internalType":"uint256"},{"type":"uint256","name":"hardCap","internalType":"uint256"},{"type":"uint256","name":"minEthPayment","internalType":"uint256"},{"type":"uint256","name":"maxEthPayment","internalType":"uint256"},{"type":"uint256","name":"listingPrice","internalType":"uint256"}]},{"type":"tuple","name":"_timestamps","internalType":"struct GPADPool.Timestamps","components":[{"type":"uint256","name":"startTimestamp","internalType":"uint256"},{"type":"uint256","name":"endTimestamp","internalType":"uint256"}]},{"type":"tuple","name":"_dexInfo","internalType":"struct GPADPool.DEXInfo","components":[{"type":"address","name":"router","internalType":"address"},{"type":"address","name":"factory","internalType":"address"},{"type":"address","name":"weth","internalType":"address"}]},{"type":"string","name":"_metadataURL","internalType":"string"},{"type":"uint256","name":"idoTokenAmount","internalType":"uint256"},{"type":"uint256","name":"lpTokenAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"divider","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"feeAmount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ERC20Burnable"}],"name":"feeToken","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"feeWallet","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"address[]"}],"name":"getIdoPools","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"idoPools","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"recoverFunds","inputs":[{"type":"address","name":"_tokenAddress","internalType":"address"},{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setBurnPercent","inputs":[{"type":"uint256","name":"_newBurnPercent","internalType":"uint256"},{"type":"uint256","name":"_newDivider","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setFeeAmount","inputs":[{"type":"uint256","name":"_newFeeAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setFeeToken","inputs":[{"type":"address","name":"_newFeeToken","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setFeeWallet","inputs":[{"type":"address","name":"_newFeeWallet","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"receive","stateMutability":"payable"}]
Contract Creation Code
0x6080346100be57601f61374538819003918201601f19168301916001600160401b038311848410176100c3578084926060946040528339810103126100be5780516001600160a01b0391828216918290036100be57604060208201519101519160005460018060a01b0319903382821617600055604051953391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a36001541617600155600355600455606460055561366b90816100da8239f35b600080fd5b634e487b7160e01b600052604160045260246000fdfe608060405260043610156200001d575b36156200001b57600080fd5b005b60003560e01c806303807ee5146200013c5780630fa3b5cc146200013657806315cce2241462000130578063378efa37146200012a578063586097541462000124578063647846a5146200011e57806369e1540414620001185780636a1860e214620001125780636b392680146200010c5780637104c9fd1462000106578063715018a614620001005780638da5cb5b14620000fa5780638dc1299114620000f457806390d49b9d14620000ee578063f25f4b5614620000e85763f2fde38b036200000f5762000aa4565b62000a79565b620009f6565b62000930565b62000905565b620008a2565b62000844565b620007e7565b62000715565b620006f5565b620006ca565b62000552565b62000532565b62000401565b62000322565b62000153565b60009103126200014e57565b600080fd5b346200014e5760003660031901126200014e576020600454604051908152f35b6001600160a01b038116036200014e57565b634e487b7160e01b600052604160045260246000fd5b60c0810190811067ffffffffffffffff821117620001b857604052565b62000185565b67ffffffffffffffff8111620001b857604052565b6040810190811067ffffffffffffffff821117620001b857604052565b90601f8019910116810190811067ffffffffffffffff821117620001b857604052565b60409060e31901126200014e57604051906040820182811067ffffffffffffffff821117620001b85760405260e4358252610104356020830152565b6060906101231901126200014e57604051906060820182811067ffffffffffffffff821117620001b85760405281610124356200028c8162000173565b8152610144356200029d8162000173565b602082015260406101643591620002b48362000173565b0152565b67ffffffffffffffff8111620001b857601f01601f191660200190565b81601f820112156200014e57803590620002ef82620002b8565b92620002ff6040519485620001f0565b828452602083830101116200014e57816000926020809301838601378301015290565b346200014e576101e03660031901126200014e57600435620003448162000173565b60c03660231901126200014e576040516200035f816200019b565b602435815260443560208201526064356040820152608435606082015260a435608082015260c43560a0820152620003973662000213565b90620003a3366200024f565b92610184359267ffffffffffffffff84116200014e57620003fd94620003d2620003e3953690600401620002d5565b916101c435946101a4359462000e5d565b6040516001600160a01b0390911681529081906020820190565b0390f35b346200014e5760203660031901126200014e57600435620004228162000173565b6000546001600160a01b03906200043d908216331462000b47565b604051906200044c82620001d3565b601a82527f4e65772061646472657373206973206e6f74206120746f6b656e00000000000060208301528216908115620004fd57823b63ffffffff1615620004da57600180546001600160a01b0319166001600160a01b0384161790557f9e892f555b0a5cac6c8cc33c82ebb9308207bd16dfdca9d0289b96d7e4186c8a620004d584620003e3565b0390a1005b604051630b0f5aa160e11b8152908190620004f9906004830162001199565b0390fd5b604051630b0f5aa160e11b815260206004820152600c60248201526b5a65726f206164647265737360a01b6044820152606490fd5b346200014e5760003660031901126200014e576020600554604051908152f35b346200014e5760403660031901126200014e57600435620005738162000173565b600080549091602435916001600160a01b03908116916200059633841462000b47565b1690816200061657505047908015600014620005fd57505b8181620005ba57505080f35b808080620005fa94620005e7620005db620005db845460018060a01b031690565b6001600160a01b031690565b5af1620005f362001164565b5062001351565b80f35b90808210156200060e5750620005ae565b9050620005ae565b6040516370a0823160e01b81523060048201529192909190602082602481875afa918215620006c45785926200068d575b5080620006725750905b816200065e575b50505080f35b620006699262001394565b38808062000658565b90808210156200068557505b9062000651565b90506200067e565b620006b491925060203d8111620006bc575b620006ab8183620001f0565b81019062001341565b903862000647565b503d6200069f565b62000cd0565b346200014e5760003660031901126200014e576001546040516001600160a01b039091168152602090f35b346200014e5760003660031901126200014e576020600354604051908152f35b346200014e57600080600319360112620007e45760405180916006549081835260208093018092600683527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90835b818110620007c657505050846200077d910385620001f0565b60405193838594850191818652518092526040850193925b828110620007a557505050500390f35b83516001600160a01b03168552869550938101939281019260010162000795565b82546001600160a01b03168452928601926001928301920162000764565b80fd5b346200014e5760203660031901126200014e577fbb128cd9921d003aff00ed910fe24b1655b2bf1f4a2090cc3523f61bdc17231660206004356200083760018060a01b0360005416331462000b47565b80600355604051908152a1005b346200014e5760203660031901126200014e576004356006548110156200014e5760066000527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01546040516001600160a01b039091168152602090f35b346200014e57600080600319360112620007e457805481906001600160a01b03811690620008d233831462000b47565b6001600160a01b03191682557f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b346200014e5760003660031901126200014e576000546040516001600160a01b039091168152602090f35b346200014e5760403660031901126200014e576004356024356200096060018060a01b0360005416331462000b47565b808211620009a257816040917fb62d69c6ddf4f4bd443dca17ea7405d8018efb872e82e77bb8ef13b5be2f2e9c936004558060055582519182526020820152a1005b60405162461bcd60e51b815260206004820152602660248201527f4275726e2070657263656e74206d757374206265206c657373207468616e206460448201526534bb34b232b960d11b6064820152608490fd5b346200014e5760203660031901126200014e577f29acee77dafcfa0143d74a7ea236018f3a6e1fa71e27fc59bbfbc6b8ca8edccd602060043562000a3a8162000173565b6000546001600160a01b03919062000a56908316331462000b47565b16806bffffffffffffffffffffffff60a01b6002541617600255604051908152a1005b346200014e5760003660031901126200014e576002546040516001600160a01b039091168152602090f35b346200014e5760203660031901126200014e5760043562000ac58162000173565b6000546001600160a01b039062000ae0908216331462000b47565b81161562000af3576200001b9062000b93565b60405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608490fd5b1562000b4f57565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b600080546001600160a01b039283166001600160a01b03198216811783559216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3565b919082519283825260005b84811062000c07575050826000602080949584010152601f8019910116010190565b60208183018101518483018201520162000be5565b9262000c8b9062000c7862000cba9462000ccd98979460018060a01b03168752602087019060a0809180518452602081015160208501526040810151604085015260608101516060850152608081015160808501520151910152565b805160e086015260200151610100850152565b80516001600160a01b039081166101208501526020820151811661014085015260409091015116610160830152565b6101a09081610180820152019062000bda565b90565b6040513d6000823e3d90fd5b919262000d3b62000ccd959462000d298562000d6a9560a0809180518452602081015160208501526040810151604085015260608101516060850152608081015160808501520151910152565b805160c08601526020015160e0850152565b80516001600160a01b039081166101008501526020820151811661012085015260409091015116610140830152565b6101809081610160820152019062000bda565b634e487b7160e01b600052601160045260246000fd5b9190820180921162000da157565b62000d7d565b60065468010000000000000000811015620001b857600181018060065581101562000e125760066000527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0180546001600160a01b0319166001600160a01b03909216919091179055565b634e487b7160e01b600052603260045260246000fd5b6001600160a01b0390911681526080602082018190529294939260609262000e539183019062000bda565b9460408201520152565b9591939492909560405161228580820182811067ffffffffffffffff821117620001b8578885898c8762000e99958897620013da893962000c1c565b03906000f0968715620006c45786957f6280933c1d06e26a4d5de0f4ae608636e94c4baf579e360d13e16c7b27302fc99162000eec60018060a01b03809b169a8b9516998a966040519485948562000cdc565b0390a362000efb818362000d93565b91853b156200014e5760405163f2fde38b60e01b815233600482015292600084602481838b5af1908115620006c4577fbb1e34963a7cf74028f70419838711c854e9716294c07397f076ff3ec10e04bb9462000f619262000f8d575b5087338862000fab565b62000f6c8662000da7565b62000f806040519283923396898562000e28565b0390a362000ccd620011f0565b8062000f9d62000fa492620001be565b8062000142565b3862000f57565b6040516323b872dd60e01b60208201526001600160a01b03928316602482015292909116604483015260648083019390935291815260a081019181831067ffffffffffffffff841117620001b857620010079260405262001083565b565b908160209103126200014e575180151581036200014e5790565b156200102b57565b60405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608490fd5b6040516001600160a01b0391909116916200109e82620001d3565b6020928383527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656484840152803b156200111f5760008281928287620010f59796519301915af1620010ee62001164565b90620011ac565b805190816200110357505050565b8262001007936200111993830101910162001009565b62001023565b60405162461bcd60e51b815260048101859052601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b3d1562001194573d906200117882620002b8565b91620011886040519384620001f0565b82523d6000602084013e565b606090565b90602062000ccd92818152019062000bda565b90919015620011b9575090565b815115620011ca5750805190602001fd5b60405162461bcd60e51b815260206004820152908190620004f990602483019062000bda565b60035480620011fc5750565b60045415620012e1575060035462001231620012296200121f600454846200130c565b6005549062001320565b8092620012fe565b80620012a8575b508062001243575b50565b6001546200125a906001600160a01b0316620005db565b803b156200014e5760405163079cc67960e41b815233600482015260248101929092526000908290604490829084905af18015620006c4571562001240578062000f9d6200100792620001be565b600154620012da9190620012c5906001600160a01b0316620005db565b6002546001600160a01b031690339062000fab565b3862001238565b600154620010079190620012c5906001600160a01b0316620005db565b9190820391821162000da157565b8181029291811591840414171562000da157565b81156200132b570490565b634e487b7160e01b600052601260045260246000fd5b908160209103126200014e575190565b156200135957565b60405162461bcd60e51b8152602060048201526013602482015272115512081d1c985b9cd9995c8819985a5b1959606a1b6044820152606490fd5b60405163a9059cbb60e01b60208201526001600160a01b039290921660248301526044808301939093529181526200100791620013d3606483620001f0565b6200108356fe608060405234620005b05762002285803803809162000020826080620005b5565b6080396101a08112620005b0576080516001600160a01b0381168103620005b05760c0601f19830112620005b05760405160c081016001600160401b038111828210176200046c57604090815260a08051835260c051602084015260e05183830152610100516060840152610120516080840152610140519083015260df19840112620005b05760408051919082018083116001600160401b03909111176200046c5760408281019052610160518252610180516020830152606061011f19850112620005b057604051926001600160401b0360608501908111908511176200046c5760608401604052620001176101a0620005d9565b8452620001266101c0620005d9565b6020850152620001386101e0620005d9565b6040850152610200516001600160401b038111620005b05760808601609f82011215620005b0576080810151906001600160401b0382116200046c57604051966200018e601f8401601f191660200189620005b5565b82885260800160a08284010111620005b05760005b8281106200059757505060206000918701015260206000543360018060a01b0319821617600055600460405180948193339060018060a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3600180556013805460ff19169055600280546001600160a01b0319166001600160a01b0392909216918217905563313ce56760e01b82525afa9081156200058b5760009162000540575b509060ff60a09216600355805160055560208101516006556040810151600755606081015160085560808101516009550151600a55805160208201511115620004e057602081015142101562000482578051600b55602090810151600c558151600d80546001600160a01b039283166001600160a01b03199182161790915591830151600e8054918316918416919091179055604090920151600f805491909316911617905580516001600160401b0381116200046c57600454600181811c9116801562000461575b60208210146200044b57601f8111620003e1575b50602091601f821160011462000377579181926000926200036b575b50508160011b916000199060031b1c1916176004555b604051611c969081620005ef8239f35b01519050388062000345565b601f19821692600460005260206000209160005b858110620003c857508360019510620003ae575b505050811b016004556200035b565b015160001960f88460031b161c191690553880806200039f565b919260206001819286850151815501940192016200038b565b60046000527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b601f830160051c8101916020841062000440575b601f0160051c01905b81811062000433575062000329565b6000815560010162000424565b90915081906200041b565b634e487b7160e01b600052602260045260246000fd5b90607f169062000315565b634e487b7160e01b600052604160045260246000fd5b60405162461bcd60e51b815260206004820152603060248201527f46696e6973682074696d657374616d70206d757374206265206d6f726520746860448201526f616e2063757272656e7420626c6f636b60801b6064820152608490fd5b60405162461bcd60e51b815260206004820152603260248201527f53746172742074696d657374616d70206d757374206265206c6573732074686160448201527106e2066696e6973682074696d657374616d760741b6064820152608490fd5b6020813d60201162000582575b816200055c60209383620005b5565b810103126200057e57519060ff821682036200057b575060ff6200024c565b80fd5b5080fd5b3d91506200054d565b6040513d6000823e3d90fd5b80602080928460800101015182828b01015201620001a3565b600080fd5b601f909101601f19168101906001600160401b038211908210176200046c57604052565b51906001600160a01b0382168203620005b05756fe6080604052600436101561001257600080fd5b60003560e01c806312d3675e146101975780631959a002146101925780631b9265b81461018d5780632dcc8c6614610188578063313ce56714610183578063491447a81461017e5780634b1a4c0c146101795780634e71d92d14610174578063586097541461016f578063586360ce1461016a578063590e1ae31461016557806362bea93f14610160578063710075091461015b578063715018a614610156578063747daec5146101515780638da5cb5b1461014c578063c6a0e27d14610147578063d6cd82bc14610142578063d8d2a56d1461013d578063ddeae03314610138578063e086e5ec14610133578063f2fde38b1461012e578063f84b903e146101295763fd46a1711461012457600080fd5b611013565b610ff0565b610f5a565b610c76565b610c41565b610c04565b610bdb565b610af8565b610acf565b610a5a565b6109df565b610926565b610903565b610861565b610843565b6105c3565b61058f565b610571565b610548565b61052a565b610445565b61024b565b6101fa565b346101df5760003660031901126101df5760c060055460065460075460085460095491600a5493604051958652602086015260408501526060840152608083015260a0820152f35b600080fd5b600435906001600160a01b03821682036101df57565b346101df5760203660031901126101df576001600160a01b0361021b6101e4565b16600052601460205260606040600020805490600260018201549101549060405192835260208301526040820152f35b60003660031901126101df57610265600b5442101561121e565b610272600c544210611258565b61028060085434101561128c565b60095461028f813411156112cf565b601054906102aa6102a03484611375565b6007541015611312565b336000908152601460205260409020906102d460028301916102cd348454611375565b11156112cf565b6102f36102ee6102e660055434611a98565b943490611375565b601055565b61030761030284601154611375565b601155565b610312348254611375565b905560018101610323838254611375565b9055610330828254611375565b905560408051348152602081019290925233917fb55d4ca2fc644ca3f8cdc9c907826e24acd68d2b2d4d73f48b8f2378dbaedf5d9190a2005b90600182811c92168015610399575b602083101461038357565b634e487b7160e01b600052602260045260246000fd5b91607f1691610378565b634e487b7160e01b600052604160045260246000fd5b6040810190811067ffffffffffffffff8211176103d557604052565b6103a3565b90601f8019910116810190811067ffffffffffffffff8211176103d557604052565b6020808252825181830181905290939260005b82811061043157505060409293506000838284010152601f8019910116010190565b81810186015184820160400152850161040f565b346101df5760008060031936011261052757604051908060045461046881610369565b808552916001918083169081156104fd57506001146104a2575b61049e85610492818703826103da565b604051918291826103fc565b0390f35b9250600483527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b5b8284106104e55750505081016020016104928261049e610482565b805460208587018101919091529093019281016104ca565b86955061049e9693506020925061049294915060ff191682840152151560051b8201019293610482565b80fd5b346101df5760003660031901126101df576020600354604051908152f35b346101df5760003660031901126101df57600b54600c5460408051928352602083019190915290f35b346101df5760003660031901126101df576020601154604051908152f35b346101df5760003660031901126101df576105af60026001541415611382565b60026001556105bd336115c1565b60018055005b346101df5760403660031901126101df576105dc6101e4565b60243560018060a01b03600092818492835416916105fb338414611031565b61060a60026001541415611382565b600260015516908161068b575050808080610669944790801560001461067557505b610637811515611c07565b81546106599061064d906001600160a01b031681565b6001600160a01b031690565b5af16106636114a4565b50611c47565b61067260018055565b80f35b9080821015610684575061062c565b905061062c565b600254919250906106a69061064d906001600160a01b031681565b82036107c357505060135460ff1680156107aa575b6106c490611b49565b6002546106d9906001600160a01b031661064d565b6040516370a0823160e01b815230600482015291602083602481855afa80156107a55761073e938591610777575b50600c5442118061076a575b1561075b57905b806107435750905b61072d821515611bbb565b83546001600160a01b0316906116cf565b610669565b908082101561075457505b90610722565b905061074e565b506107646119e1565b9061071a565b5060105460065411610713565b610798915060203d811161079e575b61079081836103da565b81019061195f565b38610707565b503d610786565b61196e565b50600c54421180156106bb5750601054600654116106bb565b6040516370a0823160e01b815230600482015292602084602481865afa9081156107a55761073e948692610823575b508061080b5750915b610806831515611b06565b6116cf565b908082101561081c57505b916107fb565b9050610816565b61083c91925060203d811161079e5761079081836103da565b90386107f2565b346101df5760003660031901126101df576020601254604051908152f35b346101df576000806003193601126105275761088260026001541415611382565b6002600155610894600c5442116113ce565b610669818080806010546108ab6006548210611413565b3382526014602052816040812081600282016108d76102ee825480976108d282151561145f565b611512565b81601154936108ef6103026001830196875490611512565b555555335af16108fd6114a4565b506114d4565b346101df5760003660031901126101df57602061091e6119e1565b604051908152f35b346101df576000806003193601126105275761094c60018060a01b038254163314611031565b610959600c5442116113ce565b61096860105460065411611413565b60025461097d906001600160a01b031661064d565b6040516370a0823160e01b815230600482015290602082602481845afa9081156107a5576106729284926109bf575b506109b8821515611a56565b33906116cf565b6109d891925060203d811161079e5761079081836103da565b90386109ac565b346101df5760008060031936011261052757805481906001600160a01b03811690610a0b338314611031565b6001600160a01b03191682557f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b67ffffffffffffffff81116103d557601f01601f191660200190565b346101df5760203660031901126101df5760043567ffffffffffffffff81116101df57366023820112156101df578060040135610a9681610a3e565b90610aa460405192836103da565b80825236602482850101116101df576020816000926024610acd96018386013783010152611134565b005b346101df5760003660031901126101df576000546040516001600160a01b039091168152602090f35b346101df5760008060031936011261052757610b1e60018060a01b038254163314611031565b60ff6013541615610b8b57610b316119e1565b8015610b535760025461067291903390610806906001600160a01b031661064d565b60405162461bcd60e51b815260206004820152601060248201526f4e6f20756e736f6c6420746f6b656e7360801b6044820152606490fd5b60405162461bcd60e51b815260206004820152602260248201527f576974686472617720616c6c6f77656420616674657220646973747269627574604482015261195960f21b6064820152608490fd5b346101df5760003660031901126101df576002546040516001600160a01b039091168152602090f35b346101df5760003660031901126101df57606060018060a01b0380600d54169080600e541690600f54169060405192835260208301526040820152f35b346101df5760203660031901126101df576105bd610c5d6101e4565b610c6c60026001541415611382565b60026001556115c1565b60008060031936011261052757610c9260026001541415611382565b6002600155610ca4600c5442116113ce565b610cb4601054600654111561188e565b610ccf610cca610cc660135460ff1690565b1590565b6118d1565b47610cdb811515611913565b610ce7600a5482611a98565b600254909190610cff906001600160a01b031661064d565b6040516370a0823160e01b815230600482015290916020918281602481875afa9081156107a557610d3b9186918891610f3d575b50101561197a565b600d546001600160a01b0394908590610d5590821661064d565b60405163095ea7b360e01b815291166001600160a01b038116600483015260248201839052909490919084908690818a816044810103925af19384156107a557610e1495606095610f0f575b5050600254610db8906001600160a01b031661064d565b92610dc242611361565b60405163f305d71960e01b81526001600160a01b0390951660048601526024850192909252600060448501819052606485015261dead608485015260a4840191909152919384928391829060c4820190565b03925af19182156107a5578384928594610eb6575b506002547f9511e219182d34f8b0080e8f348c16b0d3630ea7e2014b20f1ef78fdf3e0520b929190610ea190610e67906001600160a01b031661064d565b600f549096906001600160a01b03169284604051958695169816968460609194939260808201958252602082015261dead60408201520152565b0390a3610669600160ff196013541617601355565b9050610efd9193507f9511e219182d34f8b0080e8f348c16b0d3630ea7e2014b20f1ef78fdf3e0520b925060603d8111610f08575b610ef581836103da565b8101906119c6565b939092909190610e29565b503d610eeb565b81610f2e92903d10610f36575b610f2681836103da565b8101906117db565b503880610da1565b503d610f1c565b610f549150853d871161079e5761079081836103da565b38610d33565b346101df5760203660031901126101df57610f736101e4565b6000546001600160a01b0390610f8c9082163314611031565b811615610f9c57610acd9061107c565b60405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608490fd5b346101df5760003660031901126101df57602060ff601354166040519015158152f35b346101df5760003660031901126101df576020601054604051908152f35b1561103857565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b600080546001600160a01b039283166001600160a01b03198216811783559216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3565b601f81116110cf575050565b600090600482527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b906020601f850160051c8301941061112a575b601f0160051c01915b82811061111f57505050565b818155600101611113565b909250829061110a565b90815167ffffffffffffffff81116103d55761115a81611155600454610369565b6110c3565b602080601f8311600114611196575081929360009261118b575b50508160011b916000199060031b1c191617600455565b015190503880611174565b90601f198316946111c960046000527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b90565b926000905b8782106112065750508360019596106111ed575b505050811b01600455565b015160001960f88460031b161c191690553880806111e2565b806001859682949686015181550195019301906111ce565b1561122557565b60405162461bcd60e51b815260206004820152600b60248201526a139bdd081cdd185c9d195960aa1b6044820152606490fd5b1561125f57565b60405162461bcd60e51b8152602060048201526005602482015264115b99195960da1b6044820152606490fd5b1561129357565b60405162461bcd60e51b815260206004820152601460248201527313195cdcc81d1a185b881b5a5b88185b5bdd5b9d60621b6044820152606490fd5b156112d657565b60405162461bcd60e51b8152602060048201526014602482015273135bdc99481d1a185b881b585e08185b5bdd5b9d60621b6044820152606490fd5b1561131957565b60405162461bcd60e51b815260206004820152600a60248201526913dd995c999a5b1b195960b21b6044820152606490fd5b634e487b7160e01b600052601160045260246000fd5b90610168820180921161137057565b61134b565b9190820180921161137057565b1561138957565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b156113d557565b60405162461bcd60e51b8152602060048201526016602482015275125113c81c1bdbdb081a185cc81b9bdd08195b99195960521b6044820152606490fd5b1561141a57565b60405162461bcd60e51b815260206004820152601d60248201527f49444f20706f6f6c20686173207265616368656420736f6674206361700000006044820152606490fd5b1561146657565b60405162461bcd60e51b8152602060048201526016602482015275165bdd481a185d99481b9bc81a5b9d995cdd1b595b9d60521b6044820152606490fd5b3d156114cf573d906114b582610a3e565b916114c360405193846103da565b82523d6000602084013e565b606090565b156114db57565b60405162461bcd60e51b815260206004820152600f60248201526e151c985b9cd9995c8819985a5b1959608a1b6044820152606490fd5b9190820391821161137057565b1561152657565b60405162461bcd60e51b815260206004820152602160248201527f49444f20706f6f6c20686173206e6f74206265656e20646973747269627574656044820152601960fa1b6064820152608490fd5b1561157c57565b60405162461bcd60e51b815260206004820152601b60248201527f596f7520646f206e6f742068617665206465627420746f6b656e7300000000006044820152606490fd5b6115ce600c5442116113ce565b6010546006541161168a576115ed6115e860135460ff1690565b61151f565b6001600160a01b03811660009081526014602052604081208054927f6352c5382c4a4578e712449ca65e83cdb392d045dfcf1cad9615189db2da244b9261168592611639861515611575565b5561164e61164985601254611375565b601255565b60025461166b9085908390610806906001600160a01b031661064d565b6040519384526001600160a01b0316929081906020820190565b0390a2565b60405162461bcd60e51b815260206004820152601f60248201527f49444f20706f6f6c20646964206e6f7420726561636820736f667420636170006044820152606490fd5b60405163a9059cbb60e01b60208083019182526001600160a01b0394909416602483015260448083019590955293815291929061170d6064846103da565b60018060a01b03169060405192611723846103b9565b8484527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656485850152823b156117965761176f939260009283809351925af16117696114a4565b90611852565b8051908161177c57505050565b826117949361178f9383010191016117db565b6117f3565b565b60405162461bcd60e51b815260048101869052601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b908160209103126101df575180151581036101df5790565b156117fa57565b60405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608490fd5b9091901561185e575090565b81511561186e5750805190602001fd5b60405162461bcd60e51b815290819061188a90600483016103fc565b0390fd5b1561189557565b60405162461bcd60e51b815260206004820152601460248201527314dbd99d0818d85c081b9bdd081c995858da195960621b6044820152606490fd5b156118d857565b60405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e48191a5cdd1c9a589d5d1959606a1b6044820152606490fd5b1561191a57565b60405162461bcd60e51b815260206004820152601a60248201527f4e6f2045544820746f20637265617465206c69717569646974790000000000006044820152606490fd5b908160209103126101df575190565b6040513d6000823e3d90fd5b1561198157565b60405162461bcd60e51b815260206004820152601a60248201527f496e73756666696369656e7420746f6b656e2062616c616e63650000000000006044820152606490fd5b908160609103126101df578051916040602083015192015190565b6002546040516370a0823160e01b815230600482015290602090829060249082906001600160a01b03165afa9081156107a557600091611a38575b5060125481018091116113705760115481039081116113705790565b611a50915060203d811161079e5761079081836103da565b38611a1c565b15611a5d57565b60405162461bcd60e51b8152602060048201526013602482015272139bc81d1bdad95b9cc81d1bc81c99599d5b99606a1b6044820152606490fd5b8115611ac15704600354604d811161137057600a0a908181029181830414901517156113705790565b60405162461bcd60e51b815260206004820152601a60248201527f546f6b656e2070726963652063616e6e6f74206265207a65726f0000000000006044820152606490fd5b15611b0d57565b60405162461bcd60e51b81526020600482015260146024820152732737903a37b5b2b739903a37903932b1b7bb32b960611b6044820152606490fd5b15611b5057565b60405162461bcd60e51b815260206004820152603f60248201527f43616e206f6e6c79207265636f7665722072657761726420746f6b656e73206160448201527f6674657220646973747269627574696f6e206f72206661696c65642049444f006064820152608490fd5b15611bc257565b60405162461bcd60e51b815260206004820152601e60248201527f4e6f20746f6b656e7320617661696c61626c6520746f207265636f76657200006044820152606490fd5b15611c0e57565b60405162461bcd60e51b815260206004820152601160248201527027379022aa24103a37903932b1b7bb32b960791b6044820152606490fd5b15611c4e57565b60405162461bcd60e51b8152602060048201526013602482015272115512081d1c985b9cd9995c8819985a5b1959606a1b6044820152606490fdfea164736f6c6343000812000aa164736f6c6343000812000a000000000000000000000000abccefb00528c9c792ac7c46997f0f6ee5dcdddd0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000032
Deployed ByteCode
0x608060405260043610156200001d575b36156200001b57600080fd5b005b60003560e01c806303807ee5146200013c5780630fa3b5cc146200013657806315cce2241462000130578063378efa37146200012a578063586097541462000124578063647846a5146200011e57806369e1540414620001185780636a1860e214620001125780636b392680146200010c5780637104c9fd1462000106578063715018a614620001005780638da5cb5b14620000fa5780638dc1299114620000f457806390d49b9d14620000ee578063f25f4b5614620000e85763f2fde38b036200000f5762000aa4565b62000a79565b620009f6565b62000930565b62000905565b620008a2565b62000844565b620007e7565b62000715565b620006f5565b620006ca565b62000552565b62000532565b62000401565b62000322565b62000153565b60009103126200014e57565b600080fd5b346200014e5760003660031901126200014e576020600454604051908152f35b6001600160a01b038116036200014e57565b634e487b7160e01b600052604160045260246000fd5b60c0810190811067ffffffffffffffff821117620001b857604052565b62000185565b67ffffffffffffffff8111620001b857604052565b6040810190811067ffffffffffffffff821117620001b857604052565b90601f8019910116810190811067ffffffffffffffff821117620001b857604052565b60409060e31901126200014e57604051906040820182811067ffffffffffffffff821117620001b85760405260e4358252610104356020830152565b6060906101231901126200014e57604051906060820182811067ffffffffffffffff821117620001b85760405281610124356200028c8162000173565b8152610144356200029d8162000173565b602082015260406101643591620002b48362000173565b0152565b67ffffffffffffffff8111620001b857601f01601f191660200190565b81601f820112156200014e57803590620002ef82620002b8565b92620002ff6040519485620001f0565b828452602083830101116200014e57816000926020809301838601378301015290565b346200014e576101e03660031901126200014e57600435620003448162000173565b60c03660231901126200014e576040516200035f816200019b565b602435815260443560208201526064356040820152608435606082015260a435608082015260c43560a0820152620003973662000213565b90620003a3366200024f565b92610184359267ffffffffffffffff84116200014e57620003fd94620003d2620003e3953690600401620002d5565b916101c435946101a4359462000e5d565b6040516001600160a01b0390911681529081906020820190565b0390f35b346200014e5760203660031901126200014e57600435620004228162000173565b6000546001600160a01b03906200043d908216331462000b47565b604051906200044c82620001d3565b601a82527f4e65772061646472657373206973206e6f74206120746f6b656e00000000000060208301528216908115620004fd57823b63ffffffff1615620004da57600180546001600160a01b0319166001600160a01b0384161790557f9e892f555b0a5cac6c8cc33c82ebb9308207bd16dfdca9d0289b96d7e4186c8a620004d584620003e3565b0390a1005b604051630b0f5aa160e11b8152908190620004f9906004830162001199565b0390fd5b604051630b0f5aa160e11b815260206004820152600c60248201526b5a65726f206164647265737360a01b6044820152606490fd5b346200014e5760003660031901126200014e576020600554604051908152f35b346200014e5760403660031901126200014e57600435620005738162000173565b600080549091602435916001600160a01b03908116916200059633841462000b47565b1690816200061657505047908015600014620005fd57505b8181620005ba57505080f35b808080620005fa94620005e7620005db620005db845460018060a01b031690565b6001600160a01b031690565b5af1620005f362001164565b5062001351565b80f35b90808210156200060e5750620005ae565b9050620005ae565b6040516370a0823160e01b81523060048201529192909190602082602481875afa918215620006c45785926200068d575b5080620006725750905b816200065e575b50505080f35b620006699262001394565b38808062000658565b90808210156200068557505b9062000651565b90506200067e565b620006b491925060203d8111620006bc575b620006ab8183620001f0565b81019062001341565b903862000647565b503d6200069f565b62000cd0565b346200014e5760003660031901126200014e576001546040516001600160a01b039091168152602090f35b346200014e5760003660031901126200014e576020600354604051908152f35b346200014e57600080600319360112620007e45760405180916006549081835260208093018092600683527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90835b818110620007c657505050846200077d910385620001f0565b60405193838594850191818652518092526040850193925b828110620007a557505050500390f35b83516001600160a01b03168552869550938101939281019260010162000795565b82546001600160a01b03168452928601926001928301920162000764565b80fd5b346200014e5760203660031901126200014e577fbb128cd9921d003aff00ed910fe24b1655b2bf1f4a2090cc3523f61bdc17231660206004356200083760018060a01b0360005416331462000b47565b80600355604051908152a1005b346200014e5760203660031901126200014e576004356006548110156200014e5760066000527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01546040516001600160a01b039091168152602090f35b346200014e57600080600319360112620007e457805481906001600160a01b03811690620008d233831462000b47565b6001600160a01b03191682557f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b346200014e5760003660031901126200014e576000546040516001600160a01b039091168152602090f35b346200014e5760403660031901126200014e576004356024356200096060018060a01b0360005416331462000b47565b808211620009a257816040917fb62d69c6ddf4f4bd443dca17ea7405d8018efb872e82e77bb8ef13b5be2f2e9c936004558060055582519182526020820152a1005b60405162461bcd60e51b815260206004820152602660248201527f4275726e2070657263656e74206d757374206265206c657373207468616e206460448201526534bb34b232b960d11b6064820152608490fd5b346200014e5760203660031901126200014e577f29acee77dafcfa0143d74a7ea236018f3a6e1fa71e27fc59bbfbc6b8ca8edccd602060043562000a3a8162000173565b6000546001600160a01b03919062000a56908316331462000b47565b16806bffffffffffffffffffffffff60a01b6002541617600255604051908152a1005b346200014e5760003660031901126200014e576002546040516001600160a01b039091168152602090f35b346200014e5760203660031901126200014e5760043562000ac58162000173565b6000546001600160a01b039062000ae0908216331462000b47565b81161562000af3576200001b9062000b93565b60405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608490fd5b1562000b4f57565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b600080546001600160a01b039283166001600160a01b03198216811783559216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3565b919082519283825260005b84811062000c07575050826000602080949584010152601f8019910116010190565b60208183018101518483018201520162000be5565b9262000c8b9062000c7862000cba9462000ccd98979460018060a01b03168752602087019060a0809180518452602081015160208501526040810151604085015260608101516060850152608081015160808501520151910152565b805160e086015260200151610100850152565b80516001600160a01b039081166101208501526020820151811661014085015260409091015116610160830152565b6101a09081610180820152019062000bda565b90565b6040513d6000823e3d90fd5b919262000d3b62000ccd959462000d298562000d6a9560a0809180518452602081015160208501526040810151604085015260608101516060850152608081015160808501520151910152565b805160c08601526020015160e0850152565b80516001600160a01b039081166101008501526020820151811661012085015260409091015116610140830152565b6101809081610160820152019062000bda565b634e487b7160e01b600052601160045260246000fd5b9190820180921162000da157565b62000d7d565b60065468010000000000000000811015620001b857600181018060065581101562000e125760066000527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0180546001600160a01b0319166001600160a01b03909216919091179055565b634e487b7160e01b600052603260045260246000fd5b6001600160a01b0390911681526080602082018190529294939260609262000e539183019062000bda565b9460408201520152565b9591939492909560405161228580820182811067ffffffffffffffff821117620001b8578885898c8762000e99958897620013da893962000c1c565b03906000f0968715620006c45786957f6280933c1d06e26a4d5de0f4ae608636e94c4baf579e360d13e16c7b27302fc99162000eec60018060a01b03809b169a8b9516998a966040519485948562000cdc565b0390a362000efb818362000d93565b91853b156200014e5760405163f2fde38b60e01b815233600482015292600084602481838b5af1908115620006c4577fbb1e34963a7cf74028f70419838711c854e9716294c07397f076ff3ec10e04bb9462000f619262000f8d575b5087338862000fab565b62000f6c8662000da7565b62000f806040519283923396898562000e28565b0390a362000ccd620011f0565b8062000f9d62000fa492620001be565b8062000142565b3862000f57565b6040516323b872dd60e01b60208201526001600160a01b03928316602482015292909116604483015260648083019390935291815260a081019181831067ffffffffffffffff841117620001b857620010079260405262001083565b565b908160209103126200014e575180151581036200014e5790565b156200102b57565b60405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608490fd5b6040516001600160a01b0391909116916200109e82620001d3565b6020928383527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656484840152803b156200111f5760008281928287620010f59796519301915af1620010ee62001164565b90620011ac565b805190816200110357505050565b8262001007936200111993830101910162001009565b62001023565b60405162461bcd60e51b815260048101859052601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b3d1562001194573d906200117882620002b8565b91620011886040519384620001f0565b82523d6000602084013e565b606090565b90602062000ccd92818152019062000bda565b90919015620011b9575090565b815115620011ca5750805190602001fd5b60405162461bcd60e51b815260206004820152908190620004f990602483019062000bda565b60035480620011fc5750565b60045415620012e1575060035462001231620012296200121f600454846200130c565b6005549062001320565b8092620012fe565b80620012a8575b508062001243575b50565b6001546200125a906001600160a01b0316620005db565b803b156200014e5760405163079cc67960e41b815233600482015260248101929092526000908290604490829084905af18015620006c4571562001240578062000f9d6200100792620001be565b600154620012da9190620012c5906001600160a01b0316620005db565b6002546001600160a01b031690339062000fab565b3862001238565b600154620010079190620012c5906001600160a01b0316620005db565b9190820391821162000da157565b8181029291811591840414171562000da157565b81156200132b570490565b634e487b7160e01b600052601260045260246000fd5b908160209103126200014e575190565b156200135957565b60405162461bcd60e51b8152602060048201526013602482015272115512081d1c985b9cd9995c8819985a5b1959606a1b6044820152606490fd5b60405163a9059cbb60e01b60208201526001600160a01b039290921660248301526044808301939093529181526200100791620013d3606483620001f0565b6200108356fe608060405234620005b05762002285803803809162000020826080620005b5565b6080396101a08112620005b0576080516001600160a01b0381168103620005b05760c0601f19830112620005b05760405160c081016001600160401b038111828210176200046c57604090815260a08051835260c051602084015260e05183830152610100516060840152610120516080840152610140519083015260df19840112620005b05760408051919082018083116001600160401b03909111176200046c5760408281019052610160518252610180516020830152606061011f19850112620005b057604051926001600160401b0360608501908111908511176200046c5760608401604052620001176101a0620005d9565b8452620001266101c0620005d9565b6020850152620001386101e0620005d9565b6040850152610200516001600160401b038111620005b05760808601609f82011215620005b0576080810151906001600160401b0382116200046c57604051966200018e601f8401601f191660200189620005b5565b82885260800160a08284010111620005b05760005b8281106200059757505060206000918701015260206000543360018060a01b0319821617600055600460405180948193339060018060a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3600180556013805460ff19169055600280546001600160a01b0319166001600160a01b0392909216918217905563313ce56760e01b82525afa9081156200058b5760009162000540575b509060ff60a09216600355805160055560208101516006556040810151600755606081015160085560808101516009550151600a55805160208201511115620004e057602081015142101562000482578051600b55602090810151600c558151600d80546001600160a01b039283166001600160a01b03199182161790915591830151600e8054918316918416919091179055604090920151600f805491909316911617905580516001600160401b0381116200046c57600454600181811c9116801562000461575b60208210146200044b57601f8111620003e1575b50602091601f821160011462000377579181926000926200036b575b50508160011b916000199060031b1c1916176004555b604051611c969081620005ef8239f35b01519050388062000345565b601f19821692600460005260206000209160005b858110620003c857508360019510620003ae575b505050811b016004556200035b565b015160001960f88460031b161c191690553880806200039f565b919260206001819286850151815501940192016200038b565b60046000527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b601f830160051c8101916020841062000440575b601f0160051c01905b81811062000433575062000329565b6000815560010162000424565b90915081906200041b565b634e487b7160e01b600052602260045260246000fd5b90607f169062000315565b634e487b7160e01b600052604160045260246000fd5b60405162461bcd60e51b815260206004820152603060248201527f46696e6973682074696d657374616d70206d757374206265206d6f726520746860448201526f616e2063757272656e7420626c6f636b60801b6064820152608490fd5b60405162461bcd60e51b815260206004820152603260248201527f53746172742074696d657374616d70206d757374206265206c6573732074686160448201527106e2066696e6973682074696d657374616d760741b6064820152608490fd5b6020813d60201162000582575b816200055c60209383620005b5565b810103126200057e57519060ff821682036200057b575060ff6200024c565b80fd5b5080fd5b3d91506200054d565b6040513d6000823e3d90fd5b80602080928460800101015182828b01015201620001a3565b600080fd5b601f909101601f19168101906001600160401b038211908210176200046c57604052565b51906001600160a01b0382168203620005b05756fe6080604052600436101561001257600080fd5b60003560e01c806312d3675e146101975780631959a002146101925780631b9265b81461018d5780632dcc8c6614610188578063313ce56714610183578063491447a81461017e5780634b1a4c0c146101795780634e71d92d14610174578063586097541461016f578063586360ce1461016a578063590e1ae31461016557806362bea93f14610160578063710075091461015b578063715018a614610156578063747daec5146101515780638da5cb5b1461014c578063c6a0e27d14610147578063d6cd82bc14610142578063d8d2a56d1461013d578063ddeae03314610138578063e086e5ec14610133578063f2fde38b1461012e578063f84b903e146101295763fd46a1711461012457600080fd5b611013565b610ff0565b610f5a565b610c76565b610c41565b610c04565b610bdb565b610af8565b610acf565b610a5a565b6109df565b610926565b610903565b610861565b610843565b6105c3565b61058f565b610571565b610548565b61052a565b610445565b61024b565b6101fa565b346101df5760003660031901126101df5760c060055460065460075460085460095491600a5493604051958652602086015260408501526060840152608083015260a0820152f35b600080fd5b600435906001600160a01b03821682036101df57565b346101df5760203660031901126101df576001600160a01b0361021b6101e4565b16600052601460205260606040600020805490600260018201549101549060405192835260208301526040820152f35b60003660031901126101df57610265600b5442101561121e565b610272600c544210611258565b61028060085434101561128c565b60095461028f813411156112cf565b601054906102aa6102a03484611375565b6007541015611312565b336000908152601460205260409020906102d460028301916102cd348454611375565b11156112cf565b6102f36102ee6102e660055434611a98565b943490611375565b601055565b61030761030284601154611375565b601155565b610312348254611375565b905560018101610323838254611375565b9055610330828254611375565b905560408051348152602081019290925233917fb55d4ca2fc644ca3f8cdc9c907826e24acd68d2b2d4d73f48b8f2378dbaedf5d9190a2005b90600182811c92168015610399575b602083101461038357565b634e487b7160e01b600052602260045260246000fd5b91607f1691610378565b634e487b7160e01b600052604160045260246000fd5b6040810190811067ffffffffffffffff8211176103d557604052565b6103a3565b90601f8019910116810190811067ffffffffffffffff8211176103d557604052565b6020808252825181830181905290939260005b82811061043157505060409293506000838284010152601f8019910116010190565b81810186015184820160400152850161040f565b346101df5760008060031936011261052757604051908060045461046881610369565b808552916001918083169081156104fd57506001146104a2575b61049e85610492818703826103da565b604051918291826103fc565b0390f35b9250600483527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b5b8284106104e55750505081016020016104928261049e610482565b805460208587018101919091529093019281016104ca565b86955061049e9693506020925061049294915060ff191682840152151560051b8201019293610482565b80fd5b346101df5760003660031901126101df576020600354604051908152f35b346101df5760003660031901126101df57600b54600c5460408051928352602083019190915290f35b346101df5760003660031901126101df576020601154604051908152f35b346101df5760003660031901126101df576105af60026001541415611382565b60026001556105bd336115c1565b60018055005b346101df5760403660031901126101df576105dc6101e4565b60243560018060a01b03600092818492835416916105fb338414611031565b61060a60026001541415611382565b600260015516908161068b575050808080610669944790801560001461067557505b610637811515611c07565b81546106599061064d906001600160a01b031681565b6001600160a01b031690565b5af16106636114a4565b50611c47565b61067260018055565b80f35b9080821015610684575061062c565b905061062c565b600254919250906106a69061064d906001600160a01b031681565b82036107c357505060135460ff1680156107aa575b6106c490611b49565b6002546106d9906001600160a01b031661064d565b6040516370a0823160e01b815230600482015291602083602481855afa80156107a55761073e938591610777575b50600c5442118061076a575b1561075b57905b806107435750905b61072d821515611bbb565b83546001600160a01b0316906116cf565b610669565b908082101561075457505b90610722565b905061074e565b506107646119e1565b9061071a565b5060105460065411610713565b610798915060203d811161079e575b61079081836103da565b81019061195f565b38610707565b503d610786565b61196e565b50600c54421180156106bb5750601054600654116106bb565b6040516370a0823160e01b815230600482015292602084602481865afa9081156107a55761073e948692610823575b508061080b5750915b610806831515611b06565b6116cf565b908082101561081c57505b916107fb565b9050610816565b61083c91925060203d811161079e5761079081836103da565b90386107f2565b346101df5760003660031901126101df576020601254604051908152f35b346101df576000806003193601126105275761088260026001541415611382565b6002600155610894600c5442116113ce565b610669818080806010546108ab6006548210611413565b3382526014602052816040812081600282016108d76102ee825480976108d282151561145f565b611512565b81601154936108ef6103026001830196875490611512565b555555335af16108fd6114a4565b506114d4565b346101df5760003660031901126101df57602061091e6119e1565b604051908152f35b346101df576000806003193601126105275761094c60018060a01b038254163314611031565b610959600c5442116113ce565b61096860105460065411611413565b60025461097d906001600160a01b031661064d565b6040516370a0823160e01b815230600482015290602082602481845afa9081156107a5576106729284926109bf575b506109b8821515611a56565b33906116cf565b6109d891925060203d811161079e5761079081836103da565b90386109ac565b346101df5760008060031936011261052757805481906001600160a01b03811690610a0b338314611031565b6001600160a01b03191682557f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b67ffffffffffffffff81116103d557601f01601f191660200190565b346101df5760203660031901126101df5760043567ffffffffffffffff81116101df57366023820112156101df578060040135610a9681610a3e565b90610aa460405192836103da565b80825236602482850101116101df576020816000926024610acd96018386013783010152611134565b005b346101df5760003660031901126101df576000546040516001600160a01b039091168152602090f35b346101df5760008060031936011261052757610b1e60018060a01b038254163314611031565b60ff6013541615610b8b57610b316119e1565b8015610b535760025461067291903390610806906001600160a01b031661064d565b60405162461bcd60e51b815260206004820152601060248201526f4e6f20756e736f6c6420746f6b656e7360801b6044820152606490fd5b60405162461bcd60e51b815260206004820152602260248201527f576974686472617720616c6c6f77656420616674657220646973747269627574604482015261195960f21b6064820152608490fd5b346101df5760003660031901126101df576002546040516001600160a01b039091168152602090f35b346101df5760003660031901126101df57606060018060a01b0380600d54169080600e541690600f54169060405192835260208301526040820152f35b346101df5760203660031901126101df576105bd610c5d6101e4565b610c6c60026001541415611382565b60026001556115c1565b60008060031936011261052757610c9260026001541415611382565b6002600155610ca4600c5442116113ce565b610cb4601054600654111561188e565b610ccf610cca610cc660135460ff1690565b1590565b6118d1565b47610cdb811515611913565b610ce7600a5482611a98565b600254909190610cff906001600160a01b031661064d565b6040516370a0823160e01b815230600482015290916020918281602481875afa9081156107a557610d3b9186918891610f3d575b50101561197a565b600d546001600160a01b0394908590610d5590821661064d565b60405163095ea7b360e01b815291166001600160a01b038116600483015260248201839052909490919084908690818a816044810103925af19384156107a557610e1495606095610f0f575b5050600254610db8906001600160a01b031661064d565b92610dc242611361565b60405163f305d71960e01b81526001600160a01b0390951660048601526024850192909252600060448501819052606485015261dead608485015260a4840191909152919384928391829060c4820190565b03925af19182156107a5578384928594610eb6575b506002547f9511e219182d34f8b0080e8f348c16b0d3630ea7e2014b20f1ef78fdf3e0520b929190610ea190610e67906001600160a01b031661064d565b600f549096906001600160a01b03169284604051958695169816968460609194939260808201958252602082015261dead60408201520152565b0390a3610669600160ff196013541617601355565b9050610efd9193507f9511e219182d34f8b0080e8f348c16b0d3630ea7e2014b20f1ef78fdf3e0520b925060603d8111610f08575b610ef581836103da565b8101906119c6565b939092909190610e29565b503d610eeb565b81610f2e92903d10610f36575b610f2681836103da565b8101906117db565b503880610da1565b503d610f1c565b610f549150853d871161079e5761079081836103da565b38610d33565b346101df5760203660031901126101df57610f736101e4565b6000546001600160a01b0390610f8c9082163314611031565b811615610f9c57610acd9061107c565b60405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608490fd5b346101df5760003660031901126101df57602060ff601354166040519015158152f35b346101df5760003660031901126101df576020601054604051908152f35b1561103857565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b600080546001600160a01b039283166001600160a01b03198216811783559216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3565b601f81116110cf575050565b600090600482527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b906020601f850160051c8301941061112a575b601f0160051c01915b82811061111f57505050565b818155600101611113565b909250829061110a565b90815167ffffffffffffffff81116103d55761115a81611155600454610369565b6110c3565b602080601f8311600114611196575081929360009261118b575b50508160011b916000199060031b1c191617600455565b015190503880611174565b90601f198316946111c960046000527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b90565b926000905b8782106112065750508360019596106111ed575b505050811b01600455565b015160001960f88460031b161c191690553880806111e2565b806001859682949686015181550195019301906111ce565b1561122557565b60405162461bcd60e51b815260206004820152600b60248201526a139bdd081cdd185c9d195960aa1b6044820152606490fd5b1561125f57565b60405162461bcd60e51b8152602060048201526005602482015264115b99195960da1b6044820152606490fd5b1561129357565b60405162461bcd60e51b815260206004820152601460248201527313195cdcc81d1a185b881b5a5b88185b5bdd5b9d60621b6044820152606490fd5b156112d657565b60405162461bcd60e51b8152602060048201526014602482015273135bdc99481d1a185b881b585e08185b5bdd5b9d60621b6044820152606490fd5b1561131957565b60405162461bcd60e51b815260206004820152600a60248201526913dd995c999a5b1b195960b21b6044820152606490fd5b634e487b7160e01b600052601160045260246000fd5b90610168820180921161137057565b61134b565b9190820180921161137057565b1561138957565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b156113d557565b60405162461bcd60e51b8152602060048201526016602482015275125113c81c1bdbdb081a185cc81b9bdd08195b99195960521b6044820152606490fd5b1561141a57565b60405162461bcd60e51b815260206004820152601d60248201527f49444f20706f6f6c20686173207265616368656420736f6674206361700000006044820152606490fd5b1561146657565b60405162461bcd60e51b8152602060048201526016602482015275165bdd481a185d99481b9bc81a5b9d995cdd1b595b9d60521b6044820152606490fd5b3d156114cf573d906114b582610a3e565b916114c360405193846103da565b82523d6000602084013e565b606090565b156114db57565b60405162461bcd60e51b815260206004820152600f60248201526e151c985b9cd9995c8819985a5b1959608a1b6044820152606490fd5b9190820391821161137057565b1561152657565b60405162461bcd60e51b815260206004820152602160248201527f49444f20706f6f6c20686173206e6f74206265656e20646973747269627574656044820152601960fa1b6064820152608490fd5b1561157c57565b60405162461bcd60e51b815260206004820152601b60248201527f596f7520646f206e6f742068617665206465627420746f6b656e7300000000006044820152606490fd5b6115ce600c5442116113ce565b6010546006541161168a576115ed6115e860135460ff1690565b61151f565b6001600160a01b03811660009081526014602052604081208054927f6352c5382c4a4578e712449ca65e83cdb392d045dfcf1cad9615189db2da244b9261168592611639861515611575565b5561164e61164985601254611375565b601255565b60025461166b9085908390610806906001600160a01b031661064d565b6040519384526001600160a01b0316929081906020820190565b0390a2565b60405162461bcd60e51b815260206004820152601f60248201527f49444f20706f6f6c20646964206e6f7420726561636820736f667420636170006044820152606490fd5b60405163a9059cbb60e01b60208083019182526001600160a01b0394909416602483015260448083019590955293815291929061170d6064846103da565b60018060a01b03169060405192611723846103b9565b8484527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656485850152823b156117965761176f939260009283809351925af16117696114a4565b90611852565b8051908161177c57505050565b826117949361178f9383010191016117db565b6117f3565b565b60405162461bcd60e51b815260048101869052601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b908160209103126101df575180151581036101df5790565b156117fa57565b60405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608490fd5b9091901561185e575090565b81511561186e5750805190602001fd5b60405162461bcd60e51b815290819061188a90600483016103fc565b0390fd5b1561189557565b60405162461bcd60e51b815260206004820152601460248201527314dbd99d0818d85c081b9bdd081c995858da195960621b6044820152606490fd5b156118d857565b60405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e48191a5cdd1c9a589d5d1959606a1b6044820152606490fd5b1561191a57565b60405162461bcd60e51b815260206004820152601a60248201527f4e6f2045544820746f20637265617465206c69717569646974790000000000006044820152606490fd5b908160209103126101df575190565b6040513d6000823e3d90fd5b1561198157565b60405162461bcd60e51b815260206004820152601a60248201527f496e73756666696369656e7420746f6b656e2062616c616e63650000000000006044820152606490fd5b908160609103126101df578051916040602083015192015190565b6002546040516370a0823160e01b815230600482015290602090829060249082906001600160a01b03165afa9081156107a557600091611a38575b5060125481018091116113705760115481039081116113705790565b611a50915060203d811161079e5761079081836103da565b38611a1c565b15611a5d57565b60405162461bcd60e51b8152602060048201526013602482015272139bc81d1bdad95b9cc81d1bc81c99599d5b99606a1b6044820152606490fd5b8115611ac15704600354604d811161137057600a0a908181029181830414901517156113705790565b60405162461bcd60e51b815260206004820152601a60248201527f546f6b656e2070726963652063616e6e6f74206265207a65726f0000000000006044820152606490fd5b15611b0d57565b60405162461bcd60e51b81526020600482015260146024820152732737903a37b5b2b739903a37903932b1b7bb32b960611b6044820152606490fd5b15611b5057565b60405162461bcd60e51b815260206004820152603f60248201527f43616e206f6e6c79207265636f7665722072657761726420746f6b656e73206160448201527f6674657220646973747269627574696f6e206f72206661696c65642049444f006064820152608490fd5b15611bc257565b60405162461bcd60e51b815260206004820152601e60248201527f4e6f20746f6b656e7320617661696c61626c6520746f207265636f76657200006044820152606490fd5b15611c0e57565b60405162461bcd60e51b815260206004820152601160248201527027379022aa24103a37903932b1b7bb32b960791b6044820152606490fd5b15611c4e57565b60405162461bcd60e51b8152602060048201526013602482015272115512081d1c985b9cd9995c8819985a5b1959606a1b6044820152606490fdfea164736f6c6343000812000aa164736f6c6343000812000a