Contract Overview
Balance:
0 Ether
EtherValue:
$0.00
| Txn Hash | Block | From | To | Value | [Txn Fee] | ||
|---|---|---|---|---|---|---|---|
| 0x4cf32fdfbdf32c1f1ffa8015e667cd6b8a9e53bb45f95eae9865b935b0c2dd72 | 9249225 | 46 days 19 hrs ago | 0xe5cd62ac8d2ca2a62a04958f07dd239c1ffe1a9e | IN | 0x7567ce8bbeb821aa39883f4e80f75da605fdd394 | 0 Ether | 0.00020543 |
| 0x1ca6bddab6d9c7dbcbe15a4c135119b6cd0a96b9c4210b5ebba22d1475b9a0aa | 8427833 | 182 days 22 hrs ago | 0xe5cd62ac8d2ca2a62a04958f07dd239c1ffe1a9e | IN | 0x7567ce8bbeb821aa39883f4e80f75da605fdd394 | 0 Ether | 0.000627258 |
[ Download CSV Export ]
Latest 1 internal transaction
| Parent Txn Hash | Block | From | To | Value | ||
|---|---|---|---|---|---|---|
| 0xfb602cc8b4bf6e833ece48249d80798a5eb40d1a97fefeace5bbbaa5169a6af9 | 8404024 | 186 days 15 hrs ago | 0x3d5409cce1d45233de1d4ebdee74b8e004abdd13 | Contract Creation | 0 Ether |
[ Download CSV Export ]
Contract Source Code Verified (Similar Match)
Note: This contract matches the deployed ByteCode of the Source Code for Contract 0x75fa3aa7e999b9899010c5f05e52cd0543dab465
Contract Name:
PublicLock
Compiler Version
v0.5.9+commit.e560f70d
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity)
/**
*Submitted for verification at Etherscan.io on 2019-06-27
*/
// File: contracts/interfaces/IERC721.sol
pragma solidity 0.5.9;
/// @title ERC-721 Non-Fungible Token Standard
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// Note: the ERC-165 identifier for this interface is 0x80ac58cd
interface IERC721 {
/// @dev This emits when ownership of any NFT changes by any mechanism.
/// This event emits when NFTs are created (`from` == 0) and destroyed
/// (`to` == 0). Exception: during contract creation, any number of NFTs
/// may be created and assigned without emitting Transfer. At the time of
/// any transfer, the approved address for that NFT (if any) is reset to none.
event Transfer(address indexed _from, address indexed _to, uint indexed _tokenId);
/// @dev This emits when the approved address for an NFT is changed or
/// reaffirmed. The zero address indicates there is no approved address.
/// When a Transfer event emits, this also indicates that the approved
/// address for that NFT (if any) is reset to none.
event Approval(address indexed _owner, address indexed _approved, uint indexed _tokenId);
/// @dev This emits when an operator is enabled or disabled for an owner.
/// The operator can manage all NFTs of the owner.
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
/// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
/// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
/// THEY MAY BE PERMANENTLY LOST
/// @dev Throws unless `msg.sender` is the current owner, an authorized
/// operator, or the approved address for this NFT. Throws if `_from` is
/// not the current owner. Throws if `_to` is the zero address. Throws if
/// `_tokenId` is not a valid NFT.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
function transferFrom(address _from, address _to, uint _tokenId) external payable;
/// @notice Set or reaffirm the approved address for an NFT
/// @dev The zero address indicates there is no approved address.
/// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized
/// operator of the current owner.
/// @param _approved The new approved NFT controller
/// @param _tokenId The NFT to approve
function approve(address _approved, uint _tokenId) external payable;
/// @notice Transfers the ownership of an NFT from one address to another address
/// @dev Throws unless `msg.sender` is the current owner, an authorized
/// operator, or the approved address for this NFT. Throws if `_from` is
/// not the current owner. Throws if `_to` is the zero address. Throws if
/// `_tokenId` is not a valid NFT. When transfer is complete, this function
/// checks if `_to` is a smart contract (code size > 0). If so, it calls
/// `onERC721Received` on `_to` and throws if the return value is not
/// `bytes4(keccak256('onERC721Received(address,address,uint,bytes)'))`.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
/// @param data Additional data with no specified format, sent in call to `_to`
function safeTransferFrom(address _from, address _to, uint _tokenId, bytes calldata data) external payable;
/// @notice Transfers the ownership of an NFT from one address to another address
/// @dev This works identically to the other function with an extra data parameter,
/// except this function just sets data to ''
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
function safeTransferFrom(address _from, address _to, uint _tokenId) external payable;
/// @notice Enable or disable approval for a third party ('operator') to manage
/// all of `msg.sender`'s assets.
/// @dev Emits the ApprovalForAll event. The contract MUST allow
/// multiple operators per owner.
/// @param _operator Address to add to the set of authorized operators.
/// @param _approved True if the operator is approved, false to revoke approval
function setApprovalForAll(address _operator, bool _approved) external;
/// @notice Count all NFTs assigned to an owner
/// @dev NFTs assigned to the zero address are considered invalid, and this
/// function throws for queries about the zero address.
/// @param _owner An address for whom to query the balance
/// @return The number of NFTs owned by `_owner`, possibly zero
function balanceOf(address _owner) external view returns (uint);
/// @notice Find the owner of an NFT
/// @dev NFTs assigned to zero address are considered invalid, and queries
/// about them do throw.
/// @param _tokenId The identifier for an NFT
/// @return The address of the owner of the NFT
function ownerOf(uint _tokenId) external view returns (address);
/// @notice Get the approved address for a single NFT
/// @dev Throws if `_tokenId` is not a valid NFT
/// @param _tokenId The NFT to find the approved address for
/// @return The approved address for this NFT, or the zero address if there is none
function getApproved(uint _tokenId) external view returns (address);
/// @notice Query if an address is an authorized operator for another address
/// @param _owner The address that owns the NFTs
/// @param _operator The address that acts on behalf of the owner
/// @return True if `_operator` is an approved operator for `_owner`, false otherwise
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
/// @notice A descriptive name for a collection of NFTs in this contract
function name() external view returns (string memory _name);
}
// File: zos-lib/contracts/Initializable.sol
pragma solidity >=0.4.24 <0.6.0;
/**
* @title Initializable
*
* @dev Helper contract to support initializer functions. To use it, replace
* the constructor with a function that has the `initializer` modifier.
* WARNING: Unlike constructors, initializer functions must be manually
* invoked. This applies both to deploying an Initializable contract, as well
* as extending an Initializable contract via inheritance.
* WARNING: When used with inheritance, manual care must be taken to not invoke
* a parent initializer twice, or ensure that all initializers are idempotent,
* because this is not dealt with automatically as with constructors.
*/
contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
*/
bool private initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private initializing;
/**
* @dev Modifier to use in the initializer function of a contract.
*/
modifier initializer() {
require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
bool isTopLevelCall = !initializing;
if (isTopLevelCall) {
initializing = true;
initialized = true;
}
_;
if (isTopLevelCall) {
initializing = false;
}
}
/// @dev Returns true if and only if the function is running in the constructor
function isConstructor() private view returns (bool) {
// extcodesize checks the size of the code stored in an address, and
// address returns the current address. Since the code is still not
// deployed when running a constructor, any checks on its code size will
// yield zero, making it an effective way to detect if a contract is
// under construction or not.
uint256 cs;
assembly { cs := extcodesize(address) }
return cs == 0;
}
// Reserved storage space to allow for layout changes in the future.
uint256[50] private ______gap;
}
// File: openzeppelin-eth/contracts/ownership/Ownable.sol
pragma solidity ^0.5.0;
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable is Initializable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function initialize(address sender) public initializer {
_owner = sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @return the address of the owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner());
_;
}
/**
* @return true if `msg.sender` is the owner of the contract.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Allows the current owner to relinquish control of the contract.
* @notice Renouncing to ownership will leave the contract without an owner.
* It will not be possible to call the functions with the `onlyOwner`
* modifier anymore.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0));
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
uint256[50] private ______gap;
}
// File: openzeppelin-solidity/contracts/introspection/IERC165.sol
pragma solidity ^0.5.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* [EIP](https://eips.ethereum.org/EIPS/eip-165).
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others (`ERC165Checker`).
*
* For an implementation, see `ERC165`.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// File: openzeppelin-solidity/contracts/introspection/ERC165.sol
pragma solidity ^0.5.0;
/**
* @dev Implementation of the `IERC165` interface.
*
* Contracts may inherit from this and call `_registerInterface` to declare
* their support of an interface.
*/
contract ERC165 is IERC165 {
/*
* bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
*/
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
/**
* @dev Mapping of interface ids to whether or not it's supported.
*/
mapping(bytes4 => bool) private _supportedInterfaces;
constructor () internal {
// Derived contracts need only register support for their own interfaces,
// we register support for ERC165 itself here
_registerInterface(_INTERFACE_ID_ERC165);
}
/**
* @dev See `IERC165.supportsInterface`.
*
* Time complexity O(1), guaranteed to always use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool) {
return _supportedInterfaces[interfaceId];
}
/**
* @dev Registers the contract as an implementer of the interface defined by
* `interfaceId`. Support of the actual ERC165 interface is automatic and
* registering its interface id is not required.
*
* See `IERC165.supportsInterface`.
*
* Requirements:
*
* - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
*/
function _registerInterface(bytes4 interfaceId) internal {
require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
_supportedInterfaces[interfaceId] = true;
}
}
// File: openzeppelin-solidity/contracts/token/ERC721/IERC721Receiver.sol
pragma solidity ^0.5.0;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
contract IERC721Receiver {
/**
* @notice Handle the receipt of an NFT
* @dev The ERC721 smart contract calls this function on the recipient
* after a `safeTransfer`. This function MUST return the function selector,
* otherwise the caller will revert the transaction. The selector to be
* returned can be obtained as `this.onERC721Received.selector`. This
* function MAY throw to revert and reject the transfer.
* Note: the ERC721 contract address is always the message sender.
* @param operator The address which called `safeTransferFrom` function
* @param from The address which previously owned the token
* @param tokenId The NFT identifier which is being transferred
* @param data Additional data with no specified format
* @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
*/
function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
public returns (bytes4);
}
// File: openzeppelin-solidity/contracts/token/ERC721/ERC721Holder.sol
pragma solidity ^0.5.0;
contract ERC721Holder is IERC721Receiver {
function onERC721Received(address, address, uint256, bytes memory) public returns (bytes4) {
return this.onERC721Received.selector;
}
}
// File: openzeppelin-solidity/contracts/token/ERC20/IERC20.sol
pragma solidity ^0.5.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see `ERC20Detailed`.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a `Transfer` event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through `transferFrom`. This is
* zero by default.
*
* This value changes when `approve` or `transferFrom` are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* > Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an `Approval` event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a `Transfer` event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to `approve`. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// File: contracts/mixins/MixinFunds.sol
pragma solidity 0.5.9;
/**
* @title An implementation of the money related functions.
* @author HardlyDifficult (unlock-protocol.com)
*/
contract MixinFunds
{
/**
* The token-type that this Lock is priced in. If 0, then use ETH, else this is
* a ERC20 token address.
*/
address public tokenAddress;
constructor(
address _tokenAddress
) public
{
require(
_tokenAddress == address(0) || IERC20(_tokenAddress).totalSupply() > 0,
'INVALID_TOKEN'
);
tokenAddress = _tokenAddress;
}
/**
* Gets the current balance of the account provided.
*/
function getBalance(
address _account
) public view
returns (uint)
{
if(tokenAddress == address(0)) {
return _account.balance;
} else {
return IERC20(tokenAddress).balanceOf(_account);
}
}
/**
* Ensures that the msg.sender has paid at least the price stated.
*
* With ETH, this means the function originally called was `payable` and the
* transaction included at least the amount requested.
*
* Security: be wary of re-entrancy when calling this function.
*/
function _chargeAtLeast(
uint _price
) internal
{
if(_price > 0) {
if(tokenAddress == address(0)) {
require(msg.value >= _price, 'NOT_ENOUGH_FUNDS');
} else {
IERC20 token = IERC20(tokenAddress);
uint balanceBefore = token.balanceOf(address(this));
token.transferFrom(msg.sender, address(this), _price);
// There are known bugs in popular ERC20 implements which means we cannot
// trust the return value of `transferFrom`. This require statement ensures
// that a transfer occurred.
require(token.balanceOf(address(this)) > balanceBefore, 'TRANSFER_FAILED');
}
}
}
/**
* Transfers funds from the contract to the account provided.
*
* Security: be wary of re-entrancy when calling this function.
*/
function _transfer(
address _to,
uint _amount
) internal
{
if(_amount > 0) {
if(tokenAddress == address(0)) {
address(uint160(_to)).transfer(_amount);
} else {
IERC20 token = IERC20(tokenAddress);
uint balanceBefore = token.balanceOf(_to);
token.transfer(_to, _amount);
// There are known bugs in popular ERC20 implements which means we cannot
// trust the return value of `transferFrom`. This require statement ensures
// that a transfer occurred.
require(token.balanceOf(_to) > balanceBefore, 'TRANSFER_FAILED');
}
}
}
}
// File: contracts/mixins/MixinDisableAndDestroy.sol
pragma solidity 0.5.9;
/**
* @title Mixin allowing the Lock owner to disable a Lock (preventing new purchases)
* and then destroy it.
* @author HardlyDifficult
* @dev `Mixins` are a design pattern seen in the 0x contracts. It simply
* separates logically groupings of code to ease readability.
*/
contract MixinDisableAndDestroy is
IERC721,
Ownable,
MixinFunds
{
// Used to disable payable functions when deprecating an old lock
bool public isAlive;
event Destroy(
uint balance,
address indexed owner
);
event Disable();
constructor(
) internal
{
isAlive = true;
}
// Only allow usage when contract is Alive
modifier onlyIfAlive() {
require(isAlive, 'LOCK_DEPRECATED');
_;
}
/**
* @dev Used to disable lock before migrating keys and/or destroying contract
*/
function disableLock()
external
onlyOwner
onlyIfAlive
{
emit Disable();
isAlive = false;
}
/**
* @dev Used to clean up old lock contracts from the blockchain
* TODO: add a check to ensure all keys are INVALID!
*/
function destroyLock()
external
onlyOwner
{
require(isAlive == false, 'DISABLE_FIRST');
emit Destroy(address(this).balance, msg.sender);
// this will send any ETH or ERC20 held by the lock to the owner
_transfer(msg.sender, getBalance(address(this)));
selfdestruct(msg.sender);
// Note we don't clean up the `locks` data in Unlock.sol as it should not be necessary
// and leaves some data behind ('Unlock.LockBalances') which may be helpful.
}
}
// File: contracts/interfaces/IUnlock.sol
pragma solidity 0.5.9;
/**
* @title The Unlock Interface
* @author Nick Furfaro (unlock-protocol.com)
**/
interface IUnlock {
// Events
event NewLock(
address indexed lockOwner,
address indexed newLockAddress
);
event NewTokenURI(
string tokenURI
);
event NewGlobalTokenSymbol(
string tokenSymbol
);
// Use initialize instead of a constructor to support proxies (for upgradeability via zos).
function initialize(address _owner) external;
/**
* @dev Create lock
* This deploys a lock for a creator. It also keeps track of the deployed lock.
* @param _tokenAddress set to the ERC20 token address, or 0 for ETH.
*/
function createLock(
uint _expirationDuration,
address _tokenAddress,
uint _keyPrice,
uint _maxNumberOfKeys,
string calldata _lockName
) external;
/**
* This function keeps track of the added GDP, as well as grants of discount tokens
* to the referrer, if applicable.
* The number of discount tokens granted is based on the value of the referal,
* the current growth rate and the lock's discount token distribution rate
* This function is invoked by a previously deployed lock only.
*/
function recordKeyPurchase(
uint _value,
address _referrer // solhint-disable-line no-unused-vars
)
external;
/**
* This function will keep track of consumed discounts by a given user.
* It will also grant discount tokens to the creator who is granting the discount based on the
* amount of discount and compensation rate.
* This function is invoked by a previously deployed lock only.
*/
function recordConsumedDiscount(
uint _discount,
uint _tokens // solhint-disable-line no-unused-vars
)
external;
/**
* This function returns the discount available for a user, when purchasing a
* a key from a lock.
* This does not modify the state. It returns both the discount and the number of tokens
* consumed to grant that discount.
*/
function computeAvailableDiscountFor(
address _purchaser, // solhint-disable-line no-unused-vars
uint _keyPrice // solhint-disable-line no-unused-vars
)
external
view
returns (uint discount, uint tokens);
// Function to read the globalTokenURI field.
function getGlobalBaseTokenURI()
external
view
returns (string memory);
/** Function to set the globalTokenURI field.
* Should throw if called by other than owner
*/
function setGlobalBaseTokenURI(
string calldata _URI
)
external;
// Function to read the globalTokenSymbol field.
function getGlobalTokenSymbol()
external
view
returns (string memory);
/** Function to set the globalTokenSymbol field.
* Should throw if called by other than owner.
*/
function setGlobalTokenSymbol(
string calldata _symbol
)
external;
}
// File: contracts/mixins/MixinLockCore.sol
pragma solidity 0.5.9;
/**
* @title Mixin for core lock data and functions.
* @author HardlyDifficult
* @dev `Mixins` are a design pattern seen in the 0x contracts. It simply
* separates logically groupings of code to ease readability.
*/
contract MixinLockCore is
Ownable,
MixinFunds,
MixinDisableAndDestroy
{
event PriceChanged(
uint oldKeyPrice,
uint keyPrice
);
event Withdrawal(
address indexed sender,
address indexed beneficiary,
uint amount
);
// Unlock Protocol address
// TODO: should we make that private/internal?
IUnlock public unlockProtocol;
// Duration in seconds for which the keys are valid, after creation
// should we take a smaller type use less gas?
// TODO: add support for a timestamp instead of duration
uint public expirationDuration;
// price in wei of the next key
// TODO: allow support for a keyPriceCalculator which could set prices dynamically
uint public keyPrice;
// Max number of keys sold if the keyReleaseMechanism is public
uint public maxNumberOfKeys;
// A count of how many new key purchases there have been
uint public numberOfKeysSold;
// The account which will receive funds on withdrawal
address public beneficiary;
// Ensure that the Lock has not sold all of its keys.
modifier notSoldOut() {
require(maxNumberOfKeys > numberOfKeysSold, 'LOCK_SOLD_OUT');
_;
}
modifier onlyOwnerOrBeneficiary()
{
require(
msg.sender == owner() || msg.sender == beneficiary,
'ONLY_LOCK_OWNER_OR_BENEFICIARY'
);
_;
}
constructor(
address _beneficiary,
uint _expirationDuration,
uint _keyPrice,
uint _maxNumberOfKeys
) internal
{
require(_expirationDuration <= 100 * 365 * 24 * 60 * 60, 'MAX_EXPIRATION_100_YEARS');
unlockProtocol = IUnlock(msg.sender); // Make sure we link back to Unlock's smart contract.
beneficiary = _beneficiary;
expirationDuration = _expirationDuration;
keyPrice = _keyPrice;
maxNumberOfKeys = _maxNumberOfKeys;
}
/**
* @dev Called by owner to withdraw all funds from the lock and send them to the `beneficiary`.
* @param _amount specifies the max amount to withdraw, which may be reduced when
* considering the available balance. Set to 0 or MAX_UINT to withdraw everything.
*
* TODO: consider allowing anybody to trigger this as long as it goes to owner anyway?
* -- however be wary of draining funds as it breaks the `cancelAndRefund` use case.
*/
function withdraw(
uint _amount
) external
onlyOwnerOrBeneficiary
{
uint balance = getBalance(address(this));
uint amount;
if(_amount == 0 || _amount > balance)
{
require(balance > 0, 'NOT_ENOUGH_FUNDS');
amount = balance;
}
else
{
amount = _amount;
}
emit Withdrawal(msg.sender, beneficiary, amount);
// Security: re-entrancy not a risk as this is the last line of an external function
_transfer(beneficiary, amount);
}
/**
* A function which lets the owner of the lock to change the price for future purchases.
*/
function updateKeyPrice(
uint _keyPrice
)
external
onlyOwner
onlyIfAlive
{
uint oldKeyPrice = keyPrice;
keyPrice = _keyPrice;
emit PriceChanged(oldKeyPrice, keyPrice);
}
/**
* A function which lets the owner of the lock update the beneficiary account,
* which receives funds on withdrawal.
*/
function updateBeneficiary(
address _beneficiary
) external
onlyOwnerOrBeneficiary
{
require(_beneficiary != address(0), 'INVALID_ADDRESS');
beneficiary = _beneficiary;
}
/**
* Public function which returns the total number of unique keys sold (both
* expired and valid)
*/
function totalSupply()
public
view
returns (uint)
{
return numberOfKeysSold;
}
}
// File: contracts/mixins/MixinKeys.sol
pragma solidity 0.5.9;
/**
* @title Mixin for managing `Key` data.
* @author HardlyDifficult
* @dev `Mixins` are a design pattern seen in the 0x contracts. It simply
* separates logically groupings of code to ease readability.
*/
contract MixinKeys is
Ownable,
MixinLockCore
{
// The struct for a key
struct Key {
uint tokenId;
uint expirationTimestamp;
}
// Called when the Lock owner expires a user's Key
event ExpireKey(uint tokenId);
// Keys
// Each owner can have at most exactly one key
// TODO: could we use public here? (this could be confusing though because it getter will
// return 0 values when missing a key)
mapping (address => Key) private keyByOwner;
// Each tokenId can have at most exactly one owner at a time.
// Returns 0 if the token does not exist
// TODO: once we decouple tokenId from owner address (incl in js), then we can consider
// merging this with numberOfKeysSold into an array instead.
mapping (uint => address) private ownerByTokenId;
// Addresses of owners are also stored in an array.
// Addresses are never removed by design to avoid abuses around referals
address[] public owners;
// Ensures that an owner owns or has owned a key in the past
modifier ownsOrHasOwnedKey(
address _owner
) {
require(
keyByOwner[_owner].expirationTimestamp > 0, 'HAS_NEVER_OWNED_KEY'
);
_;
}
// Ensures that an owner has a valid key
modifier hasValidKey(
address _owner
) {
require(
getHasValidKey(_owner), 'KEY_NOT_VALID'
);
_;
}
// Ensures that a key has an owner
modifier isKey(
uint _tokenId
) {
require(
ownerByTokenId[_tokenId] != address(0), 'NO_SUCH_KEY'
);
_;
}
// Ensure that the caller owns the key
modifier onlyKeyOwner(
uint _tokenId
) {
require(
isKeyOwner(_tokenId, msg.sender), 'ONLY_KEY_OWNER'
);
_;
}
/**
* A function which lets the owner of the lock expire a users' key.
*/
function expireKeyFor(
address _owner
)
public
onlyOwner
hasValidKey(_owner)
{
Key storage key = keyByOwner[_owner];
key.expirationTimestamp = block.timestamp; // Effectively expiring the key
emit ExpireKey(key.tokenId);
}
/**
* In the specific case of a Lock, each owner can own only at most 1 key.
* @return The number of NFTs owned by `_owner`, either 0 or 1.
*/
function balanceOf(
address _owner
)
external
view
returns (uint)
{
require(_owner != address(0), 'INVALID_ADDRESS');
return getHasValidKey(_owner) ? 1 : 0;
}
/**
* Checks if the user has a non-expired key.
*/
function getHasValidKey(
address _owner
)
public
view
returns (bool)
{
return keyByOwner[_owner].expirationTimestamp > block.timestamp;
}
/**
* @notice Find the tokenId for a given user
* @return The tokenId of the NFT, else revert
*/
function getTokenIdFor(
address _account
)
external
view
hasValidKey(_account)
returns (uint)
{
return keyByOwner[_account].tokenId;
}
/**
* A function which returns a subset of the keys for this Lock as an array
* @param _page the page of key owners requested when faceted by page size
* @param _pageSize the number of Key Owners requested per page
*/
function getOwnersByPage(uint _page, uint _pageSize)
public
view
returns (address[] memory)
{
require(owners.length > 0, 'NO_OUTSTANDING_KEYS');
uint pageSize = _pageSize;
uint _startIndex = _page * pageSize;
uint endOfPageIndex;
if (_startIndex + pageSize > owners.length) {
endOfPageIndex = owners.length;
pageSize = owners.length - _startIndex;
} else {
endOfPageIndex = (_startIndex + pageSize);
}
// new temp in-memory array to hold pageSize number of requested owners:
address[] memory ownersByPage = new address[](pageSize);
uint pageIndex = 0;
// Build the requested set of owners into a new temporary array:
for (uint i = _startIndex; i < endOfPageIndex; i++) {
ownersByPage[pageIndex] = owners[i];
pageIndex++;
}
return ownersByPage;
}
/**
* Checks if the given address owns the given tokenId.
*/
function isKeyOwner(
uint _tokenId,
address _owner
) public view
returns (bool)
{
return ownerByTokenId[_tokenId] == _owner;
}
/**
* @dev Returns the key's ExpirationTimestamp field for a given owner.
* @param _owner address of the user for whom we search the key
*/
function keyExpirationTimestampFor(
address _owner
)
public view
ownsOrHasOwnedKey(_owner)
returns (uint timestamp)
{
return keyByOwner[_owner].expirationTimestamp;
}
/**
* Public function which returns the total number of unique owners (both expired
* and valid). This may be larger than totalSupply.
*/
function numberOfOwners()
public
view
returns (uint)
{
return owners.length;
}
/**
* @notice ERC721: Find the owner of an NFT
* @return The address of the owner of the NFT, if applicable
*/
function ownerOf(
uint _tokenId
)
public view
isKey(_tokenId)
returns (address)
{
return ownerByTokenId[_tokenId];
}
/**
* Assigns the key a new tokenId (from numberOfKeysSold) if it does not already have
* one assigned.
*/
function _assignNewTokenId(
Key storage _key
) internal
{
if (_key.tokenId == 0) {
// This is a brand new owner, else an owner of an expired key buying an extension.
// We increment the tokenId counter
numberOfKeysSold++;
// we assign the incremented `numberOfKeysSold` as the tokenId for the new key
_key.tokenId = numberOfKeysSold;
}
}
/**
* Records the owner of a given tokenId
*/
function _recordOwner(
address _owner,
uint _tokenId
) internal
{
if (ownerByTokenId[_tokenId] != _owner) {
// TODO: this may include duplicate entries
owners.push(_owner);
// We register the owner of the tokenID
ownerByTokenId[_tokenId] = _owner;
}
}
/**
* Returns the Key struct for the given owner.
*/
function _getKeyFor(
address _owner
) internal view
returns (Key storage)
{
return keyByOwner[_owner];
}
}
// File: contracts/mixins/MixinApproval.sol
pragma solidity 0.5.9;
/**
* @title Mixin for the Approval related functions needed to meet the ERC721
* standard.
* @author HardlyDifficult
* @dev `Mixins` are a design pattern seen in the 0x contracts. It simply
* separates logically groupings of code to ease readability.
*/
contract MixinApproval is
IERC721,
MixinDisableAndDestroy,
MixinKeys
{
// Keeping track of approved transfers
// This is a mapping of addresses which have approved
// the transfer of a key to another address where their key can be transfered
// Note: the approver may actually NOT have a key... and there can only
// be a single approved beneficiary
// Note 2: for transfer, both addresses will be different
// Note 3: for sales (new keys on restricted locks), both addresses will be the same
mapping (uint => address) private approved;
// Keeping track of approved operators for a Key owner.
// Since an owner can have up to 1 Key, this is similiar to above
// but the approval does not reset when a transfer occurs.
mapping (address => mapping (address => bool)) private ownerToOperatorApproved;
// Ensure that the caller has a key
// or that the caller has been approved
// for ownership of that key
modifier onlyKeyOwnerOrApproved(
uint _tokenId
) {
require(
isKeyOwner(_tokenId, msg.sender) ||
_isApproved(_tokenId, msg.sender) ||
isApprovedForAll(ownerOf(_tokenId), msg.sender),
'ONLY_KEY_OWNER_OR_APPROVED');
_;
}
/**
* This approves _approved to get ownership of _tokenId.
* Note: that since this is used for both purchase and transfer approvals
* the approved token may not exist.
*/
function approve(
address _approved,
uint _tokenId
)
external
payable
onlyIfAlive
onlyKeyOwnerOrApproved(_tokenId)
{
require(msg.sender != _approved, 'APPROVE_SELF');
approved[_tokenId] = _approved;
emit Approval(ownerOf(_tokenId), _approved, _tokenId);
}
/**
* @dev Sets or unsets the approval of a given operator
* An operator is allowed to transfer all tokens of the sender on their behalf
* @param _to operator address to set the approval
* @param _approved representing the status of the approval to be set
*/
function setApprovalForAll(
address _to,
bool _approved
) external
onlyIfAlive
{
require(_to != msg.sender, 'APPROVE_SELF');
ownerToOperatorApproved[msg.sender][_to] = _approved;
emit ApprovalForAll(msg.sender, _to, _approved);
}
/**
* external version
* Will return the approved recipient for a key, if any.
*/
function getApproved(
uint _tokenId
)
external
view
returns (address)
{
return _getApproved(_tokenId);
}
/**
* @dev Tells whether an operator is approved by a given owner
* @param _owner owner address which you want to query the approval of
* @param _operator operator address which you want to query the approval of
* @return bool whether the given operator is approved by the given owner
*/
function isApprovedForAll(
address _owner,
address _operator
) public view
returns (bool)
{
return ownerToOperatorApproved[_owner][_operator];
}
/**
* @dev Checks if the given user is approved to transfer the tokenId.
*/
function _isApproved(
uint _tokenId,
address _user
) internal view
returns (bool)
{
return approved[_tokenId] == _user;
}
/**
* Will return the approved recipient for a key transfer or ownership.
* Note: this does not check that a corresponding key
* actually exists.
*/
function _getApproved(
uint _tokenId
)
internal
view
returns (address)
{
address approvedRecipient = approved[_tokenId];
require(approvedRecipient != address(0), 'NONE_APPROVED');
return approvedRecipient;
}
/**
* @dev Function to clear current approval of a given token ID
* @param _tokenId uint256 ID of the token to be transferred
*/
function _clearApproval(
uint256 _tokenId
) internal
{
if (approved[_tokenId] != address(0)) {
approved[_tokenId] = address(0);
}
}
}
// File: contracts/mixins/MixinGrantKeys.sol
pragma solidity 0.5.9;
/**
* @title Mixin allowing the Lock owner to grant / gift keys to users.
* @author HardlyDifficult
* @dev `Mixins` are a design pattern seen in the 0x contracts. It simply
* separates logically groupings of code to ease readability.
*/
contract MixinGrantKeys is
IERC721,
Ownable,
MixinKeys
{
/**
* Allows the Lock owner to give a user a key with no charge.
*/
function grantKey(
address _recipient,
uint _expirationTimestamp
) external
onlyOwner
{
_grantKey(_recipient, _expirationTimestamp);
}
/**
* Allows the Lock owner to give a collection of users a key with no charge.
* All keys granted have the same expiration date.
*/
function grantKeys(
address[] calldata _recipients,
uint _expirationTimestamp
) external
onlyOwner
{
for(uint i = 0; i < _recipients.length; i++) {
_grantKey(_recipients[i], _expirationTimestamp);
}
}
/**
* Allows the Lock owner to give a collection of users a key with no charge.
* Each key may be assigned a different expiration date.
*/
function grantKeys(
address[] calldata _recipients,
uint[] calldata _expirationTimestamps
) external
onlyOwner
{
for(uint i = 0; i < _recipients.length; i++) {
_grantKey(_recipients[i], _expirationTimestamps[i]);
}
}
/**
* Give a key to the given user
*/
function _grantKey(
address _recipient,
uint _expirationTimestamp
) private
{
require(_recipient != address(0), 'INVALID_ADDRESS');
Key storage toKey = _getKeyFor(_recipient);
require(_expirationTimestamp > toKey.expirationTimestamp, 'ALREADY_OWNS_KEY');
_assignNewTokenId(toKey);
_recordOwner(_recipient, toKey.tokenId);
toKey.expirationTimestamp = _expirationTimestamp;
// trigger event
emit Transfer(
address(0), // This is a creation.
_recipient,
toKey.tokenId
);
}
}
// File: contracts/UnlockUtils.sol
pragma solidity 0.5.9;
// This contract provides some utility methods for use with the unlock protocol smart contracts.
// Borrowed from:
// https://github.com/oraclize/ethereum-api/blob/master/oraclizeAPI_0.5.sol#L943
contract UnlockUtils {
function strConcat(
string memory _a,
string memory _b,
string memory _c,
string memory _d
) public
pure
returns (string memory _concatenatedString)
{
bytes memory _ba = bytes(_a);
bytes memory _bb = bytes(_b);
bytes memory _bc = bytes(_c);
bytes memory _bd = bytes(_d);
string memory abcd = new string(_ba.length + _bb.length + _bc.length + _bd.length);
bytes memory babcd = bytes(abcd);
uint k = 0;
uint i = 0;
for (i = 0; i < _ba.length; i++) {
babcd[k++] = _ba[i];
}
for (i = 0; i < _bb.length; i++) {
babcd[k++] = _bb[i];
}
for (i = 0; i < _bc.length; i++) {
babcd[k++] = _bc[i];
}
for (i = 0; i < _bd.length; i++) {
babcd[k++] = _bd[i];
}
return string(babcd);
}
function uint2Str(
uint _i
) public
pure
returns (string memory _uintAsString)
{
// make a copy of the param to avoid security/no-assign-params error
uint c = _i;
if (_i == 0) {
return '0';
}
uint j = _i;
uint len;
while (j != 0) {
len++;
j /= 10;
}
bytes memory bstr = new bytes(len);
uint k = len - 1;
while (c != 0) {
bstr[k--] = byte(uint8(48 + c % 10));
c /= 10;
}
return string(bstr);
}
function address2Str(
address _addr
) public
pure
returns(string memory)
{
bytes32 value = bytes32(uint256(_addr));
bytes memory alphabet = '0123456789abcdef';
bytes memory str = new bytes(42);
str[0] = '0';
str[1] = 'x';
for (uint i = 0; i < 20; i++) {
str[2+i*2] = alphabet[uint8(value[i + 12] >> 4)];
str[3+i*2] = alphabet[uint8(value[i + 12] & 0x0f)];
}
return string(str);
}
}
// File: contracts/mixins/MixinLockMetadata.sol
pragma solidity 0.5.9;
/**
* @title Mixin for metadata about the Lock.
* @author HardlyDifficult
* @dev `Mixins` are a design pattern seen in the 0x contracts. It simply
* separates logically groupings of code to ease readability.
*/
contract MixinLockMetadata is
IERC721,
ERC165,
Ownable,
MixinLockCore,
UnlockUtils,
MixinKeys
{
/// A descriptive name for a collection of NFTs in this contract.Defaults to "Unlock-Protocol" but is settable by lock owner
string private lockName;
/// An abbreviated name for NFTs in this contract. Defaults to "KEY" but is settable by lock owner
string private lockSymbol;
// the base Token URI for this Lock. If not set by lock owner, the global URI stored in Unlock is used.
string private baseTokenURI;
event NewLockSymbol(
string symbol
);
constructor(
string memory _lockName
) internal
{
lockName = _lockName;
// registering the optional erc721 metadata interface with ERC165.sol using
// the ID specified in the standard: https://eips.ethereum.org/EIPS/eip-721
_registerInterface(0x5b5e139f);
}
/**
* Allows the Lock owner to assign a descriptive name for this Lock.
*/
function updateLockName(
string calldata _lockName
) external
onlyOwner
{
lockName = _lockName;
}
/**
* @dev Gets the token name
* @return string representing the token name
*/
function name(
) external view
returns (string memory)
{
return lockName;
}
/**
* Allows the Lock owner to assign a Symbol for this Lock.
*/
function updateLockSymbol(
string calldata _lockSymbol
) external
onlyOwner
{
lockSymbol = _lockSymbol;
emit NewLockSymbol(_lockSymbol);
}
/**
* @dev Gets the token symbol
* @return string representing the token name
*/
function symbol()
external view
returns(string memory)
{
if(bytes(lockSymbol).length == 0) {
return unlockProtocol.getGlobalTokenSymbol();
} else {
return lockSymbol;
}
}
/**
* Allows the Lock owner to update the baseTokenURI for this Lock.
*/
function setBaseTokenURI(
string calldata _baseTokenURI
) external
onlyOwner
{
baseTokenURI = _baseTokenURI;
}
/** @notice A distinct Uniform Resource Identifier (URI) for a given asset.
* @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
* 3986. The URI may point to a JSON file that conforms to the "ERC721
* Metadata JSON Schema".
* https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
function tokenURI(
uint256 _tokenId
) external
view
isKey(_tokenId)
returns(string memory)
{
string memory URI;
if(bytes(baseTokenURI).length == 0) {
URI = unlockProtocol.getGlobalBaseTokenURI();
} else {
URI = baseTokenURI;
}
return UnlockUtils.strConcat(
URI,
UnlockUtils.address2Str(address(this)),
'/',
UnlockUtils.uint2Str(_tokenId)
);
}
}
// File: contracts/mixins/MixinNoFallback.sol
pragma solidity 0.5.9;
/**
* @title Mixin for the fallback function implementation, which simply reverts.
* @author HardlyDifficult
* @dev `Mixins` are a design pattern seen in the 0x contracts. It simply
* separates logically groupings of code to ease readability.
*/
contract MixinNoFallback
{
/**
* @dev the fallback function should not be used. This explicitly reverts
* to ensure it's never used.
*/
function()
external
{
revert('NO_FALLBACK');
}
}
// File: openzeppelin-solidity/contracts/math/SafeMath.sol
pragma solidity ^0.5.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, "SafeMath: division by zero");
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}
// File: contracts/mixins/MixinPurchase.sol
pragma solidity 0.5.9;
/**
* @title Mixin for the purchase-related functions.
* @author HardlyDifficult
* @dev `Mixins` are a design pattern seen in the 0x contracts. It simply
* separates logically groupings of code to ease readability.
*/
contract MixinPurchase is
MixinFunds,
MixinDisableAndDestroy,
MixinLockCore,
MixinKeys
{
using SafeMath for uint;
/**
* @dev Purchase function, public version, with no referrer.
* @param _recipient address of the recipient of the purchased key
*/
function purchaseFor(
address _recipient
)
external
payable
onlyIfAlive
{
return _purchaseFor(_recipient, address(0));
}
/**
* @dev Purchase function, public version, with referrer.
* @param _recipient address of the recipient of the purchased key
* @param _referrer address of the user making the referral
*/
function purchaseForFrom(
address _recipient,
address _referrer
)
external
payable
onlyIfAlive
hasValidKey(_referrer)
{
return _purchaseFor(_recipient, _referrer);
}
/**
* @dev Purchase function: this lets a user purchase a key from the lock for another user
* @param _recipient address of the recipient of the purchased key
* This will fail if
* - the keyReleaseMechanism is private
* - the keyReleaseMechanism is Approved and the recipient has not been previously approved
* - the amount value is smaller than the price
* - the recipient already owns a key
* TODO: next version of solidity will allow for message to be added to require.
*/
function _purchaseFor(
address _recipient,
address _referrer
)
private
notSoldOut()
{ // solhint-disable-line function-max-lines
require(_recipient != address(0), 'INVALID_ADDRESS');
// Let's get the actual price for the key from the Unlock smart contract
uint discount;
uint tokens;
uint inMemoryKeyPrice = keyPrice;
(discount, tokens) = unlockProtocol.computeAvailableDiscountFor(_recipient, inMemoryKeyPrice);
uint netPrice = inMemoryKeyPrice;
if (discount > inMemoryKeyPrice) {
netPrice = 0;
} else {
// SafeSub not required as the if statement already confirmed `inMemoryKeyPrice - discount` cannot underflow
netPrice = inMemoryKeyPrice - discount;
}
// Assign the key
Key storage toKey = _getKeyFor(_recipient);
if (toKey.tokenId == 0) {
// Assign a new tokenId (if a new owner or previously transfered)
_assignNewTokenId(toKey);
_recordOwner(_recipient, toKey.tokenId);
}
if (toKey.expirationTimestamp >= block.timestamp) {
// This is an existing owner trying to extend their key
toKey.expirationTimestamp = toKey.expirationTimestamp.add(expirationDuration);
} else {
// SafeAdd is not required here since expirationDuration is capped to a tiny value
// (relative to the size of a uint)
toKey.expirationTimestamp = block.timestamp + expirationDuration;
}
if (discount > 0) {
unlockProtocol.recordConsumedDiscount(discount, tokens);
}
unlockProtocol.recordKeyPurchase(netPrice, _referrer);
// trigger event
emit Transfer(
address(0), // This is a creation.
_recipient,
numberOfKeysSold
);
// We explicitly allow for greater amounts of ETH to allow 'donations'
// Security: last line to minimize risk of re-entrancy
_chargeAtLeast(netPrice);
}
}
// File: openzeppelin-solidity/contracts/cryptography/ECDSA.sol
pragma solidity ^0.5.0;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* (.note) This call _does not revert_ if the signature is invalid, or
* if the signer is otherwise unable to be retrieved. In those scenarios,
* the zero address is returned.
*
* (.warning) `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise)
* be too long), and then calling `toEthSignedMessageHash` on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
// Check the signature length
if (signature.length != 65) {
return (address(0));
}
// Divide the signature in r, s and v variables
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
// solhint-disable-next-line no-inline-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return address(0);
}
if (v != 27 && v != 28) {
return address(0);
}
// If the signature is valid (and not malleable), return the signer address
return ecrecover(hash, v, r, s);
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* replicates the behavior of the
* [`eth_sign`](https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign)
* JSON-RPC method.
*
* See `recover`.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
}
// File: contracts/mixins/MixinRefunds.sol
pragma solidity 0.5.9;
contract MixinRefunds is
Ownable,
MixinFunds,
MixinLockCore,
MixinKeys
{
using SafeMath for uint;
// CancelAndRefund will return funds based on time remaining minus this penalty.
// This is calculated as `proRatedRefund * refundPenaltyNumerator / refundPenaltyDenominator`.
uint public refundPenaltyNumerator = 1;
uint public refundPenaltyDenominator = 10;
// Stores a nonce per user to use for signed messages
mapping(address => uint) public keyOwnerToNonce;
event CancelKey(
uint indexed tokenId,
address indexed owner,
address indexed sendTo,
uint refund
);
event RefundPenaltyChanged(
uint oldRefundPenaltyNumerator,
uint oldRefundPenaltyDenominator,
uint refundPenaltyNumerator,
uint refundPenaltyDenominator
);
/**
* @dev Destroys the user's key and sends a refund based on the amount of time remaining.
*/
function cancelAndRefund()
external
{
_cancelAndRefund(msg.sender);
}
/**
* @dev Cancels a key owned by a different user and sends the funds to the msg.sender.
* @param _keyOwner this user's key will be canceled
* @param _signature getCancelAndRefundApprovalHash signed by the _keyOwner
*/
function cancelAndRefundFor(
address _keyOwner,
bytes calldata _signature
) external
{
require(
ECDSA.recover(
ECDSA.toEthSignedMessageHash(
getCancelAndRefundApprovalHash(_keyOwner, msg.sender)
),
_signature
) == _keyOwner, 'INVALID_SIGNATURE'
);
keyOwnerToNonce[_keyOwner]++;
_cancelAndRefund(_keyOwner);
}
/**
* @dev Increments the current nonce for the msg.sender.
* This can be used to invalidate a previously signed message.
*/
function incrementNonce(
) external
{
keyOwnerToNonce[msg.sender]++;
}
/**
* Allow the owner to change the refund penalty.
*/
function updateRefundPenalty(
uint _refundPenaltyNumerator,
uint _refundPenaltyDenominator
)
external
onlyOwner
{
require(_refundPenaltyDenominator != 0, 'INVALID_RATE');
emit RefundPenaltyChanged(
refundPenaltyNumerator,
refundPenaltyDenominator,
_refundPenaltyNumerator,
_refundPenaltyDenominator
);
refundPenaltyNumerator = _refundPenaltyNumerator;
refundPenaltyDenominator = _refundPenaltyDenominator;
}
/**
* @dev Determines how much of a refund a key owner would receive if they issued
* a cancelAndRefund block.timestamp.
* Note that due to the time required to mine a tx, the actual refund amount will be lower
* than what the user reads from this call.
*/
function getCancelAndRefundValueFor(
address _owner
)
external view
returns (uint refund)
{
return _getCancelAndRefundValue(_owner);
}
/**
* @dev returns the hash to sign in order to allow another user to cancel on your behalf.
*/
function getCancelAndRefundApprovalHash(
address _keyOwner,
address _txSender
) public view
returns (bytes32 approvalHash)
{
return keccak256(
abi.encodePacked(
// Approval is specific to this Lock
address(this),
// Approval enables only one cancel call
keyOwnerToNonce[_keyOwner],
// Approval allows only one account to broadcast the tx
_txSender
)
);
}
/**
* @dev cancels the key for the given keyOwner and sends the refund to the msg.sender.
*/
function _cancelAndRefund(
address _keyOwner
) internal
{
Key storage key = _getKeyFor(_keyOwner);
uint refund = _getCancelAndRefundValue(_keyOwner);
emit CancelKey(key.tokenId, _keyOwner, msg.sender, refund);
// expirationTimestamp is a proxy for hasKey, setting this to `block.timestamp` instead
// of 0 so that we can still differentiate hasKey from hasValidKey.
key.expirationTimestamp = block.timestamp;
if (refund > 0) {
// Security: doing this last to avoid re-entrancy concerns
_transfer(msg.sender, refund);
}
}
/**
* @dev Determines how much of a refund a key owner would receive if they issued
* a cancelAndRefund now.
* @param _owner The owner of the key check the refund value for.
*/
function _getCancelAndRefundValue(
address _owner
)
private view
hasValidKey(_owner)
returns (uint refund)
{
Key storage key = _getKeyFor(_owner);
// Math: safeSub is not required since `hasValidKey` confirms timeRemaining is positive
uint timeRemaining = key.expirationTimestamp - block.timestamp;
if(timeRemaining >= expirationDuration) {
refund = keyPrice;
} else {
// Math: using safeMul in case keyPrice or timeRemaining is very large
refund = keyPrice.mul(timeRemaining) / expirationDuration;
}
uint penalty = keyPrice.mul(refundPenaltyNumerator) / refundPenaltyDenominator;
if (refund > penalty) {
// Math: safeSub is not required since the if confirms this won't underflow
refund -= penalty;
} else {
refund = 0;
}
}
}
// File: openzeppelin-solidity/contracts/utils/Address.sol
pragma solidity ^0.5.0;
/**
* @dev Collection of functions related to the address type,
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* This test is non-exhaustive, and there may be false-negatives: during the
* execution of a contract's constructor, its address will be reported as
* not containing a contract.
*
* > It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*/
function isContract(address account) internal view returns (bool) {
// This method relies in extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
}
// File: contracts/mixins/MixinTransfer.sol
pragma solidity 0.5.9;
/**
* @title Mixin for the transfer-related functions needed to meet the ERC721
* standard.
* @author Nick Furfaro
* @dev `Mixins` are a design pattern seen in the 0x contracts. It simply
* separates logically groupings of code to ease readability.
*/
contract MixinTransfer is
MixinFunds,
MixinLockCore,
MixinKeys,
MixinApproval
{
using SafeMath for uint;
using Address for address;
event TransferFeeChanged(
uint oldTransferFeeNumerator,
uint oldTransferFeeDenominator,
uint transferFeeNumerator,
uint transferFeeDenominator
);
// 0x150b7a02 == bytes4(keccak256('onERC721Received(address,address,uint256,bytes)'))
bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
// The fee relative to keyPrice to charge when transfering a Key to another account
// (potentially on a 0x marketplace).
// This is calculated as `keyPrice * transferFeeNumerator / transferFeeDenominator`.
uint public transferFeeNumerator = 0;
uint public transferFeeDenominator = 100;
/**
* This is payable because at some point we want to allow the LOCK to capture a fee on 2ndary
* market transactions...
*/
function transferFrom(
address _from,
address _recipient,
uint _tokenId
)
public
payable
onlyIfAlive
hasValidKey(_from)
onlyKeyOwnerOrApproved(_tokenId)
{
require(_recipient != address(0), 'INVALID_ADDRESS');
_chargeAtLeast(getTransferFee(_from));
Key storage fromKey = _getKeyFor(_from);
Key storage toKey = _getKeyFor(_recipient);
uint previousExpiration = toKey.expirationTimestamp;
if (toKey.tokenId == 0) {
toKey.tokenId = fromKey.tokenId;
_recordOwner(_recipient, toKey.tokenId);
}
if (previousExpiration <= block.timestamp) {
// The recipient did not have a key, or had a key but it expired. The new expiration is the
// sender's key expiration
// an expired key is no longer a valid key, so the new tokenID is the sender's tokenID
toKey.expirationTimestamp = fromKey.expirationTimestamp;
toKey.tokenId = fromKey.tokenId;
_recordOwner(_recipient, _tokenId);
} else {
// The recipient has a non expired key. We just add them the corresponding remaining time
// SafeSub is not required since the if confirms `previousExpiration - block.timestamp` cannot underflow
toKey.expirationTimestamp = fromKey
.expirationTimestamp.add(previousExpiration - block.timestamp);
}
// Effectively expiring the key for the previous owner
fromKey.expirationTimestamp = block.timestamp;
// Set the tokenID to 0 for the previous owner to avoid duplicates
fromKey.tokenId = 0;
// Clear any previous approvals
_clearApproval(_tokenId);
// trigger event
emit Transfer(
_from,
_recipient,
_tokenId
);
}
/**
* @notice Transfers the ownership of an NFT from one address to another address
* @dev This works identically to the other function with an extra data parameter,
* except this function just sets data to ''
* @param _from The current owner of the NFT
* @param _to The new owner
* @param _tokenId The NFT to transfer
*/
function safeTransferFrom(
address _from,
address _to,
uint _tokenId
)
external
payable
{
safeTransferFrom(_from, _to, _tokenId, '');
}
/**
* @notice Transfers the ownership of an NFT from one address to another address.
* When transfer is complete, this functions
* checks if `_to` is a smart contract (code size > 0). If so, it calls
* `onERC721Received` on `_to` and throws if the return value is not
* `bytes4(keccak256('onERC721Received(address,address,uint,bytes)'))`.
* @param _from The current owner of the NFT
* @param _to The new owner
* @param _tokenId The NFT to transfer
* @param _data Additional data with no specified format, sent in call to `_to`
*/
function safeTransferFrom(
address _from,
address _to,
uint _tokenId,
bytes memory _data
)
public
payable
onlyIfAlive
onlyKeyOwnerOrApproved(_tokenId)
hasValidKey(ownerOf(_tokenId))
{
transferFrom(_from, _to, _tokenId);
require(_checkOnERC721Received(_from, _to, _tokenId, _data), 'NON_COMPLIANT_ERC721_RECEIVER');
}
/**
* Allow the Lock owner to change the transfer fee.
*/
function updateTransferFee(
uint _transferFeeNumerator,
uint _transferFeeDenominator
)
external
onlyOwner
{
require(_transferFeeDenominator != 0, 'INVALID_RATE');
emit TransferFeeChanged(
transferFeeNumerator,
transferFeeDenominator,
_transferFeeNumerator,
_transferFeeDenominator
);
transferFeeNumerator = _transferFeeNumerator;
transferFeeDenominator = _transferFeeDenominator;
}
/**
* Determines how much of a fee a key owner would need to pay in order to
* transfer the key to another account. This is pro-rated so the fee goes down
* overtime.
* @param _owner The owner of the key check the transfer fee for.
*/
function getTransferFee(
address _owner
)
public view
hasValidKey(_owner)
returns (uint)
{
Key storage key = _getKeyFor(_owner);
// Math: safeSub is not required since `hasValidKey` confirms timeRemaining is positive
uint timeRemaining = key.expirationTimestamp - block.timestamp;
uint fee;
if(timeRemaining >= expirationDuration) {
// Max the potential impact of this fee for keys with long durations remaining
fee = keyPrice;
} else {
// Math: using safeMul in case keyPrice or timeRemaining is very large
fee = keyPrice.mul(timeRemaining) / expirationDuration;
}
return fee.mul(transferFeeNumerator) / transferFeeDenominator;
}
/**
* @dev Internal function to invoke `onERC721Received` on a target address
* The call is not executed if the target address is not a contract
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param _data bytes optional data to send along with the call
* @return whether the call correctly returned the expected magic value
*/
function _checkOnERC721Received(
address from,
address to,
uint256 tokenId,
bytes memory _data
)
internal
returns (bool)
{
if (!to.isContract()) {
return true;
}
bytes4 retval = IERC721Receiver(to).onERC721Received(
msg.sender, from, tokenId, _data);
return (retval == _ERC721_RECEIVED);
}
}
// File: contracts/PublicLock.sol
pragma solidity 0.5.9;
/**
* @title The Lock contract
* @author Julien Genestoux (unlock-protocol.com)
* Eventually: implement ERC721.
* @dev ERC165 allows our contract to be queried to determine whether it implements a given interface.
* Every ERC-721 compliant contract must implement the ERC165 interface.
* https://eips.ethereum.org/EIPS/eip-721
*/
contract PublicLock is
IERC721,
MixinNoFallback,
ERC165,
Ownable,
ERC721Holder,
MixinFunds,
MixinDisableAndDestroy,
MixinLockCore,
MixinKeys,
MixinLockMetadata,
MixinGrantKeys,
MixinPurchase,
MixinApproval,
MixinTransfer,
MixinRefunds
{
constructor(
address _owner,
uint _expirationDuration,
address _tokenAddress,
uint _keyPrice,
uint _maxNumberOfKeys,
string memory _lockName
)
public
MixinFunds(_tokenAddress)
MixinLockCore(_owner, _expirationDuration, _keyPrice, _maxNumberOfKeys)
MixinLockMetadata(_lockName)
{
// registering the interface for erc721 with ERC165.sol using
// the ID specified in the standard: https://eips.ethereum.org/EIPS/eip-721
_registerInterface(0x80ac58cd);
// We must manually initialize Ownable.sol
Ownable.initialize(_owner);
}
// The version number of the current implementation on this network
function publicLockVersion(
) external pure
returns (uint16)
{
return 4;
}
}[{"constant":true,"inputs":[{"name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"owners","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_approved","type":"address"},{"name":"_tokenId","type":"uint256"}],"name":"approve","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_beneficiary","type":"address"}],"name":"updateBeneficiary","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"refundPenaltyDenominator","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"refundPenaltyNumerator","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"unlockProtocol","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_page","type":"uint256"},{"name":"_pageSize","type":"uint256"}],"name":"getOwnersByPage","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"keyPrice","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"expirationDuration","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"},{"name":"","type":"uint256"},{"name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"name":"","type":"bytes4"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_keyPrice","type":"uint256"}],"name":"updateKeyPrice","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"cancelAndRefund","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_recipient","type":"address"},{"name":"_tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_baseTokenURI","type":"string"}],"name":"setBaseTokenURI","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"beneficiary","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_refundPenaltyNumerator","type":"uint256"},{"name":"_refundPenaltyDenominator","type":"uint256"}],"name":"updateRefundPenalty","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"numberOfKeysSold","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_transferFeeNumerator","type":"uint256"},{"name":"_transferFeeDenominator","type":"uint256"}],"name":"updateTransferFee","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"isAlive","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"_a","type":"string"},{"name":"_b","type":"string"},{"name":"_c","type":"string"},{"name":"_d","type":"string"}],"name":"strConcat","outputs":[{"name":"_concatenatedString","type":"string"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"keyOwnerToNonce","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_i","type":"uint256"}],"name":"uint2Str","outputs":[{"name":"_uintAsString","type":"string"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"getCancelAndRefundValueFor","outputs":[{"name":"refund","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_lockName","type":"string"}],"name":"updateLockName","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_addr","type":"address"}],"name":"address2Str","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[],"name":"incrementNonce","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"getHasValidKey","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_recipient","type":"address"},{"name":"_referrer","type":"address"}],"name":"purchaseForFrom","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"maxNumberOfKeys","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_lockSymbol","type":"string"}],"name":"updateLockSymbol","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_recipients","type":"address[]"},{"name":"_expirationTimestamps","type":"uint256[]"}],"name":"grantKeys","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"getTransferFee","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_keyOwner","type":"address"},{"name":"_txSender","type":"address"}],"name":"getCancelAndRefundApprovalHash","outputs":[{"name":"approvalHash","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isOwner","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"transferFeeNumerator","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"numberOfOwners","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_account","type":"address"}],"name":"getTokenIdFor","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_tokenId","type":"uint256"},{"name":"_owner","type":"address"}],"name":"isKeyOwner","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_recipient","type":"address"},{"name":"_expirationTimestamp","type":"uint256"}],"name":"grantKey","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"tokenAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_owner","type":"address"}],"name":"expireKeyFor","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"destroyLock","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"keyExpirationTimestampFor","outputs":[{"name":"timestamp","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_tokenId","type":"uint256"},{"name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_keyOwner","type":"address"},{"name":"_signature","type":"bytes"}],"name":"cancelAndRefundFor","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"disableLock","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"sender","type":"address"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"publicLockVersion","outputs":[{"name":"","type":"uint16"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"transferFeeDenominator","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_recipient","type":"address"}],"name":"purchaseFor","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"_account","type":"address"}],"name":"getBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_recipients","type":"address[]"},{"name":"_expirationTimestamp","type":"uint256"}],"name":"grantKeys","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_owner","type":"address"},{"name":"_expirationDuration","type":"uint256"},{"name":"_tokenAddress","type":"address"},{"name":"_keyPrice","type":"uint256"},{"name":"_maxNumberOfKeys","type":"uint256"},{"name":"_lockName","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":false,"stateMutability":"nonpayable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"tokenId","type":"uint256"},{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"sendTo","type":"address"},{"indexed":false,"name":"refund","type":"uint256"}],"name":"CancelKey","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldRefundPenaltyNumerator","type":"uint256"},{"indexed":false,"name":"oldRefundPenaltyDenominator","type":"uint256"},{"indexed":false,"name":"refundPenaltyNumerator","type":"uint256"},{"indexed":false,"name":"refundPenaltyDenominator","type":"uint256"}],"name":"RefundPenaltyChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldTransferFeeNumerator","type":"uint256"},{"indexed":false,"name":"oldTransferFeeDenominator","type":"uint256"},{"indexed":false,"name":"transferFeeNumerator","type":"uint256"},{"indexed":false,"name":"transferFeeDenominator","type":"uint256"}],"name":"TransferFeeChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"symbol","type":"string"}],"name":"NewLockSymbol","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"tokenId","type":"uint256"}],"name":"ExpireKey","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldKeyPrice","type":"uint256"},{"indexed":false,"name":"keyPrice","type":"uint256"}],"name":"PriceChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":true,"name":"beneficiary","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Withdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"balance","type":"uint256"},{"indexed":true,"name":"owner","type":"address"}],"name":"Destroy","type":"event"},{"anonymous":false,"inputs":[],"name":"Disable","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":true,"name":"_tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_approved","type":"address"},{"indexed":true,"name":"_tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_operator","type":"address"},{"indexed":false,"name":"_approved","type":"bool"}],"name":"ApprovalForAll","type":"event"}]Contract Creation Code
6080604052600060765560646077556001607855600a6079553480156200002557600080fd5b5060405162004af138038062004af1833981810160405260c08110156200004b57600080fd5b8151602083015160408401516060850151608086015160a087018051959794969395929491938201926401000000008111156200008757600080fd5b820160208101848111156200009b57600080fd5b8151640100000000811182820187101715620000b657600080fd5b50909350839250889150879050858588620000fa7f01ffc9a7000000000000000000000000000000000000000000000000000000006001600160e01b036200037316565b6001600160a01b03811615806200017857506000816001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156200014857600080fd5b505afa1580156200015d573d6000803e3d6000fd5b505050506040513d60208110156200017457600080fd5b5051115b620001e457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f494e56414c49445f544f4b454e00000000000000000000000000000000000000604482015290519081900360640190fd5b6067805460ff60a01b196001600160a01b039093166001600160a01b031990911617919091167401000000000000000000000000000000000000000017905563bbf81e008311156200029757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4d41585f45585049524154494f4e5f3130305f59454152530000000000000000604482015290519081900360640190fd5b60688054336001600160a01b031991821617909155606d80549091166001600160a01b039590951694909417909355606991909155606a55606b558051620002e790607190602084019062000565565b506200031c7f5b5e139f000000000000000000000000000000000000000000000000000000006001600160e01b036200037316565b50620003517f80ac58cd000000000000000000000000000000000000000000000000000000006001600160e01b036200037316565b62000367866200044260201b620030891760201c565b50505050505062000607565b7fffffffff0000000000000000000000000000000000000000000000000000000080821614156200040557604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f4552433136353a20696e76616c696420696e7465726661636520696400000000604482015290519081900360640190fd5b7fffffffff00000000000000000000000000000000000000000000000000000000166000908152602081905260409020805460ff19166001179055565b600154610100900460ff1680620004675750620004676001600160e01b036200055e16565b8062000476575060015460ff16155b620004cd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e81526020018062004ac3602e913960400191505060405180910390fd5b600154610100900460ff16158015620004f8576001805460ff1961ff00199091166101001716811790555b603480546001600160a01b0319166001600160a01b0384811691909117918290556040519116906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a380156200055a576001805461ff00191690555b5050565b303b155b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620005a857805160ff1916838001178555620005d8565b82800160010185558215620005d8579182015b82811115620005d8578251825591602001919060010190620005bb565b50620005e6929150620005ea565b5090565b6200056291905b80821115620005e65760008155600101620005f1565b6144ac80620006176000396000f3fe6080604052600436106103e45760003560e01c80636d8ea5b4116102085780639d76ea5811610118578063c4d66de8116100ab578063e985e9c51161007a578063e985e9c5146113c6578063f2fde38b14611401578063f6e4641f14611434578063f8b2cb4f1461145a578063f8faf9421461148d576103e4565b8063c4d66de814611328578063c87b56dd1461135b578063d1bbd49c14611385578063d42cfc41146113b1576103e4565b8063abdf82ce116100e7578063abdf82ce14611191578063b88d4fde146111c4578063b8968bb414611288578063c1c98d0314611313576103e4565b80639d76ea58146110f95780639f98d3cb1461110e578063a22cb46514611141578063a843a4e71461117c576103e4565b80638d0361fc1161019b57806393fd18441161016a57806393fd18441461102a57806395d89b411461103f578063970aaeb714611054578063994a8a71146110875780639b3f0b17146110c0576103e4565b80638d0361fc14610fb05780638da5cb5b14610feb5780638f32d59b146110005780638f98ce8f14611015576103e4565b806374b6c106116101d757806374b6c10614610e22578063782a4ade14610e375780637c7c425314610eb257806383cb0fa914610f7d576103e4565b80636d8ea5b414610d7957806370a0823114610dac57806370efb77014610ddf578063715018a614610e0d576103e4565b806323b872dd1161030357806342842e0e1161029657806352d6a8e41161026557806352d6a8e414610c59578063550ef3a814610c8c5780635fdb97e114610d07578063627cdcb914610d3a5780636352211e14610d4f576103e4565b806342842e0e1461098657806345e965cd146109bc5780634bc5a13514610bfc5780634c7a12a014610c2f576103e4565b806339f46986116102d257806339f46986146108fc5780633ba70e311461092c5780633d3359cb146109415780634136aa3514610971576103e4565b806323b872dd1461080c5780632e1a7d4d1461084257806330176e131461086c57806338af3eed146108e7576103e4565b80630f15023b1161037b578063150b7a021161034a578063150b7a02146106ca57806318160ddd146107b85780631f1ec029146107cd5780632009dc65146107f7576103e4565b80630f15023b1461060b57806310803b721461062057806310e56973146106a057806311a4c03a146106b5576103e4565b8063095ea7b3116103b7578063095ea7b31461056e5780630aaffd2a1461059c5780630c79130f146105cf5780630ed3e2cc146105f6576103e4565b806301ffc9a71461042c578063025e7c271461047457806306fdde03146104ba578063081812fc14610544575b3480156103f057600080fd5b506040805162461bcd60e51b815260206004820152600b60248201526a4e4f5f46414c4c4241434b60a81b604482015290519081900360640190fd5b34801561043857600080fd5b506104606004803603602081101561044f57600080fd5b50356001600160e01b031916611508565b604080519115158252519081900360200190f35b34801561048057600080fd5b5061049e6004803603602081101561049757600080fd5b503561152b565b604080516001600160a01b039092168252519081900360200190f35b3480156104c657600080fd5b506104cf611552565b6040805160208082528351818301528351919283929083019185019080838360005b838110156105095781810151838201526020016104f1565b50505050905090810190601f1680156105365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561055057600080fd5b5061049e6004803603602081101561056757600080fd5b50356115e9565b61059a6004803603604081101561058457600080fd5b506001600160a01b0381351690602001356115fa565b005b3480156105a857600080fd5b5061059a600480360360208110156105bf57600080fd5b50356001600160a01b0316611787565b3480156105db57600080fd5b506105e4611878565b60408051918252519081900360200190f35b34801561060257600080fd5b506105e461187e565b34801561061757600080fd5b5061049e611884565b34801561062c57600080fd5b506106506004803603604081101561064357600080fd5b5080359060200135611893565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561068c578181015183820152602001610674565b505050509050019250505060405180910390f35b3480156106ac57600080fd5b506105e46119ab565b3480156106c157600080fd5b506105e46119b1565b3480156106d657600080fd5b5061079b600480360360808110156106ed57600080fd5b6001600160a01b03823581169260208101359091169160408201359190810190608081016060820135600160201b81111561072757600080fd5b82018360208201111561073957600080fd5b803590602001918460018302840111600160201b8311171561075a57600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295506119b7945050505050565b604080516001600160e01b03199092168252519081900360200190f35b3480156107c457600080fd5b506105e46119c8565b3480156107d957600080fd5b5061059a600480360360208110156107f057600080fd5b50356119ce565b34801561080357600080fd5b5061059a611a76565b61059a6004803603606081101561082257600080fd5b506001600160a01b03813581169160208101359091169060400135611a81565b34801561084e57600080fd5b5061059a6004803603602081101561086557600080fd5b5035611ccb565b34801561087857600080fd5b5061059a6004803603602081101561088f57600080fd5b810190602081018135600160201b8111156108a957600080fd5b8201836020820111156108bb57600080fd5b803590602001918460018302840111600160201b831117156108dc57600080fd5b509092509050611e1f565b3480156108f357600080fd5b5061049e611e3c565b34801561090857600080fd5b5061059a6004803603604081101561091f57600080fd5b5080359060200135611e4b565b34801561093857600080fd5b506105e4611ef5565b34801561094d57600080fd5b5061059a6004803603604081101561096457600080fd5b5080359060200135611efb565b34801561097d57600080fd5b50610460611fa5565b61059a6004803603606081101561099c57600080fd5b506001600160a01b03813581169160208101359091169060400135611fb5565b3480156109c857600080fd5b506104cf600480360360808110156109df57600080fd5b810190602081018135600160201b8111156109f957600080fd5b820183602082011115610a0b57600080fd5b803590602001918460018302840111600160201b83111715610a2c57600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295949360208101935035915050600160201b811115610a7e57600080fd5b820183602082011115610a9057600080fd5b803590602001918460018302840111600160201b83111715610ab157600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295949360208101935035915050600160201b811115610b0357600080fd5b820183602082011115610b1557600080fd5b803590602001918460018302840111600160201b83111715610b3657600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295949360208101935035915050600160201b811115610b8857600080fd5b820183602082011115610b9a57600080fd5b803590602001918460018302840111600160201b83111715610bbb57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550611fd0945050505050565b348015610c0857600080fd5b506105e460048036036020811015610c1f57600080fd5b50356001600160a01b0316612185565b348015610c3b57600080fd5b506104cf60048036036020811015610c5257600080fd5b5035612197565b348015610c6557600080fd5b506105e460048036036020811015610c7c57600080fd5b50356001600160a01b031661225b565b348015610c9857600080fd5b5061059a60048036036020811015610caf57600080fd5b810190602081018135600160201b811115610cc957600080fd5b820183602082011115610cdb57600080fd5b803590602001918460018302840111600160201b83111715610cfc57600080fd5b509092509050612266565b348015610d1357600080fd5b506104cf60048036036020811015610d2a57600080fd5b50356001600160a01b0316612283565b348015610d4657600080fd5b5061059a6123ff565b348015610d5b57600080fd5b5061049e60048036036020811015610d7257600080fd5b5035612417565b348015610d8557600080fd5b5061046060048036036020811015610d9c57600080fd5b50356001600160a01b031661248d565b348015610db857600080fd5b506105e460048036036020811015610dcf57600080fd5b50356001600160a01b03166124ad565b61059a60048036036040811015610df557600080fd5b506001600160a01b038135811691602001351661251c565b348015610e1957600080fd5b5061059a6125c1565b348015610e2e57600080fd5b506105e461261c565b348015610e4357600080fd5b5061059a60048036036020811015610e5a57600080fd5b810190602081018135600160201b811115610e7457600080fd5b820183602082011115610e8657600080fd5b803590602001918460018302840111600160201b83111715610ea757600080fd5b509092509050612622565b348015610ebe57600080fd5b5061059a60048036036040811015610ed557600080fd5b810190602081018135600160201b811115610eef57600080fd5b820183602082011115610f0157600080fd5b803590602001918460208302840111600160201b83111715610f2257600080fd5b919390929091602081019035600160201b811115610f3f57600080fd5b820183602082011115610f5157600080fd5b803590602001918460208302840111600160201b83111715610f7257600080fd5b5090925090506126a4565b348015610f8957600080fd5b506105e460048036036020811015610fa057600080fd5b50356001600160a01b0316612706565b348015610fbc57600080fd5b506105e460048036036040811015610fd357600080fd5b506001600160a01b03813581169160200135166127c9565b348015610ff757600080fd5b5061049e61282d565b34801561100c57600080fd5b5061046061283c565b34801561102157600080fd5b506105e461284d565b34801561103657600080fd5b506105e4612853565b34801561104b57600080fd5b506104cf612859565b34801561106057600080fd5b506105e46004803603602081101561107757600080fd5b50356001600160a01b03166129e4565b34801561109357600080fd5b50610460600480360360408110156110aa57600080fd5b50803590602001356001600160a01b0316612a4e565b3480156110cc57600080fd5b5061059a600480360360408110156110e357600080fd5b506001600160a01b038135169060200135612a6f565b34801561110557600080fd5b5061049e612a8e565b34801561111a57600080fd5b5061059a6004803603602081101561113157600080fd5b50356001600160a01b0316612a9d565b34801561114d57600080fd5b5061059a6004803603604081101561116457600080fd5b506001600160a01b0381351690602001351515612b54565b34801561118857600080fd5b5061059a612c5f565b34801561119d57600080fd5b506105e4600480360360208110156111b457600080fd5b50356001600160a01b0316612d0b565b61059a600480360360808110156111da57600080fd5b6001600160a01b03823581169260208101359091169160408201359190810190608081016060820135600160201b81111561121457600080fd5b82018360208201111561122657600080fd5b803590602001918460018302840111600160201b8311171561124757600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550612d90945050505050565b34801561129457600080fd5b5061059a600480360360408110156112ab57600080fd5b6001600160a01b038235169190810190604081016020820135600160201b8111156112d557600080fd5b8201836020820111156112e757600080fd5b803590602001918460018302840111600160201b8311171561130857600080fd5b509092509050612f1e565b34801561131f57600080fd5b5061059a612ff0565b34801561133457600080fd5b5061059a6004803603602081101561134b57600080fd5b50356001600160a01b0316613089565b34801561136757600080fd5b506104cf6004803603602081101561137e57600080fd5b5035613179565b34801561139157600080fd5b5061139a613391565b6040805161ffff9092168252519081900360200190f35b3480156113bd57600080fd5b506105e4613396565b3480156113d257600080fd5b50610460600480360360408110156113e957600080fd5b506001600160a01b038135811691602001351661339c565b34801561140d57600080fd5b5061059a6004803603602081101561142457600080fd5b50356001600160a01b03166133ca565b61059a6004803603602081101561144a57600080fd5b50356001600160a01b03166133e7565b34801561146657600080fd5b506105e46004803603602081101561147d57600080fd5b50356001600160a01b0316613442565b34801561149957600080fd5b5061059a600480360360408110156114b057600080fd5b810190602081018135600160201b8111156114ca57600080fd5b8201836020820111156114dc57600080fd5b803590602001918460208302840111600160201b831117156114fd57600080fd5b9193509150356134e6565b6001600160e01b0319811660009081526020819052604090205460ff165b919050565b6070818154811061153857fe5b6000918252602090912001546001600160a01b0316905081565b60718054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156115de5780601f106115b3576101008083540402835291602001916115de565b820191906000526020600020905b8154815290600101906020018083116115c157829003601f168201915b505050505090505b90565b60006115f482613535565b92915050565b606754600160a01b900460ff1661164a576040805162461bcd60e51b815260206004820152600f60248201526e1313d0d2d7d11154149150d0551151608a1b604482015290519081900360640190fd5b806116558133612a4e565b806116655750611665813361358f565b8061167d575061167d61167782612417565b3361339c565b6116cb576040805162461bcd60e51b815260206004820152601a60248201527913d3931657d2d15657d3d5d3915497d3d497d054141493d5915160321b604482015290519081900360640190fd5b336001600160a01b0384161415611718576040805162461bcd60e51b815260206004820152600c60248201526b20a8282927ab22afa9a2a62360a11b604482015290519081900360640190fd5b600082815260746020526040902080546001600160a01b0319166001600160a01b038516908117909155829061174d82612417565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a4505050565b61178f61282d565b6001600160a01b0316336001600160a01b031614806117b85750606d546001600160a01b031633145b611809576040805162461bcd60e51b815260206004820152601e60248201527f4f4e4c595f4c4f434b5f4f574e45525f4f525f42454e45464943494152590000604482015290519081900360640190fd5b6001600160a01b038116611856576040805162461bcd60e51b815260206004820152600f60248201526e494e56414c49445f4144445245535360881b604482015290519081900360640190fd5b606d80546001600160a01b0319166001600160a01b0392909216919091179055565b60795481565b60785481565b6068546001600160a01b031681565b6070546060906118e0576040805162461bcd60e51b81526020600482015260136024820152724e4f5f4f55545354414e44494e475f4b45595360681b604482015290519081900360640190fd5b607054829084820290600090828401111561190357506070548181039250611908565b508082015b606083604051908082528060200260200182016040528015611934578160200160208202803883390190505b5090506000835b8381101561199e576070818154811061195057fe5b9060005260206000200160009054906101000a90046001600160a01b031683838151811061197a57fe5b6001600160a01b03909216602092830291909101909101526001918201910161193b565b5090979650505050505050565b606a5481565b60695481565b630a85bd0160e11b5b949350505050565b606c5490565b6119d661283c565b6119df57600080fd5b606754600160a01b900460ff16611a2f576040805162461bcd60e51b815260206004820152600f60248201526e1313d0d2d7d11154149150d0551151608a1b604482015290519081900360640190fd5b606a805490829055604080518281526020810184905281517f8aa4fa52648a6d15edce8a179c792c86f3719d0cc3c572cf90f91948f0f2cb68929181900390910190a15050565b611a7f336135b0565b565b606754600160a01b900460ff16611ad1576040805162461bcd60e51b815260206004820152600f60248201526e1313d0d2d7d11154149150d0551151608a1b604482015290519081900360640190fd5b82611adb8161248d565b611b1c576040805162461bcd60e51b815260206004820152600d60248201526c12d15657d393d517d590531251609a1b604482015290519081900360640190fd5b81611b278133612a4e565b80611b375750611b37813361358f565b80611b495750611b4961167782612417565b611b97576040805162461bcd60e51b815260206004820152601a60248201527913d3931657d2d15657d3d5d3915497d3d497d054141493d5915160321b604482015290519081900360640190fd5b6001600160a01b038416611be4576040805162461bcd60e51b815260206004820152600f60248201526e494e56414c49445f4144445245535360881b604482015290519081900360640190fd5b611bf5611bf086612706565b613625565b6000611c0086613842565b90506000611c0d86613842565b6001810154815491925090611c2c578254808355611c2c90889061385c565b428111611c50576001808401549083015582548255611c4b878761385c565b611c6d565b6001830154611c679042830363ffffffff6138e316565b60018301555b42600184015560008355611c8086613944565b85876001600160a01b0316896001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a45050505050505050565b611cd361282d565b6001600160a01b0316336001600160a01b03161480611cfc5750606d546001600160a01b031633145b611d4d576040805162461bcd60e51b815260206004820152601e60248201527f4f4e4c595f4c4f434b5f4f574e45525f4f525f42454e45464943494152590000604482015290519081900360640190fd5b6000611d5830613442565b90506000821580611d6857508183115b15611dbc5760008211611db5576040805162461bcd60e51b815260206004820152601060248201526f4e4f545f454e4f5547485f46554e445360801b604482015290519081900360640190fd5b5080611dbf565b50815b606d546040805183815290516001600160a01b039092169133917f2717ead6b9200dd235aad468c9809ea400fe33ac69b5bfaa6d3e90fc922b6398919081900360200190a3606d54611e1a906001600160a01b03168261397f565b505050565b611e2761283c565b611e3057600080fd5b611e1a60738383614390565b606d546001600160a01b031681565b611e5361283c565b611e5c57600080fd5b80611e9d576040805162461bcd60e51b815260206004820152600c60248201526b494e56414c49445f5241544560a01b604482015290519081900360640190fd5b60785460795460408051928352602083019190915281810184905260608201839052517fa7d13855fe99b8c8b2780f36ff9e06133878b66a664a951b2375fe2296cc00089181900360800190a1607891909155607955565b606c5481565b611f0361283c565b611f0c57600080fd5b80611f4d576040805162461bcd60e51b815260206004820152600c60248201526b494e56414c49445f5241544560a01b604482015290519081900360640190fd5b60765460775460408051928352602083019190915281810184905260608201839052517ff0cc3b8bd450a20cce7720453e064b35cf490baa5d53c7c6ef8b2e1b02898a7d9181900360800190a1607691909155607755565b606754600160a01b900460ff1681565b611e1a83838360405180602001604052806000815250612d90565b606080859050606085905060608590506060859050606081518351855187510101016040519080825280601f01601f19166020018201604052801561201c576020820181803883390190505b509050806000805b87518110156120755787818151811061203957fe5b602001015160f81c60f81b83838060010194508151811061205657fe5b60200101906001600160f81b031916908160001a905350600101612024565b5060005b86518110156120ca5786818151811061208e57fe5b602001015160f81c60f81b8383806001019450815181106120ab57fe5b60200101906001600160f81b031916908160001a905350600101612079565b5060005b855181101561211f578581815181106120e357fe5b602001015160f81c60f81b83838060010194508151811061210057fe5b60200101906001600160f81b031916908160001a9053506001016120ce565b5060005b84518110156121745784818151811061213857fe5b602001015160f81c60f81b83838060010194508151811061215557fe5b60200101906001600160f81b031916908160001a905350600101612123565b50909b9a5050505050505050505050565b607a6020526000908152604090205481565b606081806121be5750506040805180820190915260018152600360fc1b6020820152611526565b8260005b81156121d657600101600a820491506121c2565b6060816040519080825280601f01601f191660200182016040528015612203576020820181803883390190505b50905060001982015b841561225157600a850660300160f81b8282806001900393508151811061222f57fe5b60200101906001600160f81b031916908160001a905350600a8504945061220c565b5095945050505050565b60006115f482613b8b565b61226e61283c565b61227757600080fd5b611e1a60718383614390565b604080518082018252601081526f181899199a1a9b1b9c1cb0b131b232b360811b60208201528151602a80825260608281019094526001600160a01b03851692918491602082018180388339019050509050600360fc1b816000815181106122e757fe5b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061231057fe5b60200101906001600160f81b031916908160001a90535060005b60148110156123f6578260048583600c016020811061234557fe5b1a60f81b6001600160f81b031916901c60f81c60ff168151811061236557fe5b602001015160f81c60f81b82826002026002018151811061238257fe5b60200101906001600160f81b031916908160001a905350828482600c01602081106123a957fe5b825191901a600f169081106123ba57fe5b602001015160f81c60f81b8282600202600301815181106123d757fe5b60200101906001600160f81b031916908160001a90535060010161232a565b50949350505050565b336000908152607a6020526040902080546001019055565b6000818152606f602052604081205482906001600160a01b0316612470576040805162461bcd60e51b815260206004820152600b60248201526a4e4f5f535543485f4b455960a81b604482015290519081900360640190fd5b50506000908152606f60205260409020546001600160a01b031690565b6001600160a01b03166000908152606e6020526040902060010154421090565b60006001600160a01b0382166124fc576040805162461bcd60e51b815260206004820152600f60248201526e494e56414c49445f4144445245535360881b604482015290519081900360640190fd5b6125058261248d565b612510576000612513565b60015b60ff1692915050565b606754600160a01b900460ff1661256c576040805162461bcd60e51b815260206004820152600f60248201526e1313d0d2d7d11154149150d0551151608a1b604482015290519081900360640190fd5b806125768161248d565b6125b7576040805162461bcd60e51b815260206004820152600d60248201526c12d15657d393d517d590531251609a1b604482015290519081900360640190fd5b611e1a8383613c6a565b6125c961283c565b6125d257600080fd5b6034546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3603480546001600160a01b0319169055565b606b5481565b61262a61283c565b61263357600080fd5b61263f60728383614390565b507f8868e22e84ebf32da89b2ebcb0ac642816304ea3863b257f240df9098719cb97828260405180806020018281038252848482818152602001925080828437600083820152604051601f909101601f19169092018290039550909350505050a15050565b6126ac61283c565b6126b557600080fd5b60005b838110156126ff576126f78585838181106126cf57fe5b905060200201356001600160a01b03168484848181106126eb57fe5b90506020020135613f35565b6001016126b8565b5050505050565b6000816127128161248d565b612753576040805162461bcd60e51b815260206004820152600d60248201526c12d15657d393d517d590531251609a1b604482015290519081900360640190fd5b600061275e84613842565b905060004282600101540390506000606954821061277f5750606a546127a0565b606954606a54612795908463ffffffff61403516565b8161279c57fe5b0490505b6077546076546127b790839063ffffffff61403516565b816127be57fe5b049695505050505050565b6001600160a01b03919091166000908152607a602090815260409182902054825130606090811b82850152603482019290925293901b6bffffffffffffffffffffffff19166054840152815180840360480181526068909301909152815191012090565b6034546001600160a01b031690565b6034546001600160a01b0316331490565b60765481565b60705490565b6072546060906002600019610100600184161502019091160461295257606860009054906101000a90046001600160a01b03166001600160a01b03166335a750de6040518163ffffffff1660e01b815260040160006040518083038186803b1580156128c457600080fd5b505afa1580156128d8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561290157600080fd5b810190808051600160201b81111561291857600080fd5b8201602081018481111561292b57600080fd5b8151600160201b81118282018710171561294457600080fd5b509094506115e69350505050565b6072805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156129d85780601f106129ad576101008083540402835291602001916129d8565b820191906000526020600020905b8154815290600101906020018083116129bb57829003601f168201915b505050505090506115e6565b6000816129f08161248d565b612a31576040805162461bcd60e51b815260206004820152600d60248201526c12d15657d393d517d590531251609a1b604482015290519081900360640190fd5b50506001600160a01b03166000908152606e602052604090205490565b6000918252606f6020526040909120546001600160a01b0391821691161490565b612a7761283c565b612a8057600080fd5b612a8a8282613f35565b5050565b6067546001600160a01b031681565b612aa561283c565b612aae57600080fd5b80612ab88161248d565b612af9576040805162461bcd60e51b815260206004820152600d60248201526c12d15657d393d517d590531251609a1b604482015290519081900360640190fd5b6001600160a01b0382166000908152606e602090815260409182902042600182015580548351908152925190927f59f2fe866dd27a1c2d34115520888c3150365cbc931aab97fa88c4b9ab40b79592908290030190a1505050565b606754600160a01b900460ff16612ba4576040805162461bcd60e51b815260206004820152600f60248201526e1313d0d2d7d11154149150d0551151608a1b604482015290519081900360640190fd5b6001600160a01b038216331415612bf1576040805162461bcd60e51b815260206004820152600c60248201526b20a8282927ab22afa9a2a62360a11b604482015290519081900360640190fd5b3360008181526075602090815260408083206001600160a01b03871680855290835292819020805460ff1916861515908117909155815190815290519293927f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31929181900390910190a35050565b612c6761283c565b612c7057600080fd5b606754600160a01b900460ff1615612cbf576040805162461bcd60e51b815260206004820152600d60248201526c111254d050931157d1925494d5609a1b604482015290519081900360640190fd5b6040805130318152905133917fa053f2a7eceda47dde76e5939c5adf7b771e665fc84c8ef6feffc290a1eb1feb919081900360200190a2612d0833612d0330613442565b61397f565b33ff5b6001600160a01b0381166000908152606e60205260408120600101548290612d70576040805162461bcd60e51b81526020600482015260136024820152724841535f4e455645525f4f574e45445f4b455960681b604482015290519081900360640190fd5b50506001600160a01b03166000908152606e602052604090206001015490565b606754600160a01b900460ff16612de0576040805162461bcd60e51b815260206004820152600f60248201526e1313d0d2d7d11154149150d0551151608a1b604482015290519081900360640190fd5b81612deb8133612a4e565b80612dfb5750612dfb813361358f565b80612e0d5750612e0d61167782612417565b612e5b576040805162461bcd60e51b815260206004820152601a60248201527913d3931657d2d15657d3d5d3915497d3d497d054141493d5915160321b604482015290519081900360640190fd5b612e6483612417565b612e6d8161248d565b612eae576040805162461bcd60e51b815260206004820152600d60248201526c12d15657d393d517d590531251609a1b604482015290519081900360640190fd5b612eb9868686611a81565b612ec58686868661408e565b612f16576040805162461bcd60e51b815260206004820152601d60248201527f4e4f4e5f434f4d504c49414e545f4552433732315f5245434549564552000000604482015290519081900360640190fd5b505050505050565b826001600160a01b0316612f79612f3d612f3886336127c9565b6141c1565b84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061421292505050565b6001600160a01b031614612fc8576040805162461bcd60e51b8152602060048201526011602482015270494e56414c49445f5349474e415455524560781b604482015290519081900360640190fd5b6001600160a01b0383166000908152607a6020526040902080546001019055611e1a836135b0565b612ff861283c565b61300157600080fd5b606754600160a01b900460ff16613051576040805162461bcd60e51b815260206004820152600f60248201526e1313d0d2d7d11154149150d0551151608a1b604482015290519081900360640190fd5b6040517f25a311358326fb18c62efc24bc28d3126acee8d2a67fd8b2145b757dc8bd1bc190600090a16067805460ff60a01b19169055565b600154610100900460ff16806130a257506130a2614300565b806130b0575060015460ff16155b6130eb5760405162461bcd60e51b815260040180806020018281038252602e81526020018061444a602e913960400191505060405180910390fd5b600154610100900460ff16158015613115576001805460ff1961ff00199091166101001716811790555b603480546001600160a01b0319166001600160a01b0384811691909117918290556040519116906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a38015612a8a576001805461ff00191690555050565b6000818152606f602052604090205460609082906001600160a01b03166131d5576040805162461bcd60e51b815260206004820152600b60248201526a4e4f5f535543485f4b455960a81b604482015290519081900360640190fd5b607354606090600260001961010060018416150201909116046132ce57606860009054906101000a90046001600160a01b03166001600160a01b0316637ff94bb26040518163ffffffff1660e01b815260040160006040518083038186803b15801561324057600080fd5b505afa158015613254573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561327d57600080fd5b810190808051600160201b81111561329457600080fd5b820160208101848111156132a757600080fd5b8151600160201b8111828201871017156132c057600080fd5b5090945061335c9350505050565b6073805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156133545780601f1061332957610100808354040283529160200191613354565b820191906000526020600020905b81548152906001019060200180831161333757829003601f168201915b505050505090505b6119c08161336930612283565b604051806040016040528060018152602001602f60f81b81525061338c88612197565b611fd0565b600490565b60775481565b6001600160a01b03918216600090815260756020908152604080832093909416825291909152205460ff1690565b6133d261283c565b6133db57600080fd5b6133e481614306565b50565b606754600160a01b900460ff16613437576040805162461bcd60e51b815260206004820152600f60248201526e1313d0d2d7d11154149150d0551151608a1b604482015290519081900360640190fd5b6133e4816000613c6a565b6067546000906001600160a01b031661346657506001600160a01b03811631611526565b606754604080516370a0823160e01b81526001600160a01b038581166004830152915191909216916370a08231916024808301926020929190829003018186803b1580156134b357600080fd5b505afa1580156134c7573d6000803e3d6000fd5b505050506040513d60208110156134dd57600080fd5b50519050611526565b6134ee61283c565b6134f757600080fd5b60005b8281101561352f5761352784848381811061351157fe5b905060200201356001600160a01b031683613f35565b6001016134fa565b50505050565b6000818152607460205260408120546001600160a01b0316806115f4576040805162461bcd60e51b815260206004820152600d60248201526c1393d39157d054141493d59151609a1b604482015290519081900360640190fd5b600091825260746020526040909120546001600160a01b0391821691161490565b60006135bb82613842565b905060006135c883613b8b565b825460408051838152905192935033926001600160a01b03871692917f0a7068a9989857441c039a14a42b67ed71dd1fcfe5a9b17cc87b252e47bce528919081900360200190a44260018301558015611e1a57611e1a338261397f565b80156133e4576067546001600160a01b03166136885780341015613683576040805162461bcd60e51b815260206004820152601060248201526f4e4f545f454e4f5547485f46554e445360801b604482015290519081900360640190fd5b6133e4565b606754604080516370a0823160e01b815230600482015290516001600160a01b039092169160009183916370a0823191602480820192602092909190829003018186803b1580156136d857600080fd5b505afa1580156136ec573d6000803e3d6000fd5b505050506040513d602081101561370257600080fd5b5051604080516323b872dd60e01b81523360048201523060248201526044810186905290519192506001600160a01b038416916323b872dd916064808201926020929091908290030181600087803b15801561375d57600080fd5b505af1158015613771573d6000803e3d6000fd5b505050506040513d602081101561378757600080fd5b5050604080516370a0823160e01b8152306004820152905182916001600160a01b038516916370a0823191602480820192602092909190829003018186803b1580156137d257600080fd5b505afa1580156137e6573d6000803e3d6000fd5b505050506040513d60208110156137fc57600080fd5b505111611e1a576040805162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b604482015290519081900360640190fd5b6001600160a01b03166000908152606e6020526040902090565b6000818152606f60205260409020546001600160a01b03838116911614612a8a5760708054600181019091557f8f6b23ffa15f0465e3176e15ca644cf24f86dc1312fe715484e3c4aead5eb78b0180546001600160a01b0384166001600160a01b031991821681179092556000838152606f60205260409020805490911690911790555050565b60008282018381101561393d576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b6000818152607460205260409020546001600160a01b0316156133e457600090815260746020526040902080546001600160a01b0319169055565b8015612a8a576067546001600160a01b03166139d1576040516001600160a01b0383169082156108fc029083906000818181858888f193505050501580156139cb573d6000803e3d6000fd5b50612a8a565b606754604080516370a0823160e01b81526001600160a01b0385811660048301529151919092169160009183916370a08231916024808301926020929190829003018186803b158015613a2357600080fd5b505afa158015613a37573d6000803e3d6000fd5b505050506040513d6020811015613a4d57600080fd5b50516040805163a9059cbb60e01b81526001600160a01b0387811660048301526024820187905291519293509084169163a9059cbb916044808201926020929091908290030181600087803b158015613aa557600080fd5b505af1158015613ab9573d6000803e3d6000fd5b505050506040513d6020811015613acf57600080fd5b5050604080516370a0823160e01b81526001600160a01b038681166004830152915183928516916370a08231916024808301926020929190829003018186803b158015613b1b57600080fd5b505afa158015613b2f573d6000803e3d6000fd5b505050506040513d6020811015613b4557600080fd5b50511161352f576040805162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b604482015290519081900360640190fd5b600081613b978161248d565b613bd8576040805162461bcd60e51b815260206004820152600d60248201526c12d15657d393d517d590531251609a1b604482015290519081900360640190fd5b6000613be384613842565b905060004282600101540390506069548110613c0357606a549350613c24565b606954606a54613c19908363ffffffff61403516565b81613c2057fe5b0493505b6000607954613c40607854606a5461403590919063ffffffff16565b81613c4757fe5b04905080851115613c5c578085039450613c61565b600094505b50505050919050565b606c54606b5411613cb2576040805162461bcd60e51b815260206004820152600d60248201526c1313d0d2d7d4d3d31117d3d555609a1b604482015290519081900360640190fd5b6001600160a01b038216613cff576040805162461bcd60e51b815260206004820152600f60248201526e494e56414c49445f4144445245535360881b604482015290519081900360640190fd5b606a5460685460408051630cb175e360e01b81526001600160a01b03868116600483015260248201859052825160009586959094921692630cb175e3926044808301939192829003018186803b158015613d5857600080fd5b505afa158015613d6c573d6000803e3d6000fd5b505050506040513d6040811015613d8257600080fd5b50805160209091015190935091508080841115613da157506000613da6565b508281035b6000613db187613842565b8054909150613dd157613dc381614375565b613dd187826000015461385c565b42816001015410613dfd576069546001820154613df39163ffffffff6138e316565b6001820155613e08565b606954420160018201555b8415613e7b5760685460408051633652466360e01b8152600481018890526024810187905290516001600160a01b039092169163365246639160448082019260009290919082900301818387803b158015613e6257600080fd5b505af1158015613e76573d6000803e3d6000fd5b505050505b6068546040805163939d9f1f60e01b8152600481018590526001600160a01b0389811660248301529151919092169163939d9f1f91604480830192600092919082900301818387803b158015613ed057600080fd5b505af1158015613ee4573d6000803e3d6000fd5b5050606c546040519092506001600160a01b038a1691506000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a4613f2c82613625565b50505050505050565b6001600160a01b038216613f82576040805162461bcd60e51b815260206004820152600f60248201526e494e56414c49445f4144445245535360881b604482015290519081900360640190fd5b6000613f8d83613842565b905080600101548211613fda576040805162461bcd60e51b815260206004820152601060248201526f414c52454144595f4f574e535f4b455960801b604482015290519081900360640190fd5b613fe381614375565b613ff183826000015461385c565b6001810182905580546040516001600160a01b038516906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a4505050565b600082614044575060006115f4565b8282028284828161405157fe5b041461393d5760405162461bcd60e51b81526004018080602001828103825260218152602001806144296021913960400191505060405180910390fd5b60006140a2846001600160a01b031661438a565b6140ae575060016119c0565b604051630a85bd0160e11b815233600482018181526001600160a01b03888116602485015260448401879052608060648501908152865160848601528651600095928a169463150b7a029490938c938b938b939260a4019060208501908083838e5b83811015614128578181015183820152602001614110565b50505050905090810190601f1680156141555780820380516001836020036101000a031916815260200191505b5095505050505050602060405180830381600087803b15801561417757600080fd5b505af115801561418b573d6000803e3d6000fd5b505050506040513d60208110156141a157600080fd5b50516001600160e01b031916630a85bd0160e11b14915050949350505050565b604080517f19457468657265756d205369676e6564204d6573736167653a0a333200000000602080830191909152603c8083019490945282518083039094018452605c909101909152815191012090565b60008151604114614225575060006115f4565b60208201516040830151606084015160001a7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a082111561426b57600093505050506115f4565b8060ff16601b1415801561428357508060ff16601c14155b1561429457600093505050506115f4565b6040805160008152602080820180845289905260ff8416828401526060820186905260808201859052915160019260a0808401939192601f1981019281900390910190855afa1580156142eb573d6000803e3d6000fd5b5050604051601f190151979650505050505050565b303b1590565b6001600160a01b03811661431957600080fd5b6034546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3603480546001600160a01b0319166001600160a01b0392909216919091179055565b80546133e457606c8054600101908190559055565b3b151590565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106143d15782800160ff198235161785556143fe565b828001600101855582156143fe579182015b828111156143fe5782358255916020019190600101906143e3565b5061440a92915061440e565b5090565b6115e691905b8082111561440a576000815560010161441456fe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77436f6e747261637420696e7374616e63652068617320616c7265616479206265656e20696e697469616c697a6564a265627a7a7230582047f15e51181351647a87d6980e0f8f20a1f88485a060e1fd9ce02eb8828eb7b464736f6c63430005090032436f6e747261637420696e7374616e63652068617320616c7265616479206265656e20696e697469616c697a6564000000000000000000000000c0f32eba9a4192d93209e83e03b95be7f81036d700000000000000000000000000000000000000000000000000000000000151800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000aad08158cd000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000094644432031204461790000000000000000000000000000000000000000000000
Deployed ByteCode Sourcemap
72624:1064:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;47743:21:0;;;-1:-1:-1;;;47743:21:0;;;;;;;;;;;;-1:-1:-1;;;47743:21:0;;;;;;;;;;;;;;12092:135;;8:9:-1;5:2;;;30:1;27;20:12;5:2;12092:135:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;12092:135:0;-1:-1:-1;;;;;;12092:135:0;;:::i;:::-;;;;;;;;;;;;;;;;;;30062:23;;8:9:-1;5:2;;;30:1;27;20:12;5:2;30062:23:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;30062:23:0;;:::i;:::-;;;;-1:-1:-1;;;;;30062:23:0;;;;;;;;;;;;;;45537:94;;8:9:-1;5:2;;;30:1;27;20:12;5:2;45537:94:0;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:100:-1;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;45537:94:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;38219:138;;8:9:-1;5:2;;;30:1;27;20:12;5:2;38219:138:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;38219:138:0;;:::i;37249:311::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;37249:311:0;;;;;;;;:::i;:::-;;28391:199;;8:9:-1;5:2;;;30:1;27;20:12;5:2;28391:199:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;28391:199:0;-1:-1:-1;;;;;28391:199:0;;:::i;59330:41::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;59330:41:0;;;:::i;:::-;;;;;;;;;;;;;;;;59287:38;;8:9:-1;5:2;;;30:1;27;20:12;5:2;59287:38:0;;;:::i;25420:29::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;25420:29:0;;;:::i;32352:879::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;32352:879:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;32352:879:0;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:100:-1;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;32352:879:0;;;;;;;;;;;;;;;;;25795:20;;8:9:-1;5:2;;;30:1;27;20:12;5:2;25795:20:0;;;:::i;25637:30::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;25637:30:0;;;:::i;14281:147::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;14281:147:0;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;-1:-1;;;;;14281:147:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5:28;;2:2;;;46:1;43;36:12;2:2;14281:147:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;14281:147:0;;;;;;100:9:-1;95:1;81:12;77:20;67:8;63:35;60:50;-1:-1;;;25:12;22:29;11:107;8:2;;;131:1;128;121:12;8:2;14281:147:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;14281:147:0;;-1:-1:-1;14281:147:0;;-1:-1:-1;;;;;14281:147:0:i;:::-;;;;-1:-1:-1;;;;;;14281:147:0;;;;;;;;;;;;;;28714:104;;8:9:-1;5:2;;;30:1;27;20:12;5:2;28714:104:0;;;:::i;28034:213::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;28034:213:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;28034:213:0;;:::i;59909:85::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;59909:85:0;;;:::i;66538:1750::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;66538:1750:0;;;;;;;;;;;;;;;;;:::i;27405:517::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;27405:517:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;27405:517:0;;:::i;46290:134::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;46290:134:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;46290:134:0;;;;;;;;-1:-1:-1;;;5:28;;2:2;;;46:1;43;36:12;2:2;46290:134:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;46290:134:0;;;;;;100:9:-1;95:1;81:12;77:20;67:8;63:35;60:50;-1:-1;;;25:12;22:29;11:107;8:2;;;131:1;128;121:12;8:2;-1:-1;46290:134:0;;-1:-1:-1;46290:134:0;-1:-1:-1;46290:134:0;:::i;26075:26::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;26075:26:0;;;:::i;60942:491::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;60942:491:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;60942:491:0;;;;;;;:::i;25983:28::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;25983:28:0;;;:::i;69844:463::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;69844:463:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;69844:463:0;;;;;;;:::i;20553:19::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;20553:19:0;;;:::i;68641:174::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;68641:174:0;;;;;;;;;;;;;;;;;:::i;42204:820::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;42204:820:0;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;42204:820:0;;;;;;;;-1:-1:-1;;;5:28;;2:2;;;46:1;43;36:12;2:2;42204:820:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;42204:820:0;;;;;;100:9:-1;95:1;81:12;77:20;67:8;63:35;60:50;-1:-1;;;25:12;22:29;11:107;8:2;;;131:1;128;121:12;8:2;42204:820:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;42204:820:0;;;;;;;;-1:-1:-1;42204:820:0;;-1:-1:-1;;;;;5:28;;2:2;;;46:1;43;36:12;2:2;42204:820:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;42204:820:0;;;;;;100:9:-1;95:1;81:12;77:20;67:8;63:35;60:50;-1:-1;;;25:12;22:29;11:107;8:2;;;131:1;128;121:12;8:2;42204:820:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;42204:820:0;;;;;;;;-1:-1:-1;42204:820:0;;-1:-1:-1;;;;;5:28;;2:2;;;46:1;43;36:12;2:2;42204:820:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;42204:820:0;;;;;;100:9:-1;95:1;81:12;77:20;67:8;63:35;60:50;-1:-1;;;25:12;22:29;11:107;8:2;;;131:1;128;121:12;8:2;42204:820:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;42204:820:0;;;;;;;;-1:-1:-1;42204:820:0;;-1:-1:-1;;;;;5:28;;2:2;;;46:1;43;36:12;2:2;42204:820:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;42204:820:0;;;;;;100:9:-1;95:1;81:12;77:20;67:8;63:35;60:50;-1:-1;;;25:12;22:29;11:107;8:2;;;131:1;128;121:12;8:2;42204:820:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;42204:820:0;;-1:-1:-1;42204:820:0;;-1:-1:-1;;;;;42204:820:0:i;59435:47::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;59435:47:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;59435:47:0;-1:-1:-1;;;;;59435:47:0;;:::i;43030:516::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;43030:516:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;43030:516:0;;:::i;61719:163::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;61719:163:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;61719:163:0;-1:-1:-1;;;;;61719:163:0;;:::i;45313:121::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;45313:121:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;45313:121:0;;;;;;;;-1:-1:-1;;;5:28;;2:2;;;46:1;43;36:12;2:2;45313:121:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;45313:121:0;;;;;;100:9:-1;95:1;81:12;77:20;67:8;63:35;60:50;-1:-1;;;25:12;22:29;11:107;8:2;;;131:1;128;121:12;8:2;-1:-1;45313:121:0;;-1:-1:-1;45313:121:0;-1:-1:-1;45313:121:0;:::i;43552:457::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;43552:457:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;43552:457:0;-1:-1:-1;;;;;43552:457:0;;:::i;60786:84::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;60786:84:0;;;:::i;34217:150::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;34217:150:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;34217:150:0;;:::i;31653:171::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;31653:171:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;31653:171:0;-1:-1:-1;;;;;31653:171:0;;:::i;31388:197::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;31388:197:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;31388:197:0;-1:-1:-1;;;;;31388:197:0;;:::i;52435:209::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;52435:209:0;;;;;;;;;;:::i;9442:140::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;9442:140:0;;;:::i;25889:27::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;25889:27:0;;;:::i;45713:167::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;45713:167:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;45713:167:0;;;;;;;;-1:-1:-1;;;5:28;;2:2;;;46:1;43;36:12;2:2;45713:167:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;45713:167:0;;;;;;100:9:-1;95:1;81:12;77:20;67:8;63:35;60:50;-1:-1;;;25:12;22:29;11:107;8:2;;;131:1;128;121:12;8:2;-1:-1;45713:167:0;;-1:-1:-1;45713:167:0;-1:-1:-1;45713:167:0;:::i;41032:257::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;41032:257:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;41032:257:0;;;;;;;;-1:-1:-1;;;5:28;;2:2;;;46:1;43;36:12;2:2;41032:257:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;41032:257:0;;;;;;101:9:-1;95:2;81:12;77:21;67:8;63:36;60:51;-1:-1;;;25:12;22:29;11:108;8:2;;;132:1;129;122:12;8:2;41032:257:0;;;;;;;;;;;-1:-1:-1;;;5:28;;2:2;;;46:1;43;36:12;2:2;41032:257:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;41032:257:0;;;;;;101:9:-1;95:2;81:12;77:21;67:8;63:36;60:51;-1:-1;;;25:12;22:29;11:108;8:2;;;132:1;129;122:12;8:2;-1:-1;41032:257:0;;-1:-1:-1;41032:257:0;-1:-1:-1;41032:257:0;:::i;70572:727::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;70572:727:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;70572:727:0;-1:-1:-1;;;;;70572:727:0;;:::i;61995:456::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;61995:456:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;61995:456:0;;;;;;;;;;:::i;8729:79::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;8729:79:0;;;:::i;9064:92::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;9064:92:0;;;:::i;66310:36::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;66310:36:0;;;:::i;33982:104::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;33982:104:0;;;:::i;45985:215::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;45985:215:0;;;:::i;31941:173::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;31941:173:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;31941:173:0;-1:-1:-1;;;;;31941:173:0;;:::i;33309:155::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;33309:155:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;33309:155:0;;;;;;-1:-1:-1;;;;;33309:155:0;;:::i;40314:163::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;40314:163:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;40314:163:0;;;;;;;;:::i;17662:27::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;17662:27:0;;;:::i;30959:266::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;30959:266:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;30959:266:0;-1:-1:-1;;;;;30959:266:0;;:::i;37848:268::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;37848:268:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;37848:268:0;;;;;;;;;;:::i;21223:500::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;21223:500:0;;;:::i;33622:200::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;33622:200:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;33622:200:0;-1:-1:-1;;;;;33622:200:0;;:::i;69387:382::-;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;-1:-1;;;;;69387:382:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5:28;;2:2;;;46:1;43;36:12;2:2;69387:382:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;69387:382:0;;;;;;100:9:-1;95:1;81:12;77:20;67:8;63:35;60:50;-1:-1;;;25:12;22:29;11:107;8:2;;;131:1;128;121:12;8:2;69387:382:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;69387:382:0;;-1:-1:-1;69387:382:0;;-1:-1:-1;;;;;69387:382:0:i;60239:401::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;60239:401:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;60239:401:0;;;;;;;;;;;;;;;-1:-1:-1;;;5:28;;2:2;;;46:1;43;36:12;2:2;60239:401:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;60239:401:0;;;;;;100:9:-1;95:1;81:12;77:20;67:8;63:35;60:50;-1:-1;;;25:12;22:29;11:107;8:2;;;131:1;128;121:12;8:2;-1:-1;60239:401:0;;-1:-1:-1;60239:401:0;-1:-1:-1;60239:401:0;:::i;20961:121::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;20961:121:0;;;:::i;8516:145::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;8516:145:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;8516:145:0;-1:-1:-1;;;;;8516:145:0;;:::i;46765:445::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;46765:445:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;46765:445:0;;:::i;73592:93::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;73592:93:0;;;:::i;:::-;;;;;;;;;;;;;;;;;;;66351:40;;8:9:-1;5:2;;;30:1;27;20:12;5:2;66351:40:0;;;:::i;38674:173::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;38674:173:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;38674:173:0;;;;;;;;;;:::i;9759:109::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;9759:109:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;9759:109:0;-1:-1:-1;;;;;9759:109:0;;:::i;52071:154::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;52071:154:0;-1:-1:-1;;;;;52071:154:0;;:::i;17992:236::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;17992:236:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;17992:236:0;-1:-1:-1;;;;;17992:236:0;;:::i;40631:241::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;40631:241:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;40631:241:0;;;;;;;;-1:-1:-1;;;5:28;;2:2;;;46:1;43;36:12;2:2;40631:241:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;40631:241:0;;;;;;101:9:-1;95:2;81:12;77:21;67:8;63:36;60:51;-1:-1;;;25:12;22:29;11:108;8:2;;;132:1;129;122:12;8:2;40631:241:0;;-1:-1:-1;40631:241:0;-1:-1:-1;40631:241:0;;:::i;12092:135::-;-1:-1:-1;;;;;;12186:33:0;;12162:4;12186:33;;;;;;;;;;;;;12092:135;;;;:::o;30062:23::-;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;30062:23:0;;-1:-1:-1;30062:23:0;:::o;45537:94::-;45617:8;45610:15;;;;;;;;-1:-1:-1;;45610:15:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;45585:13;;45610:15;;45617:8;;45610:15;;45617:8;45610:15;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;45537:94;;:::o;38219:138::-;38303:7;38329:22;38342:8;38329:12;:22::i;:::-;38322:29;38219:138;-1:-1:-1;;38219:138:0:o;37249:311::-;20820:7;;-1:-1:-1;;;20820:7:0;;;;20812:35;;;;;-1:-1:-1;;;20812:35:0;;;;;;;;;;;;-1:-1:-1;;;20812:35:0;;;;;;;;;;;;;;;37387:8;36862:32;36873:8;36883:10;36862;:32::i;:::-;:78;;;;36907:33;36919:8;36929:10;36907:11;:33::i;:::-;36862:138;;;;36953:47;36970:17;36978:8;36970:7;:17::i;:::-;36989:10;36953:16;:47::i;:::-;36846:192;;;;;-1:-1:-1;;;36846:192:0;;;;;;;;;;;;-1:-1:-1;;;36846:192:0;;;;;;;;;;;;;;;37415:10;-1:-1:-1;;;;;37415:23:0;;;;37407:48;;;;;-1:-1:-1;;;37407:48:0;;;;;;;;;;;;-1:-1:-1;;;37407:48:0;;;;;;;;;;;;;;;37464:18;;;;:8;:18;;;;;:30;;-1:-1:-1;;;;;;37464:30:0;-1:-1:-1;;;;;37464:30:0;;;;;;;;:18;;37515:17;37464:18;37515:7;:17::i;:::-;-1:-1:-1;;;;;37506:48:0;;;;;;;;;;;20854:1;37249:311;;:::o;28391:199::-;26348:7;:5;:7::i;:::-;-1:-1:-1;;;;;26334:21:0;:10;-1:-1:-1;;;;;26334:21:0;;:50;;;-1:-1:-1;26373:11:0;;-1:-1:-1;;;;;26373:11:0;26359:10;:25;26334:50;26318:114;;;;;-1:-1:-1;;;26318:114:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;28505:26:0;;28497:54;;;;;-1:-1:-1;;;28497:54:0;;;;;;;;;;;;-1:-1:-1;;;28497:54:0;;;;;;;;;;;;;;;28558:11;:26;;-1:-1:-1;;;;;;28558:26:0;-1:-1:-1;;;;;28558:26:0;;;;;;;;;;28391:199::o;59330:41::-;;;;:::o;59287:38::-;;;;:::o;25420:29::-;;;-1:-1:-1;;;;;25420:29:0;;:::o;32352:879::-;32477:6;:13;32441:16;;32469:49;;;;;-1:-1:-1;;;32469:49:0;;;;;;;;;;;;-1:-1:-1;;;32469:49:0;;;;;;;;;;;;;;;32656:6;:13;32541:9;;32576:16;;;;32525:13;;32631:22;;;:38;32627:202;;;-1:-1:-1;32697:6:0;:13;32730:27;;;;-1:-1:-1;32627:202:0;;;-1:-1:-1;32798:22:0;;;32627:202;32915:29;32961:8;32947:23;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;32947:23:0;-1:-1:-1;32915:55:0;-1:-1:-1;32977:14:0;33088:11;33074:124;33105:14;33101:1;:18;33074:124;;;33161:6;33168:1;33161:9;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;33161:9:0;33135:12;33148:9;33135:23;;;;;;;;-1:-1:-1;;;;;33135:35:0;;;:23;;;;;;;;;;;:35;33179:11;;;;;33121:3;33074:124;;;-1:-1:-1;33213:12:0;;32352:879;-1:-1:-1;;;;;;;32352:879:0:o;25795:20::-;;;;:::o;25637:30::-;;;;:::o;14281:147::-;-1:-1:-1;;;14281:147:0;;;;;;;:::o;28714:104::-;28796:16;;28714:104;:::o;28034:213::-;8941:9;:7;:9::i;:::-;8933:18;;;;;;20820:7;;-1:-1:-1;;;20820:7:0;;;;20812:35;;;;;-1:-1:-1;;;20812:35:0;;;;;;;;;;;;-1:-1:-1;;;20812:35:0;;;;;;;;;;;;;;;28159:8;;;28174:20;;;;28206:35;;;;;;;;;;;;;;;;;;;;;;;;;20854:1;28034:213;:::o;59909:85::-;59960:28;59977:10;59960:16;:28::i;:::-;59909:85::o;66538:1750::-;20820:7;;-1:-1:-1;;;20820:7:0;;;;20812:35;;;;;-1:-1:-1;;;20812:35:0;;;;;;;;;;;;-1:-1:-1;;;20812:35:0;;;;;;;;;;;;;;;66689:5;30438:22;30453:6;30438:14;:22::i;:::-;30422:62;;;;;-1:-1:-1;;;30422:62:0;;;;;;;;;;;;-1:-1:-1;;;30422:62:0;;;;;;;;;;;;;;;66724:8;36862:32;36873:8;36883:10;36862;:32::i;:::-;:78;;;;36907:33;36919:8;36929:10;36907:11;:33::i;:::-;36862:138;;;;36953:47;36970:17;36978:8;36970:7;:17::i;36953:47::-;36846:192;;;;;-1:-1:-1;;;36846:192:0;;;;;;;;;;;;-1:-1:-1;;;36846:192:0;;;;;;;;;;;;;;;-1:-1:-1;;;;;66752:24:0;;66744:52;;;;;-1:-1:-1;;;66744:52:0;;;;;;;;;;;;-1:-1:-1;;;66744:52:0;;;;;;;;;;;;;;;66803:37;66818:21;66833:5;66818:14;:21::i;:::-;66803:14;:37::i;:::-;66849:19;66871:17;66882:5;66871:10;:17::i;:::-;66849:39;;66895:17;66915:22;66926:10;66915;:22::i;:::-;66972:25;;;;67010:13;;66895:42;;-1:-1:-1;66972:25:0;67006:120;;67055:15;;67039:31;;;67079:39;;67092:10;;67079:12;:39::i;:::-;67160:15;67138:18;:37;67134:764;;67441:27;;;;;67413:25;;;:55;67493:15;;67477:31;;67517:34;67530:10;67542:8;67517:12;:34::i;:::-;67134:764;;;67811:37;;;;:79;;67874:15;67853:36;;67811:79;:41;:79;:::i;:::-;67783:25;;;:107;67134:764;67996:15;67966:27;;;:45;68110:1;68092:19;;68157:24;68172:8;68157:14;:24::i;:::-;68267:8;68248:10;-1:-1:-1;;;;;68217:65:0;68234:5;-1:-1:-1;;;;;68217:65:0;;;;;;;;;;;37045:1;;;30491;20854;66538:1750;;;:::o;27405:517::-;26348:7;:5;:7::i;:::-;-1:-1:-1;;;;;26334:21:0;:10;-1:-1:-1;;;;;26334:21:0;;:50;;;-1:-1:-1;26373:11:0;;-1:-1:-1;;;;;26373:11:0;26359:10;:25;26334:50;26318:114;;;;;-1:-1:-1;;;26318:114:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;27494:12;27509:25;27528:4;27509:10;:25::i;:::-;27494:40;-1:-1:-1;27541:11:0;27562:12;;;:33;;;27588:7;27578;:17;27562:33;27559:174;;;27629:1;27619:7;:11;27611:40;;;;;-1:-1:-1;;;27611:40:0;;;;;;;;;;;;-1:-1:-1;;;27611:40:0;;;;;;;;;;;;;;;-1:-1:-1;27669:7:0;27559:174;;;-1:-1:-1;27718:7:0;27559:174;27769:11;;27746:43;;;;;;;;-1:-1:-1;;;;;27769:11:0;;;;27757:10;;27746:43;;;;;;;;;;27896:11;;27886:30;;-1:-1:-1;;;;;27896:11:0;27909:6;27886:9;:30::i;:::-;26439:1;;27405:517;:::o;46290:134::-;8941:9;:7;:9::i;:::-;8933:18;;;;;;46390:28;:12;46405:13;;46390:28;:::i;26075:26::-;;;-1:-1:-1;;;;;26075:26:0;;:::o;60942:491::-;8941:9;:7;:9::i;:::-;8933:18;;;;;;61095:30;61087:55;;;;;-1:-1:-1;;;61087:55:0;;;;;;;;;;;;-1:-1:-1;;;61087:55:0;;;;;;;;;;;;;;;61185:22;;61216:24;;61156:157;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;61320:22;:48;;;;61375:24;:52;60942:491::o;25983:28::-;;;;:::o;69844:463::-;8941:9;:7;:9::i;:::-;8933:18;;;;;;69991:28;69983:53;;;;;-1:-1:-1;;;69983:53:0;;;;;;;;;;;;-1:-1:-1;;;69983:53:0;;;;;;;;;;;;;;;70075:20;;70104:22;;70048:147;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;70202:20;:44;;;;70253:22;:48;69844:463::o;20553:19::-;;;-1:-1:-1;;;20553:19:0;;;;;:::o;68641:174::-;68767:42;68784:5;68791:3;68796:8;68767:42;;;;;;;;;;;;:16;:42::i;42204:820::-;42351:33;42396:16;42421:2;42396:28;;42431:16;42456:2;42431:28;;42466:16;42491:2;42466:28;;42501:16;42526:2;42501:28;;42536:18;42607:3;:10;42594:3;:10;42581:3;:10;42568:3;:10;:23;:36;:49;42557:61;;;;;;;;;;;;;;;;;;;;;;;;;21:6:-1;;104:10;42557:61:0;87:34:-1;135:17;;-1:-1;42557:61:0;-1:-1:-1;42536:82:0;-1:-1:-1;42536:82:0;42664:6;;42698:69;42714:3;:10;42710:1;:14;42698:69;;;42753:3;42757:1;42753:6;;;;;;;;;;;;;;;;42740:5;42746:3;;;;;;42740:10;;;;;;;;;;;:19;-1:-1:-1;;;;;42740:19:0;;;;;;;;-1:-1:-1;42726:3:0;;42698:69;;;-1:-1:-1;42782:1:0;42773:69;42789:3;:10;42785:1;:14;42773:69;;;42828:3;42832:1;42828:6;;;;;;;;;;;;;;;;42815:5;42821:3;;;;;;42815:10;;;;;;;;;;;:19;-1:-1:-1;;;;;42815:19:0;;;;;;;;-1:-1:-1;42801:3:0;;42773:69;;;-1:-1:-1;42857:1:0;42848:69;42864:3;:10;42860:1;:14;42848:69;;;42903:3;42907:1;42903:6;;;;;;;;;;;;;;;;42890:5;42896:3;;;;;;42890:10;;;;;;;;;;;:19;-1:-1:-1;;;;;42890:19:0;;;;;;;;-1:-1:-1;42876:3:0;;42848:69;;;-1:-1:-1;42932:1:0;42923:69;42939:3;:10;42935:1;:14;42923:69;;;42978:3;42982:1;42978:6;;;;;;;;;;;;;;;;42965:5;42971:3;;;;;;42965:10;;;;;;;;;;;:19;-1:-1:-1;;;;;42965:19:0;;;;;;;;-1:-1:-1;42951:3:0;;42923:69;;;-1:-1:-1;43012:5:0;;42204:820;-1:-1:-1;;;;;;;;;;;42204:820:0:o;59435:47::-;;;;;;;;;;;;;:::o;43030:516::-;43098:27;43220:2;43233:7;43229:40;;-1:-1:-1;;43251:10:0;;;;;;;;;;;;-1:-1:-1;;;43251:10:0;;;;;;43229:40;43284:2;43275:6;43308:53;43315:6;;43308:53;;43332:5;;43351:2;43346:7;;;;43308:53;;;43367:17;43397:3;43387:14;;;;;;;;;;;;;;;;;;;;;;;;;21:6:-1;;104:10;43387:14:0;87:34:-1;135:17;;-1:-1;43387:14:0;-1:-1:-1;43367:34:0;-1:-1:-1;;;43417:7:0;;43431:84;43438:6;;43431:84;;43487:2;43483:1;:6;43478:2;:11;43467:24;;43455:4;43460:3;;;;;;;43455:9;;;;;;;;;;;:36;-1:-1:-1;;;;;43455:36:0;;;;;;;;-1:-1:-1;43505:2:0;43500:7;;;;43431:84;;;-1:-1:-1;43535:4:0;43030:516;-1:-1:-1;;;;;43030:516:0:o;61719:163::-;61814:11;61844:32;61869:6;61844:24;:32::i;45313:121::-;8941:9;:7;:9::i;:::-;8933:18;;;;;;45408:20;:8;45419:9;;45408:20;:::i;43552:457::-;43699:42;;;;;;;;;;;-1:-1:-1;;;43699:42:0;;;;43767:13;;43777:2;43767:13;;;43628;43767;;;;;;-1:-1:-1;;;;;43677:14:0;;;43699:42;43628:13;;43767;;;21:6:-1;;104:10;43767:13:0;87:34:-1;135:17;;-1:-1;43767:13:0;43748:32;;-1:-1:-1;;;43787:3:0;43791:1;43787:6;;;;;;;;;;;:12;-1:-1:-1;;;;;43787:12:0;;;;;;;;;-1:-1:-1;;;43806:3:0;43810:1;43806:6;;;;;;;;;;;:12;-1:-1:-1;;;;;43806:12:0;;;;;;;;-1:-1:-1;43830:6:0;43825:154;43846:2;43842:1;:6;43825:154;;;43877:8;43909:1;43892:5;43898:1;43902:2;43898:6;43892:13;;;;;;;;;;-1:-1:-1;;;;;43892:18:0;;;;43886:25;;43877:35;;;;;;;;;;;;;;;;;;43864:3;43870:1;43872;43870:3;43868:1;:5;43864:10;;;;;;;;;;;:48;-1:-1:-1;;;;;43864:48:0;;;;;;;;;43934:8;43949:5;43955:1;43959:2;43955:6;43949:13;;;;;;;43934:37;;43949:13;;;43965:4;43943:27;;43934:37;;;;;;;;;;;;;;43921:3;43927:1;43929;43927:3;43925:1;:5;43921:10;;;;;;;;;;;:50;-1:-1:-1;;;;;43921:50:0;;;;;;;;-1:-1:-1;43850:3:0;;43825:154;;;-1:-1:-1;43999:3:0;43552:457;-1:-1:-1;;;;43552:457:0:o;60786:84::-;60851:10;60835:27;;;;:15;:27;;;;;:29;;;;;;60786:84::o;34217:150::-;34311:7;30605:24;;;:14;:24;;;;;;34287:8;;-1:-1:-1;;;;;30605:24:0;30589:76;;;;;-1:-1:-1;;;30589:76:0;;;;;;;;;;;;-1:-1:-1;;;30589:76:0;;;;;;;;;;;;;;;-1:-1:-1;;34337:24:0;;;;:14;:24;;;;;;-1:-1:-1;;;;;34337:24:0;;34217:150::o;31653:171::-;-1:-1:-1;;;;;31762:18:0;31739:4;31762:18;;;:10;:18;;;;;:38;;;31803:15;-1:-1:-1;;31653:171:0:o;31388:197::-;31471:4;-1:-1:-1;;;;;31495:20:0;;31487:48;;;;;-1:-1:-1;;;31487:48:0;;;;;;;;;;;;-1:-1:-1;;;31487:48:0;;;;;;;;;;;;;;;31549:22;31564:6;31549:14;:22::i;:::-;:30;;31578:1;31549:30;;;31574:1;31549:30;31542:37;;;31388:197;-1:-1:-1;;31388:197:0:o;52435:209::-;20820:7;;-1:-1:-1;;;20820:7:0;;;;20812:35;;;;;-1:-1:-1;;;20812:35:0;;;;;;;;;;;;-1:-1:-1;;;20812:35:0;;;;;;;;;;;;;;;52575:9;30438:22;30453:6;30438:14;:22::i;:::-;30422:62;;;;;-1:-1:-1;;;30422:62:0;;;;;;;;;;;;-1:-1:-1;;;30422:62:0;;;;;;;;;;;;;;;52603:35;52616:10;52628:9;52603:12;:35::i;9442:140::-;8941:9;:7;:9::i;:::-;8933:18;;;;;;9525:6;;9504:40;;9541:1;;-1:-1:-1;;;;;9525:6:0;;9504:40;;9541:1;;9504:40;9555:6;:19;;-1:-1:-1;;;;;;9555:19:0;;;9442:140::o;25889:27::-;;;;:::o;45713:167::-;8941:9;:7;:9::i;:::-;8933:18;;;;;;45812:24;:10;45825:11;;45812:24;:::i;:::-;;45848:26;45862:11;;45848:26;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;;74:27;45848:26:0;;137:4:-1;117:14;;;-1:-1;;113:30;157:16;;;45848:26:0;;;;-1:-1:-1;45848:26:0;;-1:-1:-1;;;;45848:26:0;45713:167;;:::o;41032:257::-;8941:9;:7;:9::i;:::-;8933:18;;;;;;41175:6;41171:113;41187:22;;;41171:113;;;41225:51;41235:11;;41247:1;41235:14;;;;;;;;;;;;;-1:-1:-1;;;;;41235:14:0;41251:21;;41273:1;41251:24;;;;;;;;;;;;;41225:9;:51::i;:::-;41211:3;;41171:113;;;;41032:257;;;;:::o;70572:727::-;70678:4;70656:6;30438:22;30453:6;30438:14;:22::i;:::-;30422:62;;;;;-1:-1:-1;;;30422:62:0;;;;;;;;;;;;-1:-1:-1;;;30422:62:0;;;;;;;;;;;;;;;70694:15;70712:18;70723:6;70712:10;:18::i;:::-;70694:36;;70830:18;70877:15;70851:3;:23;;;:41;70830:62;;70899:8;70934:18;;70917:13;:35;70914:312;;-1:-1:-1;71055:8:0;;70914:312;;;71200:18;;71170:8;;:27;;71183:13;71170:27;:12;:27;:::i;:::-;:48;;;;;;71164:54;;70914:312;71271:22;;71247:20;;71239:29;;:3;;:29;:7;:29;:::i;:::-;:54;;;;;;;70572:727;-1:-1:-1;;;;;;70572:727:0:o;61995:456::-;-1:-1:-1;;;;;62318:26:0;;;;62114:20;62318:26;;;:15;:26;;;;;;;;;;62171:267;;62252:4;62171:267;;;;;;;;;;;;;;;;;;-1:-1:-1;;62171:267:0;;;;;;;26:21:-1;;;22:32;;6:49;;62171:267:0;;;;;;;62153:292;;;;;;61995:456::o;8729:79::-;8794:6;;-1:-1:-1;;;;;8794:6:0;8729:79;:::o;9064:92::-;9142:6;;-1:-1:-1;;;;;9142:6:0;9128:10;:20;;9064:92::o;66310:36::-;;;;:::o;33982:104::-;34067:6;:13;33982:104;:::o;45985:215::-;46069:10;46063:24;46035:13;;46063:24;-1:-1:-1;;46063:24:0;;;;;;;;;;;46060:135;;46110:14;;;;;;;;;-1:-1:-1;;;;;46110:14:0;-1:-1:-1;;;;;46110:35:0;;:37;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;46110:37:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;46110:37:0;;;;;;39:16:-1;36:1;17:17;2:54;101:4;46110:37:0;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;13:2;5:11;;2:2;;;29:1;26;19:12;2:2;46110:37:0;;;;;;-1:-1:-1;;;14:3;11:20;8:2;;;44:1;41;34:12;8:2;62:21;;123:4;114:14;;138:31;;;135:2;;;182:1;179;172:12;135:2;213:10;;-1:-1;;;244:29;;285:43;;;282:58;-1:-1;233:115;230:2;;;361:1;358;351:12;230:2;-1:-1;46110:37:0;;-1:-1:-1;46103:44:0;;-1:-1:-1;;;;46103:44:0;46060:135;46177:10;46170:17;;;;;;;;;;;;;-1:-1:-1;;46170:17:0;;;;;;;;;;;;;;;;;;;;;;;;;;;46177:10;46170:17;;46177:10;46170:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;31941:173;32057:4;32033:8;30438:22;30453:6;30438:14;:22::i;:::-;30422:62;;;;;-1:-1:-1;;;30422:62:0;;;;;;;;;;;;-1:-1:-1;;;30422:62:0;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;32080:20:0;;;;;:10;:20;;;;;:28;;31941:173::o;33309:155::-;33401:4;33424:24;;;:14;:24;;;;;;;-1:-1:-1;;;;;33424:34:0;;;:24;;:34;;33309:155::o;40314:163::-;8941:9;:7;:9::i;:::-;8933:18;;;;;;40428:43;40438:10;40450:20;40428:9;:43::i;:::-;40314:163;;:::o;17662:27::-;;;-1:-1:-1;;;;;17662:27:0;;:::o;30959:266::-;8941:9;:7;:9::i;:::-;8933:18;;;;;;31051:6;30438:22;30453:6;30438:14;:22::i;:::-;30422:62;;;;;-1:-1:-1;;;30422:62:0;;;;;;;;;;;;-1:-1:-1;;;30422:62:0;;;;;;;;;;;;;;;-1:-1:-1;;;;;31087:18:0;;31069:15;31087:18;;;:10;:18;;;;;;;;;31138:15;31112:23;;;:41;31207:11;;31197:22;;;;;;;31087:18;;31197:22;;;;;;;;;30491:1;8962;30959:266;:::o;37848:268::-;20820:7;;-1:-1:-1;;;20820:7:0;;;;20812:35;;;;;-1:-1:-1;;;20812:35:0;;;;;;;;;;;;-1:-1:-1;;;20812:35:0;;;;;;;;;;;;;;;-1:-1:-1;;;;;37963:17:0;;37970:10;37963:17;;37955:42;;;;;-1:-1:-1;;;37955:42:0;;;;;;;;;;;;-1:-1:-1;;;37955:42:0;;;;;;;;;;;;;;;38028:10;38004:35;;;;:23;:35;;;;;;;;-1:-1:-1;;;;;38004:40:0;;;;;;;;;;;;:52;;-1:-1:-1;;38004:52:0;;;;;;;;;;38068:42;;;;;;;38004:40;;38028:10;38068:42;;;;;;;;;;;37848:268;;:::o;21223:500::-;8941:9;:7;:9::i;:::-;8933:18;;;;;;21293:7;;-1:-1:-1;;;21293:7:0;;;;:16;21285:42;;;;;-1:-1:-1;;;21285:42:0;;;;;;;;;;;;-1:-1:-1;;;21285:42:0;;;;;;;;;;;;;;;21341;;;21357:4;21349:21;21341:42;;;;21372:10;;21341:42;;;;;;;;;;21462:48;21472:10;21484:25;21503:4;21484:10;:25::i;:::-;21462:9;:48::i;:::-;21530:10;21517:24;33622:200;-1:-1:-1;;;;;30232:18:0;;33745:14;30232:18;;;:10;:18;;;;;:38;;;33723:6;;30216:88;;;;;-1:-1:-1;;;30216:88:0;;;;;;;;;;;;-1:-1:-1;;;30216:88:0;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;33778:18:0;;;;;:10;:18;;;;;:38;;;;33622:200::o;69387:382::-;20820:7;;-1:-1:-1;;;20820:7:0;;;;20812:35;;;;;-1:-1:-1;;;20812:35:0;;;;;;;;;;;;-1:-1:-1;;;20812:35:0;;;;;;;;;;;;;;;69571:8;36862:32;36873:8;36883:10;36862;:32::i;:::-;:78;;;;36907:33;36919:8;36929:10;36907:11;:33::i;:::-;36862:138;;;;36953:47;36970:17;36978:8;36970:7;:17::i;36953:47::-;36846:192;;;;;-1:-1:-1;;;36846:192:0;;;;;;;;;;;;-1:-1:-1;;;36846:192:0;;;;;;;;;;;;;;;69598:17;69606:8;69598:7;:17::i;:::-;30438:22;30453:6;30438:14;:22::i;:::-;30422:62;;;;;-1:-1:-1;;;30422:62:0;;;;;;;;;;;;-1:-1:-1;;;30422:62:0;;;;;;;;;;;;;;;69627:34;69640:5;69647:3;69652:8;69627:12;:34::i;:::-;69676:51;69699:5;69706:3;69711:8;69721:5;69676:22;:51::i;:::-;69668:93;;;;;-1:-1:-1;;;69668:93:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;37045:1;20854;69387:382;;;;:::o;60239:401::-;60526:9;-1:-1:-1;;;;;60363:172:0;:159;60387:105;60428:53;60459:9;60470:10;60428:30;:53::i;:::-;60387:28;:105::i;:::-;60503:10;;60363:159;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;60363:13:0;;-1:-1:-1;;;60363:159:0:i;:::-;-1:-1:-1;;;;;60363:172:0;;60347:216;;;;;-1:-1:-1;;;60347:216:0;;;;;;;;;;;;-1:-1:-1;;;60347:216:0;;;;;;;;;;;;;;;-1:-1:-1;;;;;60572:26:0;;;;;;:15;:26;;;;;:28;;;;;;60607:27;60588:9;60607:16;:27::i;20961:121::-;8941:9;:7;:9::i;:::-;8933:18;;;;;;20820:7;;-1:-1:-1;;;20820:7:0;;;;20812:35;;;;;-1:-1:-1;;;20812:35:0;;;;;;;;;;;;-1:-1:-1;;;20812:35:0;;;;;;;;;;;;;;;21045:9;;;;;;;21061:7;:15;;-1:-1:-1;;;;21061:15:0;;;20961:121::o;8516:145::-;6947:12;;;;;;;;:31;;;6963:15;:13;:15::i;:::-;6947:47;;;-1:-1:-1;6983:11:0;;;;6982:12;6947:47;6939:106;;;;-1:-1:-1;;;6939:106:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7077:12;;;;;;;7076:13;7096:83;;;;7140:4;7125:19;;-1:-1:-1;;;;7125:19:0;;;;;7153:18;;;;;7096:83;8582:6;:15;;-1:-1:-1;;;;;;8582:15:0;-1:-1:-1;;;;;8582:15:0;;;;;;;;;;;8613:40;;8646:6;;;-1:-1:-1;;8613:40:0;;-1:-1:-1;;8613:40:0;7201:14;7197:57;;;7226:12;:20;;-1:-1:-1;;7226:20:0;;;8516:145;;:::o;46765:445::-;30641:1;30605:24;;;:14;:24;;;;;;46864:13;;46841:8;;-1:-1:-1;;;;;30605:24:0;30589:76;;;;;-1:-1:-1;;;30589:76:0;;;;;;;;;;;;-1:-1:-1;;;30589:76:0;;;;;;;;;;;;;;;46922:12;46916:26;46889:17;;46916:26;-1:-1:-1;;46916:26:0;;;;;;;;;;;46913:138;;46964:14;;;;;;;;;-1:-1:-1;;;;;46964:14:0;-1:-1:-1;;;;;46964:36:0;;:38;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;46964:38:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;46964:38:0;;;;;;39:16:-1;36:1;17:17;2:54;101:4;46964:38:0;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;13:2;5:11;;2:2;;;29:1;26;19:12;2:2;46964:38:0;;;;;;-1:-1:-1;;;14:3;11:20;8:2;;;44:1;41;34:12;8:2;62:21;;123:4;114:14;;138:31;;;135:2;;;182:1;179;172:12;135:2;213:10;;-1:-1;;;244:29;;285:43;;;282:58;-1:-1;233:115;230:2;;;361:1;358;351:12;230:2;-1:-1;46964:38:0;;-1:-1:-1;46913:138:0;;-1:-1:-1;;;;46913:138:0;;47031:12;47025:18;;;;;;;;;;;;;-1:-1:-1;;47025:18:0;;;;;;;;;;;;;;;;;;;;;;;;;;;47031:12;47025:18;;47031:12;47025:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;46913:138;47066;47096:3;47108:38;47140:4;47108:23;:38::i;:::-;47066:138;;;;;;;;;;;;;-1:-1:-1;;;47066:138:0;;;47167:30;47188:8;47167:20;:30::i;:::-;47066:21;:138::i;73592:93::-;73678:1;73592:93;:::o;66351:40::-;;;;:::o;38674:173::-;-1:-1:-1;;;;;38799:31:0;;;38776:4;38799:31;;;:23;:31;;;;;;;;:42;;;;;;;;;;;;;;;38674:173::o;9759:109::-;8941:9;:7;:9::i;:::-;8933:18;;;;;;9832:28;9851:8;9832:18;:28::i;:::-;9759:109;:::o;52071:154::-;20820:7;;-1:-1:-1;;;20820:7:0;;;;20812:35;;;;;-1:-1:-1;;;20812:35:0;;;;;;;;;;;;-1:-1:-1;;;20812:35:0;;;;;;;;;;;;;;;52183:36;52196:10;52216:1;52183:12;:36::i;17992:236::-;18085:12;;18066:4;;-1:-1:-1;;;;;18085:12:0;18082:141;;-1:-1:-1;;;;;;18129:16:0;;;18122:23;;18082:141;18182:12;;18175:40;;;-1:-1:-1;;;18175:40:0;;-1:-1:-1;;;;;18175:40:0;;;;;;;;;18182:12;;;;;18175:30;;:40;;;;;;;;;;;;;;18182:12;18175:40;;;5:2:-1;;;;30:1;27;20:12;5:2;18175:40:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;18175:40:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;18175:40:0;;-1:-1:-1;18168:47:0;;40631:241;8941:9;:7;:9::i;:::-;8933:18;;;;;;40762:6;40758:109;40774:22;;;40758:109;;;40812:47;40822:11;;40834:1;40822:14;;;;;;;;;;;;;-1:-1:-1;;;;;40822:14:0;40838:20;40812:9;:47::i;:::-;40798:3;;40758:109;;;;40631:241;;;:::o;39264:251::-;39349:7;39396:18;;;:8;:18;;;;;;-1:-1:-1;;;;;39396:18:0;39429:31;39421:57;;;;;-1:-1:-1;;;39421:57:0;;;;;;;;;;;;-1:-1:-1;;;39421:57:0;;;;;;;;;;;;;;38940:150;39034:4;39057:18;;;:8;:18;;;;;;;-1:-1:-1;;;;;39057:27:0;;;:18;;:27;;38940:150::o;62561:594::-;62635:15;62653:21;62664:9;62653:10;:21::i;:::-;62635:39;;62683:11;62697:35;62722:9;62697:24;:35::i;:::-;62756:11;;62746:53;;;;;;;;62683:49;;-1:-1:-1;62780:10:0;;-1:-1:-1;;;;;62746:53:0;;;62756:11;62746:53;;;;;;;;;;62998:15;62972:23;;;:41;63026:10;;63022:128;;63113:29;63123:10;63135:6;63113:9;:29::i;18535:684::-;18604:10;;18601:613;;18628:12;;-1:-1:-1;;;;;18628:12:0;18625:582;;18688:6;18675:9;:19;;18667:48;;;;;-1:-1:-1;;;18667:48:0;;;;;;;;;;;;-1:-1:-1;;;18667:48:0;;;;;;;;;;;;;;;18625:582;;;18764:12;;18809:30;;;-1:-1:-1;;;18809:30:0;;18833:4;18809:30;;;;;;-1:-1:-1;;;;;18764:12:0;;;;18742;;18764;;18809:15;;:30;;;;;;;;;;;;;;;18764:12;18809:30;;;5:2:-1;;;;30:1;27;20:12;5:2;18809:30:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;18809:30:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;18809:30:0;18850:53;;;-1:-1:-1;;;18850:53:0;;18869:10;18850:53;;;;18889:4;18850:53;;;;;;;;;;;;18809:30;;-1:-1:-1;;;;;;18850:18:0;;;;;:53;;;;;18809:30;;18850:53;;;;;;;;-1:-1:-1;18850:18:0;:53;;;5:2:-1;;;;30:1;27;20:12;5:2;18850:53:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;18850:53:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;19131:30:0;;;-1:-1:-1;;;19131:30:0;;19155:4;19131:30;;;;;;19164:13;;-1:-1:-1;;;;;19131:15:0;;;;;:30;;;;;18850:53;;19131:30;;;;;;;;:15;:30;;;5:2:-1;;;;30:1;27;20:12;5:2;19131:30:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;19131:30:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;19131:30:0;:46;19123:74;;;;;-1:-1:-1;;;19123:74:0;;;;;;;;;;;;-1:-1:-1;;;19123:74:0;;;;;;;;;;;;;;35332:128;-1:-1:-1;;;;;35436:18:0;35406:11;35436:18;;;:10;:18;;;;;;35332:128::o;34954:308::-;35045:24;;;;:14;:24;;;;;;-1:-1:-1;;;;;35045:34:0;;;:24;;:34;35041:216;;35141:6;27:10:-1;;39:1;23:18;;45:23;;;35141:19:0;;;;-1:-1:-1;;;;;35141:19:0;;-1:-1:-1;;;;;;35141:19:0;;;;;;;;-1:-1:-1;35216:24:0;;;:14;35141:19;35216:24;;;;:33;;;;;;;;;;34954:308;;:::o;48698:181::-;48756:7;48788:5;;;48812:6;;;;48804:46;;;;;-1:-1:-1;;;48804:46:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;48870:1;48698:181;-1:-1:-1;;;48698:181:0:o;39665:162::-;39770:1;39740:18;;;:8;:18;;;;;;-1:-1:-1;;;;;39740:18:0;:32;39736:86;;39812:1;39783:18;;;:8;:18;;;;;:31;;-1:-1:-1;;;;;;39783:31:0;;;39665:162::o;19377:645::-;19460:11;;19457:560;;19485:12;;-1:-1:-1;;;;;19485:12:0;19482:528;;19524:39;;-1:-1:-1;;;;;19524:30:0;;;:39;;;;;19555:7;;19524:39;;;;19555:7;19524:30;:39;;;;;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;19524:39:0;19482:528;;;19612:12;;19657:20;;;-1:-1:-1;;;19657:20:0;;-1:-1:-1;;;;;19657:20:0;;;;;;;;;19612:12;;;;;19590;;19612;;19657:15;;:20;;;;;;;;;;;;;;19612:12;19657:20;;;5:2:-1;;;;30:1;27;20:12;5:2;19657:20:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;19657:20:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;19657:20:0;19688:28;;;-1:-1:-1;;;19688:28:0;;-1:-1:-1;;;;;19688:28:0;;;;;;;;;;;;;;;19657:20;;-1:-1:-1;19688:14:0;;;;;;:28;;;;;19657:20;;19688:28;;;;;;;;-1:-1:-1;19688:14:0;:28;;;5:2:-1;;;;30:1;27;20:12;5:2;19688:28:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;19688:28:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;19944:20:0;;;-1:-1:-1;;;19944:20:0;;-1:-1:-1;;;;;19944:20:0;;;;;;;;;19967:13;;19944:15;;;;;:20;;;;;19688:28;;19944:20;;;;;;;:15;:20;;;5:2:-1;;;;30:1;27;20:12;5:2;19944:20:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;19944:20:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;19944:20:0;:36;19936:64;;;;;-1:-1:-1;;;19936:64:0;;;;;;;;;;;;-1:-1:-1;;;19936:64:0;;;;;;;;;;;;;;63357:845;63474:11;63452:6;30438:22;30453:6;30438:14;:22::i;:::-;30422:62;;;;;-1:-1:-1;;;30422:62:0;;;;;;;;;;;;-1:-1:-1;;;30422:62:0;;;;;;;;;;;;;;;63497:15;63515:18;63526:6;63515:10;:18::i;:::-;63497:36;;63633:18;63680:15;63654:3;:23;;;:41;63633:62;;63722:18;;63705:13;:35;63702:232;;63760:8;;63751:17;;63702:232;;;63908:18;;63878:8;;:27;;63891:13;63878:27;:12;:27;:::i;:::-;:48;;;;;;63869:57;;63702:232;63940:12;63994:24;;63955:36;63968:22;;63955:8;;:12;;:36;;;;:::i;:::-;:63;;;;;;63940:78;;64038:7;64029:6;:16;64025:172;;;64149:7;64139:17;;;;64025:172;;;64188:1;64179:10;;64025:172;30491:1;;;63357:845;;;;:::o;53160:1928::-;26220:16;;26202:15;;:34;26194:60;;;;;-1:-1:-1;;;26194:60:0;;;;;;;;;;;;-1:-1:-1;;;26194:60:0;;;;;;;;;;;;;;;-1:-1:-1;;;;;53328:24:0;;53320:52;;;;;-1:-1:-1;;;53320:52:0;;;;;;;;;;;;-1:-1:-1;;;53320:52:0;;;;;;;;;;;;;;;53521:8;;53557:14;;:72;;;-1:-1:-1;;;53557:72:0;;-1:-1:-1;;;;;53557:72:0;;;;;;;;;;;;;;;53459:13;;;;53521:8;;53557:14;;;:42;;:72;;;;;;;;;;;;:14;:72;;;5:2:-1;;;;30:1;27;20:12;5:2;53557:72:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;53557:72:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;53557:72:0;;;;;;;;;-1:-1:-1;53557:72:0;-1:-1:-1;53652:16:0;53679:27;;;53675:239;;;-1:-1:-1;53728:1:0;53675:239;;;-1:-1:-1;53879:27:0;;;53675:239;53945:17;53965:22;53976:10;53965;:22::i;:::-;54000:13;;53945:42;;-1:-1:-1;53996:186:0;;54102:24;54120:5;54102:17;:24::i;:::-;54135:39;54148:10;54160:5;:13;;;54135:12;:39::i;:::-;54223:15;54194:5;:25;;;:44;54190:427;;54370:18;;54340:25;;;;:49;;;:29;:49;:::i;:::-;54312:25;;;:77;54190:427;;;54591:18;;54573:15;:36;54545:25;;;:64;54190:427;54629:12;;54625:90;;54652:14;;:55;;;-1:-1:-1;;;54652:55:0;;;;;;;;;;;;;;;;-1:-1:-1;;;;;54652:14:0;;;;:37;;:55;;;;;:14;;:55;;;;;;;;:14;;:55;;;5:2:-1;;;;30:1;27;20:12;5:2;54652:55:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;54652:55:0;;;;54625:90;54723:14;;:53;;;-1:-1:-1;;;54723:53:0;;;;;;;;-1:-1:-1;;;;;54723:53:0;;;;;;;;;:14;;;;;:32;;:53;;;;;:14;;:53;;;;;;;:14;;:53;;;5:2:-1;;;;30:1;27;20:12;5:2;54723:53:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;54890:16:0;;54812:101;;54890:16;;-1:-1:-1;;;;;;54812:101:0;;;-1:-1:-1;54837:1:0;;54812:101;;54837:1;;54812:101;55058:24;55073:8;55058:14;:24::i;:::-;26261:1;;;;;53160:1928;;:::o;41344:560::-;-1:-1:-1;;;;;41451:24:0;;41443:52;;;;;-1:-1:-1;;;41443:52:0;;;;;;;;;;;;-1:-1:-1;;;41443:52:0;;;;;;;;;;;;;;;41504:17;41524:22;41535:10;41524;:22::i;:::-;41504:42;;41584:5;:25;;;41561:20;:48;41553:77;;;;;-1:-1:-1;;;41553:77:0;;;;;;;;;;;;-1:-1:-1;;;41553:77:0;;;;;;;;;;;;;;;41639:24;41657:5;41639:17;:24::i;:::-;41670:39;41683:10;41695:5;:13;;;41670:12;:39::i;:::-;41716:25;;;:48;;;41878:13;;41800:98;;-1:-1:-1;;;;;41800:98:0;;;41878:13;;41800:98;;41878:13;;41800:98;41344:560;;;:::o;49589:470::-;49647:7;49891:6;49887:47;;-1:-1:-1;49921:1:0;49914:8;;49887:47;49958:5;;;49962:1;49958;:5;:1;49982:5;;;;;:10;49974:56;;;;-1:-1:-1;;;49974:56:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;71813:362;71961:4;71982:15;:2;-1:-1:-1;;;;;71982:13:0;;:15::i;:::-;71977:50;;-1:-1:-1;72015:4:0;72008:11;;71977:50;72049:78;;-1:-1:-1;;;72049:78:0;;72094:10;72049:78;;;;;;-1:-1:-1;;;;;72049:78:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;72033:13;;72049:36;;;;;;72094:10;;72106:4;;72112:7;;72121:5;;72049:78;;;;;;;;;;;72033:13;8:100:-1;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;72049:78:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;72049:78:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;72049:78:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;72049:78:0;-1:-1:-1;;;;;;72142:26:0;-1:-1:-1;;;72142:26:0;;-1:-1:-1;;71813:362:0;;;;;;:::o;58624:269::-;58826:58;;;;;;;;;;;;;;;;;;;;;;26:21:-1;;;22:32;;;6:49;;58826:58:0;;;;;;;58816:69;;;;;;58624:269::o;56418:1930::-;56496:7;56559:9;:16;56579:2;56559:22;56555:74;;-1:-1:-1;56614:1:0;56598:19;;56555:74;56990:4;56975:20;;56969:27;57036:4;57021:20;;57015:27;57090:4;57075:20;;57069:27;56698:9;57061:36;58020:66;58007:79;;58003:129;;;58118:1;58103:17;;;;;;;58003:129;58148:1;:7;;58153:2;58148:7;;:18;;;;;58159:1;:7;;58164:2;58159:7;;58148:18;58144:68;;;58198:1;58183:17;;;;;;;58144:68;58316:24;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;58316:24:0;;;;;;;;;;;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;58316:24:0;;-1:-1:-1;;58316:24:0;;;56418:1930;-1:-1:-1;;;;;;;56418:1930:0:o;7348:476::-;7788:7;7776:20;7811:7;7348:476;:::o;10018:187::-;-1:-1:-1;;;;;10092:22:0;;10084:31;;;;;;10152:6;;10131:38;;-1:-1:-1;;;;;10131:38:0;;;;10152:6;;10131:38;;10152:6;;10131:38;10180:6;:17;;-1:-1:-1;;;;;;10180:17:0;-1:-1:-1;;;;;10180:17:0;;;;;;;;;;10018:187::o;34495:396::-;34573:12;;34569:317;;34734:16;:18;;;;;;;;34847:31;;34495:396::o;64829:422::-;65196:20;65235:8;;;64829:422::o;72624:1064::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;72624:1064:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;72624:1064:0;;;-1:-1:-1;72624:1064:0;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;
Swarm Source
bzzr://47f15e51181351647a87d6980e0f8f20a1f88485a060e1fd9ce02eb8828eb7b4
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.

