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.
View contract in Sourcify repository
- Contract name:
- pCNV
- Optimization enabled
- false
- Compiler version
- v0.8.11+commit.d7f03943
- EVM Version
- london
- Verified at
- 2026-04-22T01:58:48.101819Z
pCNV.sol
// SPDX-License-Identifier: WTFPL
pragma solidity >=0.8.0;
/**
██████╗ ██████╗ ███╗ ██╗ ██████╗ █████╗ ██╗ ██╗██████╗
██╔════╝██╔═══██╗████╗ ██║██╔════╝██╔══██╗██║ ██║╚════██╗
██║ ██║ ██║██╔██╗ ██║██║ ███████║██║ ██║ █████╔╝
██║ ██║ ██║██║╚██╗██║██║ ██╔══██║╚██╗ ██╔╝ ╚═══██╗
╚██████╗╚██████╔╝██║ ╚████║╚██████╗██║ ██║ ╚████╔╝ ██████╔╝
╚═════╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═════╝╚═╝ ╚═╝ ╚═══╝ ╚═════╝
Concave Presale Token
pCNV to CNV mechanics
---------------------
The contract features two vesting schedules to redeem pCNV into CNV. Both
schedules are linear, and have a duration of 2 years.
The first vesting schedule determines how many pCNV a holder can redeem at
any point in time. At contract inception - 0% of a holder's pCNV can be
redeemed. At the end of 2 years, 100% of a holder's pCNV can be redeemed.
It goes from 0% to 100% in a linear fashion.
The second vesting schedule determines the percent of CNV supply that pCNV
corresponds to. This vesting schedule also begins at 0% on day one, and
advances linearly to reach 10% at the end of year two.
The following is a breakdown of a pCNV to CNV redemption:
Assumptions:
- Alice holds 100 pCNV
- pCNV total supply is 200
- CNV total supply is 1000
- 1 year has passed and Alice has not made any previous redemptions
Then:
- The first vesting schedule tells us that users may redeem 50% of their
holdings, so Alice may redeem 50 pCNV.
- The second vesting schedule tells us that pCNV total supply corresponds
to 5% of total CNV supply.
- Since total CNV supply is 1000, 5% of it is 50, so 50 CNV are what
correspond to the 200 pCNV supply.
- Alice has 50% of total pCNV supply
- Thus, Alice is entitled to 50% of the claimable CNV supply, i.e Alice
is entitled to 25 CNV
Conclusion:
- Alice burns 50 pCNV
- Alice mints 25 CNV
---------------------
@author 0xBarista & Dionysus (ConcaveFi)
*/
/* -------------------------------------------------------------------------- */
/* IMPORTS */
/* -------------------------------------------------------------------------- */
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*///////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*///////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*///////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
bytes32 public constant PERMIT_TYPEHASH =
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*///////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*///////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*///////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
bytes32 digest = keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
)
);
address recoveredAddress = ecrecover(digest, v, r, s);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*///////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
pragma solidity >=0.8.0;
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Gnosis (https://github.com/gnosis/gp-v2-contracts/blob/main/src/contracts/libraries/GPv2SafeERC20.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
library SafeTransferLib {
/*///////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferETH(address to, uint256 amount) internal {
bool callStatus;
assembly {
// Transfer the ETH and store if it succeeded or not.
callStatus := call(gas(), to, amount, 0, 0, 0, 0)
}
require(callStatus, "ETH_TRANSFER_FAILED");
}
/*///////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool callStatus;
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata to memory piece by piece:
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "from" argument.
mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
mstore(add(freeMemoryPointer, 68), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.
// Call the token and store if it succeeded or not.
// We use 100 because the calldata length is 4 + 32 * 3.
callStatus := call(gas(), token, 0, freeMemoryPointer, 100, 0, 0)
}
require(didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool callStatus;
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata to memory piece by piece:
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.
// Call the token and store if it succeeded or not.
// We use 68 because the calldata length is 4 + 32 * 2.
callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
}
require(didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool callStatus;
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata to memory piece by piece:
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.
// Call the token and store if it succeeded or not.
// We use 68 because the calldata length is 4 + 32 * 2.
callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
}
require(didLastOptionalReturnCallSucceed(callStatus), "APPROVE_FAILED");
}
/*///////////////////////////////////////////////////////////////
INTERNAL HELPER LOGIC
//////////////////////////////////////////////////////////////*/
function didLastOptionalReturnCallSucceed(bool callStatus) private pure returns (bool success) {
assembly {
// Get how many bytes the call returned.
let returnDataSize := returndatasize()
// If the call reverted:
if iszero(callStatus) {
// Copy the revert message into memory.
returndatacopy(0, 0, returnDataSize)
// Revert with the same message.
revert(0, returnDataSize)
}
switch returnDataSize
case 32 {
// Copy the return data into memory.
returndatacopy(0, 0, returnDataSize)
// Set success to whether it returned true.
success := iszero(iszero(mload(0)))
}
case 0 {
// There was no return data.
success := 1
}
default {
// It returned some malformed input.
success := 0
}
}
}
}
// OpenZeppelin Contracts v4.4.1 (utils/cryptography/MerkleProof.sol)
pragma solidity ^0.8.0;
/**
* @dev These functions deal with verification of Merkle Trees proofs.
*
* The proofs can be generated using the JavaScript library
* https://github.com/miguelmota/merkletreejs[merkletreejs].
* Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
*
* See `test/utils/cryptography/MerkleProof.test.js` for some examples.
*/
library MerkleProof {
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*/
function verify(
bytes32[] memory proof,
bytes32 root,
bytes32 leaf
) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merklee tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leafs & pre-images are assumed to be sorted.
*
* _Available since v4.4._
*/
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
bytes32 proofElement = proof[i];
if (computedHash <= proofElement) {
// Hash(current computed hash + current element of the proof)
computedHash = _efficientHash(computedHash, proofElement);
} else {
// Hash(current element of the proof + current computed hash)
computedHash = _efficientHash(proofElement, computedHash);
}
}
return computedHash;
}
function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
assembly {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}
pragma solidity >=0.8.0;
interface ICNV {
function mint(address to, uint256 amount) external returns (uint256);
function totalSupply() external view returns (uint256);
}
/// @notice Concave Presale Token
/// @author 0xBarista & Dionysus (ConcaveFi)
contract pCNV is ERC20("Concave Presale Token", "pCNV", 18) {
/* ---------------------------------------------------------------------- */
/* DEPENDENCIES */
/* ---------------------------------------------------------------------- */
using SafeTransferLib for ERC20;
/* ---------------------------------------------------------------------- */
/* IMMUTABLE STATE */
/* ---------------------------------------------------------------------- */
/// @notice UNIX timestamp when contact was created
uint256 public immutable GENESIS = block.timestamp;
/// @notice Two years in seconds
uint256 public immutable TWO_YEARS = 63072000;
/// @notice FRAX tokenIn address
ERC20 public immutable FRAX = ERC20(0x853d955aCEf822Db058eb8505911ED77F175b99e);
/// @notice DAI tokenIn address
ERC20 public immutable DAI = ERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F);
/// @notice Error related to amount
string constant AMOUNT_ERROR = "!AMOUNT";
/// @notice Error related to token address
string constant TOKEN_IN_ERROR = "!TOKEN_IN";
/* ---------------------------------------------------------------------- */
/* MUTABLE STATE */
/* ---------------------------------------------------------------------- */
/// @notice CNV ERC20 token
/// @dev will be address(0) until redeemable = true
ICNV public CNV;
/// @notice Address that is recipient of raised funds + access control
address public treasury = 0x226e7AF139a0F34c6771DeB252F9988876ac1Ced;
/// @notice Returns the current merkle root being used
bytes32 public merkleRoot;
/// @notice Returns an array of all merkle roots used
bytes32[] public roots;
/// @notice Returns the current pCNV price in DAI/FRAX
uint256 public rate;
/// @notice Returns the max supply of pCNV that is allowed to be minted (in total)
uint256 public maxSupply = 33000000000000000000000000;
/// @notice Returns the total amount of pCNV that has cumulatively been minted
uint256 public totalMinted;
/// @notice Returns if pCNV are redeemable for CNV
bool public redeemable;
/// @notice Returns whether transfers are paused
bool public transfersPaused;
/* ---------------------------------------------------------------------- */
/* STRUCTURED STATE */
/* ---------------------------------------------------------------------- */
/// @notice Structure of Participant vesting storage
struct Participant {
uint256 purchased; // amount (in total) of pCNV that user has purchased
uint256 redeemed; // amount (in total) of pCNV that user has redeemed
}
/// @notice maps an account to vesting storage
/// address - account to check
/// returns Participant - Structured vesting storage
mapping(address => Participant) public participants;
/// @notice amount of DAI/FRAX user has spent for a specific root
/// bytes32 - merkle root
/// address - account to check
/// returns uint256 - amount in DAI/FRAX (denominated in ether) spent purchasing pCNV
mapping(bytes32 => mapping(address => uint256)) public spentAmounts;
/* ---------------------------------------------------------------------- */
/* EVENTS */
/* ---------------------------------------------------------------------- */
/// @notice Emitted when treasury changes treasury address
/// @param treasury address of new treasury
event TreasurySet(address treasury);
/// @notice Emitted when redeemable is set to true and CNV address is set
/// @param CNV address of CNV token
event RedeemableSet(address CNV);
/// @notice Emitted when a new round is set by treasury
/// @param merkleRoot new merkle root
/// @param rate new price of pCNV in DAI/FRAX
event NewRound(bytes32 merkleRoot, uint256 rate);
/// @notice Emitted when maxSupply of pCNV is burned or minted to target
/// @param target target to which to mint pCNV or burn if target = address(0)
/// @param amount amount of pCNV minted to target or burned
/// @param maxSupply new maxSupply
event Managed(address target, uint256 amount, uint256 maxSupply);
/// @notice Emitted when pCNV minted via "mint()" or "mintWithPermit"
/// @param depositedFrom address from which DAI/FRAX was deposited
/// @param mintedTo address to which pCNV were minted to
/// @param amount amount of pCNV minted
/// @param deposited amount of DAI/FRAX deposited
/// @param totalMinted total amount of pCNV minted so far
event Minted(
address indexed depositedFrom,
address indexed mintedTo,
uint256 amount,
uint256 deposited,
uint256 totalMinted
);
/// @notice Emitted when pCNV redeemed for CNV by callign "redeem()"
/// @param burnedFrom address from which pCNV were burned
/// @param mintedTo address to which CNV were minted to
/// @param burned amount of pCNV burned
/// @param minted amount of CNV minted
event Redeemed(
address indexed burnedFrom,
address indexed mintedTo,
uint256 burned,
uint256 minted
);
/* ---------------------------------------------------------------------- */
/* MODIFIERS */
/* ---------------------------------------------------------------------- */
/// @notice only allows Concave treasury
modifier onlyConcave() {
require(msg.sender == treasury, "!CONCAVE");
_;
}
/* ---------------------------------------------------------------------- */
/* ONLY CONCAVE */
/* ---------------------------------------------------------------------- */
/// @notice Set a new treasury address if treasury
function setTreasury(
address _treasury
) external onlyConcave {
treasury = _treasury;
emit TreasurySet(_treasury);
}
/// @notice allow pCNV to be redeemed for CNV by setting redeemable as true and setting CNV address
/// @param _CNV address of CNV
function setRedeemable(
address _CNV
) external onlyConcave {
// Allow tokens to be redeemed for CNV
redeemable = true;
// Set CNV address so tokens can be minted
CNV = ICNV(_CNV);
emit RedeemableSet(_CNV);
}
/// @notice Update merkle root and rate
/// @param _merkleRoot root of merkle tree
/// @param _rate price of pCNV in DAI/FRAX
function setRound(
bytes32 _merkleRoot,
uint256 _rate
) external onlyConcave {
require(_rate > 0, "!RATE");
// push new root to array of all roots - for viewing
roots.push(_merkleRoot);
// update merkle root
merkleRoot = _merkleRoot;
// update rate
rate = _rate;
emit NewRound(merkleRoot,rate);
}
/// @notice Reduce an "amount" of available supply of pCNV or mint it to "target"
/// @param target address to which to mint; if address(0), will burn
/// @param amount to reduce from max supply or mint to "target"
function manage(
address target,
uint256 amount
) external onlyConcave {
// declare new variable for totalMinted + amount (gas savings)
uint256 newAmount;
// if target is address 0, reduce supply
if (target == address(0)) {
// avoid subtracting into negatives
require(amount <= maxSupply, AMOUNT_ERROR);
// new maxSupply would be set to maxSupply minus amount
newAmount = maxSupply - amount;
// Make sure there's enough unminted supply to allow for supply reduction
require(newAmount >= totalMinted, AMOUNT_ERROR);
// Reduce max supply by "amount"
maxSupply = newAmount;
// end the function
emit Managed(target, amount, maxSupply);
return;
}
// new maxSupply would be set to totalMinted + amount
newAmount = totalMinted + amount;
// make sure total newAmount (totalMinted + amount) is less than or equal to maximum supply
require(newAmount <= maxSupply, AMOUNT_ERROR);
// set totalMinted to newAmount (totalMinted + amount)
totalMinted = newAmount;
// mint target amount
_mint(target, amount);
emit Managed(target, amount, maxSupply);
}
/// @notice Allows Concave to pause transfers in the event of a bug
/// @param paused if transfers should be paused or not
function setTransfersPaused(bool paused) external onlyConcave {
transfersPaused = paused;
}
/* ---------------------------------------------------------------------- */
/* PUBLIC LOGIC */
/* ---------------------------------------------------------------------- */
/// @notice mint pCNV by providing merkle proof and depositing DAI/FRAX
/// @param to whitelisted address pCNV will be minted to
/// @param tokenIn address of tokenIn user wishes to deposit (DAI/FRAX)
/// @param maxAmount max amount of DAI/FRAX sender can deposit for pCNV, to verify merkle proof
/// @param amountIn amount of DAI/FRAX sender wishes to deposit for pCNV
/// @param proof merkle proof to prove "to" and "maxAmount" are in merkle tree
function mint(
address to,
address tokenIn,
uint256 maxAmount,
uint256 amountIn,
bytes32[] calldata proof
) external returns (uint256 amountOut) {
return _purchase(msg.sender, to, tokenIn, maxAmount, amountIn, proof);
}
/// @notice mint pCNV by providing merkle proof and depositing DAI; uses EIP-2612 permit to save a transaction
/// @param to whitelisted address pCNV will be minted to
/// @param tokenIn address of tokenIn user wishes to deposit (DAI)
/// @param maxAmount max amount of DAI sender can deposit for pCNV, to verify merkle proof
/// @param amountIn amount of DAI sender wishes to deposit for pCNV
/// @param proof merkle proof to prove "to" and "maxAmount" are in merkle tree
/// @param permitDeadline EIP-2612 : time when permit is no longer valid
/// @param v EIP-2612 : part of EIP-2612 signature
/// @param r EIP-2612 : part of EIP-2612 signature
/// @param s EIP-2612 : part of EIP-2612 signature
function mintWithPermit(
address to,
address tokenIn,
uint256 maxAmount,
uint256 amountIn,
bytes32[] calldata proof,
uint256 permitDeadline,
uint8 v,
bytes32 r,
bytes32 s
) external returns (uint256 amountOut) {
// Make sure payment tokenIn is DAI
require(tokenIn == address(DAI), TOKEN_IN_ERROR);
// Approve tokens for spender - https://eips.ethereum.org/EIPS/eip-2612
ERC20(tokenIn).permit(msg.sender, address(this), amountIn, permitDeadline, v, r, s);
// allow sender to mint for "to"
return _purchase(msg.sender, to, tokenIn, maxAmount, amountIn, proof);
}
/// @notice transfer "amount" of tokens from msg.sender to "to"
/// @dev calls "_beforeTransfer" to update vesting storage for "from" and "to"
/// @param to address tokens are being sent to
/// @param amount number of tokens being transfered
function transfer(
address to,
uint256 amount
) public virtual override returns (bool) {
// update vesting storage for both users
_beforeTransfer(msg.sender, to, amount);
// default ERC20 transfer
return super.transfer(to, amount);
}
/// @notice transfer "amount" of tokens from "from" to "to"
/// @dev calls "_beforeTransfer" to update vesting storage for "from" and "to"
/// @param from address tokens are being transfered from
/// @param to address tokens are being sent to
/// @param amount number of tokens being transfered
function transferFrom(
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
// update vesting storage for both users
_beforeTransfer(from, to, amount);
// default ERC20 transfer
return super.transferFrom(from, to, amount);
}
/// @notice redeem pCNV for CNV
/// @param to address that will receive redeemed CNV
/// @param amountIn amount of pCNV to redeem
function redeem(
address to,
uint256 amountIn
) external {
// Make sure pCNV is currently redeemable for CNV
require(redeemable, "!REDEEMABLE");
// Access participant storage
Participant storage participant = participants[msg.sender];
// Calculate CNV owed to sender for redeeming "amountIn"
uint256 amountOut = redeemAmountOut(msg.sender, amountIn);
// Increase participant.redeemed by amount being redeemed
participant.redeemed += amountIn;
// Burn users pCNV
_burn(msg.sender, amountIn);
// Mint user CNV
CNV.mint(to, amountOut);
emit Redeemed(msg.sender, to, amountIn, amountOut);
}
/* ---------------------------------------------------------------------- */
/* PUBLIC VIEW */
/* ---------------------------------------------------------------------- */
/// @notice Returns the amount of CNV a user will receive for redeeming `amountIn` of pCNV
/// @param who address that will receive redeemed CNV
/// @param amountIn amount of pCNV
function redeemAmountOut(address who, uint256 amountIn) public view returns (uint256) {
// Make sure amountIn is less than participants maximum redeem amount in
require(amountIn <= maxRedeemAmountIn(who), AMOUNT_ERROR);
// Calculate percentage of maxRedeemAmountIn that participant is redeeming
uint256 ratio = 1e18 * amountIn / maxRedeemAmountIn(who);
// Calculate portion of maxRedeemAmountOut to mint using above percentage
return maxRedeemAmountOut(who) * ratio / 1e18;
}
/// @notice Returns amount of pCNV that user can redeem according to vesting schedule
/// @dev Returns redeem * percentageVested / (eth normalized) - total already redeemed
/// @param who address to check
function maxRedeemAmountIn(
address who
) public view returns (uint256) {
// Make sure pCNV is currently redeemable for CNV
if (!redeemable) return 0;
// Make sure there's CNV supply
if (CNV.totalSupply() == 0) return 0;
// Access sender's participant memory
Participant memory participant = participants[who];
// return maximum amount of pCNV "who" can currently redeem
return participant.purchased * purchaseVested() / 1e18 - participant.redeemed;
}
/// @notice Returns amount of CNV that an account can currently redeem for
/// @param who address to check
function maxRedeemAmountOut(
address who
) public view returns (uint256) {
return amountVested() * percentToRedeem(who) / 1e18;
}
/// @notice Returns percentage (denominated in ether) of pCNV supply that a given account can currently redeem
/// @param who address to check
function percentToRedeem(
address who
) public view returns (uint256) {
return 1e18 * maxRedeemAmountIn(who) / maxSupply;
}
/// @notice Returns the amount of time (in seconds) that has passed since the contract was created
function elapsed() public view returns (uint256) {
return block.timestamp - GENESIS;
}
/// @notice Returns the percentage of CNV supply (denominated in ether) that all pCNV is currently redeemable for
function supplyVested() public view returns (uint256) {
return elapsed() > TWO_YEARS ? 1e17 : 1e17 * elapsed() / TWO_YEARS;
}
/// @notice Returns the percent of pCNV that is redeemable
function purchaseVested() public view returns (uint256) {
return elapsed() > TWO_YEARS ? 1e18 : 1e18 * elapsed() / TWO_YEARS;
}
/// @notice Returns total amount of CNV supply that is vested
function amountVested() public view returns (uint256) {
return CNV.totalSupply() * supplyVested() / 1e18;
}
/* ---------------------------------------------------------------------- */
/* INTERNAL LOGIC */
/* ---------------------------------------------------------------------- */
/// @notice Deposits FRAX/DAI for pCNV if merkle proof exists in specified round
/// @param sender address sending transaction
/// @param to whitelisted address purchased pCNV will be sent to
/// @param tokenIn address of tokenIn user wishes to deposit
/// @param maxAmount max amount of DAI/FRAX sender can deposit for pCNV
/// @param amountIn amount of DAI/FRAX sender wishes to deposit for pCNV
/// @param proof merkle proof to prove address and amount are in tree
function _purchase(
address sender,
address to,
address tokenIn,
uint256 maxAmount,
uint256 amountIn,
bytes32[] calldata proof
) internal returns(uint256 amountOut) {
// Make sure payment tokenIn is either DAI or FRAX
require(tokenIn == address(DAI) || tokenIn == address(FRAX), TOKEN_IN_ERROR);
// Require merkle proof with `to` and `maxAmount` to be successfully verified
require(MerkleProof.verify(proof, merkleRoot, keccak256(abi.encodePacked(to, maxAmount))), "!PROOF");
// Verify amount claimed by user does not surpass "maxAmount"
uint256 newAmount = spentAmounts[merkleRoot][to] + amountIn; // save gas
require(newAmount <= maxAmount, AMOUNT_ERROR);
spentAmounts[merkleRoot][to] = newAmount;
// Calculate rate of pCNV that should be returned for "amountIn"
amountOut = amountIn * 1e18 / rate;
// make sure total minted + amount is less than or equal to maximum supply
require(totalMinted + amountOut <= maxSupply, AMOUNT_ERROR);
// Interface storage for participant
Participant storage participant = participants[to];
// Increase participant.purchased to account for newly purchased tokens
participant.purchased += amountOut;
// Increase totalMinted to account for newly minted supply
totalMinted += amountOut;
// Transfer amountIn*ratio of tokenIn to treasury address
ERC20(tokenIn).safeTransferFrom(sender, treasury, amountIn);
// Mint tokens to address after pulling
_mint(to, amountOut);
emit Minted(sender, to, amountOut, amountIn, totalMinted);
}
/// @notice Maintains total amount of redeemable tokens when pCNV is being transfered
/// @param from address tokens are being transfered from
/// @param to address tokens are being sent to
/// @param amount number of tokens being transfered
function _beforeTransfer(
address from,
address to,
uint256 amount
) internal {
// transfers must not be paused
require(!transfersPaused, "PAUSED");
// Interface "to" participant storage
Participant storage toParticipant = participants[to];
// Interface "from" participant storage
Participant storage fromParticipant = participants[from];
// calculate amount to adjust redeem amounts by
uint256 adjustedAmount = amount * fromParticipant.redeemed / fromParticipant.purchased;
// reduce "from" redeemed by amount * "from" redeem purchase ratio
fromParticipant.redeemed -= adjustedAmount;
// reduce "from" purchased amount by the amount being sent
fromParticipant.purchased -= amount;
// increase "to" redeemed by amount * "from" redeem purchase ratio
toParticipant.redeemed += adjustedAmount;
// increase "to" purchased by amount received
toParticipant.purchased += amount;
}
/// @notice Rescues accidentally sent tokens and ETH
/// @param token address of token to rescue, if address(0) rescue ETH
function rescue(address token) external onlyConcave {
if (token == address(0)) payable(treasury).transfer( address(this).balance );
else ERC20(token).transfer(treasury, ERC20(token).balanceOf(address(this)));
}
}
/**
"someone spent a lot of computational power and time to bruteforce that contract address
so basically to have that many leading zeros
you can't just create a contract and get that, the odds are 1 in trillions to get something like that
there's a way to guess which contract address you will get, using a script.. and you have to bruteforce for a very long time to get that many leading 0's
fun fact, the more leading 0's a contract has, the cheaper gas will be for users to interact with the contract"
- some solidity dev
© 2022 WTFPL – Do What the Fuck You Want to Public License.
*/
Compiler Settings
{"remappings":[],"optimizer":{"runs":200,"enabled":false},"metadata":{"bytecodeHash":"ipfs"},"libraries":{},"evmVersion":"london","compilationTarget":{"pCNV.sol":"pCNV"}}
Contract ABI
[{"type":"event","name":"Approval","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"address","name":"spender","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Managed","inputs":[{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"uint256","name":"maxSupply","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Minted","inputs":[{"type":"address","name":"depositedFrom","internalType":"address","indexed":true},{"type":"address","name":"mintedTo","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"uint256","name":"deposited","internalType":"uint256","indexed":false},{"type":"uint256","name":"totalMinted","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"NewRound","inputs":[{"type":"bytes32","name":"merkleRoot","internalType":"bytes32","indexed":false},{"type":"uint256","name":"rate","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"RedeemableSet","inputs":[{"type":"address","name":"CNV","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"Redeemed","inputs":[{"type":"address","name":"burnedFrom","internalType":"address","indexed":true},{"type":"address","name":"mintedTo","internalType":"address","indexed":true},{"type":"uint256","name":"burned","internalType":"uint256","indexed":false},{"type":"uint256","name":"minted","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Transfer","inputs":[{"type":"address","name":"from","internalType":"address","indexed":true},{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"TreasurySet","inputs":[{"type":"address","name":"treasury","internalType":"address","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ICNV"}],"name":"CNV","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ERC20"}],"name":"DAI","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"DOMAIN_SEPARATOR","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ERC20"}],"name":"FRAX","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"GENESIS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"PERMIT_TYPEHASH","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"TWO_YEARS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"allowance","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"amountVested","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"approve","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"balanceOf","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"decimals","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"elapsed","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"manage","inputs":[{"type":"address","name":"target","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxRedeemAmountIn","inputs":[{"type":"address","name":"who","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxRedeemAmountOut","inputs":[{"type":"address","name":"who","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxSupply","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"merkleRoot","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"}],"name":"mint","inputs":[{"type":"address","name":"to","internalType":"address"},{"type":"address","name":"tokenIn","internalType":"address"},{"type":"uint256","name":"maxAmount","internalType":"uint256"},{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"bytes32[]","name":"proof","internalType":"bytes32[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"}],"name":"mintWithPermit","inputs":[{"type":"address","name":"to","internalType":"address"},{"type":"address","name":"tokenIn","internalType":"address"},{"type":"uint256","name":"maxAmount","internalType":"uint256"},{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"bytes32[]","name":"proof","internalType":"bytes32[]"},{"type":"uint256","name":"permitDeadline","internalType":"uint256"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"name","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"nonces","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"purchased","internalType":"uint256"},{"type":"uint256","name":"redeemed","internalType":"uint256"}],"name":"participants","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"percentToRedeem","inputs":[{"type":"address","name":"who","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"permit","inputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"purchaseVested","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"rate","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"redeem","inputs":[{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"amountIn","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"redeemAmountOut","inputs":[{"type":"address","name":"who","internalType":"address"},{"type":"uint256","name":"amountIn","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"redeemable","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"rescue","inputs":[{"type":"address","name":"token","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"roots","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setRedeemable","inputs":[{"type":"address","name":"_CNV","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setRound","inputs":[{"type":"bytes32","name":"_merkleRoot","internalType":"bytes32"},{"type":"uint256","name":"_rate","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setTransfersPaused","inputs":[{"type":"bool","name":"paused","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setTreasury","inputs":[{"type":"address","name":"_treasury","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"spentAmounts","inputs":[{"type":"bytes32","name":"","internalType":"bytes32"},{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"supplyVested","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"symbol","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalMinted","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalSupply","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transfer","inputs":[{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transferFrom","inputs":[{"type":"address","name":"from","internalType":"address"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transfersPaused","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"treasury","inputs":[]}]
Contract Creation Code
0x6101606040524260e0908152506303c267006101009081525073853d955acef822db058eb8505911ed77f175b99e73ffffffffffffffffffffffffffffffffffffffff166101209073ffffffffffffffffffffffffffffffffffffffff16815250736b175474e89094c44da98b954eedeac495271d0f73ffffffffffffffffffffffffffffffffffffffff166101409073ffffffffffffffffffffffffffffffffffffffff1681525073226e7af139a0f34c6771deb252f9988876ac1ced600760006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506a1b4c0595a86aa1c1000000600b553480156200011957600080fd5b506040518060400160405280601581526020017f436f6e636176652050726573616c6520546f6b656e00000000000000000000008152506040518060400160405280600481526020017f70434e560000000000000000000000000000000000000000000000000000000081525060128260009080519060200190620001a092919062000280565b508160019080519060200190620001b992919062000280565b508060ff1660808160ff16815250504660a08181525050620001e0620001f060201b60201c565b60c0818152505050505062000531565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f600060405162000224919062000440565b60405180910390207fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6463060405160200162000265959493929190620004d4565b60405160208183030381529060405280519060200120905090565b8280546200028e906200035f565b90600052602060002090601f016020900481019282620002b25760008555620002fe565b82601f10620002cd57805160ff1916838001178555620002fe565b82800160010185558215620002fe579182015b82811115620002fd578251825591602001919060010190620002e0565b5b5090506200030d919062000311565b5090565b5b808211156200032c57600081600090555060010162000312565b5090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200037857607f821691505b602082108114156200038f576200038e62000330565b5b50919050565b600081905092915050565b60008190508160005260206000209050919050565b60008154620003c4816200035f565b620003d0818662000395565b94506001821660008114620003ee5760018114620004005762000437565b60ff1983168652818601935062000437565b6200040b85620003a0565b60005b838110156200042f578154818901526001820191506020810190506200040e565b838801955050505b50505092915050565b60006200044e8284620003b5565b915081905092915050565b6000819050919050565b6200046e8162000459565b82525050565b6000819050919050565b620004898162000474565b82525050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620004bc826200048f565b9050919050565b620004ce81620004af565b82525050565b600060a082019050620004eb600083018862000463565b620004fa602083018762000463565b62000509604083018662000463565b6200051860608301856200047e565b620005276080830184620004c3565b9695505050505050565b60805160a05160c05160e05161010051610120516101405161425b620005c46000396000818161196701528181611f4c01526127fe015260008181611b7301526128530152600081816108cb015281816114430152818161147101528181611ac60152611af40152600081816112420152611b9701526000611323015260006112ef015260006112c9015261425b6000f3fe608060405234801561001057600080fd5b506004361061027f5760003560e01c8063500432de1161015c578063b0e4556f116100ce578063d5abeb0111610087578063d5abeb01146107f3578063dd62ed3e14610811578063e0bab4c414610841578063e1de7c801461085f578063f0f442601461087d578063f4b6c10b146108995761027f565b8063b0e4556f1461071d578063b7dec1b71461073b578063c2b40ae414610759578063c89380fd14610789578063d505accf146107b9578063d51a020b146107d55761027f565b8063839bacea11610120578063839bacea1461063357806395d89b411461066357806396fb8b10146106815780639b68c01a146106b1578063a2309ff8146106cf578063a9059cbb146106ed5761027f565b8063500432de1461057b57806361d027b31461059957806370a08231146105b75780637ecebe00146105e7578063839006f2146106175761027f565b80632c4e722e116101f557806330bbb032116101b957806330bbb032146104a5578063313ce567146104d55780633644e515146104f357806344e46dff146105115780634563f30a1461052d578063472598ac1461054b5761027f565b80632c4e722e1461040f5780632d7ecd111461042d5780632eb4a7ab1461044b5780632f29d8c51461046957806330adf81f146104875761027f565b80630cf5e156116102475780630cf5e1561461033d578063103a34721461035957806318160ddd146103895780631e9a6950146103a757806323b872dd146103c357806329175b59146103f35761027f565b806301edf6a01461028457806306fdde03146102a2578063095ea7b3146102c057806309e69ede146102f05780630b0eee3014610321575b600080fd5b61028c6108c9565b6040516102999190612fd5565b60405180910390f35b6102aa6108ed565b6040516102b79190613089565b60405180910390f35b6102da60048036038101906102d5919061313f565b61097b565b6040516102e7919061319a565b60405180910390f35b61030a600480360381019061030591906131b5565b610a6d565b6040516103189291906131e2565b60405180910390f35b61033b6004803603810190610336919061313f565b610a91565b005b61035760048036038101906103529190613241565b610d8f565b005b610373600480360381019061036e9190613281565b610eda565b6040516103809190612fd5565b60405180910390f35b610391610eff565b60405161039e9190612fd5565b60405180910390f35b6103c160048036038101906103bc919061313f565b610f05565b005b6103dd60048036038101906103d891906132c1565b6110d8565b6040516103ea919061319a565b60405180910390f35b61040d600480360381019061040891906131b5565b6110f9565b005b61041761121f565b6040516104249190612fd5565b60405180910390f35b610435611225565b604051610442919061319a565b60405180910390f35b610453611238565b6040516104609190613323565b60405180910390f35b61047161123e565b60405161047e9190612fd5565b60405180910390f35b61048f611271565b60405161049c9190613323565b60405180910390f35b6104bf60048036038101906104ba91906131b5565b611295565b6040516104cc9190612fd5565b60405180910390f35b6104dd6112c7565b6040516104ea919061335a565b60405180910390f35b6104fb6112eb565b6040516105089190613323565b60405180910390f35b61052b600480360381019061052691906133a1565b611348565b005b6105356113f5565b604051610542919061319a565b60405180910390f35b610565600480360381019061056091906131b5565b611408565b6040516105729190612fd5565b60405180910390f35b61058361143f565b6040516105909190612fd5565b60405180910390f35b6105a16114c9565b6040516105ae91906133dd565b60405180910390f35b6105d160048036038101906105cc91906131b5565b6114ef565b6040516105de9190612fd5565b60405180910390f35b61060160048036038101906105fc91906131b5565b611507565b60405161060e9190612fd5565b60405180910390f35b610631600480360381019061062c91906131b5565b61151f565b005b61064d600480360381019061064891906131b5565b611770565b60405161065a9190612fd5565b60405180910390f35b61066b6118d5565b6040516106789190613089565b60405180910390f35b61069b60048036038101906106969190613489565b611963565b6040516106a89190612fd5565b60405180910390f35b6106b9611ac2565b6040516106c69190612fd5565b60405180910390f35b6106d7611b4c565b6040516106e49190612fd5565b60405180910390f35b6107076004803603810190610702919061313f565b611b52565b604051610714919061319a565b60405180910390f35b610725611b71565b60405161073291906135d3565b60405180910390f35b610743611b95565b6040516107509190612fd5565b60405180910390f35b610773600480360381019061076e91906135ee565b611bb9565b6040516107809190613323565b60405180910390f35b6107a3600480360381019061079e919061361b565b611bdd565b6040516107b09190612fd5565b60405180910390f35b6107d360048036038101906107ce91906136b5565b611bfa565b005b6107dd611ef9565b6040516107ea9190613778565b60405180910390f35b6107fb611f1f565b6040516108089190612fd5565b60405180910390f35b61082b60048036038101906108269190613793565b611f25565b6040516108389190612fd5565b60405180910390f35b610849611f4a565b60405161085691906135d3565b60405180910390f35b610867611f6e565b6040516108749190612fd5565b60405180910390f35b610897600480360381019061089291906131b5565b61202b565b005b6108b360048036038101906108ae919061313f565b612136565b6040516108c09190612fd5565b60405180910390f35b7f000000000000000000000000000000000000000000000000000000000000000081565b600080546108fa90613802565b80601f016020809104026020016040519081016040528092919081815260200182805461092690613802565b80156109735780601f1061094857610100808354040283529160200191610973565b820191906000526020600020905b81548152906001019060200180831161095657829003601f168201915b505050505081565b600081600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92584604051610a5b9190612fd5565b60405180910390a36001905092915050565b600e6020528060005260406000206000915090508060000154908060010154905082565b600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610b21576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b1890613880565b60405180910390fd5b60008073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610cad57600b548211156040518060400160405280600781526020017f21414d4f554e540000000000000000000000000000000000000000000000000081525090610bd4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bcb9190613089565b60405180910390fd5b5081600b54610be391906138cf565b9050600c548110156040518060400160405280600781526020017f21414d4f554e540000000000000000000000000000000000000000000000000081525090610c62576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c599190613089565b60405180910390fd5b5080600b819055507fc5c33ab5e6467a87a001b77efc45aa27f6890b08565b99b7101becae3d82320e8383600b54604051610c9f93929190613903565b60405180910390a150610d8b565b81600c54610cbb919061393a565b9050600b548111156040518060400160405280600781526020017f21414d4f554e540000000000000000000000000000000000000000000000000081525090610d3a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d319190613089565b60405180910390fd5b5080600c81905550610d4c8383612217565b7fc5c33ab5e6467a87a001b77efc45aa27f6890b08565b99b7101becae3d82320e8383600b54604051610d8193929190613903565b60405180910390a1505b5050565b600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610e1f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e1690613880565b60405180910390fd5b60008111610e62576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e59906139dc565b60405180910390fd5b60098290806001815401808255809150506001900390600052602060002001600090919091909150558160088190555080600a819055507fc9714d3f318877e8200732d0c1e80981c397cbde2518a4b32c68d9ba1eab3b50600854600a54604051610ece9291906139fc565b60405180910390a15050565b600f602052816000526040600020602052806000526040600020600091509150505481565b60025481565b600d60009054906101000a900460ff16610f54576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f4b90613a71565b60405180910390fd5b6000600e60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506000610fa33384612136565b905082826001016000828254610fb9919061393a565b92505081905550610fca33846122e7565b600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166340c10f1985836040518363ffffffff1660e01b8152600401611027929190613a91565b6020604051808303816000875af1158015611046573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061106a9190613acf565b508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f5cdf07ad0fc222442720b108e3ed4c4640f0fadc2ab2253e66f259a0fea8348085846040516110ca9291906131e2565b60405180910390a350505050565b60006110e58484846123b7565b6110f0848484612524565b90509392505050565b600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611189576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161118090613880565b60405180910390fd5b6001600d60006101000a81548160ff02191690831515021790555080600660006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f08a70b59fdbd316124e15d456e01ec3de1649714455b3f77a6302bb3a3feb95e8160405161121491906133dd565b60405180910390a150565b600a5481565b600d60009054906101000a900460ff1681565b60085481565b60007f00000000000000000000000000000000000000000000000000000000000000004261126c91906138cf565b905090565b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b6000600b546112a383611770565b670de0b6b3a76400006112b69190613afc565b6112c09190613b85565b9050919050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60007f000000000000000000000000000000000000000000000000000000000000000046146113215761131c61276e565b611343565b7f00000000000000000000000000000000000000000000000000000000000000005b905090565b600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146113d8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113cf90613880565b60405180910390fd5b80600d60016101000a81548160ff02191690831515021790555050565b600d60019054906101000a900460ff1681565b6000670de0b6b3a764000061141c83611295565b611424611f6e565b61142e9190613afc565b6114389190613b85565b9050919050565b60007f000000000000000000000000000000000000000000000000000000000000000061146a61123e565b116114ba577f000000000000000000000000000000000000000000000000000000000000000061149861123e565b67016345785d8a00006114ab9190613afc565b6114b59190613b85565b6114c4565b67016345785d8a00005b905090565b600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60036020528060005260406000206000915090505481565b60056020528060005260406000206000915090505481565b600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146115af576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016115a690613880565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561165257600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc479081150290604051600060405180830381858888f1935050505015801561164c573d6000803e3d6000fd5b5061176d565b8073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016116ca91906133dd565b602060405180830381865afa1580156116e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061170b9190613acf565b6040518363ffffffff1660e01b8152600401611728929190613a91565b6020604051808303816000875af1158015611747573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061176b9190613bcb565b505b50565b6000600d60009054906101000a900460ff1661178f57600090506118d0565b6000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156117fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118229190613acf565b141561183157600090506118d0565b6000600e60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518060400160405290816000820154815260200160018201548152505090508060200151670de0b6b3a76400006118a9611ac2565b83600001516118b89190613afc565b6118c29190613b85565b6118cc91906138cf565b9150505b919050565b600180546118e290613802565b80601f016020809104026020016040519081016040528092919081815260200182805461190e90613802565b801561195b5780601f106119305761010080835404028352916020019161195b565b820191906000526020600020905b81548152906001019060200180831161193e57829003601f168201915b505050505081565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff16146040518060400160405280600981526020017f21544f4b454e5f494e000000000000000000000000000000000000000000000081525090611a2b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611a229190613089565b60405180910390fd5b508973ffffffffffffffffffffffffffffffffffffffff1663d505accf33308b898989896040518863ffffffff1660e01b8152600401611a719796959493929190613bf8565b600060405180830381600087803b158015611a8b57600080fd5b505af1158015611a9f573d6000803e3d6000fd5b50505050611ab2338c8c8c8c8c8c6127fa565b90509a9950505050505050505050565b60007f0000000000000000000000000000000000000000000000000000000000000000611aed61123e565b11611b3d577f0000000000000000000000000000000000000000000000000000000000000000611b1b61123e565b670de0b6b3a7640000611b2e9190613afc565b611b389190613b85565b611b47565b670de0b6b3a76400005b905090565b600c5481565b6000611b5f3384846123b7565b611b698383612cf9565b905092915050565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b60098181548110611bc957600080fd5b906000526020600020016000915090505481565b6000611bee338888888888886127fa565b90509695505050505050565b42841015611c3d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c3490613cb3565b60405180910390fd5b6000611c476112eb565b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9898989600560008e73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000815480929190600101919050558a604051602001611ccf96959493929190613cd3565b60405160208183030381529060405280519060200120604051602001611cf6929190613dac565b604051602081830303815290604052805190602001209050600060018286868660405160008152602001604052604051611d339493929190613de3565b6020604051602081039080840390855afa158015611d55573d6000803e3d6000fd5b505050602060405103519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614158015611dc957508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b611e08576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611dff90613e74565b60405180910390fd5b86600460008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555050508573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92587604051611ee89190612fd5565b60405180910390a350505050505050565b600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600b5481565b6004602052816000526040600020602052806000526040600020600091509150505481565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000670de0b6b3a7640000611f8161143f565b600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611fee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120129190613acf565b61201c9190613afc565b6120269190613b85565b905090565b600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146120bb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120b290613880565b60405180910390fd5b80600760006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f3c864541ef71378c6229510ed90f376565ee42d9c5e0904a984a9e863e6db44f8160405161212b91906133dd565b60405180910390a150565b600061214183611770565b8211156040518060400160405280600781526020017f21414d4f554e5400000000000000000000000000000000000000000000000000815250906121bb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016121b29190613089565b60405180910390fd5b5060006121c784611770565b83670de0b6b3a76400006121db9190613afc565b6121e59190613b85565b9050670de0b6b3a7640000816121fa86611408565b6122049190613afc565b61220e9190613b85565b91505092915050565b8060026000828254612229919061393a565b9250508190555080600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516122db9190612fd5565b60405180910390a35050565b80600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461233691906138cf565b9250508190555080600260008282540392505081905550600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516123ab9190612fd5565b60405180910390a35050565b600d60019054906101000a900460ff1615612407576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016123fe90613ee0565b60405180910390fd5b6000600e60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506000600e60008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050600081600001548260010154856124a49190613afc565b6124ae9190613b85565b9050808260010160008282546124c491906138cf565b92505081905550838260000160008282546124df91906138cf565b92505081905550808360010160008282546124fa919061393a565b9250508190555083836000016000828254612515919061393a565b92505081905550505050505050565b600080600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811461265a5782816125d991906138cf565b600460008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b82600360008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546126a991906138cf565b9250508190555082600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8560405161275a9190612fd5565b60405180910390a360019150509392505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60006040516127a09190613f9f565b60405180910390207fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc646306040516020016127df959493929190613fb6565b60405160208183030381529060405280519060200120905090565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614806128a157507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16145b6040518060400160405280600981526020017f21544f4b454e5f494e000000000000000000000000000000000000000000000081525090612918576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161290f9190613089565b60405180910390fd5b5061298f838380806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050506008548988604051602001612974929190614072565b60405160208183030381529060405280519060200120612e0d565b6129ce576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016129c5906140ea565b60405180910390fd5b600084600f6000600854815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054612a2e919061393a565b9050858111156040518060400160405280600781526020017f21414d4f554e540000000000000000000000000000000000000000000000000081525090612aab576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612aa29190613089565b60405180910390fd5b5080600f6000600854815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550600a54670de0b6b3a764000086612b1a9190613afc565b612b249190613b85565b9150600b5482600c54612b37919061393a565b11156040518060400160405280600781526020017f21414d4f554e540000000000000000000000000000000000000000000000000081525090612bb0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612ba79190613089565b60405180910390fd5b506000600e60008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905082816000016000828254612c08919061393a565b9250508190555082600c6000828254612c21919061393a565b92505081905550612c778a600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16888b73ffffffffffffffffffffffffffffffffffffffff16612e24909392919063ffffffff16565b612c818984612217565b8873ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff167f68c721f9a38584842eb66945b43c95066397ce5f2576fcc0588471c85d209d0c8589600c54604051612ce49392919061410a565b60405180910390a35050979650505050505050565b600081600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254612d4a91906138cf565b9250508190555081600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051612dfb9190612fd5565b60405180910390a36001905092915050565b600082612e1a8584612ee7565b1490509392505050565b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff8416602482015282604482015260008060648360008a5af1915050612ea181612f5c565b612ee0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612ed79061418d565b60405180910390fd5b5050505050565b60008082905060005b8451811015612f51576000858281518110612f0e57612f0d6141ad565b5b60200260200101519050808311612f3057612f298382612fa5565b9250612f3d565b612f3a8184612fa5565b92505b508080612f49906141dc565b915050612ef0565b508091505092915050565b60003d82612f6e57806000803e806000fd5b8060208114612f885760008114612f995760009250612f9e565b816000803e60005115159250612f9e565b600192505b5050919050565b600082600052816020526040600020905092915050565b6000819050919050565b612fcf81612fbc565b82525050565b6000602082019050612fea6000830184612fc6565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561302a57808201518184015260208101905061300f565b83811115613039576000848401525b50505050565b6000601f19601f8301169050919050565b600061305b82612ff0565b6130658185612ffb565b935061307581856020860161300c565b61307e8161303f565b840191505092915050565b600060208201905081810360008301526130a38184613050565b905092915050565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006130e0826130b5565b9050919050565b6130f0816130d5565b81146130fb57600080fd5b50565b60008135905061310d816130e7565b92915050565b61311c81612fbc565b811461312757600080fd5b50565b60008135905061313981613113565b92915050565b60008060408385031215613156576131556130ab565b5b6000613164858286016130fe565b92505060206131758582860161312a565b9150509250929050565b60008115159050919050565b6131948161317f565b82525050565b60006020820190506131af600083018461318b565b92915050565b6000602082840312156131cb576131ca6130ab565b5b60006131d9848285016130fe565b91505092915050565b60006040820190506131f76000830185612fc6565b6132046020830184612fc6565b9392505050565b6000819050919050565b61321e8161320b565b811461322957600080fd5b50565b60008135905061323b81613215565b92915050565b60008060408385031215613258576132576130ab565b5b60006132668582860161322c565b92505060206132778582860161312a565b9150509250929050565b60008060408385031215613298576132976130ab565b5b60006132a68582860161322c565b92505060206132b7858286016130fe565b9150509250929050565b6000806000606084860312156132da576132d96130ab565b5b60006132e8868287016130fe565b93505060206132f9868287016130fe565b925050604061330a8682870161312a565b9150509250925092565b61331d8161320b565b82525050565b60006020820190506133386000830184613314565b92915050565b600060ff82169050919050565b6133548161333e565b82525050565b600060208201905061336f600083018461334b565b92915050565b61337e8161317f565b811461338957600080fd5b50565b60008135905061339b81613375565b92915050565b6000602082840312156133b7576133b66130ab565b5b60006133c58482850161338c565b91505092915050565b6133d7816130d5565b82525050565b60006020820190506133f260008301846133ce565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261341d5761341c6133f8565b5b8235905067ffffffffffffffff81111561343a576134396133fd565b5b60208301915083602082028301111561345657613455613402565b5b9250929050565b6134668161333e565b811461347157600080fd5b50565b6000813590506134838161345d565b92915050565b6000806000806000806000806000806101208b8d0312156134ad576134ac6130ab565b5b60006134bb8d828e016130fe565b9a505060206134cc8d828e016130fe565b99505060406134dd8d828e0161312a565b98505060606134ee8d828e0161312a565b97505060808b013567ffffffffffffffff81111561350f5761350e6130b0565b5b61351b8d828e01613407565b965096505060a061352e8d828e0161312a565b94505060c061353f8d828e01613474565b93505060e06135508d828e0161322c565b9250506101006135628d828e0161322c565b9150509295989b9194979a5092959850565b6000819050919050565b600061359961359461358f846130b5565b613574565b6130b5565b9050919050565b60006135ab8261357e565b9050919050565b60006135bd826135a0565b9050919050565b6135cd816135b2565b82525050565b60006020820190506135e860008301846135c4565b92915050565b600060208284031215613604576136036130ab565b5b60006136128482850161312a565b91505092915050565b60008060008060008060a08789031215613638576136376130ab565b5b600061364689828a016130fe565b965050602061365789828a016130fe565b955050604061366889828a0161312a565b945050606061367989828a0161312a565b935050608087013567ffffffffffffffff81111561369a576136996130b0565b5b6136a689828a01613407565b92509250509295509295509295565b600080600080600080600060e0888a0312156136d4576136d36130ab565b5b60006136e28a828b016130fe565b97505060206136f38a828b016130fe565b96505060406137048a828b0161312a565b95505060606137158a828b0161312a565b94505060806137268a828b01613474565b93505060a06137378a828b0161322c565b92505060c06137488a828b0161322c565b91505092959891949750929550565b6000613762826135a0565b9050919050565b61377281613757565b82525050565b600060208201905061378d6000830184613769565b92915050565b600080604083850312156137aa576137a96130ab565b5b60006137b8858286016130fe565b92505060206137c9858286016130fe565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061381a57607f821691505b6020821081141561382e5761382d6137d3565b5b50919050565b7f21434f4e43415645000000000000000000000000000000000000000000000000600082015250565b600061386a600883612ffb565b915061387582613834565b602082019050919050565b600060208201905081810360008301526138998161385d565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006138da82612fbc565b91506138e583612fbc565b9250828210156138f8576138f76138a0565b5b828203905092915050565b600060608201905061391860008301866133ce565b6139256020830185612fc6565b6139326040830184612fc6565b949350505050565b600061394582612fbc565b915061395083612fbc565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115613985576139846138a0565b5b828201905092915050565b7f2152415445000000000000000000000000000000000000000000000000000000600082015250565b60006139c6600583612ffb565b91506139d182613990565b602082019050919050565b600060208201905081810360008301526139f5816139b9565b9050919050565b6000604082019050613a116000830185613314565b613a1e6020830184612fc6565b9392505050565b7f2152454445454d41424c45000000000000000000000000000000000000000000600082015250565b6000613a5b600b83612ffb565b9150613a6682613a25565b602082019050919050565b60006020820190508181036000830152613a8a81613a4e565b9050919050565b6000604082019050613aa660008301856133ce565b613ab36020830184612fc6565b9392505050565b600081519050613ac981613113565b92915050565b600060208284031215613ae557613ae46130ab565b5b6000613af384828501613aba565b91505092915050565b6000613b0782612fbc565b9150613b1283612fbc565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615613b4b57613b4a6138a0565b5b828202905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000613b9082612fbc565b9150613b9b83612fbc565b925082613bab57613baa613b56565b5b828204905092915050565b600081519050613bc581613375565b92915050565b600060208284031215613be157613be06130ab565b5b6000613bef84828501613bb6565b91505092915050565b600060e082019050613c0d600083018a6133ce565b613c1a60208301896133ce565b613c276040830188612fc6565b613c346060830187612fc6565b613c41608083018661334b565b613c4e60a0830185613314565b613c5b60c0830184613314565b98975050505050505050565b7f5045524d49545f444541444c494e455f45585049524544000000000000000000600082015250565b6000613c9d601783612ffb565b9150613ca882613c67565b602082019050919050565b60006020820190508181036000830152613ccc81613c90565b9050919050565b600060c082019050613ce86000830189613314565b613cf560208301886133ce565b613d0260408301876133ce565b613d0f6060830186612fc6565b613d1c6080830185612fc6565b613d2960a0830184612fc6565b979650505050505050565b600081905092915050565b7f1901000000000000000000000000000000000000000000000000000000000000600082015250565b6000613d75600283613d34565b9150613d8082613d3f565b600282019050919050565b6000819050919050565b613da6613da18261320b565b613d8b565b82525050565b6000613db782613d68565b9150613dc38285613d95565b602082019150613dd38284613d95565b6020820191508190509392505050565b6000608082019050613df86000830187613314565b613e05602083018661334b565b613e126040830185613314565b613e1f6060830184613314565b95945050505050565b7f494e56414c49445f5349474e4552000000000000000000000000000000000000600082015250565b6000613e5e600e83612ffb565b9150613e6982613e28565b602082019050919050565b60006020820190508181036000830152613e8d81613e51565b9050919050565b7f5041555345440000000000000000000000000000000000000000000000000000600082015250565b6000613eca600683612ffb565b9150613ed582613e94565b602082019050919050565b60006020820190508181036000830152613ef981613ebd565b9050919050565b600081905092915050565b60008190508160005260206000209050919050565b60008154613f2d81613802565b613f378186613f00565b94506001821660008114613f525760018114613f6357613f96565b60ff19831686528186019350613f96565b613f6c85613f0b565b60005b83811015613f8e57815481890152600182019150602081019050613f6f565b838801955050505b50505092915050565b6000613fab8284613f20565b915081905092915050565b600060a082019050613fcb6000830188613314565b613fd86020830187613314565b613fe56040830186613314565b613ff26060830185612fc6565b613fff60808301846133ce565b9695505050505050565b60008160601b9050919050565b600061402182614009565b9050919050565b600061403382614016565b9050919050565b61404b614046826130d5565b614028565b82525050565b6000819050919050565b61406c61406782612fbc565b614051565b82525050565b600061407e828561403a565b60148201915061408e828461405b565b6020820191508190509392505050565b7f2150524f4f460000000000000000000000000000000000000000000000000000600082015250565b60006140d4600683612ffb565b91506140df8261409e565b602082019050919050565b60006020820190508181036000830152614103816140c7565b9050919050565b600060608201905061411f6000830186612fc6565b61412c6020830185612fc6565b6141396040830184612fc6565b949350505050565b7f5452414e534645525f46524f4d5f4641494c4544000000000000000000000000600082015250565b6000614177601483612ffb565b915061418282614141565b602082019050919050565b600060208201905081810360008301526141a68161416a565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006141e782612fbc565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561421a576142196138a0565b5b60018201905091905056fea2646970667358221220217a641487b29466e91201f217f80331f68cd4f02432c373946b117282bb64f264736f6c634300080b0033
Deployed ByteCode
0x608060405234801561001057600080fd5b506004361061027f5760003560e01c8063500432de1161015c578063b0e4556f116100ce578063d5abeb0111610087578063d5abeb01146107f3578063dd62ed3e14610811578063e0bab4c414610841578063e1de7c801461085f578063f0f442601461087d578063f4b6c10b146108995761027f565b8063b0e4556f1461071d578063b7dec1b71461073b578063c2b40ae414610759578063c89380fd14610789578063d505accf146107b9578063d51a020b146107d55761027f565b8063839bacea11610120578063839bacea1461063357806395d89b411461066357806396fb8b10146106815780639b68c01a146106b1578063a2309ff8146106cf578063a9059cbb146106ed5761027f565b8063500432de1461057b57806361d027b31461059957806370a08231146105b75780637ecebe00146105e7578063839006f2146106175761027f565b80632c4e722e116101f557806330bbb032116101b957806330bbb032146104a5578063313ce567146104d55780633644e515146104f357806344e46dff146105115780634563f30a1461052d578063472598ac1461054b5761027f565b80632c4e722e1461040f5780632d7ecd111461042d5780632eb4a7ab1461044b5780632f29d8c51461046957806330adf81f146104875761027f565b80630cf5e156116102475780630cf5e1561461033d578063103a34721461035957806318160ddd146103895780631e9a6950146103a757806323b872dd146103c357806329175b59146103f35761027f565b806301edf6a01461028457806306fdde03146102a2578063095ea7b3146102c057806309e69ede146102f05780630b0eee3014610321575b600080fd5b61028c6108c9565b6040516102999190612fd5565b60405180910390f35b6102aa6108ed565b6040516102b79190613089565b60405180910390f35b6102da60048036038101906102d5919061313f565b61097b565b6040516102e7919061319a565b60405180910390f35b61030a600480360381019061030591906131b5565b610a6d565b6040516103189291906131e2565b60405180910390f35b61033b6004803603810190610336919061313f565b610a91565b005b61035760048036038101906103529190613241565b610d8f565b005b610373600480360381019061036e9190613281565b610eda565b6040516103809190612fd5565b60405180910390f35b610391610eff565b60405161039e9190612fd5565b60405180910390f35b6103c160048036038101906103bc919061313f565b610f05565b005b6103dd60048036038101906103d891906132c1565b6110d8565b6040516103ea919061319a565b60405180910390f35b61040d600480360381019061040891906131b5565b6110f9565b005b61041761121f565b6040516104249190612fd5565b60405180910390f35b610435611225565b604051610442919061319a565b60405180910390f35b610453611238565b6040516104609190613323565b60405180910390f35b61047161123e565b60405161047e9190612fd5565b60405180910390f35b61048f611271565b60405161049c9190613323565b60405180910390f35b6104bf60048036038101906104ba91906131b5565b611295565b6040516104cc9190612fd5565b60405180910390f35b6104dd6112c7565b6040516104ea919061335a565b60405180910390f35b6104fb6112eb565b6040516105089190613323565b60405180910390f35b61052b600480360381019061052691906133a1565b611348565b005b6105356113f5565b604051610542919061319a565b60405180910390f35b610565600480360381019061056091906131b5565b611408565b6040516105729190612fd5565b60405180910390f35b61058361143f565b6040516105909190612fd5565b60405180910390f35b6105a16114c9565b6040516105ae91906133dd565b60405180910390f35b6105d160048036038101906105cc91906131b5565b6114ef565b6040516105de9190612fd5565b60405180910390f35b61060160048036038101906105fc91906131b5565b611507565b60405161060e9190612fd5565b60405180910390f35b610631600480360381019061062c91906131b5565b61151f565b005b61064d600480360381019061064891906131b5565b611770565b60405161065a9190612fd5565b60405180910390f35b61066b6118d5565b6040516106789190613089565b60405180910390f35b61069b60048036038101906106969190613489565b611963565b6040516106a89190612fd5565b60405180910390f35b6106b9611ac2565b6040516106c69190612fd5565b60405180910390f35b6106d7611b4c565b6040516106e49190612fd5565b60405180910390f35b6107076004803603810190610702919061313f565b611b52565b604051610714919061319a565b60405180910390f35b610725611b71565b60405161073291906135d3565b60405180910390f35b610743611b95565b6040516107509190612fd5565b60405180910390f35b610773600480360381019061076e91906135ee565b611bb9565b6040516107809190613323565b60405180910390f35b6107a3600480360381019061079e919061361b565b611bdd565b6040516107b09190612fd5565b60405180910390f35b6107d360048036038101906107ce91906136b5565b611bfa565b005b6107dd611ef9565b6040516107ea9190613778565b60405180910390f35b6107fb611f1f565b6040516108089190612fd5565b60405180910390f35b61082b60048036038101906108269190613793565b611f25565b6040516108389190612fd5565b60405180910390f35b610849611f4a565b60405161085691906135d3565b60405180910390f35b610867611f6e565b6040516108749190612fd5565b60405180910390f35b610897600480360381019061089291906131b5565b61202b565b005b6108b360048036038101906108ae919061313f565b612136565b6040516108c09190612fd5565b60405180910390f35b7f0000000000000000000000000000000000000000000000000000000003c2670081565b600080546108fa90613802565b80601f016020809104026020016040519081016040528092919081815260200182805461092690613802565b80156109735780601f1061094857610100808354040283529160200191610973565b820191906000526020600020905b81548152906001019060200180831161095657829003601f168201915b505050505081565b600081600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92584604051610a5b9190612fd5565b60405180910390a36001905092915050565b600e6020528060005260406000206000915090508060000154908060010154905082565b600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610b21576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b1890613880565b60405180910390fd5b60008073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610cad57600b548211156040518060400160405280600781526020017f21414d4f554e540000000000000000000000000000000000000000000000000081525090610bd4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bcb9190613089565b60405180910390fd5b5081600b54610be391906138cf565b9050600c548110156040518060400160405280600781526020017f21414d4f554e540000000000000000000000000000000000000000000000000081525090610c62576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c599190613089565b60405180910390fd5b5080600b819055507fc5c33ab5e6467a87a001b77efc45aa27f6890b08565b99b7101becae3d82320e8383600b54604051610c9f93929190613903565b60405180910390a150610d8b565b81600c54610cbb919061393a565b9050600b548111156040518060400160405280600781526020017f21414d4f554e540000000000000000000000000000000000000000000000000081525090610d3a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d319190613089565b60405180910390fd5b5080600c81905550610d4c8383612217565b7fc5c33ab5e6467a87a001b77efc45aa27f6890b08565b99b7101becae3d82320e8383600b54604051610d8193929190613903565b60405180910390a1505b5050565b600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610e1f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e1690613880565b60405180910390fd5b60008111610e62576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e59906139dc565b60405180910390fd5b60098290806001815401808255809150506001900390600052602060002001600090919091909150558160088190555080600a819055507fc9714d3f318877e8200732d0c1e80981c397cbde2518a4b32c68d9ba1eab3b50600854600a54604051610ece9291906139fc565b60405180910390a15050565b600f602052816000526040600020602052806000526040600020600091509150505481565b60025481565b600d60009054906101000a900460ff16610f54576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f4b90613a71565b60405180910390fd5b6000600e60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506000610fa33384612136565b905082826001016000828254610fb9919061393a565b92505081905550610fca33846122e7565b600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166340c10f1985836040518363ffffffff1660e01b8152600401611027929190613a91565b6020604051808303816000875af1158015611046573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061106a9190613acf565b508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f5cdf07ad0fc222442720b108e3ed4c4640f0fadc2ab2253e66f259a0fea8348085846040516110ca9291906131e2565b60405180910390a350505050565b60006110e58484846123b7565b6110f0848484612524565b90509392505050565b600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611189576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161118090613880565b60405180910390fd5b6001600d60006101000a81548160ff02191690831515021790555080600660006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f08a70b59fdbd316124e15d456e01ec3de1649714455b3f77a6302bb3a3feb95e8160405161121491906133dd565b60405180910390a150565b600a5481565b600d60009054906101000a900460ff1681565b60085481565b60007f0000000000000000000000000000000000000000000000000000000061e122544261126c91906138cf565b905090565b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b6000600b546112a383611770565b670de0b6b3a76400006112b69190613afc565b6112c09190613b85565b9050919050565b7f000000000000000000000000000000000000000000000000000000000000001281565b60007f000000000000000000000000000000000000000000000000000000000000000146146113215761131c61276e565b611343565b7fb56ca4693fdb6ac6263e1072b2f3aeeb0cbb15df689937d45305506f01bc53cb5b905090565b600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146113d8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113cf90613880565b60405180910390fd5b80600d60016101000a81548160ff02191690831515021790555050565b600d60019054906101000a900460ff1681565b6000670de0b6b3a764000061141c83611295565b611424611f6e565b61142e9190613afc565b6114389190613b85565b9050919050565b60007f0000000000000000000000000000000000000000000000000000000003c2670061146a61123e565b116114ba577f0000000000000000000000000000000000000000000000000000000003c2670061149861123e565b67016345785d8a00006114ab9190613afc565b6114b59190613b85565b6114c4565b67016345785d8a00005b905090565b600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60036020528060005260406000206000915090505481565b60056020528060005260406000206000915090505481565b600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146115af576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016115a690613880565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561165257600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc479081150290604051600060405180830381858888f1935050505015801561164c573d6000803e3d6000fd5b5061176d565b8073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016116ca91906133dd565b602060405180830381865afa1580156116e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061170b9190613acf565b6040518363ffffffff1660e01b8152600401611728929190613a91565b6020604051808303816000875af1158015611747573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061176b9190613bcb565b505b50565b6000600d60009054906101000a900460ff1661178f57600090506118d0565b6000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156117fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118229190613acf565b141561183157600090506118d0565b6000600e60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518060400160405290816000820154815260200160018201548152505090508060200151670de0b6b3a76400006118a9611ac2565b83600001516118b89190613afc565b6118c29190613b85565b6118cc91906138cf565b9150505b919050565b600180546118e290613802565b80601f016020809104026020016040519081016040528092919081815260200182805461190e90613802565b801561195b5780601f106119305761010080835404028352916020019161195b565b820191906000526020600020905b81548152906001019060200180831161193e57829003601f168201915b505050505081565b60007f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f73ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff16146040518060400160405280600981526020017f21544f4b454e5f494e000000000000000000000000000000000000000000000081525090611a2b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611a229190613089565b60405180910390fd5b508973ffffffffffffffffffffffffffffffffffffffff1663d505accf33308b898989896040518863ffffffff1660e01b8152600401611a719796959493929190613bf8565b600060405180830381600087803b158015611a8b57600080fd5b505af1158015611a9f573d6000803e3d6000fd5b50505050611ab2338c8c8c8c8c8c6127fa565b90509a9950505050505050505050565b60007f0000000000000000000000000000000000000000000000000000000003c26700611aed61123e565b11611b3d577f0000000000000000000000000000000000000000000000000000000003c26700611b1b61123e565b670de0b6b3a7640000611b2e9190613afc565b611b389190613b85565b611b47565b670de0b6b3a76400005b905090565b600c5481565b6000611b5f3384846123b7565b611b698383612cf9565b905092915050565b7f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e81565b7f0000000000000000000000000000000000000000000000000000000061e1225481565b60098181548110611bc957600080fd5b906000526020600020016000915090505481565b6000611bee338888888888886127fa565b90509695505050505050565b42841015611c3d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c3490613cb3565b60405180910390fd5b6000611c476112eb565b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9898989600560008e73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000815480929190600101919050558a604051602001611ccf96959493929190613cd3565b60405160208183030381529060405280519060200120604051602001611cf6929190613dac565b604051602081830303815290604052805190602001209050600060018286868660405160008152602001604052604051611d339493929190613de3565b6020604051602081039080840390855afa158015611d55573d6000803e3d6000fd5b505050602060405103519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614158015611dc957508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b611e08576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611dff90613e74565b60405180910390fd5b86600460008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555050508573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92587604051611ee89190612fd5565b60405180910390a350505050505050565b600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600b5481565b6004602052816000526040600020602052806000526040600020600091509150505481565b7f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f81565b6000670de0b6b3a7640000611f8161143f565b600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611fee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120129190613acf565b61201c9190613afc565b6120269190613b85565b905090565b600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146120bb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120b290613880565b60405180910390fd5b80600760006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f3c864541ef71378c6229510ed90f376565ee42d9c5e0904a984a9e863e6db44f8160405161212b91906133dd565b60405180910390a150565b600061214183611770565b8211156040518060400160405280600781526020017f21414d4f554e5400000000000000000000000000000000000000000000000000815250906121bb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016121b29190613089565b60405180910390fd5b5060006121c784611770565b83670de0b6b3a76400006121db9190613afc565b6121e59190613b85565b9050670de0b6b3a7640000816121fa86611408565b6122049190613afc565b61220e9190613b85565b91505092915050565b8060026000828254612229919061393a565b9250508190555080600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516122db9190612fd5565b60405180910390a35050565b80600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461233691906138cf565b9250508190555080600260008282540392505081905550600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516123ab9190612fd5565b60405180910390a35050565b600d60019054906101000a900460ff1615612407576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016123fe90613ee0565b60405180910390fd5b6000600e60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506000600e60008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050600081600001548260010154856124a49190613afc565b6124ae9190613b85565b9050808260010160008282546124c491906138cf565b92505081905550838260000160008282546124df91906138cf565b92505081905550808360010160008282546124fa919061393a565b9250508190555083836000016000828254612515919061393a565b92505081905550505050505050565b600080600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811461265a5782816125d991906138cf565b600460008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b82600360008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546126a991906138cf565b9250508190555082600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8560405161275a9190612fd5565b60405180910390a360019150509392505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60006040516127a09190613f9f565b60405180910390207fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc646306040516020016127df959493929190613fb6565b60405160208183030381529060405280519060200120905090565b60007f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614806128a157507f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16145b6040518060400160405280600981526020017f21544f4b454e5f494e000000000000000000000000000000000000000000000081525090612918576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161290f9190613089565b60405180910390fd5b5061298f838380806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050506008548988604051602001612974929190614072565b60405160208183030381529060405280519060200120612e0d565b6129ce576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016129c5906140ea565b60405180910390fd5b600084600f6000600854815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054612a2e919061393a565b9050858111156040518060400160405280600781526020017f21414d4f554e540000000000000000000000000000000000000000000000000081525090612aab576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612aa29190613089565b60405180910390fd5b5080600f6000600854815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550600a54670de0b6b3a764000086612b1a9190613afc565b612b249190613b85565b9150600b5482600c54612b37919061393a565b11156040518060400160405280600781526020017f21414d4f554e540000000000000000000000000000000000000000000000000081525090612bb0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612ba79190613089565b60405180910390fd5b506000600e60008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905082816000016000828254612c08919061393a565b9250508190555082600c6000828254612c21919061393a565b92505081905550612c778a600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16888b73ffffffffffffffffffffffffffffffffffffffff16612e24909392919063ffffffff16565b612c818984612217565b8873ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff167f68c721f9a38584842eb66945b43c95066397ce5f2576fcc0588471c85d209d0c8589600c54604051612ce49392919061410a565b60405180910390a35050979650505050505050565b600081600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254612d4a91906138cf565b9250508190555081600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051612dfb9190612fd5565b60405180910390a36001905092915050565b600082612e1a8584612ee7565b1490509392505050565b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff8416602482015282604482015260008060648360008a5af1915050612ea181612f5c565b612ee0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612ed79061418d565b60405180910390fd5b5050505050565b60008082905060005b8451811015612f51576000858281518110612f0e57612f0d6141ad565b5b60200260200101519050808311612f3057612f298382612fa5565b9250612f3d565b612f3a8184612fa5565b92505b508080612f49906141dc565b915050612ef0565b508091505092915050565b60003d82612f6e57806000803e806000fd5b8060208114612f885760008114612f995760009250612f9e565b816000803e60005115159250612f9e565b600192505b5050919050565b600082600052816020526040600020905092915050565b6000819050919050565b612fcf81612fbc565b82525050565b6000602082019050612fea6000830184612fc6565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561302a57808201518184015260208101905061300f565b83811115613039576000848401525b50505050565b6000601f19601f8301169050919050565b600061305b82612ff0565b6130658185612ffb565b935061307581856020860161300c565b61307e8161303f565b840191505092915050565b600060208201905081810360008301526130a38184613050565b905092915050565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006130e0826130b5565b9050919050565b6130f0816130d5565b81146130fb57600080fd5b50565b60008135905061310d816130e7565b92915050565b61311c81612fbc565b811461312757600080fd5b50565b60008135905061313981613113565b92915050565b60008060408385031215613156576131556130ab565b5b6000613164858286016130fe565b92505060206131758582860161312a565b9150509250929050565b60008115159050919050565b6131948161317f565b82525050565b60006020820190506131af600083018461318b565b92915050565b6000602082840312156131cb576131ca6130ab565b5b60006131d9848285016130fe565b91505092915050565b60006040820190506131f76000830185612fc6565b6132046020830184612fc6565b9392505050565b6000819050919050565b61321e8161320b565b811461322957600080fd5b50565b60008135905061323b81613215565b92915050565b60008060408385031215613258576132576130ab565b5b60006132668582860161322c565b92505060206132778582860161312a565b9150509250929050565b60008060408385031215613298576132976130ab565b5b60006132a68582860161322c565b92505060206132b7858286016130fe565b9150509250929050565b6000806000606084860312156132da576132d96130ab565b5b60006132e8868287016130fe565b93505060206132f9868287016130fe565b925050604061330a8682870161312a565b9150509250925092565b61331d8161320b565b82525050565b60006020820190506133386000830184613314565b92915050565b600060ff82169050919050565b6133548161333e565b82525050565b600060208201905061336f600083018461334b565b92915050565b61337e8161317f565b811461338957600080fd5b50565b60008135905061339b81613375565b92915050565b6000602082840312156133b7576133b66130ab565b5b60006133c58482850161338c565b91505092915050565b6133d7816130d5565b82525050565b60006020820190506133f260008301846133ce565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261341d5761341c6133f8565b5b8235905067ffffffffffffffff81111561343a576134396133fd565b5b60208301915083602082028301111561345657613455613402565b5b9250929050565b6134668161333e565b811461347157600080fd5b50565b6000813590506134838161345d565b92915050565b6000806000806000806000806000806101208b8d0312156134ad576134ac6130ab565b5b60006134bb8d828e016130fe565b9a505060206134cc8d828e016130fe565b99505060406134dd8d828e0161312a565b98505060606134ee8d828e0161312a565b97505060808b013567ffffffffffffffff81111561350f5761350e6130b0565b5b61351b8d828e01613407565b965096505060a061352e8d828e0161312a565b94505060c061353f8d828e01613474565b93505060e06135508d828e0161322c565b9250506101006135628d828e0161322c565b9150509295989b9194979a5092959850565b6000819050919050565b600061359961359461358f846130b5565b613574565b6130b5565b9050919050565b60006135ab8261357e565b9050919050565b60006135bd826135a0565b9050919050565b6135cd816135b2565b82525050565b60006020820190506135e860008301846135c4565b92915050565b600060208284031215613604576136036130ab565b5b60006136128482850161312a565b91505092915050565b60008060008060008060a08789031215613638576136376130ab565b5b600061364689828a016130fe565b965050602061365789828a016130fe565b955050604061366889828a0161312a565b945050606061367989828a0161312a565b935050608087013567ffffffffffffffff81111561369a576136996130b0565b5b6136a689828a01613407565b92509250509295509295509295565b600080600080600080600060e0888a0312156136d4576136d36130ab565b5b60006136e28a828b016130fe565b97505060206136f38a828b016130fe565b96505060406137048a828b0161312a565b95505060606137158a828b0161312a565b94505060806137268a828b01613474565b93505060a06137378a828b0161322c565b92505060c06137488a828b0161322c565b91505092959891949750929550565b6000613762826135a0565b9050919050565b61377281613757565b82525050565b600060208201905061378d6000830184613769565b92915050565b600080604083850312156137aa576137a96130ab565b5b60006137b8858286016130fe565b92505060206137c9858286016130fe565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061381a57607f821691505b6020821081141561382e5761382d6137d3565b5b50919050565b7f21434f4e43415645000000000000000000000000000000000000000000000000600082015250565b600061386a600883612ffb565b915061387582613834565b602082019050919050565b600060208201905081810360008301526138998161385d565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006138da82612fbc565b91506138e583612fbc565b9250828210156138f8576138f76138a0565b5b828203905092915050565b600060608201905061391860008301866133ce565b6139256020830185612fc6565b6139326040830184612fc6565b949350505050565b600061394582612fbc565b915061395083612fbc565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115613985576139846138a0565b5b828201905092915050565b7f2152415445000000000000000000000000000000000000000000000000000000600082015250565b60006139c6600583612ffb565b91506139d182613990565b602082019050919050565b600060208201905081810360008301526139f5816139b9565b9050919050565b6000604082019050613a116000830185613314565b613a1e6020830184612fc6565b9392505050565b7f2152454445454d41424c45000000000000000000000000000000000000000000600082015250565b6000613a5b600b83612ffb565b9150613a6682613a25565b602082019050919050565b60006020820190508181036000830152613a8a81613a4e565b9050919050565b6000604082019050613aa660008301856133ce565b613ab36020830184612fc6565b9392505050565b600081519050613ac981613113565b92915050565b600060208284031215613ae557613ae46130ab565b5b6000613af384828501613aba565b91505092915050565b6000613b0782612fbc565b9150613b1283612fbc565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615613b4b57613b4a6138a0565b5b828202905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000613b9082612fbc565b9150613b9b83612fbc565b925082613bab57613baa613b56565b5b828204905092915050565b600081519050613bc581613375565b92915050565b600060208284031215613be157613be06130ab565b5b6000613bef84828501613bb6565b91505092915050565b600060e082019050613c0d600083018a6133ce565b613c1a60208301896133ce565b613c276040830188612fc6565b613c346060830187612fc6565b613c41608083018661334b565b613c4e60a0830185613314565b613c5b60c0830184613314565b98975050505050505050565b7f5045524d49545f444541444c494e455f45585049524544000000000000000000600082015250565b6000613c9d601783612ffb565b9150613ca882613c67565b602082019050919050565b60006020820190508181036000830152613ccc81613c90565b9050919050565b600060c082019050613ce86000830189613314565b613cf560208301886133ce565b613d0260408301876133ce565b613d0f6060830186612fc6565b613d1c6080830185612fc6565b613d2960a0830184612fc6565b979650505050505050565b600081905092915050565b7f1901000000000000000000000000000000000000000000000000000000000000600082015250565b6000613d75600283613d34565b9150613d8082613d3f565b600282019050919050565b6000819050919050565b613da6613da18261320b565b613d8b565b82525050565b6000613db782613d68565b9150613dc38285613d95565b602082019150613dd38284613d95565b6020820191508190509392505050565b6000608082019050613df86000830187613314565b613e05602083018661334b565b613e126040830185613314565b613e1f6060830184613314565b95945050505050565b7f494e56414c49445f5349474e4552000000000000000000000000000000000000600082015250565b6000613e5e600e83612ffb565b9150613e6982613e28565b602082019050919050565b60006020820190508181036000830152613e8d81613e51565b9050919050565b7f5041555345440000000000000000000000000000000000000000000000000000600082015250565b6000613eca600683612ffb565b9150613ed582613e94565b602082019050919050565b60006020820190508181036000830152613ef981613ebd565b9050919050565b600081905092915050565b60008190508160005260206000209050919050565b60008154613f2d81613802565b613f378186613f00565b94506001821660008114613f525760018114613f6357613f96565b60ff19831686528186019350613f96565b613f6c85613f0b565b60005b83811015613f8e57815481890152600182019150602081019050613f6f565b838801955050505b50505092915050565b6000613fab8284613f20565b915081905092915050565b600060a082019050613fcb6000830188613314565b613fd86020830187613314565b613fe56040830186613314565b613ff26060830185612fc6565b613fff60808301846133ce565b9695505050505050565b60008160601b9050919050565b600061402182614009565b9050919050565b600061403382614016565b9050919050565b61404b614046826130d5565b614028565b82525050565b6000819050919050565b61406c61406782612fbc565b614051565b82525050565b600061407e828561403a565b60148201915061408e828461405b565b6020820191508190509392505050565b7f2150524f4f460000000000000000000000000000000000000000000000000000600082015250565b60006140d4600683612ffb565b91506140df8261409e565b602082019050919050565b60006020820190508181036000830152614103816140c7565b9050919050565b600060608201905061411f6000830186612fc6565b61412c6020830185612fc6565b6141396040830184612fc6565b949350505050565b7f5452414e534645525f46524f4d5f4641494c4544000000000000000000000000600082015250565b6000614177601483612ffb565b915061418282614141565b602082019050919050565b600060208201905081810360008301526141a68161416a565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006141e782612fbc565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561421a576142196138a0565b5b60018201905091905056fea2646970667358221220217a641487b29466e91201f217f80331f68cd4f02432c373946b117282bb64f264736f6c634300080b0033