Overview
POL Balance
0 POL
POL Value
$0.00More Info
Private Name Tags
ContractCreator
Loading...
Loading
Contract Name:
PublicLock
Compiler Version
v0.8.7+commit.e28d00a7
Optimization Enabled:
Yes with 80 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.7; import './interfaces/IPublicLock.sol'; import '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol'; import '@openzeppelin/contracts-upgradeable/utils/introspection/ERC165StorageUpgradeable.sol'; import './mixins/MixinDisable.sol'; import './mixins/MixinERC721Enumerable.sol'; import './mixins/MixinFunds.sol'; import './mixins/MixinGrantKeys.sol'; import './mixins/MixinKeys.sol'; import './mixins/MixinLockCore.sol'; import './mixins/MixinLockMetadata.sol'; import './mixins/MixinPurchase.sol'; import './mixins/MixinRefunds.sol'; import './mixins/MixinTransfer.sol'; import './mixins/MixinRoles.sol'; import './mixins/MixinConvenienceOwnable.sol'; /** * @title The Lock contract * @author Julien Genestoux (unlock-protocol.com) * @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 Initializable, ERC165StorageUpgradeable, MixinRoles, MixinFunds, MixinDisable, MixinLockCore, MixinKeys, MixinLockMetadata, MixinERC721Enumerable, MixinGrantKeys, MixinPurchase, MixinTransfer, MixinRefunds, MixinConvenienceOwnable { function initialize( address payable _lockCreator, uint _expirationDuration, address _tokenAddress, uint _keyPrice, uint _maxNumberOfKeys, string calldata _lockName ) public initializer() { MixinFunds._initializeMixinFunds(_tokenAddress); MixinLockCore._initializeMixinLockCore(_lockCreator, _expirationDuration, _keyPrice, _maxNumberOfKeys); MixinLockMetadata._initializeMixinLockMetadata(_lockName); MixinERC721Enumerable._initializeMixinERC721Enumerable(); MixinRefunds._initializeMixinRefunds(); MixinRoles._initializeMixinRoles(_lockCreator); MixinConvenienceOwnable._initializeMixinConvenienceOwnable(_lockCreator); // registering the interface for erc721 with ERC165.sol using // the ID specified in the standard: https://eips.ethereum.org/EIPS/eip-721 _registerInterface(0x80ac58cd); } /** * @notice Allow the contract to accept tips in ETH sent directly to the contract. * @dev This is okay to use even if the lock is priced in ERC-20 tokens */ receive() external payable {} /** Overrides */ function supportsInterface(bytes4 interfaceId) public view virtual override( MixinERC721Enumerable, MixinLockMetadata, AccessControlUpgradeable, ERC165StorageUpgradeable ) returns (bool) { return super.supportsInterface(interfaceId); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.5.17 <0.9.0; pragma experimental ABIEncoderV2; /** * @title The PublicLock Interface * @author Nick Furfaro (unlock-protocol.com) */ interface IPublicLock { // See indentationissue description here: // https://github.com/duaraghav8/Ethlint/issues/268 // solium-disable indentation /// Functions function initialize( address _lockCreator, uint _expirationDuration, address _tokenAddress, uint _keyPrice, uint _maxNumberOfKeys, string calldata _lockName ) external; /** * @notice Allow the contract to accept tips in ETH sent directly to the contract. * @dev This is okay to use even if the lock is priced in ERC-20 tokens */ // receive() external payable; // roles function DEFAULT_ADMIN_ROLE() external pure returns (bytes32); function KEY_GRANTER_ROLE() external pure returns (bytes32); function LOCK_MANAGER_ROLE() external pure returns (bytes32); /** * @notice The version number of the current implementation on this network. * @return The current version number. */ function publicLockVersion() external pure returns (uint16); /** * @dev Called by a lock manager or beneficiary to withdraw all funds from the lock and send them to the `beneficiary`. * @dev Throws if called by other than a lock manager or beneficiary * @param _tokenAddress specifies the token address to withdraw or 0 for ETH. This is usually * the same as `tokenAddress` in MixinFunds. * @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. * -- however be wary of draining funds as it breaks the `cancelAndRefund` and `expireAndRefundFor` * use cases. */ function withdraw( address _tokenAddress, uint _amount ) external; /** * @notice An ERC-20 style approval, allowing the spender to transfer funds directly from this lock. */ function approveBeneficiary( address _spender, uint _amount ) external returns (bool); /** * A function which lets a Lock manager of the lock to change the price for future purchases. * @dev Throws if called by other than a Lock manager * @dev Throws if lock has been disabled * @dev Throws if _tokenAddress is not a valid token * @param _keyPrice The new price to set for keys * @param _tokenAddress The address of the erc20 token to use for pricing the keys, * or 0 to use ETH */ function updateKeyPricing( uint _keyPrice, address _tokenAddress ) external; /** * A function to change the default duration of each key in the lock * @notice keys previously bought are unaffected by this change (i.e. * existing keys timestamps are not recalculated/updated) * @param _newExpirationDuration the new amount of time for each key purchased * or type(uint).max for a non-expiring key */ function setExpirationDuration(uint _newExpirationDuration) external; /** * A function which lets a Lock manager update the beneficiary account, * which receives funds on withdrawal. * @dev Throws if called by other than a Lock manager or beneficiary * @dev Throws if _beneficiary is address(0) * @param _beneficiary The new address to set as the beneficiary */ function updateBeneficiary( address _beneficiary ) external; /** * Checks if the user has a non-expired key. * @param _user The address of the key owner */ function getHasValidKey( address _user ) external view returns (bool); /** * @dev Returns the key's ExpirationTimestamp field for a given owner. * @param _tokenId the id of the key * @dev Returns 0 if the owner has never owned a key for this lock */ function keyExpirationTimestampFor( uint _tokenId ) external view returns (uint timestamp); /** * Public function which returns the total number of unique owners (both expired * and valid). This may be larger than totalSupply. */ function numberOfOwners() external view returns (uint); /** * Allows a Lock manager to assign a descriptive name for this Lock. * @param _lockName The new name for the lock * @dev Throws if called by other than a Lock manager */ function updateLockName( string calldata _lockName ) external; /** * Allows a Lock manager to assign a Symbol for this Lock. * @param _lockSymbol The new Symbol for the lock * @dev Throws if called by other than a Lock manager */ function updateLockSymbol( string calldata _lockSymbol ) external; /** * @dev Gets the token symbol * @return string representing the token symbol */ function symbol() external view returns(string memory); /** * Allows a Lock manager to update the baseTokenURI for this Lock. * @dev Throws if called by other than a Lock manager * @param _baseTokenURI String representing the base of the URI for this lock. */ function setBaseTokenURI( string calldata _baseTokenURI ) external; /** @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 * @param _tokenId The tokenID we're inquiring about * @return String representing the URI for the requested token */ function tokenURI( uint256 _tokenId ) external view returns(string memory); /** * @notice Allows a Lock manager to add or remove an event hook */ function setEventHooks( address _onKeyPurchaseHook, address _onKeyCancelHook, address _onValidKeyHook, address _onTokenURIHook ) external; /** * Allows a Lock manager to give a collection of users a key with no charge. * Each key may be assigned a different expiration date. * @dev Throws if called by other than a Lock manager * @param _recipients An array of receiving addresses * @param _expirationTimestamps An array of expiration Timestamps for the keys being granted */ function grantKeys( address[] calldata _recipients, uint[] calldata _expirationTimestamps, address[] calldata _keyManagers ) external; /** * @dev Purchase function * @param _values array of tokens amount to pay for this purchase >= the current keyPrice - any applicable discount * (_values is ignored when using ETH) * @param _recipients array of addresses of the recipients of the purchased key * @param _referrers array of addresses of the users making the referral * @param _keyManagers optional array of addresses to grant managing rights to a specific address on creation * @param _data array of arbitrary data populated by the front-end which initiated the sale * @notice when called for an existing and non-expired key, the `_keyManager` param will be ignored * @dev Setting _value to keyPrice exactly doubles as a security feature. That way if the lock owner increases the * price while my transaction is pending I can't be charged more than I expected (only applicable to ERC-20 when more * than keyPrice is approved for spending). */ function purchase( uint256[] calldata _values, address[] calldata _recipients, address[] calldata _referrers, address[] calldata _keyManagers, bytes[] calldata _data ) external payable; /** * @dev Extend function * @param _value the number of tokens to pay for this purchase >= the current keyPrice - any applicable discount * (_value is ignored when using ETH) * @param _tokenId the id of the key to extend * @param _referrer address of the user making the referral * @param _data arbitrary data populated by the front-end which initiated the sale * @dev Throws if lock is disabled or key does not exist for _recipient. Throws if _recipient == address(0). */ function extend( uint _value, uint _tokenId, address _referrer, bytes calldata _data ) external payable; /** * Merge existing keys * @param _tokenIdFrom the id of the token to substract time from * @param _tokenIdTo the id of the destination token to add time * @param _amount the amount of time to transfer (in seconds) */ function mergeKeys(uint _tokenIdFrom, uint _tokenIdTo, uint _amount) external; /** * Deactivate an existing key * @param _tokenId the id of token to burn * @notice the key will be expired and ownership records will be destroyed */ function burn(uint _tokenId) external; /** * @param _gasRefundValue price in wei or token in smallest price unit * @dev Set the value to be refunded to the sender on purchase */ function setGasRefundValue(uint256 _gasRefundValue) external; /** * _gasRefundValue price in wei or token in smallest price unit * @dev Returns the value/rpice to be refunded to the sender on purchase */ function gasRefundValue() external view returns (uint256 _gasRefundValue); /** * @notice returns the minimum price paid for a purchase with these params. * @dev this considers any discount from Unlock or the OnKeyPurchase hook. */ function purchasePriceFor( address _recipient, address _referrer, bytes calldata _data ) external view returns (uint); /** * Allow a Lock manager to change the transfer fee. * @dev Throws if called by other than a Lock manager * @param _transferFeeBasisPoints The new transfer fee in basis-points(bps). * Ex: 200 bps = 2% */ function updateTransferFee( uint _transferFeeBasisPoints ) external; /** * Determines how much of a fee would need to be paid in order to * transfer to another account. This is pro-rated so the fee goes * down overtime. * @dev Throws if _tokenId does not have a valid key * @param _tokenId The id of the key check the transfer fee for. * @param _time The amount of time to calculate the fee for. * @return The transfer fee in seconds. */ function getTransferFee( uint _tokenId, uint _time ) external view returns (uint); /** * @dev Invoked by a Lock manager to expire the user's key * and perform a refund and cancellation of the key * @param _tokenId The key id we wish to refund to * @param _amount The amount to refund to the key-owner * @dev Throws if called by other than a Lock manager * @dev Throws if _keyOwner does not have a valid key */ function expireAndRefundFor( uint _tokenId, uint _amount ) external; /** * @dev allows the key manager to expire a given tokenId * and send a refund to the keyOwner based on the amount of time remaining. * @param _tokenId The id of the key to cancel. */ function cancelAndRefund(uint _tokenId) external; /** * Allow a Lock manager to change the refund penalty. * @dev Throws if called by other than a Lock manager * @param _freeTrialLength The new duration of free trials for this lock * @param _refundPenaltyBasisPoints The new refund penaly in basis-points(bps) */ function updateRefundPenalty( uint _freeTrialLength, uint _refundPenaltyBasisPoints ) external; /** * @dev Determines how much of a refund a key owner would receive if they issued * @param _keyOwner The key owner to get the refund value for. * 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 getCancelAndRefundValue( address _keyOwner ) external view returns (uint refund); function addKeyGranter(address account) external; function addLockManager(address account) external; function isKeyGranter(address account) external view returns (bool); function isLockManager(address account) external view returns (bool); function onKeyPurchaseHook() external view returns(address); function onKeyCancelHook() external view returns(address); function onValidKeyHook() external view returns(bool); function onTokenURIHook() external view returns(string memory); function revokeKeyGranter(address _granter) external; function renounceLockManager() external; /** * @dev Change the maximum number of keys the lock can edit * @param _maxNumberOfKeys uint the maximum number of keys */ function setMaxNumberOfKeys (uint _maxNumberOfKeys) external; /** * Set the maximum number of keys a specific address can use * @param _maxKeysPerAddress the maximum amount of key a user can own */ function setMaxKeysPerAddress (uint _maxKeysPerAddress) external; /** * @return the maximum number of key allowed for a single address */ function maxKeysPerAddress() external view returns (uint); ///=================================================================== /// Auto-generated getter functions from public state variables function beneficiary() external view returns (address ); function expirationDuration() external view returns (uint256 ); function freeTrialLength() external view returns (uint256 ); function keyPrice() external view returns (uint256 ); function maxNumberOfKeys() external view returns (uint256 ); function refundPenaltyBasisPoints() external view returns (uint256 ); function tokenAddress() external view returns (address ); function transferFeeBasisPoints() external view returns (uint256 ); function unlockProtocol() external view returns (address ); function keyManagerOf(uint) external view returns (address ); ///=================================================================== /** * @notice Allows the key owner to safely share their key (parent key) by * transferring a portion of the remaining time to a new key (child key). * @dev Throws if key is not valid. * @dev Throws if `_to` is the zero address * @param _to The recipient of the shared key * @param _tokenId the key to share * @param _timeShared The amount of time shared * 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)'))`. * @dev Emit Transfer event */ function shareKey( address _to, uint _tokenId, uint _timeShared ) external; /** * @notice Update transfer and cancel rights for a given key * @param _tokenId The id of the key to assign rights for * @param _keyManager The address to assign the rights to for the given key */ function setKeyManagerOf( uint _tokenId, address _keyManager ) external; /** * Check if a certain key is valid * @param _tokenId the id of the key to check validity * @notice this makes use of the onValidKeyHook if it is set */ function isValidKey( uint _tokenId ) external view returns (bool); /// @notice A descriptive name for a collection of NFTs in this contract function name() external view returns (string memory _name); ///=================================================================== /// From ERC165.sol function supportsInterface(bytes4 interfaceId) external view returns (bool); ///=================================================================== /// From ERC-721 /** * @dev Returns the number of NFTs in `owner`'s account. */ function balanceOf(address _owner) external view returns (uint256 balance); /** * @dev Returns the owner of the NFT specified by `tokenId`. */ function ownerOf(uint256 tokenId) external view returns (address _owner); /** * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to * another (`to`). * * Requirements: * - `from`, `to` cannot be zero. * - `tokenId` must be owned by `from`. * - If the caller is not `from`, it must be have been allowed to move this * NFT by either {approve} or {setApprovalForAll}. */ function safeTransferFrom(address from, address to, uint256 tokenId) external; /** * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to * another (`to`). * * Requirements: * - If the caller is not `from`, it must be approved to move this NFT by * either {approve} or {setApprovalForAll}. */ function transferFrom(address from, address to, uint256 tokenId) external; function approve(address to, uint256 tokenId) external; /** * @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 operator The approved address for this NFT, or the zero address if there is none */ function getApproved(uint256 _tokenId) external view returns (address operator); /** * @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 _operator operator address to set the approval * @param _approved representing the status of the approval to be set * @notice disabled when transfers are disabled */ function setApprovalForAll(address _operator, bool _approved) external; /** * @dev Tells whether an operator is approved by a given keyManager * @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) external view returns (bool); function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; function totalSupply() external view returns (uint256); function tokenOfOwnerByIndex(address _owner, uint256 index) external view returns (uint256 tokenId); function tokenByIndex(uint256 index) external view returns (uint256); /** * Innherited from Open Zeppelin AccessControl.sol */ function getRoleAdmin(bytes32 role) external view returns (bytes32); function grantRole(bytes32 role, address account) external; function revokeRole(bytes32 role, address account) external; function renounceRole(bytes32 role, address account) external; function hasRole(bytes32 role, address account) external view returns (bool); /** * @notice An ERC-20 style transfer. * @param _value sends a token with _value * expirationDuration (the amount of time remaining on a standard purchase). * @dev The typical use case would be to call this with _value 1, which is on par with calling `transferFrom`. If the user * has more than `expirationDuration` time remaining this may use the `shareKey` function to send some but not all of the token. */ function transfer( address _to, uint _value ) external returns (bool success); /** `owner()` is provided as an helper to mimick the `Ownable` contract ABI. * The `Ownable` logic is used by many 3rd party services to determine * contract ownership - e.g. who is allowed to edit metadata on Opensea. * * @notice This logic is NOT used internally by the Unlock Protocol and is made * available only as a convenience helper. */ function owner() external view returns (address); function setOwner(address account) external; function isOwner(address account) external returns (bool); /** * Migrate data from the previous single owner => key mapping to * the new data structure w multiple tokens. * @param _calldata an ABI-encoded representation of the params (v10: the number of records to migrate as `uint`) * @dev when all record schemas are sucessfully upgraded, this function will update the `schemaVersion` * variable to the latest/current lock version */ function migrate(bytes calldata _calldata) external; /** * Returns the version number of the data schema currently used by the lock * @notice if this is different from `publicLockVersion`, then the ability to purchase, grant * or extend keys is disabled. * @dev will return 0 if no ;igration has ever been run */ function schemaVersion() external view returns (uint); /** * Set the schema version to the latest * @notice only lock manager call call this */ function updateSchemaVersion() external; /** * Renew a given token * @notice only works for non-free, expiring, ERC20 locks * @param _tokenId the ID fo the token to renew * @param _referrer the address of the person to be granted UDT */ function renewMembershipFor( uint _tokenId, address _referrer ) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (proxy/utils/Initializable.sol) pragma solidity ^0.8.0; import "../../utils/AddressUpgradeable.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() initializer {} * ``` * ==== */ abstract 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 protect an initializer function from being invoked twice. */ modifier initializer() { // If the contract is initializing we ignore whether _initialized is set in order to support multiple // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the // contract may have been reentered. require(_initializing ? _isConstructor() : !_initialized, "Initializable: contract is already initialized"); bool isTopLevelCall = !_initializing; if (isTopLevelCall) { _initializing = true; _initialized = true; } _; if (isTopLevelCall) { _initializing = false; } } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} modifier, directly or indirectly. */ modifier onlyInitializing() { require(_initializing, "Initializable: contract is not initializing"); _; } function _isConstructor() private view returns (bool) { return !AddressUpgradeable.isContract(address(this)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165Storage.sol) pragma solidity ^0.8.0; import "./ERC165Upgradeable.sol"; import "../../proxy/utils/Initializable.sol"; /** * @dev Storage based implementation of the {IERC165} interface. * * Contracts may inherit from this and call {_registerInterface} to declare * their support of an interface. */ abstract contract ERC165StorageUpgradeable is Initializable, ERC165Upgradeable { function __ERC165Storage_init() internal onlyInitializing { __ERC165_init_unchained(); __ERC165Storage_init_unchained(); } function __ERC165Storage_init_unchained() internal onlyInitializing { } /** * @dev Mapping of interface ids to whether or not it's supported. */ mapping(bytes4 => bool) private _supportedInterfaces; /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return super.supportsInterface(interfaceId) || _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 virtual { require(interfaceId != 0xffffffff, "ERC165: invalid interface id"); _supportedInterfaces[interfaceId] = true; } uint256[49] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * The ability to disable locks has been removed on v10 to decrease contract code size. * Disabling locks can be achieved by setting `setMaxNumberOfKeys` to `totalSupply` * and expire all existing keys. * @dev the variables are kept to prevent conflicts in storage layout during upgrades */ contract MixinDisable { bool isAlive; uint256[1000] private __safe_upgrade_gap; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import './MixinKeys.sol'; import './MixinLockCore.sol'; // import '@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721EnumerableUpgradeable.sol'; import '@openzeppelin/contracts-upgradeable/utils/introspection/ERC165StorageUpgradeable.sol'; /** * @title Implements the ERC-721 Enumerable extension. */ contract MixinERC721Enumerable is ERC165StorageUpgradeable, MixinLockCore, // Implements totalSupply MixinKeys { function _initializeMixinERC721Enumerable() internal { /** * register the supported interface to conform to ERC721Enumerable via ERC165 * 0x780e9d63 === * bytes4(keccak256('totalSupply()')) ^ * bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) ^ * bytes4(keccak256('tokenByIndex(uint256)')) */ _registerInterface(0x780e9d63); } /// @notice Enumerate valid NFTs /// @dev Throws if `_index` >= `totalSupply()`. /// @param _index A counter less than `totalSupply()` /// @return The token identifier for the `_index`th NFT, /// (sort order not specified) function tokenByIndex( uint256 _index ) public view returns (uint256) { require(_index < _totalSupply, 'OUT_OF_RANGE'); return _index; } function supportsInterface(bytes4 interfaceId) public view virtual override( AccessControlUpgradeable, ERC165StorageUpgradeable ) returns (bool) { return super.supportsInterface(interfaceId); } uint256[1000] private __safe_upgrade_gap; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import '@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol'; import '@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol'; import '@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol'; /** * @title An implementation of the money related functions. * @author HardlyDifficult (unlock-protocol.com) */ contract MixinFunds { using AddressUpgradeable for address payable; using SafeERC20Upgradeable for IERC20Upgradeable; /** * The token-type that this Lock is priced in. If 0, then use ETH, else this is * a ERC20 token address. */ address public tokenAddress; function _initializeMixinFunds( address _tokenAddress ) internal { _isValidToken(_tokenAddress); tokenAddress = _tokenAddress; } function _isValidToken( address _tokenAddress ) internal view { require( _tokenAddress == address(0) || IERC20Upgradeable(_tokenAddress).totalSupply() > 0, 'INVALID_TOKEN' ); } /** * Transfers funds from the contract to the account provided. * * Security: be wary of re-entrancy when calling this function. */ function _transfer( address _tokenAddress, address payable _to, uint _amount ) internal { if(_amount > 0) { if(_tokenAddress == address(0)) { // https://diligence.consensys.net/blog/2019/09/stop-using-soliditys-transfer-now/ _to.sendValue(_amount); } else { IERC20Upgradeable token = IERC20Upgradeable(_tokenAddress); token.safeTransfer(_to, _amount); } } } uint256[1000] private __safe_upgrade_gap; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import './MixinKeys.sol'; import './MixinRoles.sol'; /** * @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 MixinRoles, MixinKeys { /** * 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, address[] calldata _keyManagers ) external { _lockIsUpToDate(); require(isKeyGranter(msg.sender) || isLockManager(msg.sender), 'ONLY_LOCK_MANAGER_OR_KEY_GRANTER'); for(uint i = 0; i < _recipients.length; i++) { require(_recipients[i] != address(0), 'INVALID_ADDRESS'); // an event is triggered _createNewKey( _recipients[i], _keyManagers[i], _expirationTimestamps[i] ); } } uint256[1000] private __safe_upgrade_gap; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import './MixinLockCore.sol'; /** * @title Mixin for managing `Key` data, as well as 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 MixinKeys is MixinLockCore { // The struct for a key struct Key { uint tokenId; uint expirationTimestamp; } // Emitted when the Lock owner expires a user's Key event ExpireKey(uint indexed tokenId); // Emitted when the expiration of a key is modified event ExpirationChanged( uint indexed _tokenId, uint _amount, bool _timeAdded ); // fire when a key is extended event KeyExtended( uint indexed tokenId, uint newTimestamp ); event KeyManagerChanged(uint indexed _tokenId, address indexed _newManager); event KeysMigrated( uint updatedRecordsCount ); // Deprecated: don't use this anymore as we know enable multiple keys per owner. mapping (address => Key) internal keyByOwner; // Each tokenId can have at most exactly one owner at a time. // Returns address(0) if the token does not exist mapping (uint => address) internal _ownerOf; // Keep track of the total number of unique owners for this lock (both expired and valid). // This may be larger than totalSupply uint public numberOfOwners; // A given key has both an owner and a manager. // If keyManager == address(0) then the key owner is also the manager // Each key can have at most 1 keyManager. mapping (uint => address) public keyManagerOf; // 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 transferred // Note: the approver may actually NOT have a key... and there can only // be a single approved address mapping (uint => address) private approved; // Keeping track of approved operators for a given Key manager. // This approves a given operator for all keys managed by the calling "keyManager" // The caller may not currently be the keyManager for ANY keys. // These approvals are never reset/revoked automatically, unlike "approved", // which is reset on transfer. mapping (address => mapping (address => bool)) internal managerToOperatorApproved; // store all keys: tokenId => token mapping(uint256 => Key) internal _keys; // store ownership: owner => array of tokens owned by that owner mapping(address => mapping(uint256 => uint256)) private _ownedKeyIds; // store indexes: owner => list of tokenIds mapping(uint256 => uint256) private _ownedKeysIndex; // Mapping owner address to token count mapping(address => uint256) private _balances; /** * Ensure that the caller is the keyManager of the key * or that the caller has been approved * for ownership of that key * @dev This is a modifier */ function _onlyKeyManagerOrApproved( uint _tokenId ) internal view { require( _isKeyManager(_tokenId, msg.sender) || approved[_tokenId] == msg.sender || isApprovedForAll(_ownerOf[_tokenId], msg.sender), 'ONLY_KEY_MANAGER_OR_APPROVED' ); } /** * Ensures that an owner has a valid key * @param _user the account to check * @dev This is a modifier */ function _hasValidKey( address _user ) internal view { require( getHasValidKey(_user), 'KEY_NOT_VALID' ); } /** * Check if a key is expired or not * @dev This is a modifier */ function _isValidKey( uint _tokenId ) internal view { require( isValidKey(_tokenId), 'KEY_NOT_VALID' ); } /** * Check if a key actually exists * @dev This is a modifier */ function _isKey( uint _tokenId ) internal view { require( _keys[_tokenId].expirationTimestamp != 0, 'NO_SUCH_KEY' ); } /** * Deactivate an existing key * @param _tokenId the id of token to burn * @notice the key will be expired and ownership records will be destroyed */ function burn(uint _tokenId) public { _isKey(_tokenId); _onlyKeyManagerOrApproved(_tokenId); emit Transfer(_ownerOf[_tokenId], address(0), _tokenId); // delete ownership and expire key _cancelKey(_tokenId); } /** * Migrate data from the previous single owner => key mapping to * the new data structure w multiple tokens. * @param _calldata an ABI-encoded representation of the params * for v10: `(uint _startIndex, uint nbRecordsToUpdate)` * - `_startIndex` : the index of the first record to migrate * - `_nbRecordsToUpdate` : number of records to migrate * @dev if all records can be processed at once, the `schemaVersion` will be updated * if not, you will have to call `updateSchemaVersion` * variable to the latest/current lock version */ function migrate( bytes calldata _calldata ) virtual public { // make sure we have correct data version before migrating require( ( (schemaVersion == publicLockVersion() - 1) || schemaVersion == 0 ), 'SCHEMA_VERSION_NOT_CORRECT' ); // set default value to 1 if(_maxKeysPerAddress == 0) { _maxKeysPerAddress = 1; } // count the records that are actually migrated uint startIndex = 0; // count the records that are actually migrated uint updatedRecordsCount; // the index of the last record to migrate in this call uint nbRecordsToUpdate; // the total number of records to migrate uint totalSupply = totalSupply(); // default to 100 when sent from Unlock, as this is called by default in the upgrade script. // If there are more than 100 keys, the migrate function will need to be called again until all keys have been migrated. if( msg.sender == address(unlockProtocol) ) { nbRecordsToUpdate = 100; } else { // decode param (startIndex, nbRecordsToUpdate) = abi.decode(_calldata, (uint, uint)); } // cap the number of records to migrate to totalSupply if(nbRecordsToUpdate > totalSupply) nbRecordsToUpdate = totalSupply; for (uint256 i = startIndex; i < startIndex + nbRecordsToUpdate; i++) { // tokenId starts at 1 uint tokenId = i + 1; address keyOwner = _ownerOf[tokenId]; Key memory k = keyByOwner[keyOwner]; // make sure key exists if(k.tokenId != 0 && k.expirationTimestamp != 0) { // copy key in new mapping _keys[i + 1] = Key(k.tokenId, k.expirationTimestamp); // delete token from previous owner delete keyByOwner[keyOwner]; // record new owner _createOwnershipRecord( tokenId, keyOwner ); // keep track of updated records updatedRecordsCount++; } } // enable lock if all keys has been migrated in a single run if(nbRecordsToUpdate >= totalSupply) { schemaVersion = publicLockVersion(); } emit KeysMigrated( updatedRecordsCount // records that have been migrated ); } /** * Set the schema version to the latest * @notice only lock manager call call this */ function updateSchemaVersion() public { _onlyLockManager(); schemaVersion = publicLockVersion(); } /** * Returns the id of a key for a specific owner at a specific index * @notice Enumerate keys assigned to an owner * @dev Throws if `_index` >= `balanceOf(_keyOwner)` or if * `_keyOwner` is the zero address, representing invalid keys. * @param _keyOwner address of the owner * @param _index position index in the array of all keys - less than `balanceOf(_keyOwner)` * @return The token identifier for the `_index`th key assigned to `_keyOwner`, * (sort order not specified) * NB: name kept to be ERC721 compatible */ function tokenOfOwnerByIndex( address _keyOwner, uint256 _index ) public view returns (uint256) { require(_index < balanceOf(_keyOwner), "OWNER_INDEX_OUT_OF_BOUNDS"); return _ownedKeyIds[_keyOwner][_index]; } /** * Create a new key with a new tokenId and store it * */ function _createNewKey( address _recipient, address _keyManager, uint expirationTimestamp ) internal returns (uint tokenId) { // We increment the tokenId counter _totalSupply++; tokenId = _totalSupply; // create the key _keys[tokenId] = Key(tokenId, expirationTimestamp); // increase total number of unique owners if(balanceOf(_recipient) == 0 ) { numberOfOwners++; } // store ownership _createOwnershipRecord(tokenId, _recipient); // set key manager _setKeyManagerOf(tokenId, _keyManager); // trigger event emit Transfer( address(0), // This is a creation. _recipient, tokenId ); } function _extendKey( uint _tokenId ) internal returns ( uint newTimestamp ) { uint expirationTimestamp = _keys[_tokenId].expirationTimestamp; // prevent extending a valid non-expiring key require(expirationTimestamp != type(uint).max, 'CANT_EXTEND_NON_EXPIRING_KEY'); // if non-expiring but not valid then extend if(expirationDuration == type(uint).max) { newTimestamp = type(uint).max; } else { if (expirationTimestamp > block.timestamp) { // extends a valid key newTimestamp = expirationTimestamp + expirationDuration; } else { // renew an expired or cancelled key newTimestamp = block.timestamp + expirationDuration; } } _keys[_tokenId].expirationTimestamp = newTimestamp; emit KeyExtended(_tokenId, newTimestamp); } /** * Record ownership info and udpate balance for new owner * @param _tokenId the id of the token to cancel * @param _recipient the address of the new owner */ function _createOwnershipRecord( uint _tokenId, address _recipient ) internal { uint length = balanceOf(_recipient); // make sure address does not have more keys than allowed require(length < _maxKeysPerAddress, 'MAX_KEYS'); // record new owner _ownedKeysIndex[_tokenId] = length; _ownedKeyIds[_recipient][length] = _tokenId; // update ownership mapping _ownerOf[_tokenId] = _recipient; _balances[_recipient] += 1; } /** * Merge existing keys * @param _tokenIdFrom the id of the token to substract time from * @param _tokenIdTo the id of the destination token to add time * @param _amount the amount of time to transfer (in seconds) */ function mergeKeys( uint _tokenIdFrom, uint _tokenIdTo, uint _amount ) public { // checks _isKey(_tokenIdFrom); _isValidKey(_tokenIdFrom); _onlyKeyManagerOrApproved(_tokenIdFrom); _isKey(_tokenIdTo); // make sure there is enough time remaining require(keyExpirationTimestampFor(_tokenIdFrom) - block.timestamp >= _amount, 'NOT_ENOUGH_TIME'); // deduct time from parent key _timeMachine(_tokenIdFrom, _amount, false); // add time to destination key _timeMachine(_tokenIdTo, _amount, true); } /** * Delete ownership info and udpate balance for previous owner * @param _tokenId the id of the token to cancel */ function _deleteOwnershipRecord( uint _tokenId ) internal { // get owner address previousOwner = _ownerOf[_tokenId]; // delete previous ownership uint lastTokenIndex = balanceOf(previousOwner) - 1; uint index = _ownedKeysIndex[_tokenId]; // When the token to delete is the last token, the swap operation is unnecessary if (index != lastTokenIndex) { uint256 lastTokenId = _ownedKeyIds[previousOwner][lastTokenIndex]; _ownedKeyIds[previousOwner][index] = lastTokenId; // Move the last token to the slot of the to-delete token _ownedKeysIndex[lastTokenId] = index; // Update the moved token's index } // Deletes the contents at the last position of the array delete _ownedKeyIds[previousOwner][lastTokenIndex]; // remove from owner count if thats the only key if(balanceOf(previousOwner) == 1 ) { numberOfOwners--; } // update balance _balances[previousOwner] -= 1; } /** * Delete ownership info about a key and expire the key * @param _tokenId the id of the token to cancel * @notice this won't 'burn' the token, as it would still exist in the record */ function _cancelKey( uint _tokenId ) internal { // Deletes the contents at the last position of the array _deleteOwnershipRecord(_tokenId); // expire the key _keys[_tokenId].expirationTimestamp = block.timestamp; // delete previous owner _ownerOf[_tokenId] = address(0); } /** * In the specific case of a Lock, each owner can own only at most 1 key. * @return The number of NFTs owned by `_keyOwner`, either 0 or 1. */ function balanceOf( address _keyOwner ) public view returns (uint) { require(_keyOwner != address(0), 'INVALID_ADDRESS'); return _balances[_keyOwner]; } /** * Check if a certain key is valid * @param _tokenId the id of the key to check validity * @notice this makes use of the onValidKeyHook if it is set */ function isValidKey( uint _tokenId ) public view returns (bool) { bool isValid = _keys[_tokenId].expirationTimestamp > block.timestamp; return isValid; } /** * Checks if the user has at least one non-expired key. * @param _keyOwner the */ function getHasValidKey( address _keyOwner ) public view returns (bool isValid) { uint length = balanceOf(_keyOwner); if(length > 0) { for (uint i = 0; i < length; i++) { if(isValidKey(tokenOfOwnerByIndex(_keyOwner, i))) { return true; // stop looping at the first valid key } } } // use hook if it exists if(address(onValidKeyHook) != address(0)) { isValid = onValidKeyHook.hasValidKey( address(this), _keyOwner, 0, // no timestamp needed (we use tokenId) isValid ); } return isValid; } /** * Returns the key's ExpirationTimestamp field for a given token. * @param _tokenId the tokenId of the key * @dev Returns 0 if the owner has never owned a key for this lock */ function keyExpirationTimestampFor( uint _tokenId ) public view returns (uint) { return _keys[_tokenId].expirationTimestamp; } /** * Returns the owner of a given tokenId * @param _tokenId the id of the token * @return the address of the owner */ function ownerOf( uint _tokenId ) public view returns(address) { return _ownerOf[_tokenId]; } /** * @notice Public function for updating transfer and cancel rights for a given key * @param _tokenId The id of the key to assign rights for * @param _keyManager The address with the manager's rights for the given key. * Setting _keyManager to address(0) means the keyOwner is also the keyManager */ function setKeyManagerOf( uint _tokenId, address _keyManager ) public { _isKey(_tokenId); require( _isKeyManager(_tokenId, msg.sender) || isLockManager(msg.sender), 'UNAUTHORIZED_KEY_MANAGER_UPDATE' ); _setKeyManagerOf(_tokenId, _keyManager); } function _setKeyManagerOf( uint _tokenId, address _keyManager ) internal { if(keyManagerOf[_tokenId] != _keyManager) { keyManagerOf[_tokenId] = _keyManager; _clearApproval(_tokenId); emit KeyManagerChanged(_tokenId, _keyManager); } } /** * 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 ) public { _onlyKeyManagerOrApproved(_tokenId); require(msg.sender != _approved, 'APPROVE_SELF'); approved[_tokenId] = _approved; emit Approval(_ownerOf[_tokenId], _approved, _tokenId); } /** * @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 ) public view returns (address) { _isKey(_tokenId); address approvedRecipient = approved[_tokenId]; return approvedRecipient; } /** * @dev Tells whether an operator is approved by a given keyManager * @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 managerToOperatorApproved[_owner][_operator]; } /** * Returns true if _keyManager is the manager of the key * identified by _tokenId */ function _isKeyManager( uint _tokenId, address _keyManager ) internal view returns (bool) { if(keyManagerOf[_tokenId] == _keyManager || (keyManagerOf[_tokenId] == address(0) && ownerOf(_tokenId) == _keyManager)) { return true; } else { return false; } } /** * @notice Modify the expirationTimestamp of a key * by a given amount. * @param _tokenId The ID of the key to modify. * @param _deltaT The amount of time in seconds by which * to modify the keys expirationTimestamp * @param _addTime Choose whether to increase or decrease * expirationTimestamp (false == decrease, true == increase) * @dev Throws if owner does not have a valid key. */ function _timeMachine( uint _tokenId, uint256 _deltaT, bool _addTime ) internal { _isKey(_tokenId); uint formerTimestamp = _keys[_tokenId].expirationTimestamp; if(_addTime) { if(formerTimestamp > block.timestamp) { // append to valid key _keys[_tokenId].expirationTimestamp = formerTimestamp + _deltaT; } else { // add from now if key is expired _keys[_tokenId].expirationTimestamp = block.timestamp + _deltaT; } } else { _keys[_tokenId].expirationTimestamp = formerTimestamp - _deltaT; } emit ExpirationChanged(_tokenId, _deltaT, _addTime); } /** * @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); } } /** * @notice Change the maximum number of keys the lock can edit * @param _maxNumberOfKeys uint the maximum number of keys * @dev Can't be smaller than the existing supply */ function setMaxNumberOfKeys (uint _maxNumberOfKeys) external { _onlyLockManager(); require (_maxNumberOfKeys >= _totalSupply, "SMALLER_THAN_SUPPLY"); maxNumberOfKeys = _maxNumberOfKeys; } /** * A function to change the default duration of each key in the lock * @notice keys previously bought are unaffected by this change (i.e. * existing keys timestamps are not recalculated/updated) * @param _newExpirationDuration the new amount of time for each key purchased * or type(uint).max for a non-expiring key */ function setExpirationDuration(uint _newExpirationDuration) external { _onlyLockManager(); expirationDuration = _newExpirationDuration; } /** * Set the maximum number of keys a specific address can use * @param _maxKeys the maximum amount of key a user can own */ function setMaxKeysPerAddress(uint _maxKeys) external { _onlyLockManager(); require(_maxKeys != 0, 'NULL_VALUE'); _maxKeysPerAddress = _maxKeys; } /** * @return the maximum number of key allowed for a single address */ function maxKeysPerAddress() external view returns (uint) { return _maxKeysPerAddress; } // decrease 1000 to 996 when adding new tokens/owners mappings in v10 uint256[996] private __safe_upgrade_gap; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // import '@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721EnumerableUpgradeable.sol'; import '@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol'; import './MixinDisable.sol'; import './MixinRoles.sol'; import '../interfaces/IUnlock.sol'; import './MixinFunds.sol'; import '../interfaces/hooks/ILockKeyCancelHook.sol'; import '../interfaces/hooks/ILockKeyPurchaseHook.sol'; import '../interfaces/hooks/ILockValidKeyHook.sol'; import '../interfaces/hooks/ILockTokenURIHook.sol'; /** * @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 MixinRoles, MixinFunds, MixinDisable { using AddressUpgradeable for address; event Withdrawal( address indexed sender, address indexed tokenAddress, address indexed beneficiary, uint amount ); event PricingChanged( uint oldKeyPrice, uint keyPrice, address oldTokenAddress, address tokenAddress ); /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); // 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? 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 internal _totalSupply; // The account which will receive funds on withdrawal address payable public beneficiary; // The denominator component for values specified in basis points. uint internal constant BASIS_POINTS_DEN = 10000; ILockKeyPurchaseHook public onKeyPurchaseHook; ILockKeyCancelHook public onKeyCancelHook; ILockValidKeyHook public onValidKeyHook; ILockTokenURIHook public onTokenURIHook; // use to check data version uint public schemaVersion; // keep track of how many key a single address can use uint internal _maxKeysPerAddress; // modifier to check if data has been upgraded function _lockIsUpToDate() internal view { require( schemaVersion == publicLockVersion(), 'MIGRATION_REQUIRED' ); } // modifier function _onlyLockManagerOrBeneficiary() internal view { require( isLockManager(msg.sender) || msg.sender == beneficiary, 'ONLY_LOCK_MANAGER_OR_BENEFICIARY' ); } function _initializeMixinLockCore( address payable _beneficiary, uint _expirationDuration, uint _keyPrice, uint _maxNumberOfKeys ) internal { unlockProtocol = IUnlock(msg.sender); // Make sure we link back to Unlock's smart contract. beneficiary = _beneficiary; expirationDuration = _expirationDuration; keyPrice = _keyPrice; maxNumberOfKeys = _maxNumberOfKeys; // update only when initialized schemaVersion = publicLockVersion(); // only a single key per address is allowed by default _maxKeysPerAddress = 1; } // The version number of the current implementation on this network function publicLockVersion( ) public pure returns (uint16) { return 10; } /** * @dev Called by owner to withdraw all funds from the lock and send them to the `beneficiary`. * @param _tokenAddress specifies the token address to withdraw or 0 for ETH. This is usually * the same as `tokenAddress` in MixinFunds. * @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. */ function withdraw( address _tokenAddress, uint _amount ) external { _onlyLockManagerOrBeneficiary(); // get balance uint balance; if(_tokenAddress == address(0)) { balance = address(this).balance; } else { balance = IERC20Upgradeable(_tokenAddress).balanceOf(address(this)); } uint amount; if(_amount == 0 || _amount > balance) { require(balance > 0, 'NOT_ENOUGH_FUNDS'); amount = balance; } else { amount = _amount; } emit Withdrawal(msg.sender, _tokenAddress, beneficiary, amount); // Security: re-entrancy not a risk as this is the last line of an external function _transfer(_tokenAddress, beneficiary, amount); } /** * A function which lets the owner of the lock change the pricing for future purchases. * This consists of 2 parts: The token address and the price in the given token. * In order to set the token to ETH, use 0 for the token Address. */ function updateKeyPricing( uint _keyPrice, address _tokenAddress ) external { _onlyLockManager(); _isValidToken(_tokenAddress); uint oldKeyPrice = keyPrice; address oldTokenAddress = tokenAddress; keyPrice = _keyPrice; tokenAddress = _tokenAddress; emit PricingChanged(oldKeyPrice, keyPrice, oldTokenAddress, tokenAddress); } /** * A function which lets the owner of the lock update the beneficiary account, * which receives funds on withdrawal. */ function updateBeneficiary( address payable _beneficiary ) external { _onlyLockManagerOrBeneficiary(); require(_beneficiary != address(0), 'INVALID_ADDRESS'); beneficiary = _beneficiary; } /** * @notice Allows a lock manager to add or remove an event hook */ function setEventHooks( address _onKeyPurchaseHook, address _onKeyCancelHook, address _onValidKeyHook, address _onTokenURIHook ) external { _onlyLockManager(); require(_onKeyPurchaseHook == address(0) || _onKeyPurchaseHook.isContract(), 'INVALID_ON_KEY_SOLD_HOOK'); require(_onKeyCancelHook == address(0) || _onKeyCancelHook.isContract(), 'INVALID_ON_KEY_CANCEL_HOOK'); require(_onValidKeyHook == address(0) || _onValidKeyHook.isContract(), 'INVALID_ON_VALID_KEY_HOOK'); require(_onTokenURIHook == address(0) || _onTokenURIHook.isContract(), 'INVALID_ON_TOKEN_URI_HOOK'); onKeyPurchaseHook = ILockKeyPurchaseHook(_onKeyPurchaseHook); onKeyCancelHook = ILockKeyCancelHook(_onKeyCancelHook); onTokenURIHook = ILockTokenURIHook(_onTokenURIHook); onValidKeyHook = ILockValidKeyHook(_onValidKeyHook); } function totalSupply() public view returns(uint256) { return _totalSupply; } /** * @notice An ERC-20 style approval, allowing the spender to transfer funds directly from this lock. */ function approveBeneficiary( address _spender, uint _amount ) public returns (bool) { _onlyLockManagerOrBeneficiary(); return IERC20Upgradeable(tokenAddress).approve(_spender, _amount); } // decreased from 1000 to 998 when adding `schemaVersion` and `maxKeysPerAddress` in v10 uint256[998] private __safe_upgrade_gap; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import '@openzeppelin/contracts-upgradeable/utils/introspection/ERC165StorageUpgradeable.sol'; // import '@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721EnumerableUpgradeable.sol'; import '../UnlockUtils.sol'; import './MixinKeys.sol'; import './MixinLockCore.sol'; import './MixinRoles.sol'; /** * @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 ERC165StorageUpgradeable, MixinRoles, MixinLockCore, MixinKeys { using UnlockUtils for uint; using UnlockUtils for address; using UnlockUtils for string; /// A descriptive name for a collection of NFTs in this contract.Defaults to "Unlock-Protocol" but is settable by lock owner string public name; /// 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 ); function _initializeMixinLockMetadata( string calldata _lockName ) internal { ERC165StorageUpgradeable.__ERC165Storage_init(); name = _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 { _onlyLockManager(); name = _lockName; } /** * Allows the Lock owner to assign a Symbol for this Lock. */ function updateLockSymbol( string calldata _lockSymbol ) external { _onlyLockManager(); 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.globalTokenSymbol(); } else { return lockSymbol; } } /** * Allows the Lock owner to update the baseTokenURI for this Lock. */ function setBaseTokenURI( string calldata _baseTokenURI ) external { _onlyLockManager(); baseTokenURI = _baseTokenURI; } /** @notice A distinct Uniform Resource Identifier (URI) for a given asset. * @param _tokenId The iD of the token for which we want to retrieve the URI. * If 0 is passed here, we just return the appropriate baseTokenURI. * If a custom URI has been set we don't return the lock address. * It may be included in the custom baseTokenURI if needed. * @dev 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 returns(string memory) { string memory URI; string memory tokenId; string memory lockAddress = address(this).address2Str(); string memory seperator; if(_tokenId != 0) { tokenId = _tokenId.uint2Str(); } else { tokenId = ''; } if(address(onTokenURIHook) != address(0)) { uint expirationTimestamp = keyExpirationTimestampFor(_tokenId); return onTokenURIHook.tokenURI( address(this), msg.sender, ownerOf(_tokenId), _tokenId, expirationTimestamp ); } if(bytes(baseTokenURI).length == 0) { URI = unlockProtocol.globalBaseTokenURI(); seperator = '/'; } else { URI = baseTokenURI; seperator = ''; lockAddress = ''; } return URI.strConcat( lockAddress, seperator, tokenId ); } function supportsInterface(bytes4 interfaceId) public view virtual override( AccessControlUpgradeable, ERC165StorageUpgradeable ) returns (bool) { return super.supportsInterface(interfaceId); } uint256[1000] private __safe_upgrade_gap; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import './MixinDisable.sol'; import './MixinKeys.sol'; import './MixinLockCore.sol'; import './MixinFunds.sol'; /** * @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, MixinDisable, MixinLockCore, MixinKeys { event RenewKeyPurchase(address indexed owner, uint newExpiration); event GasRefunded(address indexed receiver, uint refundedAmount, address tokenAddress); event UnlockCallFailed(address indexed lockAddress, address unlockAddress); // default to 0 uint256 internal _gasRefundValue; // Keep track of ERC20 price when purchased mapping(uint256 => uint256) private _originalPrices; // Keep track of duration when purchased mapping(uint256 => uint256) internal _originalDurations; // keep track of token pricing when purchased mapping(uint256 => address) private _originalTokens; /** * @dev Set the value/price to be refunded to the sender on purchase */ function setGasRefundValue(uint256 _refundValue) external { _onlyLockManager(); _gasRefundValue = _refundValue; } /** * @dev Returns value/price to be refunded to the sender on purchase */ function gasRefundValue() external view returns (uint256 _refundValue) { return _gasRefundValue; } /** * @dev Helper to communicate with Unlock (record GNP and mint UDT tokens) */ function _recordKeyPurchase (uint _keyPrice, address _referrer) internal { // make sure unlock is a contract, and we catch possible reverts if (address(unlockProtocol).code.length > 0) { // call Unlock contract to record GNP // the function is capped by gas to prevent running out of gas try unlockProtocol.recordKeyPurchase{gas: 300000}(_keyPrice, _referrer) {} catch { // emit missing unlock emit UnlockCallFailed(address(this), address(unlockProtocol)); } } else { // emit missing unlock emit UnlockCallFailed(address(this), address(unlockProtocol)); } } /** * @dev Purchase function * @param _values array of tokens amount to pay for this purchase >= the current keyPrice - any applicable discount * (_values is ignored when using ETH) * @param _recipients array of addresses of the recipients of the purchased key * @param _referrers array of addresses of the users making the referral * @param _keyManagers optional array of addresses to grant managing rights to a specific address on creation * @param _data arbitrary data populated by the front-end which initiated the sale * @notice when called for an existing and non-expired key, the `_keyManager` param will be ignored * @dev Setting _value to keyPrice exactly doubles as a security feature. That way if the lock owner increases the * price while my transaction is pending I can't be charged more than I expected (only applicable to ERC-20 when more * than keyPrice is approved for spending). */ function purchase( uint256[] memory _values, address[] memory _recipients, address[] memory _referrers, address[] memory _keyManagers, bytes[] calldata _data ) external payable { _lockIsUpToDate(); require(maxNumberOfKeys > _totalSupply, 'LOCK_SOLD_OUT'); require(_recipients.length == _referrers.length, 'INVALID_REFERRERS_LENGTH'); require(_recipients.length == _keyManagers.length, 'INVALID_KEY_MANAGERS_LENGTH'); uint totalPriceToPay; uint tokenId; for (uint256 i = 0; i < _recipients.length; i++) { // check recipient address address _recipient = _recipients[i]; require(_recipient != address(0), 'INVALID_ADDRESS'); // check for a non-expiring key if (expirationDuration == type(uint).max) { // create a new key tokenId = _createNewKey( _recipient, _keyManagers[i], type(uint).max ); } else { tokenId = _createNewKey( _recipient, _keyManagers[i], block.timestamp + expirationDuration ); } // price uint inMemoryKeyPrice = purchasePriceFor(_recipient, _referrers[i], _data[i]); totalPriceToPay = totalPriceToPay + inMemoryKeyPrice; // store values at purchase time _originalPrices[tokenId] = inMemoryKeyPrice; _originalDurations[tokenId] = expirationDuration; _originalTokens[tokenId] = tokenAddress; if(tokenAddress != address(0)) { require(inMemoryKeyPrice <= _values[i], 'INSUFFICIENT_ERC20_VALUE'); } // store in unlock _recordKeyPurchase(inMemoryKeyPrice, _referrers[i]); // fire hook uint pricePaid = tokenAddress == address(0) ? msg.value : _values[i]; if(address(onKeyPurchaseHook) != address(0)) { onKeyPurchaseHook.onKeyPurchase( msg.sender, _recipient, _referrers[i], _data[i], inMemoryKeyPrice, pricePaid ); } } // transfer the ERC20 tokens if(tokenAddress != address(0)) { IERC20Upgradeable token = IERC20Upgradeable(tokenAddress); token.transferFrom(msg.sender, address(this), totalPriceToPay); } else { // We explicitly allow for greater amounts of ETH or tokens to allow 'donations' require(totalPriceToPay <= msg.value, 'INSUFFICIENT_VALUE'); } // refund gas _refundGas(); } /** * @dev Extend function * @param _value the number of tokens to pay for this purchase >= the current keyPrice - any applicable discount * (_value is ignored when using ETH) * @param _tokenId id of the key to extend * @param _referrer address of the user making the referral * @param _data arbitrary data populated by the front-end which initiated the sale * @dev Throws if lock is disabled or key does not exist for _recipient. Throws if _recipient == address(0). */ function extend( uint _value, uint _tokenId, address _referrer, bytes calldata _data ) public payable { _lockIsUpToDate(); _isKey(_tokenId); // extend key duration _extendKey(_tokenId); // transfer the tokens uint inMemoryKeyPrice = purchasePriceFor(ownerOf(_tokenId), _referrer, _data); if(tokenAddress != address(0)) { require(inMemoryKeyPrice <= _value, 'INSUFFICIENT_ERC20_VALUE'); IERC20Upgradeable token = IERC20Upgradeable(tokenAddress); token.transferFrom(msg.sender, address(this), inMemoryKeyPrice); } else { // We explicitly allow for greater amounts of ETH or tokens to allow 'donations' require(inMemoryKeyPrice <= msg.value, 'INSUFFICIENT_VALUE'); } // refund gas (if applicable) _refundGas(); } /** * Renew a given token * @notice only works for non-free, expiring, ERC20 locks * @param _tokenId the ID fo the token to renew * @param _referrer the address of the person to be granted UDT */ function renewMembershipFor( uint _tokenId, address _referrer ) public { _lockIsUpToDate(); _isKey(_tokenId); // check the lock require(_originalDurations[_tokenId] != type(uint).max, 'NON_EXPIRING_LOCK'); require(tokenAddress != address(0), 'NON_ERC20_LOCK'); // make sure duration and pricing havent changed uint keyPrice = purchasePriceFor(ownerOf(_tokenId), _referrer, ''); require(_originalPrices[_tokenId] == keyPrice, 'PRICE_CHANGED'); require(_originalDurations[_tokenId] == expirationDuration, 'DURATION_CHANGED'); require(_originalTokens[_tokenId] == tokenAddress, 'TOKEN_CHANGED'); // make sure key is ready for renewal require(isValidKey(_tokenId) == false, 'NOT_READY'); // extend key duration _extendKey(_tokenId); // store in unlock _recordKeyPurchase(keyPrice, _referrer); // transfer the tokens IERC20Upgradeable token = IERC20Upgradeable(tokenAddress); token.transferFrom(ownerOf(_tokenId), address(this), keyPrice); // refund gas if applicable _refundGas(); } /** * @notice returns the minimum price paid for a purchase with these params. * @dev minKeyPrice considers any discount from Unlock or the OnKeyPurchase hook */ function purchasePriceFor( address _recipient, address _referrer, bytes memory _data ) public view returns (uint minKeyPrice) { if(address(onKeyPurchaseHook) != address(0)) { minKeyPrice = onKeyPurchaseHook.keyPurchasePrice(msg.sender, _recipient, _referrer, _data); } else { minKeyPrice = keyPrice; } } /** * Refund the specified gas amount and emit an event * @notice this does sth only if `_gasRefundValue` is non-null */ function _refundGas() internal { if (_gasRefundValue != 0) { if(tokenAddress != address(0)) { IERC20Upgradeable token = IERC20Upgradeable(tokenAddress); token.transferFrom(address(this), msg.sender, _gasRefundValue); } else { (bool success, ) = msg.sender.call{value: _gasRefundValue}(""); require(success, "REFUND_FAILED"); } emit GasRefunded(msg.sender, _gasRefundValue, tokenAddress); } } // decreased from 1000 to 997 when added mappings for initial purchases pricing and duration on v10 uint256[997] private __safe_upgrade_gap; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import './MixinKeys.sol'; import './MixinLockCore.sol'; import './MixinRoles.sol'; import './MixinFunds.sol'; import './MixinPurchase.sol'; contract MixinRefunds is MixinRoles, MixinFunds, MixinLockCore, MixinKeys, MixinPurchase { // CancelAndRefund will return funds based on time remaining minus this penalty. // This is calculated as `proRatedRefund * refundPenaltyBasisPoints / BASIS_POINTS_DEN`. uint public refundPenaltyBasisPoints; uint public freeTrialLength; event CancelKey( uint indexed tokenId, address indexed owner, address indexed sendTo, uint refund ); event RefundPenaltyChanged( uint freeTrialLength, uint refundPenaltyBasisPoints ); function _initializeMixinRefunds() internal { // default to 10% refundPenaltyBasisPoints = 1000; } /** * @dev Invoked by the lock owner to destroy the user's key and perform a refund and cancellation * of the key * @param _tokenId The id of the key to expire * @param _amount The amount to refund */ function expireAndRefundFor( uint _tokenId, uint _amount ) external { _isKey(_tokenId); _isValidKey(_tokenId); _onlyLockManager(); _cancelAndRefund(_tokenId, _amount); } /** * @dev Destroys the key and sends a refund based on the amount of time remaining. * @param _tokenId The id of the key to cancel. */ function cancelAndRefund(uint _tokenId) external { _isKey(_tokenId); _isValidKey(_tokenId); _onlyKeyManagerOrApproved(_tokenId); uint refund = getCancelAndRefundValue(_tokenId); _cancelAndRefund(_tokenId, refund); } /** * Allow the owner to change the refund penalty. */ function updateRefundPenalty( uint _freeTrialLength, uint _refundPenaltyBasisPoints ) external { _onlyLockManager(); emit RefundPenaltyChanged( _freeTrialLength, _refundPenaltyBasisPoints ); freeTrialLength = _freeTrialLength; refundPenaltyBasisPoints = _refundPenaltyBasisPoints; } /** * @dev cancels the key for the given keyOwner and sends the refund to the msg.sender. * @notice this deletes ownership info and expire the key, but doesnt 'burn' it */ function _cancelAndRefund( uint _tokenId, uint refund ) internal { address payable keyOwner = payable(ownerOf(_tokenId)); // delete ownership info and expire the key _cancelKey(_tokenId); // emit event emit CancelKey(_tokenId, keyOwner, msg.sender, refund); if (refund > 0) { _transfer(tokenAddress, keyOwner, refund); } // make future reccuring transactions impossible _originalDurations[_tokenId] = 0; // inform the hook if there is one registered if(address(onKeyCancelHook) != address(0)) { onKeyCancelHook.onKeyCancel(msg.sender, keyOwner, refund); } } /** * @dev Determines how much of a refund a key would be worth if a cancelAndRefund * is issued now. * @param _tokenId the key to check the refund value for. * @notice 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 getCancelAndRefundValue( uint _tokenId ) public view returns (uint refund) { _isValidKey(_tokenId); // return entire purchased price if key is non-expiring if(expirationDuration == type(uint).max) { return keyPrice; } // substract free trial value uint timeRemaining = keyExpirationTimestampFor(_tokenId) - block.timestamp; if(timeRemaining + freeTrialLength >= expirationDuration) { refund = keyPrice; } else { refund = keyPrice * timeRemaining / expirationDuration; } // Apply the penalty if this is not a free trial if(freeTrialLength == 0 || timeRemaining + freeTrialLength < expirationDuration) { uint penalty = keyPrice * refundPenaltyBasisPoints / BASIS_POINTS_DEN; if (refund > penalty) { refund -= penalty; } else { refund = 0; } } } uint256[1000] private __safe_upgrade_gap; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import './MixinRoles.sol'; import './MixinDisable.sol'; import './MixinKeys.sol'; import './MixinFunds.sol'; import './MixinLockCore.sol'; import './MixinPurchase.sol'; import '@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol'; import '@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol'; /** * @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 MixinRoles, MixinFunds, MixinLockCore, MixinKeys, MixinPurchase { using AddressUpgradeable for address; event TransferFeeChanged( uint transferFeeBasisPoints ); // 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 * transferFeeBasisPoints / BASIS_POINTS_DEN`. uint public transferFeeBasisPoints; /** * @notice Allows the key owner to safely transfer a portion of the remaining time * from their key to a new key * @param _tokenIdFrom the key to share * @param _to The recipient of the shared time * @param _timeShared The amount of time shared */ function shareKey( address _to, uint _tokenIdFrom, uint _timeShared ) public { _lockIsUpToDate(); require(maxNumberOfKeys > _totalSupply, 'LOCK_SOLD_OUT'); _onlyKeyManagerOrApproved(_tokenIdFrom); _isValidKey(_tokenIdFrom); require(transferFeeBasisPoints < BASIS_POINTS_DEN, 'KEY_TRANSFERS_DISABLED'); require(_to != address(0), 'INVALID_ADDRESS'); address keyOwner = _ownerOf[_tokenIdFrom]; require(keyOwner != _to, 'TRANSFER_TO_SELF'); // store time to be added uint time; // get the remaining time for the origin key uint timeRemaining = keyExpirationTimestampFor(_tokenIdFrom) - block.timestamp; // get the transfer fee based on amount of time wanted share uint fee = getTransferFee(_tokenIdFrom, _timeShared); uint timePlusFee = _timeShared + fee; // ensure that we don't try to share too much if(timePlusFee < timeRemaining) { // now we can safely set the time time = _timeShared; // deduct time from parent key, including transfer fee _timeMachine(_tokenIdFrom, timePlusFee, false); } else { // we have to recalculate the fee here fee = getTransferFee(_tokenIdFrom, timeRemaining); time = timeRemaining - fee; _keys[_tokenIdFrom].expirationTimestamp = block.timestamp; // Effectively expiring the key emit ExpireKey(_tokenIdFrom); } // create new key uint tokenIdTo = _createNewKey( _to, address(0), block.timestamp + time ); // trigger event emit Transfer( keyOwner, _to, tokenIdTo ); require(_checkOnERC721Received(keyOwner, _to, tokenIdTo, ''), 'NON_COMPLIANT_ERC721_RECEIVER'); } function transferFrom( address _from, address _recipient, uint _tokenId ) public { _isValidKey(_tokenId); _onlyKeyManagerOrApproved(_tokenId); require(ownerOf(_tokenId) == _from, 'TRANSFER_FROM: NOT_KEY_OWNER'); require(transferFeeBasisPoints < BASIS_POINTS_DEN, 'KEY_TRANSFERS_DISABLED'); require(_recipient != address(0), 'INVALID_ADDRESS'); require(_from != _recipient, 'TRANSFER_TO_SELF'); // subtract the fee from the senders key before the transfer _timeMachine(_tokenId, getTransferFee(_tokenId, 0), false); // transfer a token Key storage key = _keys[_tokenId]; // update expiration key.expirationTimestamp = keyExpirationTimestampFor(_tokenId); // increase total number of unique owners if(balanceOf(_recipient) == 0 ) { numberOfOwners++; } // delete token from previous owner _deleteOwnershipRecord(_tokenId); // record new owner _createOwnershipRecord(_tokenId, _recipient); // clear any previous approvals _setKeyManagerOf(_tokenId, address(0)); _clearApproval(_tokenId); // make future reccuring transactions impossible _originalDurations[_tokenId] = 0; // trigger event emit Transfer( _from, _recipient, _tokenId ); } /** * @notice An ERC-20 style transfer. * @param _tokenId the Id of the token to send * @param _to the destination address * @param _valueBasisPoint a percentage (expressed as basis points) of the time to be transferred * @return success bool success/failure of the transfer */ function transfer( uint _tokenId, address _to, uint _valueBasisPoint ) public returns (bool success) { _isValidKey(_tokenId); uint timeShared = ( keyExpirationTimestampFor(_tokenId) - block.timestamp ) * _valueBasisPoint / BASIS_POINTS_DEN; shareKey( _to, _tokenId, timeShared); // Errors will cause a revert return true; } /** * @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 ) public { safeTransferFrom(_from, _to, _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 * @notice disabled when transfers are disabled */ function setApprovalForAll( address _to, bool _approved ) public { require(_to != msg.sender, 'APPROVE_SELF'); require(transferFeeBasisPoints < BASIS_POINTS_DEN, 'KEY_TRANSFERS_DISABLED'); managerToOperatorApproved[msg.sender][_to] = _approved; emit ApprovalForAll(msg.sender, _to, _approved); } /** * @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 { 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 _transferFeeBasisPoints ) external { _onlyLockManager(); emit TransferFeeChanged( _transferFeeBasisPoints ); transferFeeBasisPoints = _transferFeeBasisPoints; } /** * Determines how much of a fee would need to be paid in order to * transfer to another account. This is pro-rated so the fee goes * down overtime. * @dev Throws if _tokenId is not have a valid key * @param _tokenId The id of the key check the transfer fee for. * @param _time The amount of time to calculate the fee for. * @return The transfer fee in seconds. */ function getTransferFee( uint _tokenId, uint _time ) public view returns (uint) { _isKey(_tokenId); uint expirationTimestamp = keyExpirationTimestampFor(_tokenId); if(expirationTimestamp < block.timestamp) { return 0; } else { uint timeToTransfer; if(_time == 0) { timeToTransfer = expirationTimestamp - block.timestamp; } else { timeToTransfer = _time; } return timeToTransfer * transferFeeBasisPoints / BASIS_POINTS_DEN; } } /** * @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 = IERC721ReceiverUpgradeable(to).onERC721Received( msg.sender, from, tokenId, _data); return (retval == _ERC721_RECEIVED); } uint256[1000] private __safe_upgrade_gap; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // This contract mostly follows the pattern established by openzeppelin in // openzeppelin/contracts-ethereum-package/contracts/access/roles import '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol'; contract MixinRoles is AccessControlUpgradeable { // roles bytes32 public constant LOCK_MANAGER_ROLE = keccak256("LOCK_MANAGER"); bytes32 public constant KEY_GRANTER_ROLE = keccak256("KEY_GRANTER"); // events event LockManagerAdded(address indexed account); event LockManagerRemoved(address indexed account); event KeyGranterAdded(address indexed account); event KeyGranterRemoved(address indexed account); // initializer function _initializeMixinRoles(address sender) internal { // for admin mamangers to add other lock admins _setRoleAdmin(LOCK_MANAGER_ROLE, LOCK_MANAGER_ROLE); // for lock managers to add/remove key granters _setRoleAdmin(KEY_GRANTER_ROLE, LOCK_MANAGER_ROLE); if (!isLockManager(sender)) { _setupRole(LOCK_MANAGER_ROLE, sender); } if (!isKeyGranter(sender)) { _setupRole(KEY_GRANTER_ROLE, sender); } } // modifiers function _onlyLockManager() internal view { require( hasRole(LOCK_MANAGER_ROLE, msg.sender), 'ONLY_LOCK_MANAGER'); } // lock manager functions function isLockManager(address account) public view returns (bool) { return hasRole(LOCK_MANAGER_ROLE, account); } function addLockManager(address account) public { _onlyLockManager(); grantRole(LOCK_MANAGER_ROLE, account); emit LockManagerAdded(account); } function renounceLockManager() public { renounceRole(LOCK_MANAGER_ROLE, msg.sender); emit LockManagerRemoved(msg.sender); } // key granter functions function isKeyGranter(address account) public view returns (bool) { return hasRole(KEY_GRANTER_ROLE, account); } function addKeyGranter(address account) public { _onlyLockManager(); grantRole(KEY_GRANTER_ROLE, account); emit KeyGranterAdded(account); } function revokeKeyGranter(address _granter) public { _onlyLockManager(); revokeRole(KEY_GRANTER_ROLE, _granter); emit KeyGranterRemoved(_granter); } uint256[1000] private __safe_upgrade_gap; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import './MixinLockCore.sol'; /** * @title Mixin to add support for `ownable()` * @dev `Mixins` are a design pattern seen in the 0x contracts. It simply * separates logically groupings of code to ease readability. */ contract MixinConvenienceOwnable is MixinLockCore { // used for `owner()`convenience helper address private _convenienceOwner; // events event OwnershipTransferred(address previousOwner, address newOwner); function _initializeMixinConvenienceOwnable(address _sender) internal { _convenienceOwner = _sender; } /** `owner()` is provided as an helper to mimick the `Ownable` contract ABI. * The `Ownable` logic is used by many 3rd party services to determine * contract ownership - e.g. who is allowed to edit metadata on Opensea. * * @notice This logic is NOT used internally by the Unlock Protocol and is made * available only as a convenience helper. */ function owner() public view returns (address) { return _convenienceOwner; } /** Setter for the `owner` convenience helper (see `owner()` docstring for more). * @notice This logic is NOT used internally by the Unlock Protocol ans is made * available only as a convenience helper. * @param account address returned by the `owner()` helper */ function setOwner(address account) public { _onlyLockManager(); require(account != address(0), 'OWNER_CANT_BE_ADDRESS_ZERO'); address _previousOwner = _convenienceOwner; _convenienceOwner = account; emit OwnershipTransferred(_previousOwner, account); } function isOwner(address account) public view returns (bool) { return _convenienceOwner == account; } uint256[1000] private __safe_upgrade_gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Address.sol) pragma solidity ^0.8.0; /** * @dev Collection of functions related to the address type */ library AddressUpgradeable { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) pragma solidity ^0.8.0; import "./IERC165Upgradeable.sol"; import "../../proxy/utils/Initializable.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable { function __ERC165_init() internal onlyInitializing { __ERC165_init_unchained(); } function __ERC165_init_unchained() internal onlyInitializing { } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165Upgradeable).interfaceId; } uint256[50] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165Upgradeable { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity >=0.5.17 <0.9.0; /** * @title The Unlock Interface * @author Nick Furfaro (unlock-protocol.com) **/ interface IUnlock { // Use initialize instead of a constructor to support proxies(for upgradeability via zos). function initialize(address _unlockOwner) external; /** * @dev deploy a ProxyAdmin contract used to upgrade locks */ function initializeProxyAdmin() external; // store contract proxy admin address function proxyAdminAddress() external view; /** * @notice Create lock (legacy) * This deploys a lock for a creator. It also keeps track of the deployed lock. * @param _expirationDuration the duration of the lock (pass 0 for unlimited duration) * @param _tokenAddress set to the ERC20 token address, or 0 for ETH. * @param _keyPrice the price of each key * @param _maxNumberOfKeys the maximum nimbers of keys to be edited * @param _lockName the name of the lock * param _salt [deprec] -- kept only for backwards copatibility * This may be implemented as a sequence ID or with RNG. It's used with `create2` * to know the lock's address before the transaction is mined. * @dev internally call `createUpgradeableLock` */ function createLock( uint _expirationDuration, address _tokenAddress, uint _keyPrice, uint _maxNumberOfKeys, string calldata _lockName, bytes12 // _salt ) external returns(address); /** * @notice Create lock (default) * This deploys a lock for a creator. It also keeps track of the deployed lock. * @param data bytes containing the call to initialize the lock template * @dev this call is passed as encoded function - for instance: * bytes memory data = abi.encodeWithSignature( * 'initialize(address,uint256,address,uint256,uint256,string)', * msg.sender, * _expirationDuration, * _tokenAddress, * _keyPrice, * _maxNumberOfKeys, * _lockName * ); * @return address of the create lock */ function createUpgradeableLock( bytes memory data ) external returns(address); /** * Create an upgradeable lock using a specific PublicLock version * @param data bytes containing the call to initialize the lock template * (refer to createUpgradeableLock for more details) * @param _lockVersion the version of the lock to use */ function createUpgradeableLockAtVersion( bytes memory data, uint16 _lockVersion ) external returns (address); /** * @notice Upgrade a lock to a specific version * @dev only available for publicLockVersion > 10 (proxyAdmin /required) * @param lockAddress the existing lock address * @param version the version number you are targeting * Likely implemented with OpenZeppelin TransparentProxy contract */ function upgradeLock( address payable lockAddress, uint16 version ) external returns(address); /** * 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; /** * @notice [DEPRECATED] Call to this function has been removed from PublicLock > v9. * @dev [DEPRECATED] Kept for backwards compatibility * 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; /** * @notice [DEPRECATED] Call to this function has been removed from PublicLock > v9. * @dev [DEPRECATED] Kept for backwards compatibility * 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 globalBaseTokenURI() external view returns(string memory); /** * @dev Redundant with globalBaseTokenURI() for backwards compatibility with v3 & v4 locks. */ function getGlobalBaseTokenURI() external view returns (string memory); // Function to read the globalTokenSymbol field. function globalTokenSymbol() external view returns(string memory); // Function to read the chainId field. function chainId() external view returns(uint); /** * @dev Redundant with globalTokenSymbol() for backwards compatibility with v3 & v4 locks. */ function getGlobalTokenSymbol() external view returns (string memory); /** * @notice Allows the owner to update configuration variables */ function configUnlock( address _udt, address _weth, uint _estimatedGasForPurchase, string calldata _symbol, string calldata _URI, uint _chainId ) external; /** * @notice Add a PublicLock template to be used for future calls to `createLock`. * @dev This is used to upgrade conytract per version number */ function addLockTemplate(address impl, uint16 version) external; // match lock templates addresses with version numbers function publicLockImpls(uint16 _version) external view; // match version numbers with lock templates addresses function publicLockVersions(address _impl) external view; // the latest existing lock template version function publicLockLatestVersion() external view; /** * @notice Upgrade the PublicLock template used for future calls to `createLock`. * @dev This will initialize the template and revokeOwnership. */ function setLockTemplate( address payable _publicLockAddress ) external; // Allows the owner to change the value tracking variables as needed. function resetTrackedValue( uint _grossNetworkProduct, uint _totalDiscountGranted ) external; function grossNetworkProduct() external view returns(uint); function totalDiscountGranted() external view returns(uint); function locks(address) external view returns(bool deployed, uint totalSales, uint yieldedDiscountTokens); // The address of the public lock template, used when `createLock` is called function publicLockAddress() external view returns(address); // Map token address to exchange contract address if the token is supported // Used for GDP calculations function uniswapOracles(address) external view returns(address); // The WETH token address, used for value calculations function weth() external view returns(address); // The UDT token address, used to mint tokens on referral function udt() external view returns(address); // The approx amount of gas required to purchase a key function estimatedGasForPurchase() external view returns(uint); /** * Helper to get the network mining basefee as introduced in EIP-1559 * @dev this helper can be wrapped in try/catch statement to avoid * revert in networks where EIP-1559 is not implemented */ function networkBaseFee() external view returns (uint); // The version number of the current Unlock implementation on this network function unlockVersion() external pure returns(uint16); /** * @notice allows the owner to set the oracle address to use for value conversions * setting the _oracleAddress to address(0) removes support for the token * @dev This will also call update to ensure at least one datapoint has been recorded. */ function setOracle( address _tokenAddress, address _oracleAddress ) external; // Initialize the Ownable contract, granting contract ownership to the specified sender function __initializeOwnable(address sender) external; /** * @dev Returns true if the caller is the current owner. */ function isOwner() external view returns(bool); /** * @dev Returns the address of the current owner. */ function owner() external view returns(address); /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() external; /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) external; }
// SPDX-License-Identifier: MIT pragma solidity >=0.5.17 <0.9.0; /** * @notice Functions to be implemented by a keyCancelHook. * @dev Lock hooks are configured by calling `setEventHooks` on the lock. */ interface ILockKeyCancelHook { /** * @notice If the lock owner has registered an implementer * then this hook is called with every key cancel. * @param operator the msg.sender issuing the cancel * @param to the account which had the key canceled * @param refund the amount sent to the `to` account (ETH or a ERC-20 token) */ function onKeyCancel( address operator, address to, uint256 refund ) external; }
// SPDX-License-Identifier: MIT pragma solidity >=0.5.17 <0.9.0; /** * @notice Functions to be implemented by a keyPurchaseHook. * @dev Lock hooks are configured by calling `setEventHooks` on the lock. */ interface ILockKeyPurchaseHook { /** * @notice Used to determine the purchase price before issueing a transaction. * This allows the hook to offer a discount on purchases. * This may revert to prevent a purchase. * @param from the msg.sender making the purchase * @param recipient the account which will be granted a key * @param referrer the account which referred this key sale * @param data arbitrary data populated by the front-end which initiated the sale * @return minKeyPrice the minimum value/price required to purchase a key with these settings * @dev the lock's address is the `msg.sender` when this function is called via * the lock's `purchasePriceFor` function */ function keyPurchasePrice( address from, address recipient, address referrer, bytes calldata data ) external view returns (uint minKeyPrice); /** * @notice If the lock owner has registered an implementer then this hook * is called with every key sold. * @param from the msg.sender making the purchase * @param recipient the account which will be granted a key * @param referrer the account which referred this key sale * @param data arbitrary data populated by the front-end which initiated the sale * @param minKeyPrice the price including any discount granted from calling this * hook's `keyPurchasePrice` function * @param pricePaid the value/pricePaid included with the purchase transaction * @dev the lock's address is the `msg.sender` when this function is called */ function onKeyPurchase( address from, address recipient, address referrer, bytes calldata data, uint minKeyPrice, uint pricePaid ) external; }
// SPDX-License-Identifier: MIT pragma solidity >=0.5.17 <0.9.0; /** * @notice Functions to be implemented by a hasValidKey Hook. * @dev Lock hooks are configured by calling `setEventHooks` on the lock. */ interface ILockValidKeyHook { /** * @notice If the lock owner has registered an implementer then this hook * is called every time balanceOf is called * @param lockAddress the address of the current lock * @param keyOwner the potential owner of the key for which we are retrieving the `balanceof` * @param expirationTimestamp the key expiration timestamp */ function hasValidKey( address lockAddress, address keyOwner, uint256 expirationTimestamp, bool isValidKey ) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity >=0.5.17 <0.9.0; /** * @notice Functions to be implemented by a tokenURIHook. * @dev Lock hooks are configured by calling `setEventHooks` on the lock. */ interface ILockTokenURIHook { /** * @notice If the lock owner has registered an implementer * then this hook is called every time `tokenURI()` is called * @param lockAddress the address of the lock * @param operator the msg.sender issuing the call * @param owner the owner of the key for which we are retrieving the `tokenUri` * @param keyId the id (tokenId) of the key (if applicable) * @param expirationTimestamp the key expiration timestamp * @return the tokenURI */ function tokenURI( address lockAddress, address operator, address owner, uint256 keyId, uint expirationTimestamp ) external view returns(string memory); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/AccessControl.sol) pragma solidity ^0.8.0; import "./IAccessControlUpgradeable.sol"; import "../utils/ContextUpgradeable.sol"; import "../utils/StringsUpgradeable.sol"; import "../utils/introspection/ERC165Upgradeable.sol"; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ``` * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ``` * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. */ abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable { function __AccessControl_init() internal onlyInitializing { __Context_init_unchained(); __ERC165_init_unchained(); __AccessControl_init_unchained(); } function __AccessControl_init_unchained() internal onlyInitializing { } struct RoleData { mapping(address => bool) members; bytes32 adminRole; } mapping(bytes32 => RoleData) private _roles; bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /** * @dev Modifier that checks that an account has a specific role. Reverts * with a standardized message including the required role. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ * * _Available since v4.1._ */ modifier onlyRole(bytes32 role) { _checkRole(role, _msgSender()); _; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControlUpgradeable).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) public view override returns (bool) { return _roles[role].members[account]; } /** * @dev Revert with a standard message if `account` is missing `role`. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ */ function _checkRole(bytes32 role, address account) internal view { if (!hasRole(role, account)) { revert( string( abi.encodePacked( "AccessControl: account ", StringsUpgradeable.toHexString(uint160(account), 20), " is missing role ", StringsUpgradeable.toHexString(uint256(role), 32) ) ) ); } } /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) public view override returns (bytes32) { return _roles[role].adminRole; } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _grantRole(role, account); } /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _revokeRole(role, account); } /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been revoked `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) public virtual override { require(account == _msgSender(), "AccessControl: can only renounce roles for self"); _revokeRole(role, account); } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. Note that unlike {grantRole}, this function doesn't perform any * checks on the calling account. * * [WARNING] * ==== * This function should only be called from the constructor when setting * up the initial roles for the system. * * Using this function in any other way is effectively circumventing the admin * system imposed by {AccessControl}. * ==== * * NOTE: This function is deprecated in favor of {_grantRole}. */ function _setupRole(bytes32 role, address account) internal virtual { _grantRole(role, account); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { bytes32 previousAdminRole = getRoleAdmin(role); _roles[role].adminRole = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /** * @dev Grants `role` to `account`. * * Internal function without access restriction. */ function _grantRole(bytes32 role, address account) internal virtual { if (!hasRole(role, account)) { _roles[role].members[account] = true; emit RoleGranted(role, account, _msgSender()); } } /** * @dev Revokes `role` from `account`. * * Internal function without access restriction. */ function _revokeRole(bytes32 role, address account) internal virtual { if (hasRole(role, account)) { _roles[role].members[account] = false; emit RoleRevoked(role, account, _msgSender()); } } uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) pragma solidity ^0.8.0; /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IAccessControlUpgradeable { /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. * * _Available since v3.1._ */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call, an admin role * bearer except when using {AccessControl-_setupRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) external view returns (bool); /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {AccessControl-_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) external view returns (bytes32); /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) external; /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) external; /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; import "../proxy/utils/Initializable.sol"; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { __Context_init_unchained(); } function __Context_init_unchained() internal onlyInitializing { } function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } uint256[50] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol) pragma solidity ^0.8.0; /** * @dev String operations. */ library StringsUpgradeable { bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { // Inspired by OraclizeAPI's implementation - MIT licence // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol if (value == 0) { return "0"; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { if (value == 0) { return "0x00"; } uint256 temp = value; uint256 length = 0; while (temp != 0) { length++; temp >>= 8; } return toHexString(value, length); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _HEX_SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20Upgradeable { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20Upgradeable.sol"; import "../../../utils/AddressUpgradeable.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20Upgradeable { using AddressUpgradeable for address; function safeTransfer( IERC20Upgradeable token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20Upgradeable token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20Upgradeable token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20Upgradeable token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20Upgradeable token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.5.17 <=0.8.7; // 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 library UnlockUtils { function strConcat( string memory _a, string memory _b, string memory _c, string memory _d ) internal pure returns (string memory _concatenatedString) { return string(abi.encodePacked(_a, _b, _c, _d)); } function uint2Str( uint _i ) internal 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; while (c != 0) { k = k-1; uint8 temp = (48 + uint8(c - c / 10 * 10)); bytes1 b1 = bytes1(temp); bstr[k] = b1; c /= 10; } return string(bstr); } function address2Str( address _addr ) internal pure returns(string memory) { bytes32 value = bytes32(uint256(uint160(_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); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol) pragma solidity ^0.8.0; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ interface IERC721ReceiverUpgradeable { /** * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} * by `operator` from `from`, this function is called. * * It must return its Solidity selector to confirm the token transfer. * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. * * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`. */ function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); }
{ "optimizer": { "enabled": true, "runs": 80 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"sendTo","type":"address"},{"indexed":false,"internalType":"uint256","name":"refund","type":"uint256"}],"name":"CancelKey","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":false,"internalType":"bool","name":"_timeAdded","type":"bool"}],"name":"ExpirationChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ExpireKey","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"refundedAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"}],"name":"GasRefunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTimestamp","type":"uint256"}],"name":"KeyExtended","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"KeyGranterAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"KeyGranterRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"_newManager","type":"address"}],"name":"KeyManagerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"updatedRecordsCount","type":"uint256"}],"name":"KeysMigrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"LockManagerAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"LockManagerRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"symbol","type":"string"}],"name":"NewLockSymbol","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldKeyPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"keyPrice","type":"uint256"},{"indexed":false,"internalType":"address","name":"oldTokenAddress","type":"address"},{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"}],"name":"PricingChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"freeTrialLength","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"refundPenaltyBasisPoints","type":"uint256"}],"name":"RefundPenaltyChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"newExpiration","type":"uint256"}],"name":"RenewKeyPurchase","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"transferFeeBasisPoints","type":"uint256"}],"name":"TransferFeeChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lockAddress","type":"address"},{"indexed":false,"internalType":"address","name":"unlockAddress","type":"address"}],"name":"UnlockCallFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":true,"internalType":"address","name":"beneficiary","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdrawal","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"KEY_GRANTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LOCK_MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"addKeyGranter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"addLockManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_approved","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"approveBeneficiary","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_keyOwner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"beneficiary","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"cancelAndRefund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"expirationDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"expireAndRefundFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"address","name":"_referrer","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"extend","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"freeTrialLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gasRefundValue","outputs":[{"internalType":"uint256","name":"_refundValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getCancelAndRefundValue","outputs":[{"internalType":"uint256","name":"refund","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_keyOwner","type":"address"}],"name":"getHasValidKey","outputs":[{"internalType":"bool","name":"isValid","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_time","type":"uint256"}],"name":"getTransferFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_recipients","type":"address[]"},{"internalType":"uint256[]","name":"_expirationTimestamps","type":"uint256[]"},{"internalType":"address[]","name":"_keyManagers","type":"address[]"}],"name":"grantKeys","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"_lockCreator","type":"address"},{"internalType":"uint256","name":"_expirationDuration","type":"uint256"},{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_keyPrice","type":"uint256"},{"internalType":"uint256","name":"_maxNumberOfKeys","type":"uint256"},{"internalType":"string","name":"_lockName","type":"string"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isKeyGranter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isLockManager","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"isValidKey","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"keyExpirationTimestampFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"keyManagerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"keyPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxKeysPerAddress","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxNumberOfKeys","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenIdFrom","type":"uint256"},{"internalType":"uint256","name":"_tokenIdTo","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"mergeKeys","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_calldata","type":"bytes"}],"name":"migrate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numberOfOwners","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"onKeyCancelHook","outputs":[{"internalType":"contract ILockKeyCancelHook","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"onKeyPurchaseHook","outputs":[{"internalType":"contract ILockKeyPurchaseHook","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"onTokenURIHook","outputs":[{"internalType":"contract ILockTokenURIHook","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"onValidKeyHook","outputs":[{"internalType":"contract ILockValidKeyHook","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"publicLockVersion","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_values","type":"uint256[]"},{"internalType":"address[]","name":"_recipients","type":"address[]"},{"internalType":"address[]","name":"_referrers","type":"address[]"},{"internalType":"address[]","name":"_keyManagers","type":"address[]"},{"internalType":"bytes[]","name":"_data","type":"bytes[]"}],"name":"purchase","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"address","name":"_referrer","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"purchasePriceFor","outputs":[{"internalType":"uint256","name":"minKeyPrice","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"refundPenaltyBasisPoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"address","name":"_referrer","type":"address"}],"name":"renewMembershipFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceLockManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_granter","type":"address"}],"name":"revokeKeyGranter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"schemaVersion","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"bool","name":"_approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_baseTokenURI","type":"string"}],"name":"setBaseTokenURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_onKeyPurchaseHook","type":"address"},{"internalType":"address","name":"_onKeyCancelHook","type":"address"},{"internalType":"address","name":"_onValidKeyHook","type":"address"},{"internalType":"address","name":"_onTokenURIHook","type":"address"}],"name":"setEventHooks","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newExpirationDuration","type":"uint256"}],"name":"setExpirationDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_refundValue","type":"uint256"}],"name":"setGasRefundValue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"address","name":"_keyManager","type":"address"}],"name":"setKeyManagerOf","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxKeys","type":"uint256"}],"name":"setMaxKeysPerAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxNumberOfKeys","type":"uint256"}],"name":"setMaxNumberOfKeys","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenIdFrom","type":"uint256"},{"internalType":"uint256","name":"_timeShared","type":"uint256"}],"name":"shareKey","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_keyOwner","type":"address"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_valueBasisPoint","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"transferFeeBasisPoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unlockProtocol","outputs":[{"internalType":"contract IUnlock","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"_beneficiary","type":"address"}],"name":"updateBeneficiary","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_keyPrice","type":"uint256"},{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"updateKeyPricing","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_lockName","type":"string"}],"name":"updateLockName","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_lockSymbol","type":"string"}],"name":"updateLockSymbol","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_freeTrialLength","type":"uint256"},{"internalType":"uint256","name":"_refundPenaltyBasisPoints","type":"uint256"}],"name":"updateRefundPenalty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateSchemaVersion","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_transferFeeBasisPoints","type":"uint256"}],"name":"updateTransferFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
608060405234801561001057600080fd5b50615ffd80620000216000396000f3fe6080604052600436106104395760003560e01c80636d8ea5b41161022b578063a98d36231161012f578063d32bfb6c116100b1578063d32bfb6c14610dbb578063d52e4a1014610ddb578063d547741f14610df1578063d813cc1914610e11578063e985e9c514610e24578063f0ba604014610e44578063f12c6b6e14610e59578063f32e8b2414610e79578063f3fef3a314610e8e578063f5766b3914610eae578063f8548e3614610ece57600080fd5b8063a98d362314610c4c578063aae4b8f714610c7f578063b11d7ec114610c9f578063b1a3b25d14610cbf578063b585a6d514610cdf578063b88d4fde14610cff578063bf4a927014610d1f578063c87b56dd14610d3f578063cb0703c614610d5f578063d1bbd49c14610d7f578063d250348514610d9b57600080fd5b80638ca2fbad116101b85780638ca2fbad14610b125780638da5cb5b14610b3457806391d1485414610b5357806392ac98a514610b7357806393fd184414610b9357806395d89b4114610baa5780639d76ea5814610bbf578063a217fddf14610be0578063a22cb46514610bf5578063a2e4cd2e14610c15578063a375cb0514610c3557600080fd5b80636d8ea5b4146109da5780636eadde43146109fa57806370a0823114610a1a57806374b6c10614610a3a578063782a4ade14610a515780637ec2a72414610a7157806381a3c94314610a925780638505fe9514610ab25780638577a6d514610ad25780638932a90d14610af257600080fd5b80632f54bf6e1161033d5780634e2ce6d3116102bf5780634e2ce6d3146108855780634f6ccce71461089c57806352b0f638146108bc57806354b249fb146108dc578063550ef3a81461090d578063558b71e91461092d578063564aa99d1461094d57806356e0d51f1461096d5780636207a8da14610984578063626485a31461099a5780636352211e146109ba57600080fd5b80632f54bf6e1461070a5780632f745c591461073a57806330176e131461075a57806331159a171461077a578063338189971461079a57806336568abe146107ad57806338af3eed146107cd57806339f46986146107ee57806342842e0e1461080e57806342966c681461082e5780634d025fed1461084e57600080fd5b806313af4035116103c657806313af4035146105a857806318160ddd146105c8578063183767da146105de578063217751bc146105f5578063231005091461061657806323b872dd14610638578063248a9ca31461065857806326e9ca07146106885780632af9162a146106a95780632d33dd5b146106c95780632f2ff15d146106ea57600080fd5b806301ffc9a714610445578063068208cd1461047a57806306fdde031461049c578063081812fc146104be578063095ea7b3146104eb578063097ba3331461050b5780630aaffd2a146105395780630f15023b1461055957806310e569731461057a57806311a4c03a1461059157600080fd5b3661044057005b600080fd5b34801561045157600080fd5b50610465610460366004615701565b610eee565b60405190151581526020015b60405180910390f35b34801561048657600080fd5b5061049a6104953660046158bd565b610eff565b005b3480156104a857600080fd5b506104b1610fab565b6040516104719190615b1e565b3480156104ca57600080fd5b506104de6104d93660046156c3565b61103a565b6040516104719190615a20565b3480156104f757600080fd5b5061049a6105063660046154f4565b611062565b34801561051757600080fd5b5061052b6105263660046153b9565b6110fd565b604051908152602001610471565b34801561054557600080fd5b5061049a610554366004615282565b6111ad565b34801561056557600080fd5b50610c83546104de906001600160a01b031681565b34801561058657600080fd5b5061052b610c855481565b34801561059d57600080fd5b5061052b610c845481565b3480156105b457600080fd5b5061049a6105c3366004615282565b6111fe565b3480156105d457600080fd5b50610c875461052b565b3480156105ea57600080fd5b5061052b6124075481565b34801561060157600080fd5b50610c8a546104de906001600160a01b031681565b34801561062257600080fd5b5061052b600080516020615f6883398151915281565b34801561064457600080fd5b5061049a61065336600461541a565b6112bc565b34801561066457600080fd5b5061052b6106733660046156c3565b60009081526097602052604090206001015490565b34801561069457600080fd5b50610c8b546104de906001600160a01b031681565b3480156106b557600080fd5b5061049a6106c4366004615282565b611460565b3480156106d557600080fd5b50610c89546104de906001600160a01b031681565b3480156106f657600080fd5b5061049a6107053660046156dc565b6114b7565b34801561071657600080fd5b50610465610725366004615282565b612bda546001600160a01b0390811691161490565b34801561074657600080fd5b5061052b6107553660046154f4565b6114dd565b34801561076657600080fd5b5061049a61077536600461573b565b61155c565b34801561078657600080fd5b5061049a6107953660046156c3565b611571565b61049a6107a83660046155ee565b6115b9565b3480156107b957600080fd5b5061049a6107c83660046156dc565b611a73565b3480156107d957600080fd5b50610c88546104de906001600160a01b031681565b3480156107fa57600080fd5b5061049a610809366004615832565b611af1565b34801561081a57600080fd5b5061049a61082936600461541a565b611b3f565b34801561083a57600080fd5b5061049a6108493660046156c3565b611b5a565b34801561085a57600080fd5b506104de6108693660046156c3565b611078602052600090815260409020546001600160a01b031681565b34801561089157600080fd5b5061052b610c8d5481565b3480156108a857600080fd5b5061052b6108b73660046156c3565b611bab565b3480156108c857600080fd5b506104656108d7366004615282565b611bf2565b3480156108e857600080fd5b5061052b6108f73660046156c3565b600090815261107b602052604090206001015490565b34801561091957600080fd5b5061049a61092836600461573b565b611c0c565b34801561093957600080fd5b5061049a610948366004615832565b611c21565b34801561095957600080fd5b5061049a610968366004615282565b611c45565b34801561097957600080fd5b5061052b6127f05481565b34801561099057600080fd5b5061201e5461052b565b3480156109a657600080fd5b5061049a6109b53660046156c3565b611c9c565b3480156109c657600080fd5b506104de6109d53660046156c3565b611caa565b3480156109e657600080fd5b506104656109f5366004615282565b611cc6565b348015610a0657600080fd5b5061049a610a1536600461529f565b611dc3565b348015610a2657600080fd5b5061052b610a35366004615282565b611f27565b348015610a4657600080fd5b5061052b610c865481565b348015610a5d57600080fd5b5061049a610a6c36600461573b565b611f6c565b348015610a7d57600080fd5b50610c8c546104de906001600160a01b031681565b348015610a9e57600080fd5b5061049a610aad366004615555565b611fb3565b348015610abe57600080fd5b5061049a610acd3660046156dc565b612105565b348015610ade57600080fd5b5061049a610aed3660046156c3565b6123db565b348015610afe57600080fd5b5061049a610b0d36600461573b565b61241c565b348015610b1e57600080fd5b5061052b600080516020615fa883398151915281565b348015610b4057600080fd5b50612bda546001600160a01b03166104de565b348015610b5f57600080fd5b50610465610b6e3660046156dc565b61263f565b348015610b7f57600080fd5b5061052b610b8e3660046156c3565b61266a565b348015610b9f57600080fd5b5061052b6110775481565b348015610bb657600080fd5b506104b161275a565b348015610bcb57600080fd5b506104b1546104de906001600160a01b031681565b348015610bec57600080fd5b5061052b600081565b348015610c0157600080fd5b5061049a610c103660046154c6565b612893565b348015610c2157600080fd5b5061049a610c303660046156dc565b61294d565b348015610c4157600080fd5b5061052b6127f15481565b348015610c5857600080fd5b50610465610c673660046156c3565b600090815261107b6020526040902060010154421090565b348015610c8b57600080fd5b50610465610c9a366004615282565b6129db565b348015610cab57600080fd5b5061049a610cba3660046156dc565b6129f5565b348015610ccb57600080fd5b5061052b610cda366004615832565b612a6d565b348015610ceb57600080fd5b50610465610cfa3660046154f4565b612add565b348015610d0b57600080fd5b5061049a610d1a36600461545b565b612b6e565b348015610d2b57600080fd5b5061049a610d3a3660046156c3565b612ba1565b348015610d4b57600080fd5b506104b1610d5a3660046156c3565b612bf8565b348015610d6b57600080fd5b5061049a610d7a36600461535d565b612ea7565b348015610d8b57600080fd5b50604051600a8152602001610471565b348015610da757600080fd5b5061049a610db6366004615282565b61309e565b348015610dc757600080fd5b5061049a610dd63660046156c3565b6130f5565b348015610de757600080fd5b50610c8e5461052b565b348015610dfd57600080fd5b5061049a610e0c3660046156dc565b613127565b61049a610e1f366004615854565b61314d565b348015610e3057600080fd5b50610465610e3f366004615324565b6132a8565b348015610e5057600080fd5b5061049a6132d7565b348015610e6557600080fd5b5061049a610e74366004615520565b61331c565b348015610e8557600080fd5b5061049a613518565b348015610e9a57600080fd5b5061049a610ea93660046154f4565b613528565b348015610eba57600080fd5b5061049a610ec93660046156c3565b613687565b348015610eda57600080fd5b50610465610ee936600461580b565b613695565b6000610ef9826136f7565b92915050565b610f0883613702565b610f118361374f565b610f1a836137a0565b610f2382613702565b8042610f3f85600090815261107b602052604090206001015490565b610f499190615e42565b1015610f8e5760405162461bcd60e51b815260206004820152600f60248201526e4e4f545f454e4f5547485f54494d4560881b60448201526064015b60405180910390fd5b610f9a83826000613841565b610fa682826001613841565b505050565b6114638054610fb990615e9c565b80601f0160208091040260200160405190810160405280929190818152602001828054610fe590615e9c565b80156110325780601f1061100757610100808354040283529160200191611032565b820191906000526020600020905b81548152906001019060200180831161101557829003601f168201915b505050505081565b600061104582613702565b50600090815261107960205260409020546001600160a01b031690565b61106b816137a0565b336001600160a01b03831614156110945760405162461bcd60e51b8152600401610f8590615c39565b60008181526110796020908152604080832080546001600160a01b0319166001600160a01b03878116918217909255611076909352818420549151859492909116917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a45050565b610c89546000906001600160a01b0316156111a057610c895460405163221c1fd160e01b81526001600160a01b039091169063221c1fd190611149903390889088908890600401615aa3565b60206040518083038186803b15801561116157600080fd5b505afa158015611175573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061119991906157f2565b90506111a6565b50610c85545b9392505050565b6111b56138f7565b6001600160a01b0381166111db5760405162461bcd60e51b8152600401610f8590615b61565b610c8880546001600160a01b0319166001600160a01b0392909216919091179055565b611206613964565b6001600160a01b0381166112595760405162461bcd60e51b815260206004820152601a6024820152794f574e45525f43414e545f42455f414444524553535f5a45524f60301b6044820152606401610f85565b612bda80546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091015b60405180910390a15050565b6112c58161374f565b6112ce816137a0565b826001600160a01b03166112e182611caa565b6001600160a01b0316146113375760405162461bcd60e51b815260206004820152601c60248201527f5452414e534645525f46524f4d3a204e4f545f4b45595f4f574e4552000000006044820152606401610f85565b612710612407541061135b5760405162461bcd60e51b8152600401610f8590615b31565b6001600160a01b0382166113815760405162461bcd60e51b8152600401610f8590615b61565b816001600160a01b0316836001600160a01b031614156113b35760405162461bcd60e51b8152600401610f8590615be8565b6113c9816113c2836000612a6d565b6000613841565b600081815261107b602052604090206113e183611f27565b6113fc5761107780549060006113f683615ed1565b91905055505b611405826139bc565b61140f8284613ac3565b61141a826000613b82565b61142382613c0f565b600082815261202060205260408082208290555183916001600160a01b038087169290881691600080516020615f8883398151915291a450505050565b611468613964565b611480600080516020615f6883398151915282613127565b6040516001600160a01b038216907f766f6199fea59554b9ff688e413302b9200f85d74811c053c12d945ac6d8dd7a90600090a250565b6000828152609760205260409020600101546114d38133613c4c565b610fa68383613cb0565b60006114e883611f27565b82106115325760405162461bcd60e51b81526020600482015260196024820152784f574e45525f494e4445585f4f55545f4f465f424f554e445360381b6044820152606401610f85565b506001600160a01b0391909116600090815261107c60209081526040808320938352929052205490565b611564613964565b610fa6611465838361503a565b611579613964565b806115b35760405162461bcd60e51b815260206004820152600a6024820152694e554c4c5f56414c554560b01b6044820152606401610f85565b610c8e55565b6115c1613d36565b610c8754610c8654116115e65760405162461bcd60e51b8152600401610f8590615c12565b83518551146116325760405162461bcd60e51b81526020600482015260186024820152770929cac82989288bea48a8c8aa4a48aa4a6be988a9c8ea8960431b6044820152606401610f85565b82518551146116835760405162461bcd60e51b815260206004820152601b60248201527f494e56414c49445f4b45595f4d414e41474552535f4c454e47544800000000006044820152606401610f85565b60008060005b875181101561199e5760008882815181106116a6576116a6615f02565b6020026020010151905060006001600160a01b0316816001600160a01b031614156116e35760405162461bcd60e51b8152600401610f8590615b61565b600019610c8454141561171d576117168188848151811061170657611706615f02565b6020026020010151600019613d7e565b9250611752565b61174f8188848151811061173357611733615f02565b6020026020010151610c84544261174a9190615da1565b613d7e565b92505b60006117d0828a858151811061176a5761176a615f02565b602002602001015189898781811061178457611784615f02565b90506020028101906117969190615ce1565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506110fd92505050565b90506117dc8186615da1565b600085815261201f60209081526040808320859055610c84546120208352818420556104b18054612021909352922080546001600160a01b0319166001600160a01b0392831617905590549196501615611869578a838151811061184257611842615f02565b60200260200101518111156118695760405162461bcd60e51b8152600401610f8590615b8a565b61188c818a858151811061187f5761187f615f02565b6020026020010151613e2e565b6104b1546000906001600160a01b0316156118c0578b84815181106118b3576118b3615f02565b60200260200101516118c2565b345b610c89549091506001600160a01b03161561198857610c89548a516001600160a01b0390911690639849965790339086908e908990811061190557611905615f02565b60200260200101518c8c8a81811061191f5761191f615f02565b90506020028101906119319190615ce1565b88886040518863ffffffff1660e01b81526004016119559796959493929190615a58565b600060405180830381600087803b15801561196f57600080fd5b505af1158015611983573d6000803e3d6000fd5b505050505b505050808061199690615ed1565b915050611689565b506104b1546001600160a01b031615611a41576104b1546040516323b872dd60e01b81526001600160a01b039091169081906323b872dd906119e890339030908890600401615a34565b602060405180830381600087803b158015611a0257600080fd5b505af1158015611a16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a3a91906156a6565b5050611a61565b34821115611a615760405162461bcd60e51b8152600401610f8590615bbc565b611a69613f33565b5050505050505050565b6001600160a01b0381163314611ae35760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610f85565b611aed82826140bb565b5050565b611af9613964565b60408051838152602081018390527fd6867bc538320e67d7bdc35860c27c08486eb490b4fd9b820fff18fb28381d3c910160405180910390a16127f1919091556127f055565b610fa683838360405180602001604052806000815250612b6e565b611b6381613702565b611b6c816137a0565b600081815261107660205260408082205490518392916001600160a01b031690600080516020615f88833981519152908390a4611ba881614122565b50565b6000610c87548210611bee5760405162461bcd60e51b815260206004820152600c60248201526b4f55545f4f465f52414e474560a01b6044820152606401610f85565b5090565b6000610ef9600080516020615f688339815191528361263f565b611c14613964565b610fa6611463838361503a565b611c2a82613702565b611c338261374f565b611c3b613964565b611aed828261415c565b611c4d613964565b611c65600080516020615f68833981519152826114b7565b6040516001600160a01b038216907f684f8a71407db0c334454ebe9c9b288549317893a20b10dc779ec5c118dcd12190600090a250565b611ca4613964565b610c8455565b600090815261107660205260409020546001600160a01b031690565b600080611cd283611f27565b90508015611d155760005b81811015611d1357611cf2610c6785836114dd565b15611d01575060019392505050565b80611d0b81615ed1565b915050611cdd565b505b610c8b546001600160a01b031615611dbd57610c8b546040516370b6638f60e11b81523060048201526001600160a01b0385811660248301526000604483015284151560648301529091169063e16cc71e9060840160206040518083038186803b158015611d8257600080fd5b505afa158015611d96573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dba91906156a6565b91505b50919050565b600054610100900460ff16611dde5760005460ff1615611de2565b303b155b611e455760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610f85565b600054610100900460ff16158015611e67576000805461ffff19166101011790555b611e7086614263565b610c838054336001600160a01b031991821617909155610c8880549091166001600160a01b038a16179055610c84879055610c85859055610c86849055600a610c8d556001610c8e55611ec3838361428f565b611ecb6142b5565b611ed76103e86127f055565b611ee0886142c5565b612bda80546001600160a01b0319166001600160a01b038a16179055611f0c6380ac58cd60e01b61434d565b8015611a69576000805461ff00191690555050505050505050565b60006001600160a01b038216611f4f5760405162461bcd60e51b8152600401610f8590615b61565b506001600160a01b0316600090815261107e602052604090205490565b611f74613964565b611f81611464838361503a565b507f8868e22e84ebf32da89b2ebcb0ac642816304ea3863b257f240df9098719cb9782826040516112b0929190615b0a565b611fbb613d36565b611fc433611bf2565b80611fd35750611fd3336129db565b61201f5760405162461bcd60e51b815260206004820181905260248201527f4f4e4c595f4c4f434b5f4d414e414745525f4f525f4b45595f4752414e5445526044820152606401610f85565b60005b858110156120fc57600087878381811061203e5761203e615f02565b90506020020160208101906120539190615282565b6001600160a01b0316141561207a5760405162461bcd60e51b8152600401610f8590615b61565b6120e987878381811061208f5761208f615f02565b90506020020160208101906120a49190615282565b8484848181106120b6576120b6615f02565b90506020020160208101906120cb9190615282565b8787858181106120dd576120dd615f02565b90506020020135613d7e565b50806120f481615ed1565b915050612022565b50505050505050565b61210d613d36565b61211682613702565b60008281526120206020526040902054600019141561216b5760405162461bcd60e51b81526020600482015260116024820152704e4f4e5f4558504952494e475f4c4f434b60781b6044820152606401610f85565b6104b1546001600160a01b03166121b55760405162461bcd60e51b815260206004820152600e60248201526d4e4f4e5f45524332305f4c4f434b60901b6044820152606401610f85565b60006121d96121c384611caa565b83604051806020016040528060008152506110fd565b600084815261201f6020526040902054909150811461222a5760405162461bcd60e51b815260206004820152600d60248201526c14149250d157d0d2105391d151609a1b6044820152606401610f85565b610c8454600084815261202060205260409020541461227e5760405162461bcd60e51b815260206004820152601060248201526f11155490551253d397d0d2105391d15160821b6044820152606401610f85565b6104b154600084815261202160205260409020546001600160a01b039081169116146122dc5760405162461bcd60e51b815260206004820152600d60248201526c1513d2d15397d0d2105391d151609a1b6044820152606401610f85565b600083815261107b602052604090206001015442101561232a5760405162461bcd60e51b81526020600482015260096024820152684e4f545f524541445960b81b6044820152606401610f85565b612333836143cc565b5061233e8183613e2e565b6104b1546001600160a01b0316806323b872dd61235a86611caa565b30856040518463ffffffff1660e01b815260040161237a93929190615a34565b602060405180830381600087803b15801561239457600080fd5b505af11580156123a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123cc91906156a6565b506123d5613f33565b50505050565b6123e3613964565b6040518181527f0496ed1e61eb69727f9659a8e859288db4758ffb1f744d1c1424634f90a257f49060200160405180910390a161240755565b6124286001600a615e1f565b61ffff16610c8d54148061243d5750610c8d54155b6124865760405162461bcd60e51b815260206004820152601a60248201527914d0d211535057d5915494d253d397d393d517d0d3d4949150d560321b6044820152606401610f85565b610c8e54612495576001610c8e555b6000808060006124a5610c875490565b610c83549091506001600160a01b03163314156124c557606491506124d7565b6124d185870187615832565b90945091505b808211156124e3578091505b835b6124ef8386615da1565b8110156125f5576000612503826001615da1565b600081815261107660209081526040808320546001600160a01b031680845261107583529281902081518083019092528054808352600190910154928201929092529293509091901580159061255c5750602081015115155b156125df5760408051808201909152815181526020808301519082015261107b6000612589876001615da1565b81526020808201929092526040908101600090812084518155938301516001948501556001600160a01b03861681526110759092528120818155909101556125d18383613ac3565b866125db81615ed1565b9750505b50505080806125ed90615ed1565b9150506124e5565b5080821061260457600a610c8d555b6040518381527f4e075b7dcd463fe1b504dad3ccd1c4f87011a70c1e50f1805183df5d559834cd9060200160405180910390a1505050505050565b60009182526097602090815260408084206001600160a01b0393909316845291905290205460ff1690565b60006126758261374f565b600019610c8454141561268b575050610c855490565b600082815261107b60205260408120600101546126a9904290615e42565b9050610c84546127f154826126be9190615da1565b106126ce57610c855491506126ee565b610c845481610c85546126e19190615e00565b6126eb9190615dde565b91505b6127f154158061270d5750610c84546127f15461270b9083615da1565b105b15611dbd5760006127106127f054610c85546127299190615e00565b6127339190615dde565b90508083111561274e576127478184615e42565b9250612753565b600092505b5050919050565b6060611464805461276a90615e9c565b1515905061280257610c8360009054906101000a90046001600160a01b03166001600160a01b031663cec410526040518163ffffffff1660e01b815260040160006040518083038186803b1580156127c157600080fd5b505afa1580156127d5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526127fd919081019061577c565b905090565b611464805461281090615e9c565b80601f016020809104026020016040519081016040528092919081815260200182805461283c90615e9c565b80156128895780601f1061285e57610100808354040283529160200191612889565b820191906000526020600020905b81548152906001019060200180831161286c57829003601f168201915b5050505050905090565b6001600160a01b0382163314156128bc5760405162461bcd60e51b8152600401610f8590615c39565b61271061240754106128e05760405162461bcd60e51b8152600401610f8590615b31565b33600081815261107a602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b612955613964565b61295e816144cd565b610c8580546104b18054928590556001600160a01b031983166001600160a01b0385811691821790925560408051848152602081018890529290941693820184905260608201529091907f3615065ccf48367ac483ac86701248e2e5ff55bdd9be845007d34a3b68d719d49060800160405180910390a150505050565b6000610ef9600080516020615fa88339815191528361263f565b6129fe82613702565b612a08823361458f565b80612a175750612a17336129db565b612a635760405162461bcd60e51b815260206004820152601f60248201527f554e415554484f52495a45445f4b45595f4d414e414745525f555044415445006044820152606401610f85565b611aed8282613b82565b6000612a7883613702565b600083815261107b602052604090206001015442811015612a9d576000915050610ef9565b600083612ab557612aae4283615e42565b9050612ab8565b50825b6127106124075482612aca9190615e00565b612ad49190615dde565b92505050610ef9565b6000612ae76138f7565b6104b15460405163095ea7b360e01b81526001600160a01b038581166004830152602482018590529091169063095ea7b390604401602060405180830381600087803b158015612b3657600080fd5b505af1158015612b4a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111a691906156a6565b612b798484846112bc565b612b8584848484614607565b6123d55760405162461bcd60e51b8152600401610f8590615caa565b612ba9613964565b610c8754811015612bf25760405162461bcd60e51b8152602060048201526013602482015272534d414c4c45525f5448414e5f535550504c5960681b6044820152606401610f85565b610c8655565b606080806000612c07306146c6565b905060608515612c2157612c1a866148b1565b9250612c34565b6040518060200160405280600081525092505b610c8c546001600160a01b031615612d1a57600086815261107b6020526040902060010154610c8c546001600160a01b031663988b93ad3033612c768b611caa565b60405160e085901b6001600160e01b03191681526001600160a01b03938416600482015291831660248301529091166044820152606481018a90526084810184905260a40160006040518083038186803b158015612cd357600080fd5b505afa158015612ce7573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612d0f919081019061577c565b979650505050505050565b6114658054612d2890615e9c565b15159050612dde57610c8360009054906101000a90046001600160a01b03166001600160a01b031663a998e9fb6040518163ffffffff1660e01b815260040160006040518083038186803b158015612d7f57600080fd5b505afa158015612d93573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612dbb919081019061577c565b9350604051806040016040528060018152602001602f60f81b8152509050612e91565b6114658054612dec90615e9c565b80601f0160208091040260200160405190810160405280929190818152602001828054612e1890615e9c565b8015612e655780601f10612e3a57610100808354040283529160200191612e65565b820191906000526020600020905b815481529060010190602001808311612e4857829003601f168201915b505050505093506040518060200160405280600081525090506040518060200160405280600081525091505b612e9d848383866149dd565b9695505050505050565b612eaf613964565b6001600160a01b0384161580612ece57506001600160a01b0384163b15155b612f155760405162461bcd60e51b8152602060048201526018602482015277494e56414c49445f4f4e5f4b45595f534f4c445f484f4f4b60401b6044820152606401610f85565b6001600160a01b0383161580612f3457506001600160a01b0383163b15155b612f7d5760405162461bcd60e51b815260206004820152601a602482015279494e56414c49445f4f4e5f4b45595f43414e43454c5f484f4f4b60301b6044820152606401610f85565b6001600160a01b0382161580612f9c57506001600160a01b0382163b15155b612fe45760405162461bcd60e51b8152602060048201526019602482015278494e56414c49445f4f4e5f56414c49445f4b45595f484f4f4b60381b6044820152606401610f85565b6001600160a01b038116158061300357506001600160a01b0381163b15155b61304b5760405162461bcd60e51b8152602060048201526019602482015278494e56414c49445f4f4e5f544f4b454e5f5552495f484f4f4b60381b6044820152606401610f85565b610c8980546001600160a01b039586166001600160a01b031991821617909155610c8a805494861694821694909417909355610c8c8054918516918416919091179055610c8b8054919093169116179055565b6130a6613964565b6130be600080516020615fa8833981519152826114b7565b6040516001600160a01b038216907f91d5c045d5bd98bf59a379b259ebca05b93bf79af1845fdf87e3172385d4c7f790600090a250565b6130fe81613702565b6131078161374f565b613110816137a0565b600061311b8261266a565b9050611aed828261415c565b6000828152609760205260409020600101546131438133613c4c565b610fa683836140bb565b613155613d36565b61315e84613702565b613167846143cc565b5060006131b361317686611caa565b8585858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506110fd92505050565b6104b1549091506001600160a01b03161561327857858111156131e85760405162461bcd60e51b8152600401610f8590615b8a565b6104b1546040516323b872dd60e01b81526001600160a01b039091169081906323b872dd9061321f90339030908790600401615a34565b602060405180830381600087803b15801561323957600080fd5b505af115801561324d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061327191906156a6565b5050613298565b348111156132985760405162461bcd60e51b8152600401610f8590615bbc565b6132a0613f33565b505050505050565b6001600160a01b03918216600090815261107a6020908152604080832093909416825291909152205460ff1690565b6132ef600080516020615fa883398151915233611a73565b60405133907f42885193b8178d25fca25a38e6fcc93918501e91be06d85e0c8afb3bad95238090600090a2565b613324613d36565b610c8754610c8654116133495760405162461bcd60e51b8152600401610f8590615c12565b613352826137a0565b61335b8261374f565b612710612407541061337f5760405162461bcd60e51b8152600401610f8590615b31565b6001600160a01b0383166133a55760405162461bcd60e51b8152600401610f8590615b61565b600082815261107660205260409020546001600160a01b039081169084168114156133e25760405162461bcd60e51b8152600401610f8590615be8565b600083815261107b60205260408120600101548190613402904290615e42565b905060006134108686612a6d565b9050600061341e8287615da1565b90508281101561343c5785935061343787826000613841565b613494565b6134468784612a6d565b91506134528284615e42565b600088815261107b6020526040808220426001909101555191955088917f59f2fe866dd27a1c2d34115520888c3150365cbc931aab97fa88c4b9ab40b7959190a25b60006134a5898261174a8842615da1565b905080896001600160a01b0316876001600160a01b0316600080516020615f8883398151915260405160405180910390a46134f1868a8360405180602001604052806000815250614607565b61350d5760405162461bcd60e51b8152600401610f8590615caa565b505050505050505050565b613520613964565b600a610c8d55565b6135306138f7565b60006001600160a01b0383166135475750476135c6565b6040516370a0823160e01b81526001600160a01b038416906370a0823190613573903090600401615a20565b60206040518083038186803b15801561358b57600080fd5b505afa15801561359f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135c391906157f2565b90505b60008215806135d457508183115b15613623576000821161361c5760405162461bcd60e51b815260206004820152601060248201526f4e4f545f454e4f5547485f46554e445360801b6044820152606401610f85565b5080613626565b50815b610c88546040518281526001600160a01b039182169186169033907f342e7ff505a8a0364cd0dc2ff195c315e43bce86b204846ecd36913e117b109e9060200160405180910390a4610c88546123d59085906001600160a01b031683614a0f565b61368f613964565b61201e55565b60006136a08461374f565b600061271083426136c188600090815261107b602052604090206001015490565b6136cb9190615e42565b6136d59190615e00565b6136df9190615dde565b90506136ec84868361331c565b506001949350505050565b6000610ef982614a4b565b600081815261107b6020526040902060010154611ba85760405162461bcd60e51b815260206004820152600b60248201526a4e4f5f535543485f4b455960a81b6044820152606401610f85565b600081815261107b60205260409020600101544210611ba85760405162461bcd60e51b815260206004820152600d60248201526c12d15657d393d517d590531251609a1b6044820152606401610f85565b6137aa813361458f565b806137cc5750600081815261107960205260409020546001600160a01b031633145b806137f55750600081815261107660205260409020546137f5906001600160a01b0316336132a8565b611ba85760405162461bcd60e51b815260206004820152601c60248201527f4f4e4c595f4b45595f4d414e414745525f4f525f415050524f564544000000006044820152606401610f85565b61384a83613702565b600083815261107b60205260409020600101548115613897574281111561388d576138758382615da1565b600085815261107b60205260409020600101556138b5565b6138758342615da1565b6138a18382615e42565b600085815261107b60205260409020600101555b60408051848152831515602082015285917fe9408df99703ae33a9d01185bcad328ea8683fb1f920da9c30959c192f21b5b3910160405180910390a250505050565b613900336129db565b806139165750610c88546001600160a01b031633145b6139625760405162461bcd60e51b815260206004820181905260248201527f4f4e4c595f4c4f434b5f4d414e414745525f4f525f42454e45464943494152596044820152606401610f85565b565b61397c600080516020615fa88339815191523361263f565b6139625760405162461bcd60e51b815260206004820152601160248201527027a7262cafa627a1a5afa6a0a720a3a2a960791b6044820152606401610f85565b600081815261107660205260408120546001600160a01b03169060016139e183611f27565b6139eb9190615e42565b600084815261107d6020526040902054909150808214613a41576001600160a01b038316600090815261107c60209081526040808320858452825280832054848452818420819055835261107d90915290208190555b6001600160a01b038316600090815261107c60209081526040808320858452909152812055613a6f83611f27565b60011415613a8e576110778054906000613a8883615e85565b91905055505b6001600160a01b038316600090815261107e60205260408120805460019290613ab8908490615e42565b909155505050505050565b6000613ace82611f27565b9050610c8e548110613b0d5760405162461bcd60e51b81526020600482015260086024820152674d41585f4b45595360c01b6044820152606401610f85565b600083815261107d602090815260408083208490556001600160a01b03851680845261107c83528184208585528352818420879055868452611076835281842080546001600160a01b03191682179055835261107e9091528120805460019290613b78908490615da1565b9091555050505050565b600082815261107860205260409020546001600160a01b03828116911614611aed5760008281526110786020526040902080546001600160a01b0319166001600160a01b038316179055613bd582613c0f565b6040516001600160a01b0382169083907f9d2895c45a420624de863a2f437b022d879f457bf7a829044055a10c5a6fd5e390600090a35050565b600081815261107960205260409020546001600160a01b031615611ba85760009081526110796020526040902080546001600160a01b0319169055565b613c56828261263f565b611aed57613c6e816001600160a01b03166014614a56565b613c79836020614a56565b604051602001613c8a9291906159b1565b60408051601f198184030181529082905262461bcd60e51b8252610f8591600401615b1e565b613cba828261263f565b611aed5760008281526097602090815260408083206001600160a01b03851684529091529020805460ff19166001179055613cf23390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b610c8d54600a146139625760405162461bcd60e51b8152602060048201526012602482015271135251d490551253d397d49154555254915160721b6044820152606401610f85565b610c87805460009182613d9083615ed1565b9091555050610c87546040805180820182528281526020808201868152600085815261107b9092529290209051815590516001909101559050613dd284611f27565b613ded576110778054906000613de783615ed1565b91905055505b613df78185613ac3565b613e018184613b82565b60405181906001600160a01b03861690600090600080516020615f88833981519152908290a49392505050565b610c83546001600160a01b03163b15613ef557610c835460405163939d9f1f60e01b8152600481018490526001600160a01b0383811660248301529091169063939d9f1f90620493e090604401600060405180830381600088803b158015613e9557600080fd5b5087f193505050508015613ea7575060015b611aed57610c835460405130917f6b18946261693dfd6c760d986b28ad2238b5b0267f8e5b6bc40a2f998e2f20ac91613ee9916001600160a01b031690615a20565b60405180910390a25050565b610c835460405130917f6b18946261693dfd6c760d986b28ad2238b5b0267f8e5b6bc40a2f998e2f20ac91613ee9916001600160a01b031690615a20565b61201e5415613962576104b1546001600160a01b031615613fe1576104b15461201e546040516323b872dd60e01b81526001600160a01b039092169182916323b872dd91613f88913091339190600401615a34565b602060405180830381600087803b158015613fa257600080fd5b505af1158015613fb6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613fda91906156a6565b505061406d565b61201e5460405160009133918381818185875af1925050503d8060008114614025576040519150601f19603f3d011682016040523d82523d6000602084013e61402a565b606091505b505090508061406b5760405162461bcd60e51b815260206004820152600d60248201526c14915195539117d19052531151609a1b6044820152606401610f85565b505b61201e546104b154604080519283526001600160a01b03909116602083015233917f522a883b471164223f18b50f326da8671372b64b4792eac0e63d447e714c3e3b910160405180910390a2565b6140c5828261263f565b15611aed5760008281526097602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b61412b816139bc565b600090815261107b6020908152604080832042600190910155611076909152902080546001600160a01b0319169055565b600061416783611caa565b905061417283614122565b336001600160a01b0316816001600160a01b0316847f0a7068a9989857441c039a14a42b67ed71dd1fcfe5a9b17cc87b252e47bce528856040516141b891815260200190565b60405180910390a481156141de576104b1546141de906001600160a01b03168284614a0f565b60008381526120206020526040812055610c8a546001600160a01b031615610fa657610c8a5460405163b499b6c560e01b81526001600160a01b039091169063b499b6c59061423590339085908790600401615a34565b600060405180830381600087803b15801561424f57600080fd5b505af11580156120fc573d6000803e3d6000fd5b61426c816144cd565b6104b180546001600160a01b0319166001600160a01b0392909216919091179055565b614297614bf1565b6142a4611463838361503a565b50611aed635b5e139f60e01b61434d565b61396263780e9d6360e01b61434d565b6142dd600080516020615fa883398151915280614c28565b614303600080516020615f68833981519152600080516020615fa8833981519152614c28565b61430c816129db565b61432857614328600080516020615fa883398151915282614c73565b61433181611bf2565b611ba857611ba8600080516020615f6883398151915282614c73565b6001600160e01b031980821614156143a75760405162461bcd60e51b815260206004820152601c60248201527f4552433136353a20696e76616c696420696e74657266616365206964000000006044820152606401610f85565b6001600160e01b0319166000908152606560205260409020805460ff19166001179055565b600081815261107b60205260408120600101546000198114156144315760405162461bcd60e51b815260206004820152601c60248201527f43414e545f455854454e445f4e4f4e5f4558504952494e475f4b4559000000006044820152606401610f85565b600019610c84541415614448576000199150614476565b4281111561446557610c845461445e9082615da1565b9150614476565b610c84546144739042615da1565b91505b600083815261107b6020526040908190206001018390555183907f3ca112768ff7861e008ace1c11570c52e404c043e585545b5957a1e20961dde3906144bf9085815260200190565b60405180910390a250919050565b6001600160a01b038116158061455357506000816001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561451957600080fd5b505afa15801561452d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061455191906157f2565b115b611ba85760405162461bcd60e51b815260206004820152600d60248201526c24a72b20a624a22faa27a5a2a760991b6044820152606401610f85565b600082815261107860205260408120546001600160a01b03838116911614806145f25750600083815261107860205260409020546001600160a01b03161580156145f25750816001600160a01b03166145e784611caa565b6001600160a01b0316145b156145ff57506001610ef9565b506000610ef9565b60006001600160a01b0384163b614620575060016146be565b604051630a85bd0160e11b81526000906001600160a01b0386169063150b7a02906146559033908a9089908990600401615ad7565b602060405180830381600087803b15801561466f57600080fd5b505af1158015614683573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146a7919061571e565b6001600160e01b031916630a85bd0160e11b149150505b949350505050565b604080518082018252601081526f181899199a1a9b1b9c1cb0b131b232b360811b60208201528151602a80825260608281019094526001600160a01b0385169291600091602082018180368337019050509050600360fc1b8160008151811061473157614731615f02565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061476057614760615f02565b60200101906001600160f81b031916908160001a90535060005b60148110156148a8578260048561479284600c615da1565b602081106147a2576147a2615f02565b1a60f81b6001600160f81b031916901c60f81c60ff16815181106147c8576147c8615f02565b01602001516001600160f81b031916826147e3836002615e00565b6147ee906002615da1565b815181106147fe576147fe615f02565b60200101906001600160f81b031916908160001a905350828461482283600c615da1565b6020811061483257614832615f02565b825191901a600f1690811061484957614849615f02565b01602001516001600160f81b03191682614864836002615e00565b61486f906003615da1565b8151811061487f5761487f615f02565b60200101906001600160f81b031916908160001a905350806148a081615ed1565b91505061477a565b50949350505050565b606081806148d85750506040805180820190915260018152600360fc1b6020820152919050565b8260005b811561490257806148ec81615ed1565b91506148fb9050600a83615dde565b91506148dc565b6000816001600160401b0381111561491c5761491c615f18565b6040519080825280601f01601f191660200182016040528015614946576020820181803683370190505b509050815b84156149d35761495c600182615e42565b9050600061496b600a87615dde565b61497690600a615e00565b6149809087615e42565b61498b906030615db9565b905060008160f81b9050808484815181106149a8576149a8615f02565b60200101906001600160f81b031916908160001a9053506149ca600a88615dde565b9650505061494b565b5095945050505050565b6060848484846040516020016149f6949392919061595a565b6040516020818303038152906040529050949350505050565b8015610fa6576001600160a01b038316614a3657610fa66001600160a01b03831682614c7d565b826123d56001600160a01b0382168484614d93565b6000610ef982614de5565b60606000614a65836002615e00565b614a70906002615da1565b6001600160401b03811115614a8757614a87615f18565b6040519080825280601f01601f191660200182016040528015614ab1576020820181803683370190505b509050600360fc1b81600081518110614acc57614acc615f02565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110614afb57614afb615f02565b60200101906001600160f81b031916908160001a9053506000614b1f846002615e00565b614b2a906001615da1565b90505b6001811115614ba2576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110614b5e57614b5e615f02565b1a60f81b828281518110614b7457614b74615f02565b60200101906001600160f81b031916908160001a90535060049490941c93614b9b81615e85565b9050614b2d565b5083156111a65760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610f85565b600054610100900460ff16614c185760405162461bcd60e51b8152600401610f8590615c5f565b614c20614e0a565b613962614e0a565b600082815260976020526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b611aed8282613cb0565b80471015614ccd5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610f85565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114614d1a576040519150601f19603f3d011682016040523d82523d6000602084013e614d1f565b606091505b5050905080610fa65760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c20726044820152791958da5c1a595b9d081b585e481a185d99481c995d995c9d195960321b6064820152608401610f85565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052610fa6908490614e31565b60006001600160e01b03198216637965db0b60e01b1480610ef95750610ef982614f03565b600054610100900460ff166139625760405162461bcd60e51b8152600401610f8590615c5f565b6000614e86826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614f3f9092919063ffffffff16565b805190915015610fa65780806020019051810190614ea491906156a6565b610fa65760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610f85565b60006301ffc9a760e01b6001600160e01b031983161480610ef95750506001600160e01b03191660009081526065602052604090205460ff1690565b60606146be848460008585843b614f985760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610f85565b600080866001600160a01b03168587604051614fb4919061593e565b60006040518083038185875af1925050503d8060008114614ff1576040519150601f19603f3d011682016040523d82523d6000602084013e614ff6565b606091505b5091509150612d0f828286606083156150105750816111a6565b8251156150205782518084602001fd5b8160405162461bcd60e51b8152600401610f859190615b1e565b82805461504690615e9c565b90600052602060002090601f01602090048101928261506857600085556150ae565b82601f106150815782800160ff198235161785556150ae565b828001600101855582156150ae579182015b828111156150ae578235825591602001919060010190615093565b50611bee9291505b80821115611bee57600081556001016150b6565b60008083601f8401126150dc57600080fd5b5081356001600160401b038111156150f357600080fd5b6020830191508360208260051b850101111561510e57600080fd5b9250929050565b600082601f83011261512657600080fd5b8135602061513b61513683615d57565b615d27565b80838252828201915082860187848660051b890101111561515b57600080fd5b60005b8581101561518357813561517181615f2e565b8452928401929084019060010161515e565b5090979650505050505050565b600082601f8301126151a157600080fd5b813560206151b161513683615d57565b80838252828201915082860187848660051b89010111156151d157600080fd5b60005b85811015615183578135845292840192908401906001016151d4565b60008083601f84011261520257600080fd5b5081356001600160401b0381111561521957600080fd5b60208301915083602082850101111561510e57600080fd5b600082601f83011261524257600080fd5b813561525061513682615d7a565b81815284602083860101111561526557600080fd5b816020850160208301376000918101602001919091529392505050565b60006020828403121561529457600080fd5b81356111a681615f2e565b600080600080600080600060c0888a0312156152ba57600080fd5b87356152c581615f2e565b96506020880135955060408801356152dc81615f2e565b9450606088013593506080880135925060a08801356001600160401b0381111561530557600080fd5b6153118a828b016151f0565b989b979a50959850939692959293505050565b6000806040838503121561533757600080fd5b823561534281615f2e565b9150602083013561535281615f2e565b809150509250929050565b6000806000806080858703121561537357600080fd5b843561537e81615f2e565b9350602085013561538e81615f2e565b9250604085013561539e81615f2e565b915060608501356153ae81615f2e565b939692955090935050565b6000806000606084860312156153ce57600080fd5b83356153d981615f2e565b925060208401356153e981615f2e565b915060408401356001600160401b0381111561540457600080fd5b61541086828701615231565b9150509250925092565b60008060006060848603121561542f57600080fd5b833561543a81615f2e565b9250602084013561544a81615f2e565b929592945050506040919091013590565b6000806000806080858703121561547157600080fd5b843561547c81615f2e565b9350602085013561548c81615f2e565b92506040850135915060608501356001600160401b038111156154ae57600080fd5b6154ba87828801615231565b91505092959194509250565b600080604083850312156154d957600080fd5b82356154e481615f2e565b9150602083013561535281615f43565b6000806040838503121561550757600080fd5b823561551281615f2e565b946020939093013593505050565b60008060006060848603121561553557600080fd5b833561554081615f2e565b95602085013595506040909401359392505050565b6000806000806000806060878903121561556e57600080fd5b86356001600160401b038082111561558557600080fd5b6155918a838b016150ca565b909850965060208901359150808211156155aa57600080fd5b6155b68a838b016150ca565b909650945060408901359150808211156155cf57600080fd5b506155dc89828a016150ca565b979a9699509497509295939492505050565b60008060008060008060a0878903121561560757600080fd5b86356001600160401b038082111561561e57600080fd5b61562a8a838b01615190565b9750602089013591508082111561564057600080fd5b61564c8a838b01615115565b9650604089013591508082111561566257600080fd5b61566e8a838b01615115565b9550606089013591508082111561568457600080fd5b6156908a838b01615115565b945060808901359150808211156155cf57600080fd5b6000602082840312156156b857600080fd5b81516111a681615f43565b6000602082840312156156d557600080fd5b5035919050565b600080604083850312156156ef57600080fd5b82359150602083013561535281615f2e565b60006020828403121561571357600080fd5b81356111a681615f51565b60006020828403121561573057600080fd5b81516111a681615f51565b6000806020838503121561574e57600080fd5b82356001600160401b0381111561576457600080fd5b615770858286016151f0565b90969095509350505050565b60006020828403121561578e57600080fd5b81516001600160401b038111156157a457600080fd5b8201601f810184136157b557600080fd5b80516157c361513682615d7a565b8181528560208385010111156157d857600080fd5b6157e9826020830160208601615e59565b95945050505050565b60006020828403121561580457600080fd5b5051919050565b60008060006060848603121561582057600080fd5b83359250602084013561544a81615f2e565b6000806040838503121561584557600080fd5b50508035926020909101359150565b60008060008060006080868803121561586c57600080fd5b8535945060208601359350604086013561588581615f2e565b925060608601356001600160401b038111156158a057600080fd5b6158ac888289016151f0565b969995985093965092949392505050565b6000806000606084860312156158d257600080fd5b505081359360208301359350604090920135919050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6000815180845261592a816020860160208601615e59565b601f01601f19169290920160200192915050565b60008251615950818460208701615e59565b9190910192915050565b6000855161596c818460208a01615e59565b855190830190615980818360208a01615e59565b8551910190615993818360208901615e59565b84519101906159a6818360208801615e59565b019695505050505050565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8152600083516159e3816017850160208801615e59565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351615a14816028840160208801615e59565b01602801949350505050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03888116825287811660208301528616604082015260c060608201819052600090615a8d90830186886158e9565b60808301949094525060a0015295945050505050565b6001600160a01b038581168252848116602083015283166040820152608060608201819052600090612e9d90830184615912565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090612e9d90830184615912565b6020815260006146be6020830184866158e9565b6020815260006111a66020830184615912565b60208082526016908201527512d15657d514905394d1915494d7d11254d05093115160521b604082015260600190565b6020808252600f908201526e494e56414c49445f4144445245535360881b604082015260600190565b602080825260189082015277494e53554646494349454e545f45524332305f56414c554560401b604082015260600190565b602080825260129082015271494e53554646494349454e545f56414c554560701b604082015260600190565b60208082526010908201526f2a2920a729a322a92faa27afa9a2a62360811b604082015260600190565b6020808252600d908201526c1313d0d2d7d4d3d31117d3d555609a1b604082015260600190565b6020808252600c908201526b20a8282927ab22afa9a2a62360a11b604082015260600190565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b6020808252601d908201527f4e4f4e5f434f4d504c49414e545f4552433732315f5245434549564552000000604082015260600190565b6000808335601e19843603018112615cf857600080fd5b8301803591506001600160401b03821115615d1257600080fd5b60200191503681900382131561510e57600080fd5b604051601f8201601f191681016001600160401b0381118282101715615d4f57615d4f615f18565b604052919050565b60006001600160401b03821115615d7057615d70615f18565b5060051b60200190565b60006001600160401b03821115615d9357615d93615f18565b50601f01601f191660200190565b60008219821115615db457615db4615eec565b500190565b600060ff821660ff84168060ff03821115615dd657615dd6615eec565b019392505050565b600082615dfb57634e487b7160e01b600052601260045260246000fd5b500490565b6000816000190483118215151615615e1a57615e1a615eec565b500290565b600061ffff83811690831681811015615e3a57615e3a615eec565b039392505050565b600082821015615e5457615e54615eec565b500390565b60005b83811015615e74578181015183820152602001615e5c565b838111156123d55750506000910152565b600081615e9457615e94615eec565b506000190190565b600181811c90821680615eb057607f821691505b60208210811415611dbd57634e487b7160e01b600052602260045260246000fd5b6000600019821415615ee557615ee5615eec565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114611ba857600080fd5b8015158114611ba857600080fd5b6001600160e01b031981168114611ba857600080fdfeb309c40027c81d382c3b58d8de24207a34b27e1db369b1434e4a11311f154b5eddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efb89cdd26cddd51301940bf2715f765b626b8a5a9e2681ac62dc83cc2db2530c0a2646970667358221220fa5eda210be05319db90c40fdffb53a7d189847a0ec560bcf8b9352f8b2407f664736f6c63430008070033
Deployed Bytecode
0x6080604052600436106104395760003560e01c80636d8ea5b41161022b578063a98d36231161012f578063d32bfb6c116100b1578063d32bfb6c14610dbb578063d52e4a1014610ddb578063d547741f14610df1578063d813cc1914610e11578063e985e9c514610e24578063f0ba604014610e44578063f12c6b6e14610e59578063f32e8b2414610e79578063f3fef3a314610e8e578063f5766b3914610eae578063f8548e3614610ece57600080fd5b8063a98d362314610c4c578063aae4b8f714610c7f578063b11d7ec114610c9f578063b1a3b25d14610cbf578063b585a6d514610cdf578063b88d4fde14610cff578063bf4a927014610d1f578063c87b56dd14610d3f578063cb0703c614610d5f578063d1bbd49c14610d7f578063d250348514610d9b57600080fd5b80638ca2fbad116101b85780638ca2fbad14610b125780638da5cb5b14610b3457806391d1485414610b5357806392ac98a514610b7357806393fd184414610b9357806395d89b4114610baa5780639d76ea5814610bbf578063a217fddf14610be0578063a22cb46514610bf5578063a2e4cd2e14610c15578063a375cb0514610c3557600080fd5b80636d8ea5b4146109da5780636eadde43146109fa57806370a0823114610a1a57806374b6c10614610a3a578063782a4ade14610a515780637ec2a72414610a7157806381a3c94314610a925780638505fe9514610ab25780638577a6d514610ad25780638932a90d14610af257600080fd5b80632f54bf6e1161033d5780634e2ce6d3116102bf5780634e2ce6d3146108855780634f6ccce71461089c57806352b0f638146108bc57806354b249fb146108dc578063550ef3a81461090d578063558b71e91461092d578063564aa99d1461094d57806356e0d51f1461096d5780636207a8da14610984578063626485a31461099a5780636352211e146109ba57600080fd5b80632f54bf6e1461070a5780632f745c591461073a57806330176e131461075a57806331159a171461077a578063338189971461079a57806336568abe146107ad57806338af3eed146107cd57806339f46986146107ee57806342842e0e1461080e57806342966c681461082e5780634d025fed1461084e57600080fd5b806313af4035116103c657806313af4035146105a857806318160ddd146105c8578063183767da146105de578063217751bc146105f5578063231005091461061657806323b872dd14610638578063248a9ca31461065857806326e9ca07146106885780632af9162a146106a95780632d33dd5b146106c95780632f2ff15d146106ea57600080fd5b806301ffc9a714610445578063068208cd1461047a57806306fdde031461049c578063081812fc146104be578063095ea7b3146104eb578063097ba3331461050b5780630aaffd2a146105395780630f15023b1461055957806310e569731461057a57806311a4c03a1461059157600080fd5b3661044057005b600080fd5b34801561045157600080fd5b50610465610460366004615701565b610eee565b60405190151581526020015b60405180910390f35b34801561048657600080fd5b5061049a6104953660046158bd565b610eff565b005b3480156104a857600080fd5b506104b1610fab565b6040516104719190615b1e565b3480156104ca57600080fd5b506104de6104d93660046156c3565b61103a565b6040516104719190615a20565b3480156104f757600080fd5b5061049a6105063660046154f4565b611062565b34801561051757600080fd5b5061052b6105263660046153b9565b6110fd565b604051908152602001610471565b34801561054557600080fd5b5061049a610554366004615282565b6111ad565b34801561056557600080fd5b50610c83546104de906001600160a01b031681565b34801561058657600080fd5b5061052b610c855481565b34801561059d57600080fd5b5061052b610c845481565b3480156105b457600080fd5b5061049a6105c3366004615282565b6111fe565b3480156105d457600080fd5b50610c875461052b565b3480156105ea57600080fd5b5061052b6124075481565b34801561060157600080fd5b50610c8a546104de906001600160a01b031681565b34801561062257600080fd5b5061052b600080516020615f6883398151915281565b34801561064457600080fd5b5061049a61065336600461541a565b6112bc565b34801561066457600080fd5b5061052b6106733660046156c3565b60009081526097602052604090206001015490565b34801561069457600080fd5b50610c8b546104de906001600160a01b031681565b3480156106b557600080fd5b5061049a6106c4366004615282565b611460565b3480156106d557600080fd5b50610c89546104de906001600160a01b031681565b3480156106f657600080fd5b5061049a6107053660046156dc565b6114b7565b34801561071657600080fd5b50610465610725366004615282565b612bda546001600160a01b0390811691161490565b34801561074657600080fd5b5061052b6107553660046154f4565b6114dd565b34801561076657600080fd5b5061049a61077536600461573b565b61155c565b34801561078657600080fd5b5061049a6107953660046156c3565b611571565b61049a6107a83660046155ee565b6115b9565b3480156107b957600080fd5b5061049a6107c83660046156dc565b611a73565b3480156107d957600080fd5b50610c88546104de906001600160a01b031681565b3480156107fa57600080fd5b5061049a610809366004615832565b611af1565b34801561081a57600080fd5b5061049a61082936600461541a565b611b3f565b34801561083a57600080fd5b5061049a6108493660046156c3565b611b5a565b34801561085a57600080fd5b506104de6108693660046156c3565b611078602052600090815260409020546001600160a01b031681565b34801561089157600080fd5b5061052b610c8d5481565b3480156108a857600080fd5b5061052b6108b73660046156c3565b611bab565b3480156108c857600080fd5b506104656108d7366004615282565b611bf2565b3480156108e857600080fd5b5061052b6108f73660046156c3565b600090815261107b602052604090206001015490565b34801561091957600080fd5b5061049a61092836600461573b565b611c0c565b34801561093957600080fd5b5061049a610948366004615832565b611c21565b34801561095957600080fd5b5061049a610968366004615282565b611c45565b34801561097957600080fd5b5061052b6127f05481565b34801561099057600080fd5b5061201e5461052b565b3480156109a657600080fd5b5061049a6109b53660046156c3565b611c9c565b3480156109c657600080fd5b506104de6109d53660046156c3565b611caa565b3480156109e657600080fd5b506104656109f5366004615282565b611cc6565b348015610a0657600080fd5b5061049a610a1536600461529f565b611dc3565b348015610a2657600080fd5b5061052b610a35366004615282565b611f27565b348015610a4657600080fd5b5061052b610c865481565b348015610a5d57600080fd5b5061049a610a6c36600461573b565b611f6c565b348015610a7d57600080fd5b50610c8c546104de906001600160a01b031681565b348015610a9e57600080fd5b5061049a610aad366004615555565b611fb3565b348015610abe57600080fd5b5061049a610acd3660046156dc565b612105565b348015610ade57600080fd5b5061049a610aed3660046156c3565b6123db565b348015610afe57600080fd5b5061049a610b0d36600461573b565b61241c565b348015610b1e57600080fd5b5061052b600080516020615fa883398151915281565b348015610b4057600080fd5b50612bda546001600160a01b03166104de565b348015610b5f57600080fd5b50610465610b6e3660046156dc565b61263f565b348015610b7f57600080fd5b5061052b610b8e3660046156c3565b61266a565b348015610b9f57600080fd5b5061052b6110775481565b348015610bb657600080fd5b506104b161275a565b348015610bcb57600080fd5b506104b1546104de906001600160a01b031681565b348015610bec57600080fd5b5061052b600081565b348015610c0157600080fd5b5061049a610c103660046154c6565b612893565b348015610c2157600080fd5b5061049a610c303660046156dc565b61294d565b348015610c4157600080fd5b5061052b6127f15481565b348015610c5857600080fd5b50610465610c673660046156c3565b600090815261107b6020526040902060010154421090565b348015610c8b57600080fd5b50610465610c9a366004615282565b6129db565b348015610cab57600080fd5b5061049a610cba3660046156dc565b6129f5565b348015610ccb57600080fd5b5061052b610cda366004615832565b612a6d565b348015610ceb57600080fd5b50610465610cfa3660046154f4565b612add565b348015610d0b57600080fd5b5061049a610d1a36600461545b565b612b6e565b348015610d2b57600080fd5b5061049a610d3a3660046156c3565b612ba1565b348015610d4b57600080fd5b506104b1610d5a3660046156c3565b612bf8565b348015610d6b57600080fd5b5061049a610d7a36600461535d565b612ea7565b348015610d8b57600080fd5b50604051600a8152602001610471565b348015610da757600080fd5b5061049a610db6366004615282565b61309e565b348015610dc757600080fd5b5061049a610dd63660046156c3565b6130f5565b348015610de757600080fd5b50610c8e5461052b565b348015610dfd57600080fd5b5061049a610e0c3660046156dc565b613127565b61049a610e1f366004615854565b61314d565b348015610e3057600080fd5b50610465610e3f366004615324565b6132a8565b348015610e5057600080fd5b5061049a6132d7565b348015610e6557600080fd5b5061049a610e74366004615520565b61331c565b348015610e8557600080fd5b5061049a613518565b348015610e9a57600080fd5b5061049a610ea93660046154f4565b613528565b348015610eba57600080fd5b5061049a610ec93660046156c3565b613687565b348015610eda57600080fd5b50610465610ee936600461580b565b613695565b6000610ef9826136f7565b92915050565b610f0883613702565b610f118361374f565b610f1a836137a0565b610f2382613702565b8042610f3f85600090815261107b602052604090206001015490565b610f499190615e42565b1015610f8e5760405162461bcd60e51b815260206004820152600f60248201526e4e4f545f454e4f5547485f54494d4560881b60448201526064015b60405180910390fd5b610f9a83826000613841565b610fa682826001613841565b505050565b6114638054610fb990615e9c565b80601f0160208091040260200160405190810160405280929190818152602001828054610fe590615e9c565b80156110325780601f1061100757610100808354040283529160200191611032565b820191906000526020600020905b81548152906001019060200180831161101557829003601f168201915b505050505081565b600061104582613702565b50600090815261107960205260409020546001600160a01b031690565b61106b816137a0565b336001600160a01b03831614156110945760405162461bcd60e51b8152600401610f8590615c39565b60008181526110796020908152604080832080546001600160a01b0319166001600160a01b03878116918217909255611076909352818420549151859492909116917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a45050565b610c89546000906001600160a01b0316156111a057610c895460405163221c1fd160e01b81526001600160a01b039091169063221c1fd190611149903390889088908890600401615aa3565b60206040518083038186803b15801561116157600080fd5b505afa158015611175573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061119991906157f2565b90506111a6565b50610c85545b9392505050565b6111b56138f7565b6001600160a01b0381166111db5760405162461bcd60e51b8152600401610f8590615b61565b610c8880546001600160a01b0319166001600160a01b0392909216919091179055565b611206613964565b6001600160a01b0381166112595760405162461bcd60e51b815260206004820152601a6024820152794f574e45525f43414e545f42455f414444524553535f5a45524f60301b6044820152606401610f85565b612bda80546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091015b60405180910390a15050565b6112c58161374f565b6112ce816137a0565b826001600160a01b03166112e182611caa565b6001600160a01b0316146113375760405162461bcd60e51b815260206004820152601c60248201527f5452414e534645525f46524f4d3a204e4f545f4b45595f4f574e4552000000006044820152606401610f85565b612710612407541061135b5760405162461bcd60e51b8152600401610f8590615b31565b6001600160a01b0382166113815760405162461bcd60e51b8152600401610f8590615b61565b816001600160a01b0316836001600160a01b031614156113b35760405162461bcd60e51b8152600401610f8590615be8565b6113c9816113c2836000612a6d565b6000613841565b600081815261107b602052604090206113e183611f27565b6113fc5761107780549060006113f683615ed1565b91905055505b611405826139bc565b61140f8284613ac3565b61141a826000613b82565b61142382613c0f565b600082815261202060205260408082208290555183916001600160a01b038087169290881691600080516020615f8883398151915291a450505050565b611468613964565b611480600080516020615f6883398151915282613127565b6040516001600160a01b038216907f766f6199fea59554b9ff688e413302b9200f85d74811c053c12d945ac6d8dd7a90600090a250565b6000828152609760205260409020600101546114d38133613c4c565b610fa68383613cb0565b60006114e883611f27565b82106115325760405162461bcd60e51b81526020600482015260196024820152784f574e45525f494e4445585f4f55545f4f465f424f554e445360381b6044820152606401610f85565b506001600160a01b0391909116600090815261107c60209081526040808320938352929052205490565b611564613964565b610fa6611465838361503a565b611579613964565b806115b35760405162461bcd60e51b815260206004820152600a6024820152694e554c4c5f56414c554560b01b6044820152606401610f85565b610c8e55565b6115c1613d36565b610c8754610c8654116115e65760405162461bcd60e51b8152600401610f8590615c12565b83518551146116325760405162461bcd60e51b81526020600482015260186024820152770929cac82989288bea48a8c8aa4a48aa4a6be988a9c8ea8960431b6044820152606401610f85565b82518551146116835760405162461bcd60e51b815260206004820152601b60248201527f494e56414c49445f4b45595f4d414e41474552535f4c454e47544800000000006044820152606401610f85565b60008060005b875181101561199e5760008882815181106116a6576116a6615f02565b6020026020010151905060006001600160a01b0316816001600160a01b031614156116e35760405162461bcd60e51b8152600401610f8590615b61565b600019610c8454141561171d576117168188848151811061170657611706615f02565b6020026020010151600019613d7e565b9250611752565b61174f8188848151811061173357611733615f02565b6020026020010151610c84544261174a9190615da1565b613d7e565b92505b60006117d0828a858151811061176a5761176a615f02565b602002602001015189898781811061178457611784615f02565b90506020028101906117969190615ce1565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506110fd92505050565b90506117dc8186615da1565b600085815261201f60209081526040808320859055610c84546120208352818420556104b18054612021909352922080546001600160a01b0319166001600160a01b0392831617905590549196501615611869578a838151811061184257611842615f02565b60200260200101518111156118695760405162461bcd60e51b8152600401610f8590615b8a565b61188c818a858151811061187f5761187f615f02565b6020026020010151613e2e565b6104b1546000906001600160a01b0316156118c0578b84815181106118b3576118b3615f02565b60200260200101516118c2565b345b610c89549091506001600160a01b03161561198857610c89548a516001600160a01b0390911690639849965790339086908e908990811061190557611905615f02565b60200260200101518c8c8a81811061191f5761191f615f02565b90506020028101906119319190615ce1565b88886040518863ffffffff1660e01b81526004016119559796959493929190615a58565b600060405180830381600087803b15801561196f57600080fd5b505af1158015611983573d6000803e3d6000fd5b505050505b505050808061199690615ed1565b915050611689565b506104b1546001600160a01b031615611a41576104b1546040516323b872dd60e01b81526001600160a01b039091169081906323b872dd906119e890339030908890600401615a34565b602060405180830381600087803b158015611a0257600080fd5b505af1158015611a16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a3a91906156a6565b5050611a61565b34821115611a615760405162461bcd60e51b8152600401610f8590615bbc565b611a69613f33565b5050505050505050565b6001600160a01b0381163314611ae35760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610f85565b611aed82826140bb565b5050565b611af9613964565b60408051838152602081018390527fd6867bc538320e67d7bdc35860c27c08486eb490b4fd9b820fff18fb28381d3c910160405180910390a16127f1919091556127f055565b610fa683838360405180602001604052806000815250612b6e565b611b6381613702565b611b6c816137a0565b600081815261107660205260408082205490518392916001600160a01b031690600080516020615f88833981519152908390a4611ba881614122565b50565b6000610c87548210611bee5760405162461bcd60e51b815260206004820152600c60248201526b4f55545f4f465f52414e474560a01b6044820152606401610f85565b5090565b6000610ef9600080516020615f688339815191528361263f565b611c14613964565b610fa6611463838361503a565b611c2a82613702565b611c338261374f565b611c3b613964565b611aed828261415c565b611c4d613964565b611c65600080516020615f68833981519152826114b7565b6040516001600160a01b038216907f684f8a71407db0c334454ebe9c9b288549317893a20b10dc779ec5c118dcd12190600090a250565b611ca4613964565b610c8455565b600090815261107660205260409020546001600160a01b031690565b600080611cd283611f27565b90508015611d155760005b81811015611d1357611cf2610c6785836114dd565b15611d01575060019392505050565b80611d0b81615ed1565b915050611cdd565b505b610c8b546001600160a01b031615611dbd57610c8b546040516370b6638f60e11b81523060048201526001600160a01b0385811660248301526000604483015284151560648301529091169063e16cc71e9060840160206040518083038186803b158015611d8257600080fd5b505afa158015611d96573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dba91906156a6565b91505b50919050565b600054610100900460ff16611dde5760005460ff1615611de2565b303b155b611e455760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610f85565b600054610100900460ff16158015611e67576000805461ffff19166101011790555b611e7086614263565b610c838054336001600160a01b031991821617909155610c8880549091166001600160a01b038a16179055610c84879055610c85859055610c86849055600a610c8d556001610c8e55611ec3838361428f565b611ecb6142b5565b611ed76103e86127f055565b611ee0886142c5565b612bda80546001600160a01b0319166001600160a01b038a16179055611f0c6380ac58cd60e01b61434d565b8015611a69576000805461ff00191690555050505050505050565b60006001600160a01b038216611f4f5760405162461bcd60e51b8152600401610f8590615b61565b506001600160a01b0316600090815261107e602052604090205490565b611f74613964565b611f81611464838361503a565b507f8868e22e84ebf32da89b2ebcb0ac642816304ea3863b257f240df9098719cb9782826040516112b0929190615b0a565b611fbb613d36565b611fc433611bf2565b80611fd35750611fd3336129db565b61201f5760405162461bcd60e51b815260206004820181905260248201527f4f4e4c595f4c4f434b5f4d414e414745525f4f525f4b45595f4752414e5445526044820152606401610f85565b60005b858110156120fc57600087878381811061203e5761203e615f02565b90506020020160208101906120539190615282565b6001600160a01b0316141561207a5760405162461bcd60e51b8152600401610f8590615b61565b6120e987878381811061208f5761208f615f02565b90506020020160208101906120a49190615282565b8484848181106120b6576120b6615f02565b90506020020160208101906120cb9190615282565b8787858181106120dd576120dd615f02565b90506020020135613d7e565b50806120f481615ed1565b915050612022565b50505050505050565b61210d613d36565b61211682613702565b60008281526120206020526040902054600019141561216b5760405162461bcd60e51b81526020600482015260116024820152704e4f4e5f4558504952494e475f4c4f434b60781b6044820152606401610f85565b6104b1546001600160a01b03166121b55760405162461bcd60e51b815260206004820152600e60248201526d4e4f4e5f45524332305f4c4f434b60901b6044820152606401610f85565b60006121d96121c384611caa565b83604051806020016040528060008152506110fd565b600084815261201f6020526040902054909150811461222a5760405162461bcd60e51b815260206004820152600d60248201526c14149250d157d0d2105391d151609a1b6044820152606401610f85565b610c8454600084815261202060205260409020541461227e5760405162461bcd60e51b815260206004820152601060248201526f11155490551253d397d0d2105391d15160821b6044820152606401610f85565b6104b154600084815261202160205260409020546001600160a01b039081169116146122dc5760405162461bcd60e51b815260206004820152600d60248201526c1513d2d15397d0d2105391d151609a1b6044820152606401610f85565b600083815261107b602052604090206001015442101561232a5760405162461bcd60e51b81526020600482015260096024820152684e4f545f524541445960b81b6044820152606401610f85565b612333836143cc565b5061233e8183613e2e565b6104b1546001600160a01b0316806323b872dd61235a86611caa565b30856040518463ffffffff1660e01b815260040161237a93929190615a34565b602060405180830381600087803b15801561239457600080fd5b505af11580156123a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123cc91906156a6565b506123d5613f33565b50505050565b6123e3613964565b6040518181527f0496ed1e61eb69727f9659a8e859288db4758ffb1f744d1c1424634f90a257f49060200160405180910390a161240755565b6124286001600a615e1f565b61ffff16610c8d54148061243d5750610c8d54155b6124865760405162461bcd60e51b815260206004820152601a60248201527914d0d211535057d5915494d253d397d393d517d0d3d4949150d560321b6044820152606401610f85565b610c8e54612495576001610c8e555b6000808060006124a5610c875490565b610c83549091506001600160a01b03163314156124c557606491506124d7565b6124d185870187615832565b90945091505b808211156124e3578091505b835b6124ef8386615da1565b8110156125f5576000612503826001615da1565b600081815261107660209081526040808320546001600160a01b031680845261107583529281902081518083019092528054808352600190910154928201929092529293509091901580159061255c5750602081015115155b156125df5760408051808201909152815181526020808301519082015261107b6000612589876001615da1565b81526020808201929092526040908101600090812084518155938301516001948501556001600160a01b03861681526110759092528120818155909101556125d18383613ac3565b866125db81615ed1565b9750505b50505080806125ed90615ed1565b9150506124e5565b5080821061260457600a610c8d555b6040518381527f4e075b7dcd463fe1b504dad3ccd1c4f87011a70c1e50f1805183df5d559834cd9060200160405180910390a1505050505050565b60009182526097602090815260408084206001600160a01b0393909316845291905290205460ff1690565b60006126758261374f565b600019610c8454141561268b575050610c855490565b600082815261107b60205260408120600101546126a9904290615e42565b9050610c84546127f154826126be9190615da1565b106126ce57610c855491506126ee565b610c845481610c85546126e19190615e00565b6126eb9190615dde565b91505b6127f154158061270d5750610c84546127f15461270b9083615da1565b105b15611dbd5760006127106127f054610c85546127299190615e00565b6127339190615dde565b90508083111561274e576127478184615e42565b9250612753565b600092505b5050919050565b6060611464805461276a90615e9c565b1515905061280257610c8360009054906101000a90046001600160a01b03166001600160a01b031663cec410526040518163ffffffff1660e01b815260040160006040518083038186803b1580156127c157600080fd5b505afa1580156127d5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526127fd919081019061577c565b905090565b611464805461281090615e9c565b80601f016020809104026020016040519081016040528092919081815260200182805461283c90615e9c565b80156128895780601f1061285e57610100808354040283529160200191612889565b820191906000526020600020905b81548152906001019060200180831161286c57829003601f168201915b5050505050905090565b6001600160a01b0382163314156128bc5760405162461bcd60e51b8152600401610f8590615c39565b61271061240754106128e05760405162461bcd60e51b8152600401610f8590615b31565b33600081815261107a602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b612955613964565b61295e816144cd565b610c8580546104b18054928590556001600160a01b031983166001600160a01b0385811691821790925560408051848152602081018890529290941693820184905260608201529091907f3615065ccf48367ac483ac86701248e2e5ff55bdd9be845007d34a3b68d719d49060800160405180910390a150505050565b6000610ef9600080516020615fa88339815191528361263f565b6129fe82613702565b612a08823361458f565b80612a175750612a17336129db565b612a635760405162461bcd60e51b815260206004820152601f60248201527f554e415554484f52495a45445f4b45595f4d414e414745525f555044415445006044820152606401610f85565b611aed8282613b82565b6000612a7883613702565b600083815261107b602052604090206001015442811015612a9d576000915050610ef9565b600083612ab557612aae4283615e42565b9050612ab8565b50825b6127106124075482612aca9190615e00565b612ad49190615dde565b92505050610ef9565b6000612ae76138f7565b6104b15460405163095ea7b360e01b81526001600160a01b038581166004830152602482018590529091169063095ea7b390604401602060405180830381600087803b158015612b3657600080fd5b505af1158015612b4a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111a691906156a6565b612b798484846112bc565b612b8584848484614607565b6123d55760405162461bcd60e51b8152600401610f8590615caa565b612ba9613964565b610c8754811015612bf25760405162461bcd60e51b8152602060048201526013602482015272534d414c4c45525f5448414e5f535550504c5960681b6044820152606401610f85565b610c8655565b606080806000612c07306146c6565b905060608515612c2157612c1a866148b1565b9250612c34565b6040518060200160405280600081525092505b610c8c546001600160a01b031615612d1a57600086815261107b6020526040902060010154610c8c546001600160a01b031663988b93ad3033612c768b611caa565b60405160e085901b6001600160e01b03191681526001600160a01b03938416600482015291831660248301529091166044820152606481018a90526084810184905260a40160006040518083038186803b158015612cd357600080fd5b505afa158015612ce7573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612d0f919081019061577c565b979650505050505050565b6114658054612d2890615e9c565b15159050612dde57610c8360009054906101000a90046001600160a01b03166001600160a01b031663a998e9fb6040518163ffffffff1660e01b815260040160006040518083038186803b158015612d7f57600080fd5b505afa158015612d93573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612dbb919081019061577c565b9350604051806040016040528060018152602001602f60f81b8152509050612e91565b6114658054612dec90615e9c565b80601f0160208091040260200160405190810160405280929190818152602001828054612e1890615e9c565b8015612e655780601f10612e3a57610100808354040283529160200191612e65565b820191906000526020600020905b815481529060010190602001808311612e4857829003601f168201915b505050505093506040518060200160405280600081525090506040518060200160405280600081525091505b612e9d848383866149dd565b9695505050505050565b612eaf613964565b6001600160a01b0384161580612ece57506001600160a01b0384163b15155b612f155760405162461bcd60e51b8152602060048201526018602482015277494e56414c49445f4f4e5f4b45595f534f4c445f484f4f4b60401b6044820152606401610f85565b6001600160a01b0383161580612f3457506001600160a01b0383163b15155b612f7d5760405162461bcd60e51b815260206004820152601a602482015279494e56414c49445f4f4e5f4b45595f43414e43454c5f484f4f4b60301b6044820152606401610f85565b6001600160a01b0382161580612f9c57506001600160a01b0382163b15155b612fe45760405162461bcd60e51b8152602060048201526019602482015278494e56414c49445f4f4e5f56414c49445f4b45595f484f4f4b60381b6044820152606401610f85565b6001600160a01b038116158061300357506001600160a01b0381163b15155b61304b5760405162461bcd60e51b8152602060048201526019602482015278494e56414c49445f4f4e5f544f4b454e5f5552495f484f4f4b60381b6044820152606401610f85565b610c8980546001600160a01b039586166001600160a01b031991821617909155610c8a805494861694821694909417909355610c8c8054918516918416919091179055610c8b8054919093169116179055565b6130a6613964565b6130be600080516020615fa8833981519152826114b7565b6040516001600160a01b038216907f91d5c045d5bd98bf59a379b259ebca05b93bf79af1845fdf87e3172385d4c7f790600090a250565b6130fe81613702565b6131078161374f565b613110816137a0565b600061311b8261266a565b9050611aed828261415c565b6000828152609760205260409020600101546131438133613c4c565b610fa683836140bb565b613155613d36565b61315e84613702565b613167846143cc565b5060006131b361317686611caa565b8585858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506110fd92505050565b6104b1549091506001600160a01b03161561327857858111156131e85760405162461bcd60e51b8152600401610f8590615b8a565b6104b1546040516323b872dd60e01b81526001600160a01b039091169081906323b872dd9061321f90339030908790600401615a34565b602060405180830381600087803b15801561323957600080fd5b505af115801561324d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061327191906156a6565b5050613298565b348111156132985760405162461bcd60e51b8152600401610f8590615bbc565b6132a0613f33565b505050505050565b6001600160a01b03918216600090815261107a6020908152604080832093909416825291909152205460ff1690565b6132ef600080516020615fa883398151915233611a73565b60405133907f42885193b8178d25fca25a38e6fcc93918501e91be06d85e0c8afb3bad95238090600090a2565b613324613d36565b610c8754610c8654116133495760405162461bcd60e51b8152600401610f8590615c12565b613352826137a0565b61335b8261374f565b612710612407541061337f5760405162461bcd60e51b8152600401610f8590615b31565b6001600160a01b0383166133a55760405162461bcd60e51b8152600401610f8590615b61565b600082815261107660205260409020546001600160a01b039081169084168114156133e25760405162461bcd60e51b8152600401610f8590615be8565b600083815261107b60205260408120600101548190613402904290615e42565b905060006134108686612a6d565b9050600061341e8287615da1565b90508281101561343c5785935061343787826000613841565b613494565b6134468784612a6d565b91506134528284615e42565b600088815261107b6020526040808220426001909101555191955088917f59f2fe866dd27a1c2d34115520888c3150365cbc931aab97fa88c4b9ab40b7959190a25b60006134a5898261174a8842615da1565b905080896001600160a01b0316876001600160a01b0316600080516020615f8883398151915260405160405180910390a46134f1868a8360405180602001604052806000815250614607565b61350d5760405162461bcd60e51b8152600401610f8590615caa565b505050505050505050565b613520613964565b600a610c8d55565b6135306138f7565b60006001600160a01b0383166135475750476135c6565b6040516370a0823160e01b81526001600160a01b038416906370a0823190613573903090600401615a20565b60206040518083038186803b15801561358b57600080fd5b505afa15801561359f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135c391906157f2565b90505b60008215806135d457508183115b15613623576000821161361c5760405162461bcd60e51b815260206004820152601060248201526f4e4f545f454e4f5547485f46554e445360801b6044820152606401610f85565b5080613626565b50815b610c88546040518281526001600160a01b039182169186169033907f342e7ff505a8a0364cd0dc2ff195c315e43bce86b204846ecd36913e117b109e9060200160405180910390a4610c88546123d59085906001600160a01b031683614a0f565b61368f613964565b61201e55565b60006136a08461374f565b600061271083426136c188600090815261107b602052604090206001015490565b6136cb9190615e42565b6136d59190615e00565b6136df9190615dde565b90506136ec84868361331c565b506001949350505050565b6000610ef982614a4b565b600081815261107b6020526040902060010154611ba85760405162461bcd60e51b815260206004820152600b60248201526a4e4f5f535543485f4b455960a81b6044820152606401610f85565b600081815261107b60205260409020600101544210611ba85760405162461bcd60e51b815260206004820152600d60248201526c12d15657d393d517d590531251609a1b6044820152606401610f85565b6137aa813361458f565b806137cc5750600081815261107960205260409020546001600160a01b031633145b806137f55750600081815261107660205260409020546137f5906001600160a01b0316336132a8565b611ba85760405162461bcd60e51b815260206004820152601c60248201527f4f4e4c595f4b45595f4d414e414745525f4f525f415050524f564544000000006044820152606401610f85565b61384a83613702565b600083815261107b60205260409020600101548115613897574281111561388d576138758382615da1565b600085815261107b60205260409020600101556138b5565b6138758342615da1565b6138a18382615e42565b600085815261107b60205260409020600101555b60408051848152831515602082015285917fe9408df99703ae33a9d01185bcad328ea8683fb1f920da9c30959c192f21b5b3910160405180910390a250505050565b613900336129db565b806139165750610c88546001600160a01b031633145b6139625760405162461bcd60e51b815260206004820181905260248201527f4f4e4c595f4c4f434b5f4d414e414745525f4f525f42454e45464943494152596044820152606401610f85565b565b61397c600080516020615fa88339815191523361263f565b6139625760405162461bcd60e51b815260206004820152601160248201527027a7262cafa627a1a5afa6a0a720a3a2a960791b6044820152606401610f85565b600081815261107660205260408120546001600160a01b03169060016139e183611f27565b6139eb9190615e42565b600084815261107d6020526040902054909150808214613a41576001600160a01b038316600090815261107c60209081526040808320858452825280832054848452818420819055835261107d90915290208190555b6001600160a01b038316600090815261107c60209081526040808320858452909152812055613a6f83611f27565b60011415613a8e576110778054906000613a8883615e85565b91905055505b6001600160a01b038316600090815261107e60205260408120805460019290613ab8908490615e42565b909155505050505050565b6000613ace82611f27565b9050610c8e548110613b0d5760405162461bcd60e51b81526020600482015260086024820152674d41585f4b45595360c01b6044820152606401610f85565b600083815261107d602090815260408083208490556001600160a01b03851680845261107c83528184208585528352818420879055868452611076835281842080546001600160a01b03191682179055835261107e9091528120805460019290613b78908490615da1565b9091555050505050565b600082815261107860205260409020546001600160a01b03828116911614611aed5760008281526110786020526040902080546001600160a01b0319166001600160a01b038316179055613bd582613c0f565b6040516001600160a01b0382169083907f9d2895c45a420624de863a2f437b022d879f457bf7a829044055a10c5a6fd5e390600090a35050565b600081815261107960205260409020546001600160a01b031615611ba85760009081526110796020526040902080546001600160a01b0319169055565b613c56828261263f565b611aed57613c6e816001600160a01b03166014614a56565b613c79836020614a56565b604051602001613c8a9291906159b1565b60408051601f198184030181529082905262461bcd60e51b8252610f8591600401615b1e565b613cba828261263f565b611aed5760008281526097602090815260408083206001600160a01b03851684529091529020805460ff19166001179055613cf23390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b610c8d54600a146139625760405162461bcd60e51b8152602060048201526012602482015271135251d490551253d397d49154555254915160721b6044820152606401610f85565b610c87805460009182613d9083615ed1565b9091555050610c87546040805180820182528281526020808201868152600085815261107b9092529290209051815590516001909101559050613dd284611f27565b613ded576110778054906000613de783615ed1565b91905055505b613df78185613ac3565b613e018184613b82565b60405181906001600160a01b03861690600090600080516020615f88833981519152908290a49392505050565b610c83546001600160a01b03163b15613ef557610c835460405163939d9f1f60e01b8152600481018490526001600160a01b0383811660248301529091169063939d9f1f90620493e090604401600060405180830381600088803b158015613e9557600080fd5b5087f193505050508015613ea7575060015b611aed57610c835460405130917f6b18946261693dfd6c760d986b28ad2238b5b0267f8e5b6bc40a2f998e2f20ac91613ee9916001600160a01b031690615a20565b60405180910390a25050565b610c835460405130917f6b18946261693dfd6c760d986b28ad2238b5b0267f8e5b6bc40a2f998e2f20ac91613ee9916001600160a01b031690615a20565b61201e5415613962576104b1546001600160a01b031615613fe1576104b15461201e546040516323b872dd60e01b81526001600160a01b039092169182916323b872dd91613f88913091339190600401615a34565b602060405180830381600087803b158015613fa257600080fd5b505af1158015613fb6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613fda91906156a6565b505061406d565b61201e5460405160009133918381818185875af1925050503d8060008114614025576040519150601f19603f3d011682016040523d82523d6000602084013e61402a565b606091505b505090508061406b5760405162461bcd60e51b815260206004820152600d60248201526c14915195539117d19052531151609a1b6044820152606401610f85565b505b61201e546104b154604080519283526001600160a01b03909116602083015233917f522a883b471164223f18b50f326da8671372b64b4792eac0e63d447e714c3e3b910160405180910390a2565b6140c5828261263f565b15611aed5760008281526097602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b61412b816139bc565b600090815261107b6020908152604080832042600190910155611076909152902080546001600160a01b0319169055565b600061416783611caa565b905061417283614122565b336001600160a01b0316816001600160a01b0316847f0a7068a9989857441c039a14a42b67ed71dd1fcfe5a9b17cc87b252e47bce528856040516141b891815260200190565b60405180910390a481156141de576104b1546141de906001600160a01b03168284614a0f565b60008381526120206020526040812055610c8a546001600160a01b031615610fa657610c8a5460405163b499b6c560e01b81526001600160a01b039091169063b499b6c59061423590339085908790600401615a34565b600060405180830381600087803b15801561424f57600080fd5b505af11580156120fc573d6000803e3d6000fd5b61426c816144cd565b6104b180546001600160a01b0319166001600160a01b0392909216919091179055565b614297614bf1565b6142a4611463838361503a565b50611aed635b5e139f60e01b61434d565b61396263780e9d6360e01b61434d565b6142dd600080516020615fa883398151915280614c28565b614303600080516020615f68833981519152600080516020615fa8833981519152614c28565b61430c816129db565b61432857614328600080516020615fa883398151915282614c73565b61433181611bf2565b611ba857611ba8600080516020615f6883398151915282614c73565b6001600160e01b031980821614156143a75760405162461bcd60e51b815260206004820152601c60248201527f4552433136353a20696e76616c696420696e74657266616365206964000000006044820152606401610f85565b6001600160e01b0319166000908152606560205260409020805460ff19166001179055565b600081815261107b60205260408120600101546000198114156144315760405162461bcd60e51b815260206004820152601c60248201527f43414e545f455854454e445f4e4f4e5f4558504952494e475f4b4559000000006044820152606401610f85565b600019610c84541415614448576000199150614476565b4281111561446557610c845461445e9082615da1565b9150614476565b610c84546144739042615da1565b91505b600083815261107b6020526040908190206001018390555183907f3ca112768ff7861e008ace1c11570c52e404c043e585545b5957a1e20961dde3906144bf9085815260200190565b60405180910390a250919050565b6001600160a01b038116158061455357506000816001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561451957600080fd5b505afa15801561452d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061455191906157f2565b115b611ba85760405162461bcd60e51b815260206004820152600d60248201526c24a72b20a624a22faa27a5a2a760991b6044820152606401610f85565b600082815261107860205260408120546001600160a01b03838116911614806145f25750600083815261107860205260409020546001600160a01b03161580156145f25750816001600160a01b03166145e784611caa565b6001600160a01b0316145b156145ff57506001610ef9565b506000610ef9565b60006001600160a01b0384163b614620575060016146be565b604051630a85bd0160e11b81526000906001600160a01b0386169063150b7a02906146559033908a9089908990600401615ad7565b602060405180830381600087803b15801561466f57600080fd5b505af1158015614683573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146a7919061571e565b6001600160e01b031916630a85bd0160e11b149150505b949350505050565b604080518082018252601081526f181899199a1a9b1b9c1cb0b131b232b360811b60208201528151602a80825260608281019094526001600160a01b0385169291600091602082018180368337019050509050600360fc1b8160008151811061473157614731615f02565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061476057614760615f02565b60200101906001600160f81b031916908160001a90535060005b60148110156148a8578260048561479284600c615da1565b602081106147a2576147a2615f02565b1a60f81b6001600160f81b031916901c60f81c60ff16815181106147c8576147c8615f02565b01602001516001600160f81b031916826147e3836002615e00565b6147ee906002615da1565b815181106147fe576147fe615f02565b60200101906001600160f81b031916908160001a905350828461482283600c615da1565b6020811061483257614832615f02565b825191901a600f1690811061484957614849615f02565b01602001516001600160f81b03191682614864836002615e00565b61486f906003615da1565b8151811061487f5761487f615f02565b60200101906001600160f81b031916908160001a905350806148a081615ed1565b91505061477a565b50949350505050565b606081806148d85750506040805180820190915260018152600360fc1b6020820152919050565b8260005b811561490257806148ec81615ed1565b91506148fb9050600a83615dde565b91506148dc565b6000816001600160401b0381111561491c5761491c615f18565b6040519080825280601f01601f191660200182016040528015614946576020820181803683370190505b509050815b84156149d35761495c600182615e42565b9050600061496b600a87615dde565b61497690600a615e00565b6149809087615e42565b61498b906030615db9565b905060008160f81b9050808484815181106149a8576149a8615f02565b60200101906001600160f81b031916908160001a9053506149ca600a88615dde565b9650505061494b565b5095945050505050565b6060848484846040516020016149f6949392919061595a565b6040516020818303038152906040529050949350505050565b8015610fa6576001600160a01b038316614a3657610fa66001600160a01b03831682614c7d565b826123d56001600160a01b0382168484614d93565b6000610ef982614de5565b60606000614a65836002615e00565b614a70906002615da1565b6001600160401b03811115614a8757614a87615f18565b6040519080825280601f01601f191660200182016040528015614ab1576020820181803683370190505b509050600360fc1b81600081518110614acc57614acc615f02565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110614afb57614afb615f02565b60200101906001600160f81b031916908160001a9053506000614b1f846002615e00565b614b2a906001615da1565b90505b6001811115614ba2576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110614b5e57614b5e615f02565b1a60f81b828281518110614b7457614b74615f02565b60200101906001600160f81b031916908160001a90535060049490941c93614b9b81615e85565b9050614b2d565b5083156111a65760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610f85565b600054610100900460ff16614c185760405162461bcd60e51b8152600401610f8590615c5f565b614c20614e0a565b613962614e0a565b600082815260976020526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b611aed8282613cb0565b80471015614ccd5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610f85565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114614d1a576040519150601f19603f3d011682016040523d82523d6000602084013e614d1f565b606091505b5050905080610fa65760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c20726044820152791958da5c1a595b9d081b585e481a185d99481c995d995c9d195960321b6064820152608401610f85565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052610fa6908490614e31565b60006001600160e01b03198216637965db0b60e01b1480610ef95750610ef982614f03565b600054610100900460ff166139625760405162461bcd60e51b8152600401610f8590615c5f565b6000614e86826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614f3f9092919063ffffffff16565b805190915015610fa65780806020019051810190614ea491906156a6565b610fa65760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610f85565b60006301ffc9a760e01b6001600160e01b031983161480610ef95750506001600160e01b03191660009081526065602052604090205460ff1690565b60606146be848460008585843b614f985760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610f85565b600080866001600160a01b03168587604051614fb4919061593e565b60006040518083038185875af1925050503d8060008114614ff1576040519150601f19603f3d011682016040523d82523d6000602084013e614ff6565b606091505b5091509150612d0f828286606083156150105750816111a6565b8251156150205782518084602001fd5b8160405162461bcd60e51b8152600401610f859190615b1e565b82805461504690615e9c565b90600052602060002090601f01602090048101928261506857600085556150ae565b82601f106150815782800160ff198235161785556150ae565b828001600101855582156150ae579182015b828111156150ae578235825591602001919060010190615093565b50611bee9291505b80821115611bee57600081556001016150b6565b60008083601f8401126150dc57600080fd5b5081356001600160401b038111156150f357600080fd5b6020830191508360208260051b850101111561510e57600080fd5b9250929050565b600082601f83011261512657600080fd5b8135602061513b61513683615d57565b615d27565b80838252828201915082860187848660051b890101111561515b57600080fd5b60005b8581101561518357813561517181615f2e565b8452928401929084019060010161515e565b5090979650505050505050565b600082601f8301126151a157600080fd5b813560206151b161513683615d57565b80838252828201915082860187848660051b89010111156151d157600080fd5b60005b85811015615183578135845292840192908401906001016151d4565b60008083601f84011261520257600080fd5b5081356001600160401b0381111561521957600080fd5b60208301915083602082850101111561510e57600080fd5b600082601f83011261524257600080fd5b813561525061513682615d7a565b81815284602083860101111561526557600080fd5b816020850160208301376000918101602001919091529392505050565b60006020828403121561529457600080fd5b81356111a681615f2e565b600080600080600080600060c0888a0312156152ba57600080fd5b87356152c581615f2e565b96506020880135955060408801356152dc81615f2e565b9450606088013593506080880135925060a08801356001600160401b0381111561530557600080fd5b6153118a828b016151f0565b989b979a50959850939692959293505050565b6000806040838503121561533757600080fd5b823561534281615f2e565b9150602083013561535281615f2e565b809150509250929050565b6000806000806080858703121561537357600080fd5b843561537e81615f2e565b9350602085013561538e81615f2e565b9250604085013561539e81615f2e565b915060608501356153ae81615f2e565b939692955090935050565b6000806000606084860312156153ce57600080fd5b83356153d981615f2e565b925060208401356153e981615f2e565b915060408401356001600160401b0381111561540457600080fd5b61541086828701615231565b9150509250925092565b60008060006060848603121561542f57600080fd5b833561543a81615f2e565b9250602084013561544a81615f2e565b929592945050506040919091013590565b6000806000806080858703121561547157600080fd5b843561547c81615f2e565b9350602085013561548c81615f2e565b92506040850135915060608501356001600160401b038111156154ae57600080fd5b6154ba87828801615231565b91505092959194509250565b600080604083850312156154d957600080fd5b82356154e481615f2e565b9150602083013561535281615f43565b6000806040838503121561550757600080fd5b823561551281615f2e565b946020939093013593505050565b60008060006060848603121561553557600080fd5b833561554081615f2e565b95602085013595506040909401359392505050565b6000806000806000806060878903121561556e57600080fd5b86356001600160401b038082111561558557600080fd5b6155918a838b016150ca565b909850965060208901359150808211156155aa57600080fd5b6155b68a838b016150ca565b909650945060408901359150808211156155cf57600080fd5b506155dc89828a016150ca565b979a9699509497509295939492505050565b60008060008060008060a0878903121561560757600080fd5b86356001600160401b038082111561561e57600080fd5b61562a8a838b01615190565b9750602089013591508082111561564057600080fd5b61564c8a838b01615115565b9650604089013591508082111561566257600080fd5b61566e8a838b01615115565b9550606089013591508082111561568457600080fd5b6156908a838b01615115565b945060808901359150808211156155cf57600080fd5b6000602082840312156156b857600080fd5b81516111a681615f43565b6000602082840312156156d557600080fd5b5035919050565b600080604083850312156156ef57600080fd5b82359150602083013561535281615f2e565b60006020828403121561571357600080fd5b81356111a681615f51565b60006020828403121561573057600080fd5b81516111a681615f51565b6000806020838503121561574e57600080fd5b82356001600160401b0381111561576457600080fd5b615770858286016151f0565b90969095509350505050565b60006020828403121561578e57600080fd5b81516001600160401b038111156157a457600080fd5b8201601f810184136157b557600080fd5b80516157c361513682615d7a565b8181528560208385010111156157d857600080fd5b6157e9826020830160208601615e59565b95945050505050565b60006020828403121561580457600080fd5b5051919050565b60008060006060848603121561582057600080fd5b83359250602084013561544a81615f2e565b6000806040838503121561584557600080fd5b50508035926020909101359150565b60008060008060006080868803121561586c57600080fd5b8535945060208601359350604086013561588581615f2e565b925060608601356001600160401b038111156158a057600080fd5b6158ac888289016151f0565b969995985093965092949392505050565b6000806000606084860312156158d257600080fd5b505081359360208301359350604090920135919050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6000815180845261592a816020860160208601615e59565b601f01601f19169290920160200192915050565b60008251615950818460208701615e59565b9190910192915050565b6000855161596c818460208a01615e59565b855190830190615980818360208a01615e59565b8551910190615993818360208901615e59565b84519101906159a6818360208801615e59565b019695505050505050565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8152600083516159e3816017850160208801615e59565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351615a14816028840160208801615e59565b01602801949350505050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03888116825287811660208301528616604082015260c060608201819052600090615a8d90830186886158e9565b60808301949094525060a0015295945050505050565b6001600160a01b038581168252848116602083015283166040820152608060608201819052600090612e9d90830184615912565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090612e9d90830184615912565b6020815260006146be6020830184866158e9565b6020815260006111a66020830184615912565b60208082526016908201527512d15657d514905394d1915494d7d11254d05093115160521b604082015260600190565b6020808252600f908201526e494e56414c49445f4144445245535360881b604082015260600190565b602080825260189082015277494e53554646494349454e545f45524332305f56414c554560401b604082015260600190565b602080825260129082015271494e53554646494349454e545f56414c554560701b604082015260600190565b60208082526010908201526f2a2920a729a322a92faa27afa9a2a62360811b604082015260600190565b6020808252600d908201526c1313d0d2d7d4d3d31117d3d555609a1b604082015260600190565b6020808252600c908201526b20a8282927ab22afa9a2a62360a11b604082015260600190565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b6020808252601d908201527f4e4f4e5f434f4d504c49414e545f4552433732315f5245434549564552000000604082015260600190565b6000808335601e19843603018112615cf857600080fd5b8301803591506001600160401b03821115615d1257600080fd5b60200191503681900382131561510e57600080fd5b604051601f8201601f191681016001600160401b0381118282101715615d4f57615d4f615f18565b604052919050565b60006001600160401b03821115615d7057615d70615f18565b5060051b60200190565b60006001600160401b03821115615d9357615d93615f18565b50601f01601f191660200190565b60008219821115615db457615db4615eec565b500190565b600060ff821660ff84168060ff03821115615dd657615dd6615eec565b019392505050565b600082615dfb57634e487b7160e01b600052601260045260246000fd5b500490565b6000816000190483118215151615615e1a57615e1a615eec565b500290565b600061ffff83811690831681811015615e3a57615e3a615eec565b039392505050565b600082821015615e5457615e54615eec565b500390565b60005b83811015615e74578181015183820152602001615e5c565b838111156123d55750506000910152565b600081615e9457615e94615eec565b506000190190565b600181811c90821680615eb057607f821691505b60208210811415611dbd57634e487b7160e01b600052602260045260246000fd5b6000600019821415615ee557615ee5615eec565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114611ba857600080fd5b8015158114611ba857600080fd5b6001600160e01b031981168114611ba857600080fdfeb309c40027c81d382c3b58d8de24207a34b27e1db369b1434e4a11311f154b5eddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efb89cdd26cddd51301940bf2715f765b626b8a5a9e2681ac62dc83cc2db2530c0a2646970667358221220fa5eda210be05319db90c40fdffb53a7d189847a0ec560bcf8b9352f8b2407f664736f6c63430008070033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.