Polygon Sponsored slots available. Book your slot here!
Contract Overview
Balance:
0 MATIC
MATIC Value:
$0.00
My Name Tag:
Not Available, login to update
Txn Hash |
Method
|
Block
|
From
|
To
|
Value | [Txn Fee] | |||
---|---|---|---|---|---|---|---|---|---|
0x3aecc503dba86bb408e5cccd841ab401fdd95f4a68e699da33c33cef37caa0e8 | 0x60806040 | 24107483 | 491 days 7 hrs ago | 0xb94ab50ef47eeccf71108d3ee350aa58177169b8 | IN | Create: PublicLock | 0 MATIC | 1.030085509238 |
[ Download CSV Export ]
Contract Name:
PublicLock
Compiler Version
v0.8.4+commit.c7e474f2
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; 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'; /** * @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 { function initialize( address payable _lockCreator, uint _expirationDuration, address _tokenAddress, uint _keyPrice, uint _maxNumberOfKeys, string calldata _lockName ) public initializer() { MixinFunds._initializeMixinFunds(_tokenAddress); MixinDisable._initializeMixinDisable(); MixinLockCore._initializeMixinLockCore(_lockCreator, _expirationDuration, _keyPrice, _maxNumberOfKeys); MixinLockMetadata._initializeMixinLockMetadata(_lockName); MixinERC721Enumerable._initializeMixinERC721Enumerable(); MixinRefunds._initializeMixinRefunds(); MixinRoles._initializeMixinRoles(_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; /** * @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); /** * @notice Used to disable lock before migrating keys and/or destroying contract. * @dev Throws if called by other than a lock manager. * @dev Throws if lock contract has already been disabled. */ function disableLock() external; /** * @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 zero (0) 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); /** * @notice Find the tokenId for a given user * @return The tokenId of the NFT, else returns 0 * @param _account The address of the key owner */ function getTokenIdFor( address _account ) external view returns (uint); /** * @dev Returns the key's ExpirationTimestamp field for a given owner. * @param _keyOwner address of the user for whom we search the key * @dev Returns 0 if the owner has never owned a key for this lock */ function keyExpirationTimestampFor( address _keyOwner ) 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 _value the number of tokens to pay for this purchase >= the current keyPrice - any applicable discount * (_value is ignored when using ETH) * @param _recipient address of the recipient of the purchased key * @param _referrer address of the user making the referral * @param _keyManager optional address to grant managing rights to a specific address on creation * @param _data arbitrary data populated by the front-end which initiated the sale * @dev Throws if lock is disabled. Throws if lock is sold-out. Throws if _recipient == address(0). * @dev Setting _value to keyPrice exactly doubles as a security feature. That way if a Lock manager 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 _value, address _recipient, address _referrer, address _keyManager, bytes calldata _data ) external payable; /** * @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 a key owner would need to pay in order to * transfer the key to another account. This is pro-rated so the fee goes down * overtime. * @dev Throws if _keyOwner does not have a valid key * @param _keyOwner The owner 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( address _keyOwner, 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 _keyOwner The key owner to whom we wish to send a refund to * @param amount The amount to refund 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( address _keyOwner, 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 getCancelAndRefundValueFor( 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; ///=================================================================== /// 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 isAlive() external view returns (bool ); 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; /// @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); function setApprovalForAll(address operator, bool _approved) external; 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); }
// 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; import './MixinFunds.sol'; import './MixinRoles.sol'; /** * @title Mixin allowing the Lock owner to disable a Lock (preventing new purchases) * and then destroy it. * @author HardlyDifficult * @dev `Mixins` are a design pattern seen in the 0x contracts. It simply * separates logically groupings of code to ease readability. */ contract MixinDisable is MixinRoles, MixinFunds { // Used to disable payable functions when deprecating an old lock bool public isAlive; event Disable(); function _initializeMixinDisable( ) internal { isAlive = true; } // Only allow usage when contract is Alive modifier onlyIfAlive() { require(isAlive, 'LOCK_DEPRECATED'); _; } /** * @dev Used to disable lock before migrating keys and/or destroying contract */ function disableLock() external onlyLockManager onlyIfAlive { emit Disable(); isAlive = false; } 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; } /// @notice Enumerate NFTs assigned to an owner /// @dev Throws if `_index` >= `balanceOf(_keyOwner)` or if /// `_keyOwner` is the zero address, representing invalid NFTs. /// @param _keyOwner An address where we are interested in NFTs owned by them /// @param _index A counter less than `balanceOf(_keyOwner)` /// @return The token identifier for the `_index`th NFT assigned to `_keyOwner`, /// (sort order not specified) function tokenOfOwnerByIndex( address _keyOwner, uint256 _index ) public view returns (uint256) { require(_index < balanceOf(_keyOwner) && _keyOwner != address(0), 'ONLY_ONE_KEY_PER_OWNER'); return getTokenIdFor(_keyOwner); } 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 { tokenAddress = _tokenAddress; 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 onlyKeyGranterOrManager { for(uint i = 0; i < _recipients.length; i++) { address recipient = _recipients[i]; uint expirationTimestamp = _expirationTimestamps[i]; address keyManager = _keyManagers[i]; require(recipient != address(0), 'INVALID_ADDRESS'); Key storage toKey = keyByOwner[recipient]; require(expirationTimestamp > toKey.expirationTimestamp, 'ALREADY_OWNS_KEY'); uint idTo = toKey.tokenId; if(idTo == 0) { _assignNewTokenId(toKey); idTo = toKey.tokenId; _recordOwner(recipient, idTo); } // Set the key Manager _setKeyManagerOf(idTo, keyManager); emit KeyManagerChanged(idTo, keyManager); toKey.expirationTimestamp = expirationTimestamp; // trigger event emit Transfer( address(0), // This is a creation. recipient, idTo ); } } 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 ); event KeyManagerChanged(uint indexed _tokenId, address indexed _newManager); // Keys // Each owner can have at most exactly one key // TODO: could we use public here? (this could be confusing though because it getter will // return 0 values when missing a key) mapping (address => Key) internal keyByOwner; // Each tokenId can have at most exactly one owner at a time. // Returns 0 if the token does not exist // TODO: once we decouple tokenId from owner address (incl in js), then we can consider // merging this with totalSupply into an array instead. 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)) private managerToOperatorApproved; // Ensure that the caller is the keyManager of the key // or that the caller has been approved // for ownership of that key modifier onlyKeyManagerOrApproved( uint _tokenId ) { require( _isKeyManager(_tokenId, msg.sender) || _isApproved(_tokenId, msg.sender) || isApprovedForAll(_ownerOf[_tokenId], msg.sender), 'ONLY_KEY_MANAGER_OR_APPROVED' ); _; } // Ensures that an owner owns or has owned a key in the past modifier ownsOrHasOwnedKey( address _keyOwner ) { require( keyByOwner[_keyOwner].expirationTimestamp > 0, 'HAS_NEVER_OWNED_KEY' ); _; } // Ensures that an owner has a valid key modifier hasValidKey( address _user ) { require( getHasValidKey(_user), 'KEY_NOT_VALID' ); _; } // Ensures that a key has an owner modifier isKey( uint _tokenId ) { require( _ownerOf[_tokenId] != address(0), 'NO_SUCH_KEY' ); _; } // Ensure that the caller owns the key modifier onlyKeyOwner( uint _tokenId ) { require( ownerOf(_tokenId) == msg.sender, 'ONLY_KEY_OWNER' ); _; } /** * 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 getHasValidKey(_keyOwner) ? 1 : 0; } /** * Checks if the user has a non-expired key. */ function getHasValidKey( address _keyOwner ) public view returns (bool isValid) { isValid = keyByOwner[_keyOwner].expirationTimestamp > block.timestamp; // use hook if it exists if(address(onValidKeyHook) != address(0)) { isValid = onValidKeyHook.hasValidKey( address(this), _keyOwner, keyByOwner[_keyOwner].expirationTimestamp, isValid ); } } /** * @notice Find the tokenId for a given user * @return The tokenId of the NFT, else returns 0 */ function getTokenIdFor( address _account ) public view returns (uint) { return keyByOwner[_account].tokenId; } /** * @dev Returns the key's ExpirationTimestamp field for a given owner. * @param _keyOwner address of the user for whom we search the key * @dev Returns 0 if the owner has never owned a key for this lock */ function keyExpirationTimestampFor( address _keyOwner ) public view returns (uint) { return keyByOwner[_keyOwner].expirationTimestamp; } // Returns the owner of a given tokenId 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 onlyIfAlive 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 isKey(_tokenId) returns (address) { 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) { uint tokenId = keyByOwner[_owner].tokenId; address keyManager = keyManagerOf[tokenId]; if(keyManager == address(0)) { return managerToOperatorApproved[_owner][_operator]; } else { return managerToOperatorApproved[keyManager][_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; } } /** * Assigns the key a new tokenId (from totalSupply) if it does not already have * one assigned. */ function _assignNewTokenId( Key storage _key ) internal { if (_key.tokenId == 0) { // This is a brand new owner // We increment the tokenId counter _totalSupply++; // we assign the incremented `_totalSupply` as the tokenId for the new key _key.tokenId = _totalSupply; } } /** * Records the owner of a given tokenId */ function _recordOwner( address _keyOwner, uint _tokenId ) internal { // check expiration ts should be set to know if owner had previously registered a key Key memory key = keyByOwner[_keyOwner]; if(key.expirationTimestamp == 0 ) { numberOfOwners++; } // We register the owner of the tokenID _ownerOf[_tokenId] = _keyOwner; } /** * @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 { address tokenOwner = ownerOf(_tokenId); require(tokenOwner != address(0), 'NON_EXISTENT_KEY'); Key storage key = keyByOwner[tokenOwner]; uint formerTimestamp = key.expirationTimestamp; bool validKey = getHasValidKey(tokenOwner); if(_addTime) { if(validKey) { key.expirationTimestamp = formerTimestamp + _deltaT; } else { key.expirationTimestamp = block.timestamp + _deltaT; } } else { key.expirationTimestamp = formerTimestamp - _deltaT; } emit ExpirationChanged(_tokenId, _deltaT, _addTime); } /** * @dev Sets or unsets the approval of a given operator * An operator is allowed to transfer all tokens of the sender on their behalf * @param _to operator address to set the approval * @param _approved representing the status of the approval to be set */ function setApprovalForAll( address _to, bool _approved ) public onlyIfAlive { require(_to != msg.sender, 'APPROVE_SELF'); managerToOperatorApproved[msg.sender][_to] = _approved; emit ApprovalForAll(msg.sender, _to, _approved); } /** * @dev Checks if the given user is approved to transfer the tokenId. */ function _isApproved( uint _tokenId, address _user ) internal view returns (bool) { return approved[_tokenId] == _user; } /** * @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); } } /** * @dev Change the maximum number of keys the lock can edit * @param _maxNumberOfKeys uint the maximum number of keys */ function setMaxNumberOfKeys (uint _maxNumberOfKeys) external onlyLockManager { require (_maxNumberOfKeys > _totalSupply, "maxNumberOfKeys is smaller than existing 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 zero (0) for a non-expiring key */ function setExpirationDuration(uint _newExpirationDuration) external onlyLockManager { expirationDuration = _newExpirationDuration; } uint256[1000] 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; // Ensure that the Lock has not sold all of its keys. modifier notSoldOut() { require(maxNumberOfKeys > _totalSupply, 'LOCK_SOLD_OUT'); _; } modifier onlyLockManagerOrBeneficiary() { require( isLockManager(msg.sender) || msg.sender == beneficiary, 'ONLY_LOCK_MANAGER_OR_BENEFICIARY' ); _; } function _initializeMixinLockCore( address payable _beneficiary, uint _expirationDuration, uint _keyPrice, uint _maxNumberOfKeys ) internal { require(_expirationDuration <= 100 * 365 * 24 * 60 * 60, 'MAX_EXPIRATION_100_YEARS'); unlockProtocol = IUnlock(msg.sender); // Make sure we link back to Unlock's smart contract. beneficiary = _beneficiary; expirationDuration = _expirationDuration == 0 ? type(uint).max : _expirationDuration; keyPrice = _keyPrice; maxNumberOfKeys = _maxNumberOfKeys; } // The version number of the current implementation on this network function publicLockVersion( ) public pure returns (uint16) { return 9; } /** * @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. * * TODO: consider allowing anybody to trigger this as long as it goes to owner anyway? * -- however be wary of draining funds as it breaks the `cancelAndRefund` and `expireAndRefundFor` * use cases. */ 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 onlyIfAlive { uint oldKeyPrice = keyPrice; address oldTokenAddress = tokenAddress; require( _tokenAddress == address(0) || IERC20Upgradeable(_tokenAddress).totalSupply() > 0, 'INVALID_TOKEN' ); 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 onlyLockManagerOrBeneficiary returns (bool) { return IERC20Upgradeable(tokenAddress).approve(_spender, _amount); } uint256[1000] 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)) { address tokenOwner = ownerOf(_tokenId); uint expirationTimestamp = keyExpirationTimestampFor(tokenOwner); return onTokenURIHook.tokenURI( address(this), msg.sender, tokenOwner, _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 private _gasRefundValue = 0; /** * @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 Purchase 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 _recipient address of the recipient of the purchased key * @param _referrer address of the user making the referral * @param _keyManager optional address 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 _value, address _recipient, address _referrer, address _keyManager, bytes calldata _data ) external payable onlyIfAlive notSoldOut { require(_recipient != address(0), 'INVALID_ADDRESS'); // Assign the key Key storage toKey = keyByOwner[_recipient]; uint idTo = toKey.tokenId; uint newTimeStamp; if (idTo == 0) { // Assign a new tokenId (if a new owner or previously transferred) _assignNewTokenId(toKey); // refresh the cached value idTo = toKey.tokenId; _recordOwner(_recipient, idTo); // check for a non-expiring key if (expirationDuration == type(uint).max) { newTimeStamp = type(uint).max; } else { newTimeStamp = block.timestamp + expirationDuration; } toKey.expirationTimestamp = newTimeStamp; // set key manager _setKeyManagerOf(idTo, _keyManager); // trigger event emit Transfer( address(0), // This is a creation. _recipient, idTo ); } else if (toKey.expirationTimestamp > block.timestamp) { // prevent re-purchase of a valid non-expiring key require(toKey.expirationTimestamp != type(uint).max, 'A valid non-expiring key can not be purchased twice'); // This is an existing owner trying to extend their key newTimeStamp = toKey.expirationTimestamp + expirationDuration; toKey.expirationTimestamp = newTimeStamp; emit RenewKeyPurchase(_recipient, newTimeStamp); } else { // This is an existing owner trying to renew their expired or cancelled key if(expirationDuration == type(uint).max) { newTimeStamp = type(uint).max; } else { newTimeStamp = block.timestamp + expirationDuration; } toKey.expirationTimestamp = newTimeStamp; _setKeyManagerOf(idTo, _keyManager); emit RenewKeyPurchase(_recipient, newTimeStamp); } uint inMemoryKeyPrice = _purchasePriceFor(_recipient, _referrer, _data); try unlockProtocol.recordKeyPurchase(inMemoryKeyPrice, _referrer) {} catch { // emit missing unlock emit UnlockCallFailed(address(this), address(unlockProtocol)); } // We explicitly allow for greater amounts of ETH or tokens to allow 'donations' uint pricePaid; if(tokenAddress != address(0)) { pricePaid = _value; IERC20Upgradeable token = IERC20Upgradeable(tokenAddress); token.transferFrom(msg.sender, address(this), pricePaid); } else { pricePaid = msg.value; } require(pricePaid >= inMemoryKeyPrice, 'INSUFFICIENT_VALUE'); if(address(onKeyPurchaseHook) != address(0)) { onKeyPurchaseHook.onKeyPurchase(msg.sender, _recipient, _referrer, _data, inMemoryKeyPrice, pricePaid); } // refund gas 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); } } /** * @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 calldata _data ) external view returns (uint minKeyPrice) { minKeyPrice = _purchasePriceFor(_recipient, _referrer, _data); } /** * @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 ) internal view returns (uint minKeyPrice) { if(address(onKeyPurchaseHook) != address(0)) { minKeyPrice = onKeyPurchaseHook.keyPurchasePrice(msg.sender, _recipient, _referrer, _data); } else { minKeyPrice = keyPrice; } return minKeyPrice; } uint256[1000] 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'; contract MixinRefunds is MixinRoles, MixinFunds, MixinLockCore, MixinKeys { // 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 ket and perform a refund and cancellation * of the key */ function expireAndRefundFor( address payable _keyOwner, uint amount ) external onlyLockManager hasValidKey(_keyOwner) { _cancelAndRefund(_keyOwner, 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 onlyKeyManagerOrApproved(_tokenId) { address payable keyOwner = payable(ownerOf(_tokenId)); uint refund = _getCancelAndRefundValue(keyOwner); _cancelAndRefund(keyOwner, 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 Determines how much of a refund a key owner would receive if they issued * a cancelAndRefund block.timestamp. * Note that due to the time required to mine a tx, the actual refund amount will be lower * than what the user reads from this call. */ function getCancelAndRefundValueFor( address _keyOwner ) external view returns (uint refund) { return _getCancelAndRefundValue(_keyOwner); } /** * @dev cancels the key for the given keyOwner and sends the refund to the msg.sender. */ function _cancelAndRefund( address payable _keyOwner, uint refund ) internal { Key storage key = keyByOwner[_keyOwner]; emit CancelKey(key.tokenId, _keyOwner, msg.sender, refund); // expirationTimestamp is a proxy for hasKey, setting this to `block.timestamp` instead // of 0 so that we can still differentiate hasKey from hasValidKey. key.expirationTimestamp = block.timestamp; if (refund > 0) { // Security: doing this last to avoid re-entrancy concerns _transfer(tokenAddress, _keyOwner, refund); } // 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 owner would receive if they issued * a cancelAndRefund now. * @param _keyOwner The owner of the key check the refund value for. */ function _getCancelAndRefundValue( address _keyOwner ) private view hasValidKey(_keyOwner) returns (uint refund) { Key storage key = keyByOwner[_keyOwner]; // return entire purchased price if key is non-expiring if(expirationDuration == type(uint).max) { return keyPrice; } // Math: safeSub is not required since `hasValidKey` confirms timeRemaining is positive uint timeRemaining = key.expirationTimestamp - 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 '@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 { 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 share their key (parent key) by * transferring a portion of the remaining time to a new key (child key). * @param _to The recipient of the shared key * @param _tokenId the key to share * @param _timeShared The amount of time shared */ function shareKey( address _to, uint _tokenId, uint _timeShared ) public onlyIfAlive onlyKeyManagerOrApproved(_tokenId) { require(transferFeeBasisPoints < BASIS_POINTS_DEN, 'KEY_TRANSFERS_DISABLED'); require(_to != address(0), 'INVALID_ADDRESS'); address keyOwner = _ownerOf[_tokenId]; require(getHasValidKey(keyOwner), 'KEY_NOT_VALID'); require(keyOwner != _to, 'TRANSFER_TO_SELF'); Key storage fromKey = keyByOwner[keyOwner]; Key storage toKey = keyByOwner[_to]; uint idTo = toKey.tokenId; uint time; // get the remaining time for the origin key uint timeRemaining = fromKey.expirationTimestamp - block.timestamp; // get the transfer fee based on amount of time wanted share uint fee = getTransferFee(keyOwner, _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(_tokenId, timePlusFee, false); } else { // we have to recalculate the fee here fee = getTransferFee(keyOwner, timeRemaining); time = timeRemaining - fee; fromKey.expirationTimestamp = block.timestamp; // Effectively expiring the key emit ExpireKey(_tokenId); } if (idTo == 0) { _assignNewTokenId(toKey); idTo = toKey.tokenId; _recordOwner(_to, idTo); emit Transfer( address(0), // This is a creation or time-sharing _to, idTo ); } else if (toKey.expirationTimestamp <= block.timestamp) { // reset the key Manager for expired keys _setKeyManagerOf(idTo, address(0)); } // add time to new key _timeMachine(idTo, time, true); // trigger event emit Transfer( keyOwner, _to, idTo ); require(_checkOnERC721Received(keyOwner, _to, idTo, ''), 'NON_COMPLIANT_ERC721_RECEIVER'); } function transferFrom( address _from, address _recipient, uint _tokenId ) public onlyIfAlive hasValidKey(_from) 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'); uint fee = getTransferFee(_from, 0); Key storage fromKey = keyByOwner[_from]; Key storage toKey = keyByOwner[_recipient]; uint previousExpiration = toKey.expirationTimestamp; // subtract the fee from the senders key before the transfer _timeMachine(_tokenId, fee, false); if (toKey.tokenId == 0) { toKey.tokenId = _tokenId; _recordOwner(_recipient, _tokenId); // Clear any previous approvals _clearApproval(_tokenId); } if (previousExpiration <= block.timestamp) { // The recipient did not have a key, or had a key but it expired. The new expiration is the sender's key expiration // An expired key is no longer a valid key, so the new tokenID is the sender's tokenID toKey.expirationTimestamp = fromKey.expirationTimestamp; toKey.tokenId = _tokenId; // Reset the key Manager to the key owner _setKeyManagerOf(_tokenId, address(0)); _recordOwner(_recipient, _tokenId); } else { require(expirationDuration != type(uint).max, 'Recipient already owns a non-expiring key'); // The recipient has a non expired key. We just add them the corresponding remaining time // SafeSub is not required since the if confirms `previousExpiration - block.timestamp` cannot underflow toKey.expirationTimestamp = fromKey.expirationTimestamp + previousExpiration - block.timestamp; } // Effectively expiring the key for the previous owner fromKey.expirationTimestamp = block.timestamp; // Set the tokenID to 0 for the previous owner to avoid duplicates fromKey.tokenId = 0; // trigger event emit Transfer( _from, _recipient, _tokenId ); } /** * @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 ) public returns (bool success) { uint maxTimeToSend = _value * expirationDuration; Key storage fromKey = keyByOwner[msg.sender]; uint timeRemaining = fromKey.expirationTimestamp - block.timestamp; if(maxTimeToSend < timeRemaining) { shareKey(_to, fromKey.tokenId, maxTimeToSend); } else { transferFrom(msg.sender, _to, fromKey.tokenId); } // 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, ''); } /** * @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 a key owner would need to pay in order to * transfer the key to another account. This is pro-rated so the fee goes down * overtime. * @param _keyOwner The owner of the key check the transfer fee for. */ function getTransferFee( address _keyOwner, uint _time ) public view returns (uint) { if(! getHasValidKey(_keyOwner)) { return 0; } else { Key storage key = keyByOwner[_keyOwner]; uint timeToTransfer; uint fee; // Math: safeSub is not required since `hasValidKey` confirms timeToTransfer is positive // this is for standard key transfers if(_time == 0) { timeToTransfer = key.expirationTimestamp - block.timestamp; } else { timeToTransfer = _time; } fee = timeToTransfer * transferFeeBasisPoints / BASIS_POINTS_DEN; return fee; } } /** * @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 modifier onlyLockManager() { require( hasRole(LOCK_MANAGER_ROLE, msg.sender), 'MixinRoles: caller does not have the LockManager role'); _; } modifier onlyKeyGranterOrManager() { require(isKeyGranter(msg.sender) || isLockManager(msg.sender), 'MixinRoles: caller does not have the KeyGranter or LockManager role'); _; } // 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 // 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 // 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 // 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 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); /** * @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 pragma solidity >=0.5.17 <0.8.5; // 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": 200 }, "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":[],"name":"Disable","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":"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":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":"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":"cancelAndRefund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"disableLock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"expirationDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"_keyOwner","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"expireAndRefundFor","outputs":[],"stateMutability":"nonpayable","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":"address","name":"_keyOwner","type":"address"}],"name":"getCancelAndRefundValueFor","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":"address","name":"_account","type":"address"}],"name":"getTokenIdFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_keyOwner","type":"address"},{"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":[],"name":"isAlive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","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":"_keyOwner","type":"address"}],"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":"maxNumberOfKeys","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","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":[{"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":"_value","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"address","name":"_referrer","type":"address"},{"internalType":"address","name":"_keyManager","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":[],"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":[{"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":"_maxNumberOfKeys","type":"uint256"}],"name":"setMaxNumberOfKeys","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","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":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","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":[{"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
6080604052600061201e5534801561001657600080fd5b50615a8280620000276000396000f3fe60806040526004361061044b5760003560e01c806370a0823111610234578063aae4b8f71161012e578063d1bbd49c116100b6578063f0ba60401161007a578063f0ba604014610d4f578063f12c6b6e14610d64578063f3fef3a314610d84578063f5766b3914610da4578063fc42b58f14610dc457600080fd5b8063d1bbd49c14610cb3578063d250348514610ccf578063d32bfb6c14610cef578063d547741f14610d0f578063e985e9c514610d2f57600080fd5b8063b88d4fde116100fd578063b88d4fde14610c1e578063bf4a927014610c3e578063c1c98d0314610c5e578063c87b56dd14610c73578063cb0703c614610c9357600080fd5b8063aae4b8f714610b84578063abdf82ce14610ba4578063b11d7ec114610bde578063b585a6d514610bfe57600080fd5b806393fd1844116101bc578063a217fddf11610180578063a217fddf14610af8578063a22cb46514610b0d578063a2e4cd2e14610b2d578063a375cb0514610b4d578063a9059cbb14610b6457600080fd5b806393fd184414610a5457806395d89b4114610a6b578063970aaeb714610a8057806397aa390a14610ab75780639d76ea5814610ad757600080fd5b806381a3c9431161020357806381a3c943146109bf5780638577a6d5146109df5780638be4b870146109ff5780638ca2fbad14610a1257806391d1485414610a3457600080fd5b806370a082311461094757806374b6c10614610967578063782a4ade1461097e5780637ec2a7241461099e57600080fd5b80632f745c591161034557806352b0f638116102cd5780636207a8da116102915780636207a8da146108b1578063626485a3146108c75780636352211e146108e75780636d8ea5b4146109075780636eadde431461092757600080fd5b806352b0f6381461081a57806352d6a8e41461083a578063550ef3a81461085a578063564aa99d1461087a57806356e0d51f1461089a57600080fd5b806339f469861161031457806339f46986146107685780634136aa351461078857806342842e0e146107a35780634d025fed146107c35780634f6ccce7146107fa57600080fd5b80632f745c59146106e757806330176e131461070757806336568abe1461072757806338af3eed1461074757600080fd5b806318160ddd116103d3578063248a9ca311610397578063248a9ca31461063557806326e9ca07146106655780632af9162a146106865780632d33dd5b146106a65780632f2ff15d146106c757600080fd5b806318160ddd146105a5578063183767da146105bb578063217751bc146105d257806323100509146105f357806323b872dd1461061557600080fd5b8063097ba3331161041a578063097ba333146105085780630aaffd2a146105365780630f15023b1461055657806310e569731461057757806311a4c03a1461058e57600080fd5b806301ffc9a71461045757806306fdde031461048c578063081812fc146104ae578063095ea7b3146104e657600080fd5b3661045257005b600080fd5b34801561046357600080fd5b506104776104723660046152e6565b610de4565b60405190151581526020015b60405180910390f35b34801561049857600080fd5b506104a1610df5565b6040516104839190615672565b3480156104ba57600080fd5b506104ce6104c93660046152aa565b610e84565b6040516001600160a01b039091168152602001610483565b3480156104f257600080fd5b506105066105013660046151b3565b610f00565b005b34801561051457600080fd5b5061052861052336600461503e565b611035565b604051908152602001610483565b34801561054257600080fd5b50610506610551366004614ee1565b611081565b34801561056257600080fd5b50610c83546104ce906001600160a01b031681565b34801561058357600080fd5b50610528610c855481565b34801561059a57600080fd5b50610528610c845481565b3480156105b157600080fd5b50610c8754610528565b3480156105c757600080fd5b506105286124075481565b3480156105de57600080fd5b50610c8a546104ce906001600160a01b031681565b3480156105ff57600080fd5b506105286000805160206159ed83398151915281565b34801561062157600080fd5b506105066106303660046150a0565b611105565b34801561064157600080fd5b506105286106503660046152aa565b60009081526097602052604090206001015490565b34801561067157600080fd5b50610c8b546104ce906001600160a01b031681565b34801561069257600080fd5b506105066106a1366004614ee1565b611434565b3480156106b257600080fd5b50610c89546104ce906001600160a01b031681565b3480156106d357600080fd5b506105066106e23660046152c2565b6114b7565b3480156106f357600080fd5b506105286107023660046151b3565b6114e2565b34801561071357600080fd5b5061050661072236600461531e565b61156a565b34801561073357600080fd5b506105066107423660046152c2565b6115ab565b34801561075357600080fd5b50610c88546104ce906001600160a01b031681565b34801561077457600080fd5b5061050661078336600461544e565b611629565b34801561079457600080fd5b5061089a546104779060ff1681565b3480156107af57600080fd5b506105066107be3660046150a0565b6116a3565b3480156107cf57600080fd5b506104ce6107de3660046152aa565b611078602052600090815260409020546001600160a01b031681565b34801561080657600080fd5b506105286108153660046152aa565b6116be565b34801561082657600080fd5b50610477610835366004614ee1565b611705565b34801561084657600080fd5b50610528610855366004614ee1565b61171f565b34801561086657600080fd5b5061050661087536600461531e565b61172a565b34801561088657600080fd5b50610506610895366004614ee1565b61176b565b3480156108a657600080fd5b506105286127f05481565b3480156108bd57600080fd5b5061201e54610528565b3480156108d357600080fd5b506105066108e23660046152aa565b6117ee565b3480156108f357600080fd5b506104ce6109023660046152aa565b611828565b34801561091357600080fd5b50610477610922366004614ee1565b611844565b34801561093357600080fd5b50610506610942366004614f28565b61191d565b34801561095357600080fd5b50610528610962366004614ee1565b611a3c565b34801561097357600080fd5b50610528610c865481565b34801561098a57600080fd5b5061050661099936600461531e565b611a84565b3480156109aa57600080fd5b50610c8c546104ce906001600160a01b031681565b3480156109cb57600080fd5b506105066109da3660046151f9565b611b03565b3480156109eb57600080fd5b506105066109fa3660046152aa565b611d71565b610506610a0d3660046153de565b611dde565b348015610a1e57600080fd5b50610528600080516020615a2d83398151915281565b348015610a4057600080fd5b50610477610a4f3660046152c2565b61245e565b348015610a6057600080fd5b506105286110775481565b348015610a7757600080fd5b506104a1612489565b348015610a8c57600080fd5b50610528610a9b366004614ee1565b6001600160a01b03166000908152611075602052604090205490565b348015610ac357600080fd5b50610506610ad2366004614efd565b6125c2565b348015610ae357600080fd5b506104b1546104ce906001600160a01b031681565b348015610b0457600080fd5b50610528600081565b348015610b1957600080fd5b50610506610b28366004615186565b612626565b348015610b3957600080fd5b50610506610b483660046152c2565b6126fe565b348015610b5957600080fd5b506105286127f15481565b348015610b7057600080fd5b50610477610b7f3660046151b3565b612896565b348015610b9057600080fd5b50610477610b9f366004614ee1565b612905565b348015610bb057600080fd5b50610528610bbf366004614ee1565b6001600160a01b03166000908152611075602052604090206001015490565b348015610bea57600080fd5b50610506610bf93660046152c2565b61291f565b348015610c0a57600080fd5b50610477610c193660046151b3565b6129e3565b348015610c2a57600080fd5b50610506610c393660046150e0565b612aa7565b348015610c4a57600080fd5b50610506610c593660046152aa565b612b10565b348015610c6a57600080fd5b50610506612bb4565b348015610c7f57600080fd5b506104a1610c8e3660046152aa565b612c41565b348015610c9f57600080fd5b50610506610cae366004614fe3565b612ef8565b348015610cbf57600080fd5b5060405160098152602001610483565b348015610cdb57600080fd5b50610506610cea366004614ee1565b61312b565b348015610cfb57600080fd5b50610506610d0a3660046152aa565b6131ae565b348015610d1b57600080fd5b50610506610d2a3660046152c2565b613232565b348015610d3b57600080fd5b50610477610d4a366004614fab565b613258565b348015610d5b57600080fd5b506105066132f1565b348015610d7057600080fd5b50610506610d7f3660046151c5565b613336565b348015610d9057600080fd5b50610506610d9f3660046151b3565b61368b565b348015610db057600080fd5b50610506610dbf3660046152aa565b613818565b348015610dd057600080fd5b50610528610ddf3660046151b3565b613852565b6000610def826138cb565b92915050565b6114638054610e0390615937565b80601f0160208091040260200160405190810160405280929190818152602001828054610e2f90615937565b8015610e7c5780601f10610e5157610100808354040283529160200191610e7c565b820191906000526020600020905b815481529060010190602001808311610e5f57829003601f168201915b505050505081565b6000818152611076602052604081205482906001600160a01b0316610ede5760405162461bcd60e51b815260206004820152600b60248201526a4e4f5f535543485f4b455960a81b60448201526064015b60405180910390fd5b600083815261107960205260409020546001600160a01b031691505b50919050565b61089a5460ff16610f235760405162461bcd60e51b8152600401610ed590615796565b80610f2e81336138d6565b80610f3e5750610f3e813361394e565b80610f67575060008181526110766020526040902054610f67906001600160a01b031633613258565b610f835760405162461bcd60e51b8152600401610ed5906156d5565b336001600160a01b0384161415610fcb5760405162461bcd60e51b815260206004820152600c60248201526b20a8282927ab22afa9a2a62360a11b6044820152606401610ed5565b60008281526110796020908152604080832080546001600160a01b0319166001600160a01b03888116918217909255611076909352818420549151869492909116917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b6000611078858585858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061397092505050565b95945050505050565b61108a33612905565b806110a05750610c88546001600160a01b031633145b6110bc5760405162461bcd60e51b8152600401610ed59061570c565b6001600160a01b0381166110e25760405162461bcd60e51b8152600401610ed590615685565b610c8880546001600160a01b0319166001600160a01b0392909216919091179055565b61089a5460ff166111285760405162461bcd60e51b8152600401610ed590615796565b8261113281611844565b61114e5760405162461bcd60e51b8152600401610ed5906156ae565b8161115981336138d6565b806111695750611169813361394e565b80611192575060008181526110766020526040902054611192906001600160a01b031633613258565b6111ae5760405162461bcd60e51b8152600401610ed5906156d5565b846001600160a01b03166111c184611828565b6001600160a01b0316146112175760405162461bcd60e51b815260206004820152601c60248201527f5452414e534645525f46524f4d3a204e4f545f4b45595f4f574e4552000000006044820152606401610ed5565b61271061240754106112645760405162461bcd60e51b815260206004820152601660248201527512d15657d514905394d1915494d7d11254d05093115160521b6044820152606401610ed5565b6001600160a01b03841661128a5760405162461bcd60e51b8152600401610ed590615685565b836001600160a01b0316856001600160a01b031614156112df5760405162461bcd60e51b815260206004820152601060248201526f2a2920a729a322a92faa27afa9a2a62360811b6044820152606401610ed5565b60006112ec866000613852565b6001600160a01b03808816600090815261107560205260408082209289168252812060018101549394509192906113269088908690613a1f565b8154611342578682556113398888613b1f565b61134287613b9d565b4281116113705760018084015490830155868255611361876000613bdc565b61136b8888613b1f565b6113f8565b600019610c845414156113d75760405162461bcd60e51b815260206004820152602960248201527f526563697069656e7420616c7265616479206f776e732061206e6f6e2d6578706044820152686972696e67206b657960b81b6064820152608401610ed5565b428184600101546113e89190615861565b6113f291906158dd565b60018301555b426001840155600080845560405188916001600160a01b03808c1692908d1691600080516020615a0d83398151915291a4505050505050505050565b61144c600080516020615a2d8339815191523361245e565b6114685760405162461bcd60e51b8152600401610ed590615741565b6114806000805160206159ed83398151915282613232565b6040516001600160a01b038216907f766f6199fea59554b9ff688e413302b9200f85d74811c053c12d945ac6d8dd7a90600090a250565b6000828152609760205260409020600101546114d38133613c69565b6114dd8383613ccd565b505050565b60006114ed83611a3c565b8210801561150357506001600160a01b03831615155b6115485760405162461bcd60e51b815260206004820152601660248201527527a7262cafa7a722afa5a2acafa822a92fa7aba722a960511b6044820152606401610ed5565b6001600160a01b038316600090815261107560205260409020545b9392505050565b611582600080516020615a2d8339815191523361245e565b61159e5760405162461bcd60e51b8152600401610ed590615741565b6114dd6114658383614dc9565b6001600160a01b038116331461161b5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610ed5565b6116258282613d53565b5050565b611641600080516020615a2d8339815191523361245e565b61165d5760405162461bcd60e51b8152600401610ed590615741565b60408051838152602081018390527fd6867bc538320e67d7bdc35860c27c08486eb490b4fd9b820fff18fb28381d3c910160405180910390a16127f1919091556127f055565b6114dd83838360405180602001604052806000815250612aa7565b6000610c875482106117015760405162461bcd60e51b815260206004820152600c60248201526b4f55545f4f465f52414e474560a01b6044820152606401610ed5565b5090565b6000610def6000805160206159ed8339815191528361245e565b6000610def82613dba565b611742600080516020615a2d8339815191523361245e565b61175e5760405162461bcd60e51b8152600401610ed590615741565b6114dd6114638383614dc9565b611783600080516020615a2d8339815191523361245e565b61179f5760405162461bcd60e51b8152600401610ed590615741565b6117b76000805160206159ed833981519152826114b7565b6040516001600160a01b038216907f684f8a71407db0c334454ebe9c9b288549317893a20b10dc779ec5c118dcd12190600090a250565b611806600080516020615a2d8339815191523361245e565b6118225760405162461bcd60e51b8152600401610ed590615741565b610c8455565b600090815261107660205260409020546001600160a01b031690565b6001600160a01b0380821660009081526110756020526040902060010154610c8b544290911191161561191857610c8b546001600160a01b0383811660008181526110756020526040908190206001015490516370b6638f60e11b815230600482015260248101929092526044820152831515606482015291169063e16cc71e9060840160206040518083038186803b1580156118e057600080fd5b505afa1580156118f4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610def919061528e565b919050565b600054610100900460ff166119385760005460ff161561193c565b303b155b61199f5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610ed5565b600054610100900460ff161580156119c1576000805461ffff19166101011790555b6119ca86613eda565b6119dd61089a805460ff19166001179055565b6119e988888787613fb1565b6119f38383614053565b6119fb614079565b611a076103e86127f055565b611a108861408b565b611a206380ac58cd60e01b614113565b8015611a32576000805461ff00191690555b5050505050505050565b60006001600160a01b038216611a645760405162461bcd60e51b8152600401610ed590615685565b611a6d82611844565b611a78576000611a7b565b60015b60ff1692915050565b611a9c600080516020615a2d8339815191523361245e565b611ab85760405162461bcd60e51b8152600401610ed590615741565b611ac56114648383614dc9565b507f8868e22e84ebf32da89b2ebcb0ac642816304ea3863b257f240df9098719cb978282604051611af792919061565e565b60405180910390a15050565b611b0c33611705565b80611b1b5750611b1b33612905565b611b995760405162461bcd60e51b815260206004820152604360248201527f4d6978696e526f6c65733a2063616c6c657220646f6573206e6f74206861766560448201527f20746865204b65794772616e746572206f72204c6f636b4d616e6167657220726064820152626f6c6560e81b608482015260a401610ed5565b60005b85811015611d68576000878783818110611bc657634e487b7160e01b600052603260045260246000fd5b9050602002016020810190611bdb9190614ee1565b90506000868684818110611bff57634e487b7160e01b600052603260045260246000fd5b9050602002013590506000858585818110611c2a57634e487b7160e01b600052603260045260246000fd5b9050602002016020810190611c3f9190614ee1565b90506001600160a01b038316611c675760405162461bcd60e51b8152600401610ed590615685565b6001600160a01b03831660009081526110756020526040902060018101548311611cc65760405162461bcd60e51b815260206004820152601060248201526f414c52454144595f4f574e535f4b455960801b6044820152606401610ed5565b805480611ce357611cd682614192565b508054611ce38582613b1f565b611ced8184613bdc565b6040516001600160a01b0384169082907f9d2895c45a420624de863a2f437b022d879f457bf7a829044055a10c5a6fd5e390600090a36001820184905560405181906001600160a01b03871690600090600080516020615a0d833981519152908290a450505050508080611d609061596c565b915050611b9c565b50505050505050565b611d89600080516020615a2d8339815191523361245e565b611da55760405162461bcd60e51b8152600401610ed590615741565b6040518181527f0496ed1e61eb69727f9659a8e859288db4758ffb1f744d1c1424634f90a257f49060200160405180910390a161240755565b61089a5460ff16611e015760405162461bcd60e51b8152600401610ed590615796565b610c8754610c865411611e465760405162461bcd60e51b815260206004820152600d60248201526c1313d0d2d7d4d3d31117d3d555609a1b6044820152606401610ed5565b6001600160a01b038516611e6c5760405162461bcd60e51b8152600401610ed590615685565b6001600160a01b0385166000908152611075602052604081208054909181611f0857611e9783614192565b82549150611ea58883613b1f565b600019610c84541415611ebb5750600019611ecc565b610c8454611ec99042615861565b90505b60018301819055611edd8287613bdc565b60405182906001600160a01b038a1690600090600080516020615a0d833981519152908290a4612062565b4283600101541115611fe65760001983600101541415611f865760405162461bcd60e51b815260206004820152603360248201527f412076616c6964206e6f6e2d6578706972696e67206b65792063616e206e6f746044820152722062652070757263686173656420747769636560681b6064820152608401610ed5565b610c84548360010154611f999190615861565b600184018190556040518181529091506001600160a01b038916907f872bd7c99ad5e7b6ed7f0a890f348839cb8e225c9deaa3909afedae54c93d17d9060200160405180910390a2612062565b600019610c84541415611ffc575060001961200d565b610c845461200a9042615861565b90505b6001830181905561201e8287613bdc565b876001600160a01b03167f872bd7c99ad5e7b6ed7f0a890f348839cb8e225c9deaa3909afedae54c93d17d8260405161205991815260200190565b60405180910390a25b60006120a5898988888080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061397092505050565b610c835460405163939d9f1f60e01b8152600481018390526001600160a01b038b8116602483015292935091169063939d9f1f90604401600060405180830381600087803b1580156120f657600080fd5b505af1925050508015612107575060015b61214f57610c83546040516001600160a01b03909116815230907f6b18946261693dfd6c760d986b28ad2238b5b0267f8e5b6bc40a2f998e2f20ac9060200160405180910390a25b6104b1546000906001600160a01b0316156121f957506104b1546040516323b872dd60e01b8152336004820152306024820152604481018c90528b916001600160a01b03169081906323b872dd90606401602060405180830381600087803b1580156121ba57600080fd5b505af11580156121ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121f2919061528e565b50506121fc565b50345b818110156122415760405162461bcd60e51b8152602060048201526012602482015271494e53554646494349454e545f56414c554560701b6044820152606401610ed5565b610c89546001600160a01b0316156122c357610c8954604051639849965760e01b81526001600160a01b03909116906398499657906122909033908e908e908d908d908a908a906004016155ac565b600060405180830381600087803b1580156122aa57600080fd5b505af11580156122be573d6000803e3d6000fd5b505050505b61201e5415612451576104b1546001600160a01b031615612377576104b15461201e546040516323b872dd60e01b815230600482015233602482015260448101919091526001600160a01b039091169081906323b872dd90606401602060405180830381600087803b15801561233857600080fd5b505af115801561234c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612370919061528e565b5050612404565b61201e5460405160009133918381818185875af1925050503d80600081146123bb576040519150601f19603f3d011682016040523d82523d6000602084013e6123c0565b606091505b50509050806124025760405162461bcd60e51b815260206004820152600e60248201526d2932b33ab732103330b4b632b21760911b6044820152606401610ed5565b505b61201e546104b154604080519283526001600160a01b03909116602083015233917f522a883b471164223f18b50f326da8671372b64b4792eac0e63d447e714c3e3b910160405180910390a25b5050505050505050505050565b60009182526097602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6060611464805461249990615937565b1515905061253157610c8360009054906101000a90046001600160a01b03166001600160a01b031663cec410526040518163ffffffff1660e01b815260040160006040518083038186803b1580156124f057600080fd5b505afa158015612504573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261252c919081019061535d565b905090565b611464805461253f90615937565b80601f016020809104026020016040519081016040528092919081815260200182805461256b90615937565b80156125b85780601f1061258d576101008083540402835291602001916125b8565b820191906000526020600020905b81548152906001019060200180831161259b57829003601f168201915b5050505050905090565b6125da600080516020615a2d8339815191523361245e565b6125f65760405162461bcd60e51b8152600401610ed590615741565b8161260081611844565b61261c5760405162461bcd60e51b8152600401610ed5906156ae565b6114dd83836141b6565b61089a5460ff166126495760405162461bcd60e51b8152600401610ed590615796565b6001600160a01b0382163314156126915760405162461bcd60e51b815260206004820152600c60248201526b20a8282927ab22afa9a2a62360a11b6044820152606401610ed5565b33600081815261107a602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b612716600080516020615a2d8339815191523361245e565b6127325760405162461bcd60e51b8152600401610ed590615741565b61089a5460ff166127555760405162461bcd60e51b8152600401610ed590615796565b610c85546104b1546001600160a01b0390811690831615806127e757506000836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156127ad57600080fd5b505afa1580156127c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127e591906153c6565b115b6128235760405162461bcd60e51b815260206004820152600d60248201526c24a72b20a624a22faa27a5a2a760991b6044820152606401610ed5565b610c858490556104b180546001600160a01b0319166001600160a01b0385811691821790925560408051858152602081018890529284169083015260608201527f3615065ccf48367ac483ac86701248e2e5ff55bdd9be845007d34a3b68d719d49060800160405180910390a150505050565b600080610c8454836128a891906158be565b336000908152611075602052604081206001810154929350916128cc9042906158dd565b9050808310156128ea576128e586836000015485613336565b6128f9565b6128f933878460000154611105565b50600195945050505050565b6000610def600080516020615a2d8339815191528361245e565b6000828152611076602052604090205482906001600160a01b03166129745760405162461bcd60e51b815260206004820152600b60248201526a4e4f5f535543485f4b455960a81b6044820152606401610ed5565b61297e83336138d6565b8061298d575061298d33612905565b6129d95760405162461bcd60e51b815260206004820152601f60248201527f554e415554484f52495a45445f4b45595f4d414e414745525f555044415445006044820152606401610ed5565b6114dd8383613bdc565b60006129ee33612905565b80612a045750610c88546001600160a01b031633145b612a205760405162461bcd60e51b8152600401610ed59061570c565b6104b15460405163095ea7b360e01b81526001600160a01b038581166004830152602482018590529091169063095ea7b390604401602060405180830381600087803b158015612a6f57600080fd5b505af1158015612a83573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611563919061528e565b612ab2848484611105565b612abe848484846142b1565b612b0a5760405162461bcd60e51b815260206004820152601d60248201527f4e4f4e5f434f4d504c49414e545f4552433732315f52454345495645520000006044820152606401610ed5565b50505050565b612b28600080516020615a2d8339815191523361245e565b612b445760405162461bcd60e51b8152600401610ed590615741565b610c87548111612bae5760405162461bcd60e51b815260206004820152602f60248201527f6d61784e756d6265724f664b65797320697320736d616c6c6572207468616e2060448201526e6578697374696e6720737570706c7960881b6064820152608401610ed5565b610c8655565b612bcc600080516020615a2d8339815191523361245e565b612be85760405162461bcd60e51b8152600401610ed590615741565b61089a5460ff16612c0b5760405162461bcd60e51b8152600401610ed590615796565b6040517f25a311358326fb18c62efc24bc28d3126acee8d2a67fd8b2145b757dc8bd1bc190600090a161089a805460ff19169055565b606080806000612c5030614370565b905060608515612c6a57612c63866145cb565b9250612c7d565b6040518060200160405280600081525092505b610c8c546001600160a01b031615612d6b576000612c9a87611828565b90506000612cc1826001600160a01b03166000908152611075602052604090206001015490565b610c8c5460405163988b93ad60e01b81523060048201523360248201526001600160a01b038581166044830152606482018c90526084820184905292935091169063988b93ad9060a40160006040518083038186803b158015612d2357600080fd5b505afa158015612d37573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612d5f919081019061535d565b98975050505050505050565b6114658054612d7990615937565b15159050612e2f57610c8360009054906101000a90046001600160a01b03166001600160a01b031663a998e9fb6040518163ffffffff1660e01b815260040160006040518083038186803b158015612dd057600080fd5b505afa158015612de4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612e0c919081019061535d565b9350604051806040016040528060018152602001602f60f81b8152509050612ee2565b6114658054612e3d90615937565b80601f0160208091040260200160405190810160405280929190818152602001828054612e6990615937565b8015612eb65780601f10612e8b57610100808354040283529160200191612eb6565b820191906000526020600020905b815481529060010190602001808311612e9957829003601f168201915b505050505093506040518060200160405280600081525090506040518060200160405280600081525091505b612eee84838386614713565b9695505050505050565b612f10600080516020615a2d8339815191523361245e565b612f2c5760405162461bcd60e51b8152600401610ed590615741565b6001600160a01b0384161580612f4b57506001600160a01b0384163b15155b612f975760405162461bcd60e51b815260206004820152601860248201527f494e56414c49445f4f4e5f4b45595f534f4c445f484f4f4b00000000000000006044820152606401610ed5565b6001600160a01b0383161580612fb657506001600160a01b0383163b15155b6130025760405162461bcd60e51b815260206004820152601a60248201527f494e56414c49445f4f4e5f4b45595f43414e43454c5f484f4f4b0000000000006044820152606401610ed5565b6001600160a01b038216158061302157506001600160a01b0382163b15155b61306d5760405162461bcd60e51b815260206004820152601960248201527f494e56414c49445f4f4e5f56414c49445f4b45595f484f4f4b000000000000006044820152606401610ed5565b6001600160a01b038116158061308c57506001600160a01b0381163b15155b6130d85760405162461bcd60e51b815260206004820152601960248201527f494e56414c49445f4f4e5f544f4b454e5f5552495f484f4f4b000000000000006044820152606401610ed5565b610c8980546001600160a01b039586166001600160a01b031991821617909155610c8a805494861694821694909417909355610c8c8054918516918416919091179055610c8b8054919093169116179055565b613143600080516020615a2d8339815191523361245e565b61315f5760405162461bcd60e51b8152600401610ed590615741565b613177600080516020615a2d833981519152826114b7565b6040516001600160a01b038216907f91d5c045d5bd98bf59a379b259ebca05b93bf79af1845fdf87e3172385d4c7f790600090a250565b806131b981336138d6565b806131c957506131c9813361394e565b806131f25750600081815261107660205260409020546131f2906001600160a01b031633613258565b61320e5760405162461bcd60e51b8152600401610ed5906156d5565b600061321983611828565b9050600061322682613dba565b9050612b0a82826141b6565b60008281526097602052604090206001015461324e8133613c69565b6114dd8383613d53565b6001600160a01b03808316600090815261107560209081526040808320548084526110789092528220549192909116806132be575050506001600160a01b03808316600090815261107a602090815260408083209385168352929052205460ff16610def565b6001600160a01b03908116600090815261107a602090815260408083209387168352929052205460ff169150610def9050565b613309600080516020615a2d833981519152336115ab565b60405133907f42885193b8178d25fca25a38e6fcc93918501e91be06d85e0c8afb3bad95238090600090a2565b61089a5460ff166133595760405162461bcd60e51b8152600401610ed590615796565b8161336481336138d6565b806133745750613374813361394e565b8061339d57506000818152611076602052604090205461339d906001600160a01b031633613258565b6133b95760405162461bcd60e51b8152600401610ed5906156d5565b61271061240754106134065760405162461bcd60e51b815260206004820152601660248201527512d15657d514905394d1915494d7d11254d05093115160521b6044820152606401610ed5565b6001600160a01b03841661342c5760405162461bcd60e51b8152600401610ed590615685565b600083815261107660205260409020546001600160a01b031661344e81611844565b61346a5760405162461bcd60e51b8152600401610ed5906156ae565b846001600160a01b0316816001600160a01b031614156134bf5760405162461bcd60e51b815260206004820152601060248201526f2a2920a729a322a92faa27afa9a2a62360811b6044820152606401610ed5565b6001600160a01b038082166000908152611075602052604080822092881682528120805460018401549192909181906134f99042906158dd565b90506000613507878a613852565b90506000613515828b615861565b9050828110156135335789935061352e8b826000613a1f565b61357e565b61353d8884613852565b915061354982846158dd565b4260018901556040519094508b907f59f2fe866dd27a1c2d34115520888c3150365cbc931aab97fa88c4b9ab40b79590600090a25b846135c55761358c86614192565b8554945061359a8c86613b1f565b60405185906001600160a01b038e1690600090600080516020615a0d833981519152908290a46135db565b428660010154116135db576135db856000613bdc565b6135e785856001613a1f565b848c6001600160a01b0316896001600160a01b0316600080516020615a0d83398151915260405160405180910390a4613631888d87604051806020016040528060008152506142b1565b61367d5760405162461bcd60e51b815260206004820152601d60248201527f4e4f4e5f434f4d504c49414e545f4552433732315f52454345495645520000006044820152606401610ed5565b505050505050505050505050565b61369433612905565b806136aa5750610c88546001600160a01b031633145b6136c65760405162461bcd60e51b8152600401610ed59061570c565b60006001600160a01b0383166136dd575047613757565b6040516370a0823160e01b81523060048201526001600160a01b038416906370a082319060240160206040518083038186803b15801561371c57600080fd5b505afa158015613730573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061375491906153c6565b90505b600082158061376557508183115b156137b457600082116137ad5760405162461bcd60e51b815260206004820152601060248201526f4e4f545f454e4f5547485f46554e445360801b6044820152606401610ed5565b50806137b7565b50815b610c88546040518281526001600160a01b039182169186169033907f342e7ff505a8a0364cd0dc2ff195c315e43bce86b204846ecd36913e117b109e9060200160405180910390a4610c8854612b0a9085906001600160a01b031683614745565b613830600080516020615a2d8339815191523361245e565b61384c5760405162461bcd60e51b8152600401610ed590615741565b61201e55565b600061385d83611844565b61386957506000610def565b6001600160a01b0383166000908152611075602052604081209080846138a05742836001015461389991906158dd565b91506138a4565b8491505b61271061240754836138b691906158be565b6138c0919061589e565b9350610def92505050565b6000610def82614781565b600082815261107860205260408120546001600160a01b03838116911614806139395750600083815261107860205260409020546001600160a01b03161580156139395750816001600160a01b031661392e84611828565b6001600160a01b0316145b1561394657506001610def565b506000610def565b60009182526110796020526040909120546001600160a01b0391821691161490565b610c89546000906001600160a01b031615613a1357610c895460405163221c1fd160e01b81526001600160a01b039091169063221c1fd1906139bc9033908890889088906004016155f7565b60206040518083038186803b1580156139d457600080fd5b505afa1580156139e8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a0c91906153c6565b9050611563565b50610c85549392505050565b6000613a2a84611828565b90506001600160a01b038116613a755760405162461bcd60e51b815260206004820152601060248201526f4e4f4e5f4558495354454e545f4b455960801b6044820152606401610ed5565b6001600160a01b03811660009081526110756020526040812060018101549091613a9e84611844565b90508415613aca578015613ac057613ab68683615861565b6001840155613ada565b613ab68642615861565b613ad486836158dd565b60018401555b60408051878152861515602082015288917fe9408df99703ae33a9d01185bcad328ea8683fb1f920da9c30959c192f21b5b3910160405180910390a250505050505050565b6001600160a01b0382166000908152611075602090815260409182902082518084019093528054835260010154908201819052613b6d576110778054906000613b678361596c565b91905055505b5060009081526110766020526040902080546001600160a01b0319166001600160a01b0392909216919091179055565b600081815261107960205260409020546001600160a01b031615613bd95760008181526110796020526040902080546001600160a01b03191690555b50565b600082815261107860205260409020546001600160a01b038281169116146116255760008281526110786020526040902080546001600160a01b0319166001600160a01b038316179055613c2f82613b9d565b6040516001600160a01b0382169083907f9d2895c45a420624de863a2f437b022d879f457bf7a829044055a10c5a6fd5e390600090a35050565b613c73828261245e565b61162557613c8b816001600160a01b0316601461478c565b613c9683602061478c565b604051602001613ca7929190615537565b60408051601f198184030181529082905262461bcd60e51b8252610ed591600401615672565b613cd7828261245e565b6116255760008281526097602090815260408083206001600160a01b03851684529091529020805460ff19166001179055613d0f3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b613d5d828261245e565b156116255760008281526097602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b600081613dc681611844565b613de25760405162461bcd60e51b8152600401610ed5906156ae565b6001600160a01b038316600090815261107560205260409020610c84546000191415613e1457610c8554925050610efa565b6000428260010154613e2691906158dd565b9050610c84546127f15482613e3b9190615861565b10613e4b57610c85549350613e6b565b610c845481610c8554613e5e91906158be565b613e68919061589e565b93505b6127f1541580613e8a5750610c84546127f154613e889083615861565b105b15613ed25760006127106127f054610c8554613ea691906158be565b613eb0919061589e565b905080851115613ecb57613ec481866158dd565b9450613ed0565b600094505b505b505050919050565b6104b180546001600160a01b0319166001600160a01b0383169081179091551580613f7557506000816001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015613f3b57600080fd5b505afa158015613f4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f7391906153c6565b115b613bd95760405162461bcd60e51b815260206004820152600d60248201526c24a72b20a624a22faa27a5a2a760991b6044820152606401610ed5565b63bbf81e008311156140055760405162461bcd60e51b815260206004820152601860248201527f4d41585f45585049524154494f4e5f3130305f594541525300000000000000006044820152606401610ed5565b610c838054336001600160a01b031991821617909155610c8880549091166001600160a01b038616179055821561403c5782614040565b6000195b610c8455610c8591909155610c86555050565b61405b61496d565b6140686114638383614dc9565b50611625635b5e139f60e01b614113565b61408963780e9d6360e01b614113565b565b6140a3600080516020615a2d833981519152806149a4565b6140c96000805160206159ed833981519152600080516020615a2d8339815191526149a4565b6140d281612905565b6140ee576140ee600080516020615a2d833981519152826149ef565b6140f781611705565b613bd957613bd96000805160206159ed833981519152826149ef565b6001600160e01b0319808216141561416d5760405162461bcd60e51b815260206004820152601c60248201527f4552433136353a20696e76616c696420696e74657266616365206964000000006044820152606401610ed5565b6001600160e01b0319166000908152606560205260409020805460ff19166001179055565b8054613bd957610c8780549060006141a98361596c565b9091555050610c87549055565b6001600160a01b0382166000818152611075602052604090819020805491519092339290917f0a7068a9989857441c039a14a42b67ed71dd1fcfe5a9b17cc87b252e47bce5289061420a9087815260200190565b60405180910390a44260018201558115614236576104b154614236906001600160a01b03168484614745565b610c8a546001600160a01b0316156114dd57610c8a5460405163b499b6c560e01b81523360048201526001600160a01b038581166024830152604482018590529091169063b499b6c590606401600060405180830381600087803b15801561429d57600080fd5b505af1158015611d68573d6000803e3d6000fd5b60006001600160a01b0384163b6142ca57506001614368565b604051630a85bd0160e11b81526000906001600160a01b0386169063150b7a02906142ff9033908a908990899060040161562b565b602060405180830381600087803b15801561431957600080fd5b505af115801561432d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143519190615302565b6001600160e01b031916630a85bd0160e11b149150505b949350505050565b604080518082018252601081526f181899199a1a9b1b9c1cb0b131b232b360811b60208201528151602a80825260608281019094526001600160a01b0385169291600091602082018180368337019050509050600360fc1b816000815181106143e957634e487b7160e01b600052603260045260246000fd5b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061442657634e487b7160e01b600052603260045260246000fd5b60200101906001600160f81b031916908160001a90535060005b60148110156145c2578260048561445884600c615861565b6020811061447657634e487b7160e01b600052603260045260246000fd5b1a60f81b6001600160f81b031916901c60f81c60ff16815181106144aa57634e487b7160e01b600052603260045260246000fd5b01602001516001600160f81b031916826144c58360026158be565b6144d0906002615861565b815181106144ee57634e487b7160e01b600052603260045260246000fd5b60200101906001600160f81b031916908160001a905350828461451283600c615861565b6020811061453057634e487b7160e01b600052603260045260246000fd5b825191901a600f1690811061455557634e487b7160e01b600052603260045260246000fd5b01602001516001600160f81b031916826145708360026158be565b61457b906003615861565b8151811061459957634e487b7160e01b600052603260045260246000fd5b60200101906001600160f81b031916908160001a905350806145ba8161596c565b915050614440565b50949350505050565b606081806145f25750506040805180820190915260018152600360fc1b6020820152919050565b8260005b811561461c57806146068161596c565b91506146159050600a8361589e565b91506145f6565b6000816001600160401b0381111561464457634e487b7160e01b600052604160045260246000fd5b6040519080825280601f01601f19166020018201604052801561466e576020820181803683370190505b509050815b8415614709576146846001826158dd565b90506000614693600a8761589e565b61469e90600a6158be565b6146a890876158dd565b6146b3906030615879565b905060008160f81b9050808484815181106146de57634e487b7160e01b600052603260045260246000fd5b60200101906001600160f81b031916908160001a905350614700600a8861589e565b96505050614673565b5095945050505050565b60608484848460405160200161472c94939291906154e0565b6040516020818303038152906040529050949350505050565b80156114dd576001600160a01b03831661476c576114dd6001600160a01b038316826149f9565b82612b0a6001600160a01b0382168484614b12565b6000610def82614b64565b6060600061479b8360026158be565b6147a6906002615861565b6001600160401b038111156147cb57634e487b7160e01b600052604160045260246000fd5b6040519080825280601f01601f1916602001820160405280156147f5576020820181803683370190505b509050600360fc1b8160008151811061481e57634e487b7160e01b600052603260045260246000fd5b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061485b57634e487b7160e01b600052603260045260246000fd5b60200101906001600160f81b031916908160001a905350600061487f8460026158be565b61488a906001615861565b90505b600181111561491e576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106148cc57634e487b7160e01b600052603260045260246000fd5b1a60f81b8282815181106148f057634e487b7160e01b600052603260045260246000fd5b60200101906001600160f81b031916908160001a90535060049490941c9361491781615920565b905061488d565b5083156115635760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610ed5565b600054610100900460ff166149945760405162461bcd60e51b8152600401610ed5906157bf565b61499c614b89565b614089614b89565b600082815260976020526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b6116258282613ccd565b80471015614a495760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610ed5565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114614a96576040519150601f19603f3d011682016040523d82523d6000602084013e614a9b565b606091505b50509050806114dd5760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610ed5565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526114dd908490614bb0565b60006001600160e01b03198216637965db0b60e01b1480610def5750610def82614c82565b600054610100900460ff166140895760405162461bcd60e51b8152600401610ed5906157bf565b6000614c05826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614cbe9092919063ffffffff16565b8051909150156114dd5780806020019051810190614c23919061528e565b6114dd5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610ed5565b60006301ffc9a760e01b6001600160e01b031983161480610def5750506001600160e01b03191660009081526065602052604090205460ff1690565b6060614368848460008585843b614d175760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610ed5565b600080866001600160a01b03168587604051614d3391906154c4565b60006040518083038185875af1925050503d8060008114614d70576040519150601f19603f3d011682016040523d82523d6000602084013e614d75565b606091505b5091509150614d85828286614d90565b979650505050505050565b60608315614d9f575081611563565b825115614daf5782518084602001fd5b8160405162461bcd60e51b8152600401610ed59190615672565b828054614dd590615937565b90600052602060002090601f016020900481019282614df75760008555614e3d565b82601f10614e105782800160ff19823516178555614e3d565b82800160010185558215614e3d579182015b82811115614e3d578235825591602001919060010190614e22565b506117019291505b808211156117015760008155600101614e45565b60008083601f840112614e6a578182fd5b5081356001600160401b03811115614e80578182fd5b6020830191508360208260051b8501011115614e9b57600080fd5b9250929050565b60008083601f840112614eb3578182fd5b5081356001600160401b03811115614ec9578182fd5b602083019150836020828501011115614e9b57600080fd5b600060208284031215614ef2578081fd5b8135611563816159b3565b60008060408385031215614f0f578081fd5b8235614f1a816159b3565b946020939093013593505050565b600080600080600080600060c0888a031215614f42578283fd5b8735614f4d816159b3565b9650602088013595506040880135614f64816159b3565b9450606088013593506080880135925060a08801356001600160401b03811115614f8c578283fd5b614f988a828b01614ea2565b989b979a50959850939692959293505050565b60008060408385031215614fbd578182fd5b8235614fc8816159b3565b91506020830135614fd8816159b3565b809150509250929050565b60008060008060808587031215614ff8578384fd5b8435615003816159b3565b93506020850135615013816159b3565b92506040850135615023816159b3565b91506060850135615033816159b3565b939692955090935050565b60008060008060608587031215615053578384fd5b843561505e816159b3565b9350602085013561506e816159b3565b925060408501356001600160401b03811115615088578283fd5b61509487828801614ea2565b95989497509550505050565b6000806000606084860312156150b4578081fd5b83356150bf816159b3565b925060208401356150cf816159b3565b929592945050506040919091013590565b600080600080608085870312156150f5578182fd5b8435615100816159b3565b93506020850135615110816159b3565b92506040850135915060608501356001600160401b03811115615131578182fd5b8501601f81018713615141578182fd5b803561515461514f8261583a565b61580a565b818152886020838501011115615168578384fd5b81602084016020830137908101602001929092525092959194509250565b60008060408385031215615198578182fd5b82356151a3816159b3565b91506020830135614fd8816159c8565b60008060408385031215614f0f578182fd5b6000806000606084860312156151d9578081fd5b83356151e4816159b3565b95602085013595506040909401359392505050565b60008060008060008060608789031215615211578384fd5b86356001600160401b0380821115615227578586fd5b6152338a838b01614e59565b9098509650602089013591508082111561524b578586fd5b6152578a838b01614e59565b9096509450604089013591508082111561526f578384fd5b5061527c89828a01614e59565b979a9699509497509295939492505050565b60006020828403121561529f578081fd5b8151611563816159c8565b6000602082840312156152bb578081fd5b5035919050565b600080604083850312156152d4578182fd5b823591506020830135614fd8816159b3565b6000602082840312156152f7578081fd5b8135611563816159d6565b600060208284031215615313578081fd5b8151611563816159d6565b60008060208385031215615330578182fd5b82356001600160401b03811115615345578283fd5b61535185828601614ea2565b90969095509350505050565b60006020828403121561536e578081fd5b81516001600160401b03811115615383578182fd5b8201601f81018413615393578182fd5b80516153a161514f8261583a565b8181528560208385010111156153b5578384fd5b6110788260208301602086016158f4565b6000602082840312156153d7578081fd5b5051919050565b60008060008060008060a087890312156153f6578384fd5b863595506020870135615408816159b3565b94506040870135615418816159b3565b93506060870135615428816159b3565b925060808701356001600160401b03811115615442578283fd5b61527c89828a01614ea2565b60008060408385031215615460578182fd5b50508035926020909101359150565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b600081518084526154b08160208601602086016158f4565b601f01601f19169290920160200192915050565b600082516154d68184602087016158f4565b9190910192915050565b600085516154f2818460208a016158f4565b855190830190615506818360208a016158f4565b85519101906155198183602089016158f4565b845191019061552c8183602088016158f4565b019695505050505050565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081526000835161556f8160178501602088016158f4565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516155a08160288401602088016158f4565b01602801949350505050565b6001600160a01b03888116825287811660208301528616604082015260c0606082018190526000906155e1908301868861546f565b60808301949094525060a0015295945050505050565b6001600160a01b038581168252848116602083015283166040820152608060608201819052600090612eee90830184615498565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090612eee90830184615498565b60208152600061436860208301848661546f565b6020815260006115636020830184615498565b6020808252600f908201526e494e56414c49445f4144445245535360881b604082015260600190565b6020808252600d908201526c12d15657d393d517d590531251609a1b604082015260600190565b6020808252601c908201527f4f4e4c595f4b45595f4d414e414745525f4f525f415050524f56454400000000604082015260600190565b6020808252818101527f4f4e4c595f4c4f434b5f4d414e414745525f4f525f42454e4546494349415259604082015260600190565b60208082526035908201527f4d6978696e526f6c65733a2063616c6c657220646f6573206e6f74206861766560408201527420746865204c6f636b4d616e6167657220726f6c6560581b606082015260800190565b6020808252600f908201526e1313d0d2d7d11154149150d0551151608a1b604082015260600190565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b604051601f8201601f191681016001600160401b03811182821017156158325761583261599d565b604052919050565b60006001600160401b038211156158535761585361599d565b50601f01601f191660200190565b6000821982111561587457615874615987565b500190565b600060ff821660ff84168060ff0382111561589657615896615987565b019392505050565b6000826158b957634e487b7160e01b81526012600452602481fd5b500490565b60008160001904831182151516156158d8576158d8615987565b500290565b6000828210156158ef576158ef615987565b500390565b60005b8381101561590f5781810151838201526020016158f7565b83811115612b0a5750506000910152565b60008161592f5761592f615987565b506000190190565b600181811c9082168061594b57607f821691505b60208210811415610efa57634e487b7160e01b600052602260045260246000fd5b600060001982141561598057615980615987565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114613bd957600080fd5b8015158114613bd957600080fd5b6001600160e01b031981168114613bd957600080fdfeb309c40027c81d382c3b58d8de24207a34b27e1db369b1434e4a11311f154b5eddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efb89cdd26cddd51301940bf2715f765b626b8a5a9e2681ac62dc83cc2db2530c0a2646970667358221220128d0e026063bdaacd55371528a91080ef08e7a20bf688474680e90fdefcf96364736f6c63430008040033
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.