Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
0x60806040 | 15394693 | 774 days ago | IN | 0 ETH | 0.00928642 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
HookERC721MultiVaultImplV1
Compiler Version
v0.8.10+commit.fc410830
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT // // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // ██████████████ ██████████████ // ██████████████ ▄▄████████████████▄▄ ▐█████████████▌ // ██████████████ ▄█████████████████████████████▄ ██████████████ // ██████████▀ ▄█████████████████████████████████ ██████████████▌ // ██████▀ ▄██████████████████████████████████▀ ▄███████████████ // ███▀ ██████████████████████████████████▀ ▄████████████████ // ▀▀ ████████████████████████████████▀▀ ▄█████████████████▌ // █████████████████████▀▀▀▀▀▀▀ ▄▄███████████████████▀ // ██████████████████▀ ▄▄▄█████████████████████████████▀ // ████████████████▀ ▄█████████████████████████████████▀ ██▄ // ▐███████████████▀ ▄██████████████████████████████████▀ █████▄ // ██████████████▀ ▄█████████████████████████████████▀ ▄████████ // ██████████████▀ ███████████████████████████████▀ ▄████████████ // ▐█████████████▌ ▀▀▀▀████████████████████▀▀▀▀ █████████████▌ // ██████████████ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ pragma solidity ^0.8.10; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "./interfaces/IHookERC721Vault.sol"; import "./interfaces/IERC721FlashLoanReceiver.sol"; import "./interfaces/IHookProtocol.sol"; import "./lib/Entitlements.sol"; import "./lib/Signatures.sol"; import "./mixin/EIP712.sol"; /// @title HookMultiVault-implementation of a Vault for multiple assets within a NFT collection, with entitlements. /// @author Jake Nyquist - [email protected] /// @custom:coauthor Regynald [email protected] /// @notice HookVault holds a multiple NFT asset in escrow on behalf of multiple beneficial owners. Other contracts /// are able to register "entitlements" for a fixed period of time on the asset, which give them the ability to /// change the vault's owner. /// @dev This contract implements ERC721Receiver /// This contract views the tokenId for the asset on the ERC721 contract as the corresponding assetId for that asset /// when deposited into the vault contract HookERC721MultiVaultImplV1 is IHookERC721Vault, EIP712, Initializable, ReentrancyGuard { /// ---------------- STORAGE ---------------- /// /// @dev these are the NFT contract address and tokenId the vault is covering IERC721 internal _nftContract; struct Asset { address beneficialOwner; address operator; uint32 expiry; } /// @dev the current entitlement applied to each asset, which includes the beneficialOwner /// for the asset /// if the entitled operator field is non-null, it means an unreleased entitlement has been /// applied; however, that entitlement could still be expired (if block.timestamp > entitlement.expiry) mapping(uint32 => Asset) internal assets; // Mapping from asset ID to approved address mapping(uint32 => address) private _assetApprovals; IHookProtocol internal _hookProtocol; /// Upgradeable Implementations cannot have a constructor, so we call the initialize instead; constructor() {} ///-constructor function initialize(address nftContract, address hookAddress) public initializer { setAddressForEipDomain(hookAddress); _nftContract = IERC721(nftContract); _hookProtocol = IHookProtocol(hookAddress); } /// ---------------- PUBLIC FUNCTIONS ---------------- /// /// /// @dev See {IERC165-supportsInterface}. /// function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == type(IHookERC721Vault).interfaceId || interfaceId == type(IERC165).interfaceId; } /// @dev See {IHookERC721Vault-withdrawalAsset}. /// @dev withdrawals can only be performed to the beneficial owner if there are no entitlements function withdrawalAsset(uint32 assetId) public virtual nonReentrant { require( !hasActiveEntitlement(assetId), "withdrawalAsset-the asset cannot be withdrawn with an active entitlement" ); require( assets[assetId].beneficialOwner == msg.sender, "withdrawalAsset-only the beneficial owner can withdrawal an asset" ); _nftContract.safeTransferFrom( address(this), assets[assetId].beneficialOwner, _assetTokenId(assetId) ); emit AssetWithdrawn(assetId, msg.sender, assets[assetId].beneficialOwner); } /// @dev See {IHookERC721Vault-imposeEntitlement}. /// @dev The entitlement must be signed by the current beneficial owner of the contract. Anyone can submit the /// entitlement function imposeEntitlement( address operator, uint32 expiry, uint32 assetId, uint8 v, bytes32 r, bytes32 s ) public virtual { // check that the asset has a current beneficial owner // before creating a new entitlement require( assets[assetId].beneficialOwner != address(0), "imposeEntitlement-beneficial owner must be set to impose an entitlement" ); // the beneficial owner of an asset is able to set any entitlement on their own asset // as long as it has not already been committed to someone else. _verifyAndRegisterEntitlement(operator, expiry, assetId, v, r, s); } /// @dev See {IHookERC721Vault-grantEntitlement}. /// @dev The entitlement must be sent by the current beneficial owner function grantEntitlement(Entitlements.Entitlement calldata entitlement) external { require( assets[entitlement.assetId].beneficialOwner == msg.sender || _assetApprovals[entitlement.assetId] == msg.sender, "grantEntitlement-only the beneficial owner or approved operator can grant an entitlement" ); // the beneficial owner of an asset is able to directly set any entitlement on their own asset // as long as it has not already been committed to someone else. _registerEntitlement( entitlement.assetId, entitlement.operator, entitlement.expiry, msg.sender ); } /// @dev See {IERC721Receiver-onERC721Received}. /// /// Always returns `IERC721Receiver.onERC721Received.selector`. function onERC721Received( address operator, // this arg is the address of the operator address from, uint256 tokenId, bytes calldata data ) external virtual override returns (bytes4) { require( tokenId <= type(uint32).max, "onERC721Received-tokenId is out of range" ); /// (1) When receiving a nft from the ERC-721 contract this vault covers, create a new entitlement entry /// with the sender as the beneficial owner to track the asset within the vault. /// /// (1a) If the transfer additionally specifies data (i.e. an abi-encoded entitlement), the entitlement will /// be imposed via that transfer, including a new beneficial owner. /// NOTE: this is an opinionated approach, however, the authors believe that anyone with the ability to /// transfer the asset into this contract could also trivially transfer the asset to another address /// they control and then deposit, so allowing this method of setting the beneficial owner simply /// saves gas and has no practical impact on the rights a hypothetical sender has regarding the asset. /// /// (2) If another nft is sent to the contract, we should verify that airdrops are allowed to this vault; /// if they are disabled, we should not return the selector, otherwise we can allow them. /// /// IMPORTANT: If an unrelated contract is currently holding the asset on behalf of an owner and then /// subsequently transfers the asset into the contract, it needs to manually call (setBeneficialOwner) /// after making this call to ensure that the true owner of the asset is known to the vault. Otherwise, /// the owner will lose the ability to reclaim their asset. Alternatively, they could pass an entitlement /// in pre-populated with the correct beneficial owner, which will give that owner the ability to reclaim /// the asset. if (msg.sender == address(_nftContract)) { // There is no need to check if we currently have this token or an entitlement set. // Even if the contract were able to get into this state, it should still accept the asset // which will allow it to enforce the entitlement. // If additional data is sent with the transfer, we attempt to parse an entitlement from it. // this allows the entitlement to be registered ahead of time. if (data.length > 0) { /// If the abi-encoded parameters are 3 words long, assume no approved operator was provided. if (data.length == 3 * 32) { // Decode the order, signature from `data`. If `data` does not encode such parameters, this // will throw. ( address beneficialOwner, address entitledOperator, uint32 expirationTime ) = abi.decode(data, (address, address, uint32)); // if someone has the asset, they should be able to set whichever beneficial owner they'd like. // equally, they could transfer the asset first to themselves and subsequently grant a specific // entitlement, which is equivalent to this. _registerEntitlement( uint32(tokenId), entitledOperator, expirationTime, beneficialOwner ); } else { /// additionally decode the approved operator from the payload. The abi decoder ensures that the /// there are exactly 4 parameters ( address beneficialOwner, address entitledOperator, uint32 expirationTime, address approvedOperator ) = abi.decode(data, (address, address, uint32, address)); _registerEntitlement( uint32(tokenId), entitledOperator, expirationTime, beneficialOwner ); /// if an approved operator is provided with this contract call, set the approval accepting it for the /// same reason. _approve(approvedOperator, uint32(tokenId)); } } else { _setBeneficialOwner(uint32(tokenId), from); } emit AssetReceived( this.getBeneficialOwner(uint32(tokenId)), operator, msg.sender, uint32(tokenId) ); } else { // If we're receiving an airdrop or other asset uncovered by escrow to this address, we should ensure // that this is allowed by our current settings. require( _hookProtocol.getCollectionConfig( address(_nftContract), keccak256("vault.multiAirdropsAllowed") ), "onERC721Received-non-escrow asset returned when airdrops are disabled" ); } return this.onERC721Received.selector; } /// @dev See {IHookERC721Vault-flashLoan}. function flashLoan( uint32 assetId, address receiverAddress, bytes calldata params ) external override nonReentrant { IERC721FlashLoanReceiver receiver = IERC721FlashLoanReceiver( receiverAddress ); require(receiverAddress != address(0), "flashLoan-zero address"); require( _assetOwner(assetId) == address(this), "flashLoan-asset not in vault" ); require( msg.sender == assets[assetId].beneficialOwner, "flashLoan-not called by the asset owner" ); require( !_hookProtocol.getCollectionConfig( address(_nftContract), keccak256("vault.flashLoanDisabled") ), "flashLoan-flashLoan feature disabled for this contract" ); // (1) store a hash of our current entitlement state as a snapshot to diff bytes32 startState = keccak256(abi.encode(assets[assetId])); // (2) send the flashloan contract the vaulted NFT _nftContract.safeTransferFrom( address(this), receiverAddress, _assetTokenId(assetId) ); // (3) call the flashloan contract, giving it a chance to do whatever it wants // NOTE: The flashloan contract MUST approve this vault contract as an operator // for the nft, such that we're able to make sure it has arrived. require( receiver.executeOperation( address(_nftContract), _assetTokenId(assetId), msg.sender, address(this), params ), "flashLoan-the flash loan contract must return true" ); // (4) return the nft back into the vault // Use transferFrom instead of safeTransfer from because transferFrom // would modify our state ( it calls erc721Receiver ). and because we know // for sure that this contract can handle ERC-721s. _nftContract.transferFrom( receiverAddress, address(this), _assetTokenId(assetId) ); // (5) sanity check to ensure the asset was actually returned to the vault. // this is a concern because its possible that the safeTransferFrom implemented by // some contract fails silently require(_assetOwner(assetId) == address(this)); // (6) additional sanity check to ensure that the internal state of // the entitlement has not somehow been modified during the flash loan, for example // via some re-entrancy attack or by sending the asset back into the contract // prematurely require( startState == keccak256(abi.encode(assets[assetId])), "flashLoan-entitlement state cannot be modified" ); // (7) emit an event to record the flashloan emit AssetFlashLoaned( assets[assetId].beneficialOwner, assetId, receiverAddress ); } /// @dev See {IHookVault-entitlementExpiration}. function entitlementExpiration(uint32 assetId) external view returns (uint32) { if (!hasActiveEntitlement(assetId)) { return 0; } else { return assets[assetId].expiry; } } /// @dev See {IHookERC721Vault-getBeneficialOwner}. function getBeneficialOwner(uint32 assetId) external view returns (address) { return assets[assetId].beneficialOwner; } /// @dev See {IHookERC721Vault-getHoldsAsset}. function getHoldsAsset(uint32 assetId) external view returns (bool) { return _assetOwner(assetId) == address(this); } function assetAddress(uint32) external view returns (address) { return address(_nftContract); } /// @dev returns the underlying token ID for a given asset. In this case /// the tokenId == the assetId function assetTokenId(uint32 assetId) external view returns (uint256) { return _assetTokenId(assetId); } /// @dev See {IHookERC721Vault-setBeneficialOwner}. /// setBeneficialOwner can only be called by the entitlementContract if there is an activeEntitlement. function setBeneficialOwner(uint32 assetId, address newBeneficialOwner) public virtual { if (hasActiveEntitlement(assetId)) { require( msg.sender == assets[assetId].operator, "setBeneficialOwner-only the contract with the active entitlement can update the beneficial owner" ); } else { require( msg.sender == assets[assetId].beneficialOwner, "setBeneficialOwner-only the current owner can update the beneficial owner" ); } _setBeneficialOwner(assetId, newBeneficialOwner); } /// @dev See {IHookERC721Vault-clearEntitlement}. /// @dev This can only be called if an entitlement currently exists, otherwise it would be a no-op function clearEntitlement(uint32 assetId) public { require( hasActiveEntitlement(assetId), "clearEntitlement-an active entitlement must exist" ); require( msg.sender == assets[assetId].operator, "clearEntitlement-only the entitled address can clear the entitlement" ); _clearEntitlement(assetId); } /// @dev See {IHookERC721Vault-clearEntitlementAndDistribute}. /// @dev The entitlement must be exist, and must be called by the {operator}. The operator can specify a /// intended receiver, which should match the beneficialOwner. The function will throw if /// the receiver and owner do not match. /// @param assetId the id of the specific vaulted asset /// @param receiver the intended receiver of the asset function clearEntitlementAndDistribute(uint32 assetId, address receiver) external nonReentrant { require( assets[assetId].beneficialOwner == receiver, "clearEntitlementAndDistribute-Only the beneficial owner can receive the asset" ); require( receiver != address(0), "clearEntitlementAndDistribute-assets cannot be sent to null address" ); clearEntitlement(assetId); IERC721(_nftContract).safeTransferFrom( address(this), receiver, _assetTokenId(assetId) ); emit AssetWithdrawn(assetId, receiver, assets[assetId].beneficialOwner); } /// @dev Validates that a specific signature is actually the entitlement /// EIP-712 signed by the beneficial owner specified in the entitlement. function validateEntitlementSignature( address operator, uint32 expiry, uint32 assetId, uint8 v, bytes32 r, bytes32 s ) public view { bytes32 entitlementHash = _getEIP712Hash( Entitlements.getEntitlementStructHash( Entitlements.Entitlement({ beneficialOwner: assets[assetId].beneficialOwner, expiry: expiry, operator: operator, assetId: assetId, vaultAddress: address(this) }) ) ); address signer = ecrecover(entitlementHash, v, r, s); require(signer != address(0), "recovered address is null"); require( signer == assets[assetId].beneficialOwner, "validateEntitlementSignature --- not signed by beneficialOwner" ); } /// /// @dev See {IHookVault-approveOperator}. /// function approveOperator(address to, uint32 assetId) public virtual override { address beneficialOwner = assets[assetId].beneficialOwner; require( to != beneficialOwner, "approve-approval to current beneficialOwner" ); require( msg.sender == beneficialOwner, "approve-approve caller is not current beneficial owner" ); _approve(to, assetId); } /// @dev See {IHookVault-getApprovedOperator}. function getApprovedOperator(uint32 assetId) public view virtual override returns (address) { return _assetApprovals[assetId]; } /// @dev Approve `to` to operate on `tokenId` /// /// Emits an {Approval} event. /// @param to the address to approve /// @param assetId the assetId on which the address will be approved function _approve(address to, uint32 assetId) internal virtual { _assetApprovals[assetId] = to; emit Approval(assets[assetId].beneficialOwner, to, assetId); } /// ---------------- INTERNAL/PRIVATE FUNCTIONS ---------------- /// /// @notice Verify that an entitlement is properly signed and apply it to the asset if able. /// @dev The entitlement must be signed by the beneficial owner of the asset in order for it to be considered valid /// @param operator the operator to entitle /// @param expiry the duration of the entitlement /// @param assetId the id of the asset within the vault /// @param v sig v /// @param r sig r /// @param s sig s function _verifyAndRegisterEntitlement( address operator, uint32 expiry, uint32 assetId, uint8 v, bytes32 r, bytes32 s ) private { validateEntitlementSignature(operator, expiry, assetId, v, r, s); _registerEntitlement( assetId, operator, expiry, assets[assetId].beneficialOwner ); } function _registerEntitlement( uint32 assetId, address operator, uint32 expiry, address beneficialOwner ) internal { require( !hasActiveEntitlement(assetId), "_registerEntitlement-existing entitlement must be cleared before registering a new one" ); require( expiry > block.timestamp, "_registerEntitlement-entitlement must expire in the future" ); assets[assetId] = Asset({ operator: operator, expiry: expiry, beneficialOwner: beneficialOwner }); emit EntitlementImposed(assetId, operator, expiry, beneficialOwner); } function _clearEntitlement(uint32 assetId) private { assets[assetId].expiry = 0; assets[assetId].operator = address(0); emit EntitlementCleared(assetId, assets[assetId].beneficialOwner); } function hasActiveEntitlement(uint32 assetId) public view returns (bool) { /// Although we do clear the expiry in _clearEntitlement, making the second half of the AND redundant, /// we choose to include it here because we rely on this field being null to clear an entitlement. return block.timestamp < assets[assetId].expiry && assets[assetId].operator != address(0); } function getCurrentEntitlementOperator(uint32 assetId) external view returns (bool, address) { bool isActive = hasActiveEntitlement(assetId); address operator = assets[assetId].operator; return (isActive, operator); } /// @dev determine the owner of a specific asset according to is contract based /// on that assets assetId within this vault. /// /// this function can be overridden if the assetId -> tokenId mapping is modified. function _assetOwner(uint32 assetId) internal view returns (address) { return _nftContract.ownerOf(_assetTokenId(assetId)); } /// @dev get the token id based on an asset's ID /// /// this function can be overridden if the assetId -> tokenId mapping is modified. function _assetTokenId(uint32 assetId) internal view virtual returns (uint256) { return assetId; } /// @dev sets the new beneficial owner for a particular asset within the vault function _setBeneficialOwner(uint32 assetId, address newBeneficialOwner) internal { require( newBeneficialOwner != address(0), "_setBeneficialOwner-new owner is the zero address" ); assets[assetId].beneficialOwner = newBeneficialOwner; _approve(address(0), assetId); emit BeneficialOwnerSet(assetId, newBeneficialOwner, msg.sender); } }
// 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 IAccessControl { /** * @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 (last updated v4.6.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.2; import "../../utils/Address.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 proxied contracts do not make use of 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. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ``` * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * 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 prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool */ uint8 private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint8 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. */ modifier initializer() { bool isTopLevelCall = _setInitializedVersion(1); if (isTopLevelCall) { _initializing = true; } _; if (isTopLevelCall) { _initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original * initialization step. This is essential to configure modules that are added through upgrades and that require * initialization. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. */ modifier reinitializer(uint8 version) { bool isTopLevelCall = _setInitializedVersion(version); if (isTopLevelCall) { _initializing = true; } _; if (isTopLevelCall) { _initializing = false; emit Initialized(version); } } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { require(_initializing, "Initializable: contract is not initializing"); _; } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. */ function _disableInitializers() internal virtual { _setInitializedVersion(type(uint8).max); } function _setInitializedVersion(uint8 version) private returns (bool) { // 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, and for the lowest level // of initializers, because in other contexts the contract may have been reentered. if (_initializing) { require( version == 1 && !Address.isContract(address(this)), "Initializable: contract is already initialized" ); return false; } else { require(_initialized < version, "Initializable: contract is already initialized"); _initialized = version; return true; } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @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); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes calldata data ) external; /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool _approved) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (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 IERC721Receiver { /** * @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 `IERC721Receiver.onERC721Received.selector`. */ function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// 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 IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // ██████████████ ██████████████ // ██████████████ ▄▄████████████████▄▄ ▐█████████████▌ // ██████████████ ▄█████████████████████████████▄ ██████████████ // ██████████▀ ▄█████████████████████████████████ ██████████████▌ // ██████▀ ▄██████████████████████████████████▀ ▄███████████████ // ███▀ ██████████████████████████████████▀ ▄████████████████ // ▀▀ ████████████████████████████████▀▀ ▄█████████████████▌ // █████████████████████▀▀▀▀▀▀▀ ▄▄███████████████████▀ // ██████████████████▀ ▄▄▄█████████████████████████████▀ // ████████████████▀ ▄█████████████████████████████████▀ ██▄ // ▐███████████████▀ ▄██████████████████████████████████▀ █████▄ // ██████████████▀ ▄█████████████████████████████████▀ ▄████████ // ██████████████▀ ███████████████████████████████▀ ▄████████████ // ▐█████████████▌ ▀▀▀▀████████████████████▀▀▀▀ █████████████▌ // ██████████████ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ pragma solidity ^0.8.10; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; /// @title Flash Loan Operator Interface (ERC-721) /// @author Jake [email protected] /// @dev contracts that will utilize vaulted assets in flash loans should implement this interface in order to /// receive the asset. Users may want to receive the asset within a single block to claim airdrops, participate /// in governance, and other things with their assets. /// /// The implementer may do whatever they like with the vaulted NFT within the executeOperation method, /// so long as they approve the vault (passed as a param) to operate the underlying NFT. The Vault /// will move the asset back into the vault after executionOperation returns, and also validate that /// it is the owner of the asset. /// /// The flashloan receiver is able to abort a flashloan by returning false from the executeOperation method. interface IERC721FlashLoanReceiver is IERC721Receiver { /// @notice the method that contains the operations to be performed with the loaned asset /// @dev executeOperation is called immediately after the asset is transferred to this contract. After return, /// the asset is returned to the vault by the vault contract. The executeOperation implementation MUST /// approve the {vault} to operate the transferred NFT /// i.e. `IERC721(nftContract).setApprovalForAll(vault, true);` /// /// @param nftContract the address of the underlying erc-721 asset /// @param tokenId the address of the received erc-721 asset /// @param beneficialOwner the current beneficialOwner of the vault, who initialized the flashLoan /// @param vault the address of the vault performing the flashloan (in most cases, equal to msg.sender) /// @param params additional params passed by the caller into the flashloan function executeOperation( address nftContract, uint256 tokenId, address beneficialOwner, address vault, bytes calldata params ) external returns (bool); }
// SPDX-License-Identifier: MIT // // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // ██████████████ ██████████████ // ██████████████ ▄▄████████████████▄▄ ▐█████████████▌ // ██████████████ ▄█████████████████████████████▄ ██████████████ // ██████████▀ ▄█████████████████████████████████ ██████████████▌ // ██████▀ ▄██████████████████████████████████▀ ▄███████████████ // ███▀ ██████████████████████████████████▀ ▄████████████████ // ▀▀ ████████████████████████████████▀▀ ▄█████████████████▌ // █████████████████████▀▀▀▀▀▀▀ ▄▄███████████████████▀ // ██████████████████▀ ▄▄▄█████████████████████████████▀ // ████████████████▀ ▄█████████████████████████████████▀ ██▄ // ▐███████████████▀ ▄██████████████████████████████████▀ █████▄ // ██████████████▀ ▄█████████████████████████████████▀ ▄████████ // ██████████████▀ ███████████████████████████████▀ ▄████████████ // ▐█████████████▌ ▀▀▀▀████████████████████▀▀▀▀ █████████████▌ // ██████████████ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ pragma solidity ^0.8.10; import "./IHookVault.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; /// @title Hook ERC-721 Vault interface /// @author Jake [email protected] /// @custom:coauthor Regynald [email protected] /// /// @dev the IHookERC721 vault is an extension of the standard IHookVault /// specifically designed to hold and receive ERC721 Tokens. /// /// FLASH LOAN - /// (1) beneficial owners are able to borrow the vaulted asset for a single function call /// (2) to borrow the asset, they must implement and deploy a {IERC721FlashLoanReceiver} /// contract, and then call the flashLoan method. /// (3) At the end of the flashLoan, we ensure the asset is still owned by the vault. interface IHookERC721Vault is IHookVault, IERC721Receiver { /// @notice emitted after an asset is flash loaned by its beneficial owner. /// @dev only one asset can be flash loaned at a time, and that asset is /// denoted by the tokenId emitted. event AssetFlashLoaned(address owner, uint256 tokenId, address flashLoanImpl); /// @notice the tokenID of the underlying ERC721 token; function assetTokenId(uint32 assetId) external view returns (uint256); /// @notice flashLoans the vaulted asset to another contract for use and return to the vault. Only the owner /// may perform the flashloan /// @dev the flashloan receiver can perform arbitrary logic, but must approve the vault as an operator /// before returning. /// @param receiverAddress the contract which implements the {IERC721FlashLoanReceiver} interface to utilize the /// asset while it is loaned out /// @param params calldata params to forward to the receiver function flashLoan( uint32 assetId, address receiverAddress, bytes calldata params ) external; }
// SPDX-License-Identifier: MIT // // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // ██████████████ ██████████████ // ██████████████ ▄▄████████████████▄▄ ▐█████████████▌ // ██████████████ ▄█████████████████████████████▄ ██████████████ // ██████████▀ ▄█████████████████████████████████ ██████████████▌ // ██████▀ ▄██████████████████████████████████▀ ▄███████████████ // ███▀ ██████████████████████████████████▀ ▄████████████████ // ▀▀ ████████████████████████████████▀▀ ▄█████████████████▌ // █████████████████████▀▀▀▀▀▀▀ ▄▄███████████████████▀ // ██████████████████▀ ▄▄▄█████████████████████████████▀ // ████████████████▀ ▄█████████████████████████████████▀ ██▄ // ▐███████████████▀ ▄██████████████████████████████████▀ █████▄ // ██████████████▀ ▄█████████████████████████████████▀ ▄████████ // ██████████████▀ ███████████████████████████████▀ ▄████████████ // ▐█████████████▌ ▀▀▀▀████████████████████▀▀▀▀ █████████████▌ // ██████████████ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ pragma solidity ^0.8.10; import "@openzeppelin/contracts/access/IAccessControl.sol"; /// @title HookProtocol configuration and access control repository /// @author Jake [email protected] /// @custom:coauthor Regynald [email protected] /// /// @dev it is critically important that the particular protocol implementation /// is correct as, if it is not, all assets contained within protocol contracts /// can be easily compromised. interface IHookProtocol is IAccessControl { /// @notice the address of the deployed CoveredCallFactory used by the protocol function coveredCallContract() external view returns (address); /// @notice the address of the deployed VaultFactory used by the protocol function vaultContract() external view returns (address); /// @notice callable function that reverts when the protocol is paused function throwWhenPaused() external; /// @notice the standard weth address on this chain /// @dev these are values for popular chains: /// mainnet: 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 /// kovan: 0xd0a1e359811322d97991e03f863a0c30c2cf029c /// ropsten: 0xc778417e063141139fce010982780140aa0cd5ab /// rinkeby: 0xc778417e063141139fce010982780140aa0cd5ab /// @return the weth address function getWETHAddress() external view returns (address); /// @notice get a configuration flag with a specific key for a collection /// @param collectionAddress the collection for which to lookup a configuration flag /// @param conf the config identifier for the configuration flag /// @return the true or false value of the config function getCollectionConfig(address collectionAddress, bytes32 conf) external view returns (bool); }
// SPDX-License-Identifier: MIT // // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // ██████████████ ██████████████ // ██████████████ ▄▄████████████████▄▄ ▐█████████████▌ // ██████████████ ▄█████████████████████████████▄ ██████████████ // ██████████▀ ▄█████████████████████████████████ ██████████████▌ // ██████▀ ▄██████████████████████████████████▀ ▄███████████████ // ███▀ ██████████████████████████████████▀ ▄████████████████ // ▀▀ ████████████████████████████████▀▀ ▄█████████████████▌ // █████████████████████▀▀▀▀▀▀▀ ▄▄███████████████████▀ // ██████████████████▀ ▄▄▄█████████████████████████████▀ // ████████████████▀ ▄█████████████████████████████████▀ ██▄ // ▐███████████████▀ ▄██████████████████████████████████▀ █████▄ // ██████████████▀ ▄█████████████████████████████████▀ ▄████████ // ██████████████▀ ███████████████████████████████▀ ▄████████████ // ▐█████████████▌ ▀▀▀▀████████████████████▀▀▀▀ █████████████▌ // ██████████████ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ pragma solidity ^0.8.10; import "../lib/Entitlements.sol"; import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /// @title Generic Hook Vault-a vault designed to contain a single asset to be used as escrow. /// @author Jake [email protected] /// @custom:coauthor Regynald [email protected] /// /// @notice The Vault holds an asset on behalf of the owner. The owner is able to post this /// asset as collateral to other protocols by signing a message, called an "entitlement", that gives /// a specific account the ability to change the owner. /// /// The vault can work with multiple assets via the assetId, where the asset or set of assets covered by /// each segment is granted an individual id. /// Every asset must be identified by an assetId to comply with this interface, even if the vault only contains /// one asset. /// /// ENTITLEMENTS - /// (1) only one entitlement can be placed at a time. /// (2) entitlements must expire, but can also be cleared by the entitled party /// (3) if an entitlement expires, the current beneficial owner gains immediate sole control over the /// asset /// (4) the entitled entity can modify the beneficial owner of the asset, but cannot withdrawal. /// (5) the beneficial owner cannot modify the beneficial owner while an entitlement is in place /// interface IHookVault is IERC165 { /// @notice emitted when an entitlement is placed on an asset event EntitlementImposed( uint32 assetId, address entitledAccount, uint32 expiry, address beneficialOwner ); /// @notice emitted when an entitlement is cleared from an asset event EntitlementCleared(uint256 assetId, address beneficialOwner); /// @notice emitted when the beneficial owner of an asset changes /// @dev it is not required that this event is emitted when an entitlement is /// imposed that also modifies the beneficial owner. event BeneficialOwnerSet( uint32 assetId, address beneficialOwner, address setBy ); /// @notice emitted when an asset is added into the vault event AssetReceived( address owner, address sender, address contractAddress, uint32 assetId ); /// @notice Emitted when `beneficialOwner` enables `approved` to manage the `assetId` asset. event Approval( address indexed beneficialOwner, address indexed approved, uint32 indexed assetId ); /// @notice emitted when an asset is withdrawn from the vault event AssetWithdrawn(uint32 assetId, address to, address beneficialOwner); /// @notice Withdrawal an unencumbered asset from this vault /// @param assetId the asset to remove from the vault function withdrawalAsset(uint32 assetId) external; /// @notice setBeneficialOwner updates the current address that can claim the asset when it is free of entitlements. /// @param assetId the id of the subject asset to impose the entitlement /// @param newBeneficialOwner the account of the person who is able to withdrawal when there are no entitlements. function setBeneficialOwner(uint32 assetId, address newBeneficialOwner) external; /// @notice Add an entitlement claim to the asset held within the contract /// @param operator the operator to entitle /// @param expiry the duration of the entitlement /// @param assetId the id of the asset within the vault /// @param v sig v /// @param r sig r /// @param s sig s function imposeEntitlement( address operator, uint32 expiry, uint32 assetId, uint8 v, bytes32 r, bytes32 s ) external; /// @notice Allows the beneficial owner to grant an entitlement to an asset within the contract /// @dev this function call is signed by the sender per the EVM, so we know the entitlement is authentic /// @param entitlement The entitlement to impose onto the contract function grantEntitlement(Entitlements.Entitlement calldata entitlement) external; /// @notice Allows the entitled address to release their claim on the asset /// @param assetId the id of the asset to clear function clearEntitlement(uint32 assetId) external; /// @notice Removes the active entitlement from a vault and returns the asset to the beneficial owner /// @param receiver the intended receiver of the asset /// @param assetId the Id of the asset to clear function clearEntitlementAndDistribute(uint32 assetId, address receiver) external; /// @notice looks up the current beneficial owner of the asset /// @param assetId the referenced asset /// @return the address of the beneficial owner of the asset function getBeneficialOwner(uint32 assetId) external view returns (address); /// @notice checks if the asset is currently stored in the vault /// @param assetId the referenced asset /// @return true if the asset is currently within the vault, false otherwise function getHoldsAsset(uint32 assetId) external view returns (bool); /// @notice the contract address of the vaulted asset /// @param assetId the referenced asset /// @return the contract address of the vaulted asset function assetAddress(uint32 assetId) external view returns (address); /// @notice looks up the current operator of an entitlement on an asset /// @param assetId the id of the underlying asset function getCurrentEntitlementOperator(uint32 assetId) external view returns (bool, address); /// @notice Looks up the expiration timestamp of the current entitlement /// @dev returns the 0 if no entitlement is set /// @return the block timestamp after which the entitlement expires function entitlementExpiration(uint32 assetId) external view returns (uint32); /// @notice Gives permission to `to` to impose an entitlement upon `assetId` /// /// @dev Only a single account can be approved at a time, so approving the zero address clears previous approvals. /// * Requirements: /// /// - The caller must be the beneficial owner /// - `tokenId` must exist. /// /// Emits an {Approval} event. function approveOperator(address to, uint32 assetId) external; /// @dev Returns the account approved for `tokenId` token. /// /// Requirements: /// /// - `assetId` must exist. /// function getApprovedOperator(uint32 assetId) external view returns (address); }
// SPDX-License-Identifier: MIT // // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // ██████████████ ██████████████ // ██████████████ ▄▄████████████████▄▄ ▐█████████████▌ // ██████████████ ▄█████████████████████████████▄ ██████████████ // ██████████▀ ▄█████████████████████████████████ ██████████████▌ // ██████▀ ▄██████████████████████████████████▀ ▄███████████████ // ███▀ ██████████████████████████████████▀ ▄████████████████ // ▀▀ ████████████████████████████████▀▀ ▄█████████████████▌ // █████████████████████▀▀▀▀▀▀▀ ▄▄███████████████████▀ // ██████████████████▀ ▄▄▄█████████████████████████████▀ // ████████████████▀ ▄█████████████████████████████████▀ ██▄ // ▐███████████████▀ ▄██████████████████████████████████▀ █████▄ // ██████████████▀ ▄█████████████████████████████████▀ ▄████████ // ██████████████▀ ███████████████████████████████▀ ▄████████████ // ▐█████████████▌ ▀▀▀▀████████████████████▀▀▀▀ █████████████▌ // ██████████████ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ pragma solidity ^0.8.10; import "./Signatures.sol"; library Entitlements { uint256 private constant _ENTITLEMENT_TYPEHASH = uint256( keccak256( abi.encodePacked( "Entitlement(", "address beneficialOwner,", "address operator,", "address vaultAddress,", "uint32 assetId,", "uint32 expiry", ")" ) ) ); /// ---- STRUCTS ----- struct Entitlement { /// @notice the beneficial owner address this entitlement applies to. This address will also be the signer. address beneficialOwner; /// @notice the operating contract that can change ownership during the entitlement period. address operator; /// @notice the contract address for the vault that contains the underlying assets address vaultAddress; /// @notice the assetId of the asset or assets within the vault uint32 assetId; /// @notice the block timestamp after which the asset is free of the entitlement uint32 expiry; } function getEntitlementStructHash(Entitlement memory entitlement) internal pure returns (bytes32) { // TODO: Hash in place to save gas. return keccak256( abi.encode( _ENTITLEMENT_TYPEHASH, entitlement.beneficialOwner, entitlement.operator, entitlement.vaultAddress, entitlement.assetId, entitlement.expiry ) ); } }
// SPDX-License-Identifier: MIT // // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // ██████████████ ██████████████ // ██████████████ ▄▄████████████████▄▄ ▐█████████████▌ // ██████████████ ▄█████████████████████████████▄ ██████████████ // ██████████▀ ▄█████████████████████████████████ ██████████████▌ // ██████▀ ▄██████████████████████████████████▀ ▄███████████████ // ███▀ ██████████████████████████████████▀ ▄████████████████ // ▀▀ ████████████████████████████████▀▀ ▄█████████████████▌ // █████████████████████▀▀▀▀▀▀▀ ▄▄███████████████████▀ // ██████████████████▀ ▄▄▄█████████████████████████████▀ // ████████████████▀ ▄█████████████████████████████████▀ ██▄ // ▐███████████████▀ ▄██████████████████████████████████▀ █████▄ // ██████████████▀ ▄█████████████████████████████████▀ ▄████████ // ██████████████▀ ███████████████████████████████▀ ▄████████████ // ▐█████████████▌ ▀▀▀▀████████████████████▀▀▀▀ █████████████▌ // ██████████████ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ pragma solidity ^0.8.10; /// @dev A library for validating signatures from ZeroEx library Signatures { /// @dev Allowed signature types. enum SignatureType { EIP712 } /// @dev Encoded EC signature. struct Signature { // How to validate the signature. SignatureType signatureType; // EC Signature data. uint8 v; // EC Signature data. bytes32 r; // EC Signature data. bytes32 s; } }
// SPDX-License-Identifier: MIT // // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // █████████████▌ ▐█████████████ // ██████████████ ██████████████ // ██████████████ ▄▄████████████████▄▄ ▐█████████████▌ // ██████████████ ▄█████████████████████████████▄ ██████████████ // ██████████▀ ▄█████████████████████████████████ ██████████████▌ // ██████▀ ▄██████████████████████████████████▀ ▄███████████████ // ███▀ ██████████████████████████████████▀ ▄████████████████ // ▀▀ ████████████████████████████████▀▀ ▄█████████████████▌ // █████████████████████▀▀▀▀▀▀▀ ▄▄███████████████████▀ // ██████████████████▀ ▄▄▄█████████████████████████████▀ // ████████████████▀ ▄█████████████████████████████████▀ ██▄ // ▐███████████████▀ ▄██████████████████████████████████▀ █████▄ // ██████████████▀ ▄█████████████████████████████████▀ ▄████████ // ██████████████▀ ███████████████████████████████▀ ▄████████████ // ▐█████████████▌ ▀▀▀▀████████████████████▀▀▀▀ █████████████▌ // ██████████████ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ // █████████████▌ ██████████████ pragma solidity ^0.8.10; /// @dev EIP712 helpers for features. abstract contract EIP712 { /// @dev The domain hash separator for the entire call option protocol bytes32 public EIP712_DOMAIN_SEPARATOR; function setAddressForEipDomain(address hookAddress) internal { // Compute `EIP712_DOMAIN_SEPARATOR` { uint256 chainId; assembly { chainId := chainid() } EIP712_DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256( "EIP712Domain(" "string name," "string version," "uint256 chainId," "address verifyingContract" ")" ), keccak256("Hook"), keccak256("1.0.0"), chainId, hookAddress ) ); } } function _getEIP712Hash(bytes32 structHash) internal view returns (bytes32) { return keccak256( abi.encodePacked(hex"1901", EIP712_DOMAIN_SEPARATOR, structHash) ); } }
{ "evmVersion": "london", "libraries": {}, "metadata": { "bytecodeHash": "ipfs", "useLiteralContent": true }, "optimizer": { "enabled": true, "runs": 10 }, "remappings": [], "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beneficialOwner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"flashLoanImpl","type":"address"}],"name":"AssetFlashLoaned","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"contractAddress","type":"address"},{"indexed":false,"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"AssetReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"assetId","type":"uint32"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"beneficialOwner","type":"address"}],"name":"AssetWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"assetId","type":"uint32"},{"indexed":false,"internalType":"address","name":"beneficialOwner","type":"address"},{"indexed":false,"internalType":"address","name":"setBy","type":"address"}],"name":"BeneficialOwnerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"assetId","type":"uint256"},{"indexed":false,"internalType":"address","name":"beneficialOwner","type":"address"}],"name":"EntitlementCleared","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"assetId","type":"uint32"},{"indexed":false,"internalType":"address","name":"entitledAccount","type":"address"},{"indexed":false,"internalType":"uint32","name":"expiry","type":"uint32"},{"indexed":false,"internalType":"address","name":"beneficialOwner","type":"address"}],"name":"EntitlementImposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"inputs":[],"name":"EIP712_DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"approveOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"assetAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"assetTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"clearEntitlement","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"},{"internalType":"address","name":"receiver","type":"address"}],"name":"clearEntitlementAndDistribute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"entitlementExpiration","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"},{"internalType":"address","name":"receiverAddress","type":"address"},{"internalType":"bytes","name":"params","type":"bytes"}],"name":"flashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"getApprovedOperator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"getBeneficialOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"getCurrentEntitlementOperator","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"getHoldsAsset","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"beneficialOwner","type":"address"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"vaultAddress","type":"address"},{"internalType":"uint32","name":"assetId","type":"uint32"},{"internalType":"uint32","name":"expiry","type":"uint32"}],"internalType":"struct Entitlements.Entitlement","name":"entitlement","type":"tuple"}],"name":"grantEntitlement","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"hasActiveEntitlement","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint32","name":"expiry","type":"uint32"},{"internalType":"uint32","name":"assetId","type":"uint32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"imposeEntitlement","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"address","name":"hookAddress","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"},{"internalType":"address","name":"newBeneficialOwner","type":"address"}],"name":"setBeneficialOwner","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":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint32","name":"expiry","type":"uint32"},{"internalType":"uint32","name":"assetId","type":"uint32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"validateEntitlementSignature","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"withdrawalAsset","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
608060405234801561001057600080fd5b506001600255612585806100256000396000f3fe608060405234801561001057600080fd5b50600436106101075760003560e01c806301ffc9a71461010c57806303e7158b14610134578063150b7a02146101495780631f48f6ab1461017557806325a23982146101ab5780632ec14d3f146101be578063485cc955146101d15780634eeeaa32146101e457806360bbf4a4146101f7578063774a76271461020a578063942811b514610232578063af024ae614610245578063ba02481b14610274578063bf6346b714610287578063c72bed151461029a578063dab400f3146102cc578063dbd74309146102e3578063e7dc956b146102f6578063eaf8b32314610309578063ebc5eb071461031c578063ecb2c20b1461032f575b600080fd5b61011f61011a366004612014565b61035e565b60405190151581526020015b60405180910390f35b61014761014236600461206e565b610395565b005b61015c610157366004612126565b610451565b6040516001600160e01b0319909116815260200161012b565b610193610183366004612198565b506003546001600160a01b031690565b6040516001600160a01b03909116815260200161012b565b6101476101b93660046121b3565b610730565b6101476101cc3660046121b3565b61094f565b6101476101df3660046121ea565b610ad1565b6101476101f2366004612198565b610c1b565b610147610205366004612208565b610e47565b61021d610218366004612198565b610f89565b60405163ffffffff909116815260200161012b565b61011f610240366004612198565b610fcb565b610193610253366004612198565b63ffffffff166000908152600560205260409020546001600160a01b031690565b61011f610282366004612198565b611020565b610147610295366004612220565b61103c565b6102ad6102a8366004612198565b6115c2565b6040805192151583526001600160a01b0390911660208301520161012b565b6102d560005481565b60405190815260200161012b565b6101476102f136600461206e565b6115fe565b6102d5610304366004612198565b6117b0565b610147610317366004612198565b6117be565b61014761032a366004612282565b6118da565b61019361033d366004612198565b63ffffffff166000908152600460205260409020546001600160a01b031690565b60006001600160e01b0319821663162ff4f760e21b148061038f57506001600160e01b031982166301ffc9a760e01b145b92915050565b63ffffffff84166000908152600460205260409020546001600160a01b031661043b5760405162461bcd60e51b815260206004820152604760248201527f696d706f7365456e7469746c656d656e742d62656e6566696369616c206f776e60448201527f6572206d7573742062652073657420746f20696d706f736520616e20656e74696064820152661d1b195b595b9d60ca1b608482015260a4015b60405180910390fd5b6104498686868686866119e2565b505050505050565b600063ffffffff8411156104b85760405162461bcd60e51b815260206004820152602860248201527f6f6e45524337323152656365697665642d746f6b656e4964206973206f7574206044820152676f662072616e676560c01b6064820152608401610432565b6003546001600160a01b031633141561060757811561053c57606082141561050457600080806104ea858701876122b7565b9250925092506104fc87838386611a1a565b505050610546565b6000808080610515868801886122fe565b935093509350935061052988848487611a1a565b6105338189611bf7565b50505050610546565b6105468486611c64565b60405163ecb2c20b60e01b815263ffffffff851660048201527f7897fd58abdfc815ef9370ad67929a217fbaa4b692c4b1086930ae97e4a7fe2d90309063ecb2c20b90602401602060405180830381865afa1580156105a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105cd9190612358565b604080516001600160a01b0392831681529189166020830152339082015263ffffffff8616606082015260800160405180910390a161071e565b60065460035460405163194f672960e01b81526001600160a01b039283169263194f67299261065d929116907f0c6296a577719ee1835d23ea2ac4f93808edda4f265df0440caeecc95a06857590600401612375565b602060405180830381865afa15801561067a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061069e919061238e565b61071e5760405162461bcd60e51b815260206004820152604560248201527f6f6e45524337323152656365697665642d6e6f6e2d657363726f77206173736560448201527f742072657475726e6564207768656e2061697264726f7073206172652064697360648201526418589b195960da1b608482015260a401610432565b50630a85bd0160e11b95945050505050565b6002805414156107525760405162461bcd60e51b8152600401610432906123b0565b6002805563ffffffff82166000908152600460205260409020546001600160a01b038281169116146108025760405162461bcd60e51b815260206004820152604d60248201527f636c656172456e7469746c656d656e74416e64446973747269627574652d4f6e60448201527f6c79207468652062656e6566696369616c206f776e65722063616e207265636560648201526c1a5d99481d1a1948185cdcd95d609a1b608482015260a401610432565b6001600160a01b03811661088a5760405162461bcd60e51b815260206004820152604360248201527f636c656172456e7469746c656d656e74416e64446973747269627574652d617360448201527f736574732063616e6e6f742062652073656e7420746f206e756c6c206164647260648201526265737360e81b608482015260a401610432565b610893826117be565b6003546001600160a01b03166342842e0e308363ffffffff86166040518463ffffffff1660e01b81526004016108cb939291906123e7565b600060405180830381600087803b1580156108e557600080fd5b505af11580156108f9573d6000803e3d6000fd5b50505063ffffffff831660009081526004602052604090819020549051600080516020612530833981519152925061093e91859185916001600160a01b03169061240b565b60405180910390a150506001600255565b61095882610fcb565b15610a1e5763ffffffff82166000908152600460205260409020600101546001600160a01b03163314610a195760405162461bcd60e51b815260206004820152606060248201527f73657442656e6566696369616c4f776e65722d6f6e6c792074686520636f6e7460448201527f726163742077697468207468652061637469766520656e7469746c656d656e7460648201527f2063616e20757064617465207468652062656e6566696369616c206f776e6572608482015260a401610432565b610ac3565b63ffffffff82166000908152600460205260409020546001600160a01b03163314610ac35760405162461bcd60e51b815260206004820152604960248201527f73657442656e6566696369616c4f776e65722d6f6e6c7920746865206375727260448201527f656e74206f776e65722063616e20757064617465207468652062656e6566696360648201526834b0b61037bbb732b960b91b608482015260a401610432565b610acd8282611c64565b5050565b6000610add6001611d4b565b90508015610af5576001805461ff0019166101001790555b604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6020808301919091527fbefac456e8d3c7dcbe25358dd865ef756e23bdcd0f4f36a6e915a0f24b6849e9828401527f06c015bd22b4c69690933c1058878ebdfef31f9aaae40bbe86d8a09fe1b2972c60608301524660808301526001600160a01b03851660a0808401919091528351808403909101815260c09092019092528051910120600055600380546001600160a01b038086166001600160a01b03199283161790925560068054928516929091169190911790558015610c16576001805461ff00191681556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050565b600280541415610c3d5760405162461bcd60e51b8152600401610432906123b0565b60028055610c4a81610fcb565b15610cce5760405162461bcd60e51b815260206004820152604860248201527f7769746864726177616c41737365742d7468652061737365742063616e6e6f7460448201527f2062652077697468647261776e207769746820616e2061637469766520656e746064820152671a5d1b195b595b9d60c21b608482015260a401610432565b63ffffffff81166000908152600460205260409020546001600160a01b03163314610d6b5760405162461bcd60e51b815260206004820152604160248201527f7769746864726177616c41737365742d6f6e6c79207468652062656e6566696360448201527f69616c206f776e65722063616e207769746864726177616c20616e20617373656064820152601d60fa1b608482015260a401610432565b60035463ffffffff82166000908152600460205260409020546001600160a01b03918216916342842e0e91309116610da68563ffffffff1690565b6040518463ffffffff1660e01b8152600401610dc4939291906123e7565b600060405180830381600087803b158015610dde57600080fd5b505af1158015610df2573d6000803e3d6000fd5b50505063ffffffff8216600090815260046020526040908190205490516000805160206125308339815191529250610e3791849133916001600160a01b03169061240b565b60405180910390a1506001600255565b3360046000610e5c6080850160608601612198565b63ffffffff1681526020810191909152604001600020546001600160a01b03161480610eba57503360056000610e986080850160608601612198565b63ffffffff1681526020810191909152604001600020546001600160a01b0316145b610f4d5760405162461bcd60e51b815260206004820152605860248201527f6772616e74456e7469746c656d656e742d6f6e6c79207468652062656e65666960448201527f6369616c206f776e6572206f7220617070726f766564206f70657261746f722060648201527718d85b8819dc985b9d08185b88195b9d1a5d1b195b595b9d60421b608482015260a401610432565b610f86610f606080830160608401612198565b610f706040840160208501612432565b610f8060a0850160808601612198565b33611a1a565b50565b6000610f9482610fcb565b610fa057506000919050565b5063ffffffff908116600090815260046020526040902060010154600160a01b90041690565b919050565b63ffffffff8082166000908152600460205260408120600101549091600160a01b909104164210801561038f57505063ffffffff166000908152600460205260409020600101546001600160a01b0316151590565b60003061102c83611dd3565b6001600160a01b03161492915050565b60028054141561105e5760405162461bcd60e51b8152600401610432906123b0565b60028055826001600160a01b0381166110b25760405162461bcd60e51b8152602060048201526016602482015275666c6173684c6f616e2d7a65726f206164647265737360501b6044820152606401610432565b306110bc86611dd3565b6001600160a01b0316146111115760405162461bcd60e51b815260206004820152601c60248201527b199b185cda131bd85b8b585cdcd95d081b9bdd081a5b881d985d5b1d60221b6044820152606401610432565b63ffffffff85166000908152600460205260409020546001600160a01b0316331461118e5760405162461bcd60e51b815260206004820152602760248201527f666c6173684c6f616e2d6e6f742063616c6c65642062792074686520617373656044820152663a1037bbb732b960c91b6064820152608401610432565b60065460035460405163194f672960e01b81526001600160a01b039283169263194f6729926111e4929116907f7b2ae2881a80edd04dac29e0424a8afcb85d139d6204e45ea854215cb864990b90600401612375565b602060405180830381865afa158015611201573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611225919061238e565b156112915760405162461bcd60e51b815260206004820152603660248201527f666c6173684c6f616e2d666c6173684c6f616e20666561747572652064697361604482015275189b195908199bdc881d1a1a5cc818dbdb9d1c9858dd60521b6064820152608401610432565b63ffffffff8516600090815260046020908152604080832090516112b5920161244f565b60408051601f19818403018152908290528051602090910120600354632142170760e11b83529092506001600160a01b0316906342842e0e90611306903090899063ffffffff8c16906004016123e7565b600060405180830381600087803b15801561132057600080fd5b505af1158015611334573d6000803e3d6000fd5b505060035460405163817dc5e560e01b81526001600160a01b03868116945063817dc5e5935061137892169063ffffffff8b1690339030908b908b90600401612484565b6020604051808303816000875af1158015611397573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113bb919061238e565b6114225760405162461bcd60e51b815260206004820152603260248201527f666c6173684c6f616e2d74686520666c617368206c6f616e20636f6e7472616360448201527174206d7573742072657475726e207472756560701b6064820152608401610432565b6003546001600160a01b03166323b872dd863063ffffffff8a166040518463ffffffff1660e01b815260040161145a939291906123e7565b600060405180830381600087803b15801561147457600080fd5b505af1158015611488573d6000803e3d6000fd5b50505050306001600160a01b031661149f87611dd3565b6001600160a01b0316146114b257600080fd5b63ffffffff861660009081526004602090815260409182902091516114d892910161244f565b6040516020818303038152906040528051906020012081146115535760405162461bcd60e51b815260206004820152602e60248201527f666c6173684c6f616e2d656e7469746c656d656e742073746174652063616e6e60448201526d1bdd081899481b5bd91a599a595960921b6064820152608401610432565b63ffffffff86166000818152600460209081526040918290205482516001600160a01b0391821681529182019390935291871682820152517fb9a0fd57b76d147a2f98aed604908c2285d360c4349bf8c59e89365e39b2f9e29181900360600190a15050600160025550505050565b60008060006115d084610fcb565b63ffffffff90941660009081526004602052604090206001015493946001600160a01b039094169392505050565b6040805160a08101825263ffffffff808716600081815260046020908152858220546001600160a01b0390811686528c169085015230948401949094526060830152871660808201526116599061165490611e46565b611f7f565b6040805160008082526020820180845284905260ff88169282019290925260608101869052608081018590529192509060019060a0016020604051602081039080840390855afa1580156116b1573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166117105760405162461bcd60e51b81526020600482015260196024820152781c9958dbdd995c9959081859191c995cdcc81a5cc81b9d5b1b603a1b6044820152606401610432565b63ffffffff86166000908152600460205260409020546001600160a01b038281169116146117a65760405162461bcd60e51b815260206004820152603e60248201527f76616c6964617465456e7469746c656d656e745369676e6174757265202d2d2d60448201527f206e6f74207369676e65642062792062656e6566696369616c4f776e657200006064820152608401610432565b5050505050505050565b600063ffffffff821661038f565b6117c781610fcb565b61182d5760405162461bcd60e51b815260206004820152603160248201527f636c656172456e7469746c656d656e742d616e2061637469766520656e7469746044820152701b195b595b9d081b5d5cdd08195e1a5cdd607a1b6064820152608401610432565b63ffffffff81166000908152600460205260409020600101546001600160a01b031633146118d15760405162461bcd60e51b8152602060048201526044602482018190527f636c656172456e7469746c656d656e742d6f6e6c792074686520656e7469746c908201527f656420616464726573732063616e20636c6561722074686520656e7469746c656064820152631b595b9d60e21b608482015260a401610432565b610f8681611fa8565b63ffffffff81166000908152600460205260409020546001600160a01b039081169083168114156119615760405162461bcd60e51b815260206004820152602b60248201527f617070726f76652d617070726f76616c20746f2063757272656e742062656e6560448201526a3334b1b4b0b627bbb732b960a91b6064820152608401610432565b336001600160a01b038216146119d85760405162461bcd60e51b815260206004820152603660248201527f617070726f76652d617070726f76652063616c6c6572206973206e6f74206375604482015275393932b73a103132b732b334b1b4b0b61037bbb732b960511b6064820152608401610432565b610c168383611bf7565b6119f08686868686866115fe565b63ffffffff8416600090815260046020526040902054610449908590889088906001600160a01b03165b611a2384610fcb565b15611ab55760405162461bcd60e51b815260206004820152605660248201527f5f7265676973746572456e7469746c656d656e742d6578697374696e6720656e60448201527f7469746c656d656e74206d75737420626520636c6561726564206265666f7265606482015275207265676973746572696e672061206e6577206f6e6560501b608482015260a401610432565b428263ffffffff1611611b2d5760405162461bcd60e51b815260206004820152603a60248201527f5f7265676973746572456e7469746c656d656e742d656e7469746c656d656e74604482015279206d7573742065787069726520696e207468652066757475726560301b6064820152608401610432565b60408051606080820183526001600160a01b03848116808452878216602080860182815263ffffffff8a8116888a018181528e83166000818152600487528c90209a518b546001600160a01b031916908a16178b5593516001909a01805491519a9098166001600160c01b031990911617600160a01b9990921698909802179094558651938452830152938101929092528101919091527f595e586d44ea8c92723ac9fdd4897e8a5ee6d55e2bad6d386fe7ccbff5912b239060800160405180910390a150505050565b63ffffffff8116600081815260056020908152604080832080546001600160a01b0319166001600160a01b03888116918217909255600490935281842054915192939116917fe26b9b29b95cb706aaebdd86a3524de879b8590b0ba7e61b104f54196bab20319190a45050565b6001600160a01b038116611cd45760405162461bcd60e51b815260206004820152603160248201527f5f73657442656e6566696369616c4f776e65722d6e6577206f776e657220697360448201527020746865207a65726f206164647265737360781b6064820152608401610432565b63ffffffff8216600090815260046020526040812080546001600160a01b0319166001600160a01b038416179055611d0c9083611bf7565b7f183944454934706f9df1285ff0207d23600c244528512cdc01c94f5ec8f601b4828233604051611d3f9392919061240b565b60405180910390a15050565b600154600090610100900460ff1615611d94578160ff166001148015611d705750303b155b611d8c5760405162461bcd60e51b8152600401610432906124e1565b506000919050565b60015460ff808416911610611dbb5760405162461bcd60e51b8152600401610432906124e1565b506001805460ff191660ff9290921691909117815590565b6003546040516331a9108f60e11b815263ffffffff831660048201526000916001600160a01b031690636352211e90602401602060405180830381865afa158015611e22573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061038f9190612358565b6040516b08adce8d2e8d8cadacadce8560a31b6020820152771859191c995cdcc818995b99599a58da585b13dddb995c8b60421b602c820152701859191c995cdcc81bdc195c985d1bdc8b607a1b6044820152741859191c995cdcc81d985d5b1d1059191c995cdccb605a1b60558201526e1d5a5b9d0ccc88185cdcd95d12590b608a1b606a8201526c75696e7433322065787069727960981b6079820152602960f81b608682015260009060870160408051601f198184030181528282528051602091820120855186830151878501516060808a01516080808c0151978a01969096526001600160a01b039485169789019790975291831691870191909152169084015263ffffffff91821660a08401521660c082015260e0015b604051602081830303815290604052805190602001209050919050565b6000805460405161190160f01b6020820152602281019190915260428101839052606201611f62565b63ffffffff81166000818152600460209081526040918290206001810180546001600160c01b03191690555482519384526001600160a01b0316908301527fd7aa6a9fe8ec62f38eafaf1c43d6816defbf427c414d3b0e865cb5cdd52a647c910160405180910390a150565b60006020828403121561202657600080fd5b81356001600160e01b03198116811461203e57600080fd5b9392505050565b6001600160a01b0381168114610f8657600080fd5b803563ffffffff81168114610fc657600080fd5b60008060008060008060c0878903121561208757600080fd5b863561209281612045565b95506120a06020880161205a565b94506120ae6040880161205a565b9350606087013560ff811681146120c457600080fd5b9598949750929560808101359460a0909101359350915050565b60008083601f8401126120f057600080fd5b5081356001600160401b0381111561210757600080fd5b60208301915083602082850101111561211f57600080fd5b9250929050565b60008060008060006080868803121561213e57600080fd5b853561214981612045565b9450602086013561215981612045565b93506040860135925060608601356001600160401b0381111561217b57600080fd5b612187888289016120de565b969995985093965092949392505050565b6000602082840312156121aa57600080fd5b61203e8261205a565b600080604083850312156121c657600080fd5b6121cf8361205a565b915060208301356121df81612045565b809150509250929050565b600080604083850312156121fd57600080fd5b82356121cf81612045565b600060a0828403121561221a57600080fd5b50919050565b6000806000806060858703121561223657600080fd5b61223f8561205a565b9350602085013561224f81612045565b925060408501356001600160401b0381111561226a57600080fd5b612276878288016120de565b95989497509550505050565b6000806040838503121561229557600080fd5b82356122a081612045565b91506122ae6020840161205a565b90509250929050565b6000806000606084860312156122cc57600080fd5b83356122d781612045565b925060208401356122e781612045565b91506122f56040850161205a565b90509250925092565b6000806000806080858703121561231457600080fd5b843561231f81612045565b9350602085013561232f81612045565b925061233d6040860161205a565b9150606085013561234d81612045565b939692955090935050565b60006020828403121561236a57600080fd5b815161203e81612045565b6001600160a01b03929092168252602082015260400190565b6000602082840312156123a057600080fd5b8151801515811461203e57600080fd5b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b63ffffffff9390931683526001600160a01b03918216602084015216604082015260600190565b60006020828403121561244457600080fd5b813561203e81612045565b81546001600160a01b03908116825260019290920154918216602082015260a09190911c63ffffffff16604082015260600190565b6001600160a01b0387811682526020820187905285811660408301528416606082015260a06080820181905281018290526000828460c0840137600060c0848401015260c0601f19601f8501168301019050979650505050505050565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b60608201526080019056fe79ab5c29ad5c9805473ba77c80b4472dd5721aae58e97158a7bbc53205ee7180a264697066735822122019cacf6d4877c733e20ae34489bd7bd1259ca90a435356574a311b65270f156264736f6c634300080a0033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101075760003560e01c806301ffc9a71461010c57806303e7158b14610134578063150b7a02146101495780631f48f6ab1461017557806325a23982146101ab5780632ec14d3f146101be578063485cc955146101d15780634eeeaa32146101e457806360bbf4a4146101f7578063774a76271461020a578063942811b514610232578063af024ae614610245578063ba02481b14610274578063bf6346b714610287578063c72bed151461029a578063dab400f3146102cc578063dbd74309146102e3578063e7dc956b146102f6578063eaf8b32314610309578063ebc5eb071461031c578063ecb2c20b1461032f575b600080fd5b61011f61011a366004612014565b61035e565b60405190151581526020015b60405180910390f35b61014761014236600461206e565b610395565b005b61015c610157366004612126565b610451565b6040516001600160e01b0319909116815260200161012b565b610193610183366004612198565b506003546001600160a01b031690565b6040516001600160a01b03909116815260200161012b565b6101476101b93660046121b3565b610730565b6101476101cc3660046121b3565b61094f565b6101476101df3660046121ea565b610ad1565b6101476101f2366004612198565b610c1b565b610147610205366004612208565b610e47565b61021d610218366004612198565b610f89565b60405163ffffffff909116815260200161012b565b61011f610240366004612198565b610fcb565b610193610253366004612198565b63ffffffff166000908152600560205260409020546001600160a01b031690565b61011f610282366004612198565b611020565b610147610295366004612220565b61103c565b6102ad6102a8366004612198565b6115c2565b6040805192151583526001600160a01b0390911660208301520161012b565b6102d560005481565b60405190815260200161012b565b6101476102f136600461206e565b6115fe565b6102d5610304366004612198565b6117b0565b610147610317366004612198565b6117be565b61014761032a366004612282565b6118da565b61019361033d366004612198565b63ffffffff166000908152600460205260409020546001600160a01b031690565b60006001600160e01b0319821663162ff4f760e21b148061038f57506001600160e01b031982166301ffc9a760e01b145b92915050565b63ffffffff84166000908152600460205260409020546001600160a01b031661043b5760405162461bcd60e51b815260206004820152604760248201527f696d706f7365456e7469746c656d656e742d62656e6566696369616c206f776e60448201527f6572206d7573742062652073657420746f20696d706f736520616e20656e74696064820152661d1b195b595b9d60ca1b608482015260a4015b60405180910390fd5b6104498686868686866119e2565b505050505050565b600063ffffffff8411156104b85760405162461bcd60e51b815260206004820152602860248201527f6f6e45524337323152656365697665642d746f6b656e4964206973206f7574206044820152676f662072616e676560c01b6064820152608401610432565b6003546001600160a01b031633141561060757811561053c57606082141561050457600080806104ea858701876122b7565b9250925092506104fc87838386611a1a565b505050610546565b6000808080610515868801886122fe565b935093509350935061052988848487611a1a565b6105338189611bf7565b50505050610546565b6105468486611c64565b60405163ecb2c20b60e01b815263ffffffff851660048201527f7897fd58abdfc815ef9370ad67929a217fbaa4b692c4b1086930ae97e4a7fe2d90309063ecb2c20b90602401602060405180830381865afa1580156105a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105cd9190612358565b604080516001600160a01b0392831681529189166020830152339082015263ffffffff8616606082015260800160405180910390a161071e565b60065460035460405163194f672960e01b81526001600160a01b039283169263194f67299261065d929116907f0c6296a577719ee1835d23ea2ac4f93808edda4f265df0440caeecc95a06857590600401612375565b602060405180830381865afa15801561067a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061069e919061238e565b61071e5760405162461bcd60e51b815260206004820152604560248201527f6f6e45524337323152656365697665642d6e6f6e2d657363726f77206173736560448201527f742072657475726e6564207768656e2061697264726f7073206172652064697360648201526418589b195960da1b608482015260a401610432565b50630a85bd0160e11b95945050505050565b6002805414156107525760405162461bcd60e51b8152600401610432906123b0565b6002805563ffffffff82166000908152600460205260409020546001600160a01b038281169116146108025760405162461bcd60e51b815260206004820152604d60248201527f636c656172456e7469746c656d656e74416e64446973747269627574652d4f6e60448201527f6c79207468652062656e6566696369616c206f776e65722063616e207265636560648201526c1a5d99481d1a1948185cdcd95d609a1b608482015260a401610432565b6001600160a01b03811661088a5760405162461bcd60e51b815260206004820152604360248201527f636c656172456e7469746c656d656e74416e64446973747269627574652d617360448201527f736574732063616e6e6f742062652073656e7420746f206e756c6c206164647260648201526265737360e81b608482015260a401610432565b610893826117be565b6003546001600160a01b03166342842e0e308363ffffffff86166040518463ffffffff1660e01b81526004016108cb939291906123e7565b600060405180830381600087803b1580156108e557600080fd5b505af11580156108f9573d6000803e3d6000fd5b50505063ffffffff831660009081526004602052604090819020549051600080516020612530833981519152925061093e91859185916001600160a01b03169061240b565b60405180910390a150506001600255565b61095882610fcb565b15610a1e5763ffffffff82166000908152600460205260409020600101546001600160a01b03163314610a195760405162461bcd60e51b815260206004820152606060248201527f73657442656e6566696369616c4f776e65722d6f6e6c792074686520636f6e7460448201527f726163742077697468207468652061637469766520656e7469746c656d656e7460648201527f2063616e20757064617465207468652062656e6566696369616c206f776e6572608482015260a401610432565b610ac3565b63ffffffff82166000908152600460205260409020546001600160a01b03163314610ac35760405162461bcd60e51b815260206004820152604960248201527f73657442656e6566696369616c4f776e65722d6f6e6c7920746865206375727260448201527f656e74206f776e65722063616e20757064617465207468652062656e6566696360648201526834b0b61037bbb732b960b91b608482015260a401610432565b610acd8282611c64565b5050565b6000610add6001611d4b565b90508015610af5576001805461ff0019166101001790555b604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6020808301919091527fbefac456e8d3c7dcbe25358dd865ef756e23bdcd0f4f36a6e915a0f24b6849e9828401527f06c015bd22b4c69690933c1058878ebdfef31f9aaae40bbe86d8a09fe1b2972c60608301524660808301526001600160a01b03851660a0808401919091528351808403909101815260c09092019092528051910120600055600380546001600160a01b038086166001600160a01b03199283161790925560068054928516929091169190911790558015610c16576001805461ff00191681556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050565b600280541415610c3d5760405162461bcd60e51b8152600401610432906123b0565b60028055610c4a81610fcb565b15610cce5760405162461bcd60e51b815260206004820152604860248201527f7769746864726177616c41737365742d7468652061737365742063616e6e6f7460448201527f2062652077697468647261776e207769746820616e2061637469766520656e746064820152671a5d1b195b595b9d60c21b608482015260a401610432565b63ffffffff81166000908152600460205260409020546001600160a01b03163314610d6b5760405162461bcd60e51b815260206004820152604160248201527f7769746864726177616c41737365742d6f6e6c79207468652062656e6566696360448201527f69616c206f776e65722063616e207769746864726177616c20616e20617373656064820152601d60fa1b608482015260a401610432565b60035463ffffffff82166000908152600460205260409020546001600160a01b03918216916342842e0e91309116610da68563ffffffff1690565b6040518463ffffffff1660e01b8152600401610dc4939291906123e7565b600060405180830381600087803b158015610dde57600080fd5b505af1158015610df2573d6000803e3d6000fd5b50505063ffffffff8216600090815260046020526040908190205490516000805160206125308339815191529250610e3791849133916001600160a01b03169061240b565b60405180910390a1506001600255565b3360046000610e5c6080850160608601612198565b63ffffffff1681526020810191909152604001600020546001600160a01b03161480610eba57503360056000610e986080850160608601612198565b63ffffffff1681526020810191909152604001600020546001600160a01b0316145b610f4d5760405162461bcd60e51b815260206004820152605860248201527f6772616e74456e7469746c656d656e742d6f6e6c79207468652062656e65666960448201527f6369616c206f776e6572206f7220617070726f766564206f70657261746f722060648201527718d85b8819dc985b9d08185b88195b9d1a5d1b195b595b9d60421b608482015260a401610432565b610f86610f606080830160608401612198565b610f706040840160208501612432565b610f8060a0850160808601612198565b33611a1a565b50565b6000610f9482610fcb565b610fa057506000919050565b5063ffffffff908116600090815260046020526040902060010154600160a01b90041690565b919050565b63ffffffff8082166000908152600460205260408120600101549091600160a01b909104164210801561038f57505063ffffffff166000908152600460205260409020600101546001600160a01b0316151590565b60003061102c83611dd3565b6001600160a01b03161492915050565b60028054141561105e5760405162461bcd60e51b8152600401610432906123b0565b60028055826001600160a01b0381166110b25760405162461bcd60e51b8152602060048201526016602482015275666c6173684c6f616e2d7a65726f206164647265737360501b6044820152606401610432565b306110bc86611dd3565b6001600160a01b0316146111115760405162461bcd60e51b815260206004820152601c60248201527b199b185cda131bd85b8b585cdcd95d081b9bdd081a5b881d985d5b1d60221b6044820152606401610432565b63ffffffff85166000908152600460205260409020546001600160a01b0316331461118e5760405162461bcd60e51b815260206004820152602760248201527f666c6173684c6f616e2d6e6f742063616c6c65642062792074686520617373656044820152663a1037bbb732b960c91b6064820152608401610432565b60065460035460405163194f672960e01b81526001600160a01b039283169263194f6729926111e4929116907f7b2ae2881a80edd04dac29e0424a8afcb85d139d6204e45ea854215cb864990b90600401612375565b602060405180830381865afa158015611201573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611225919061238e565b156112915760405162461bcd60e51b815260206004820152603660248201527f666c6173684c6f616e2d666c6173684c6f616e20666561747572652064697361604482015275189b195908199bdc881d1a1a5cc818dbdb9d1c9858dd60521b6064820152608401610432565b63ffffffff8516600090815260046020908152604080832090516112b5920161244f565b60408051601f19818403018152908290528051602090910120600354632142170760e11b83529092506001600160a01b0316906342842e0e90611306903090899063ffffffff8c16906004016123e7565b600060405180830381600087803b15801561132057600080fd5b505af1158015611334573d6000803e3d6000fd5b505060035460405163817dc5e560e01b81526001600160a01b03868116945063817dc5e5935061137892169063ffffffff8b1690339030908b908b90600401612484565b6020604051808303816000875af1158015611397573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113bb919061238e565b6114225760405162461bcd60e51b815260206004820152603260248201527f666c6173684c6f616e2d74686520666c617368206c6f616e20636f6e7472616360448201527174206d7573742072657475726e207472756560701b6064820152608401610432565b6003546001600160a01b03166323b872dd863063ffffffff8a166040518463ffffffff1660e01b815260040161145a939291906123e7565b600060405180830381600087803b15801561147457600080fd5b505af1158015611488573d6000803e3d6000fd5b50505050306001600160a01b031661149f87611dd3565b6001600160a01b0316146114b257600080fd5b63ffffffff861660009081526004602090815260409182902091516114d892910161244f565b6040516020818303038152906040528051906020012081146115535760405162461bcd60e51b815260206004820152602e60248201527f666c6173684c6f616e2d656e7469746c656d656e742073746174652063616e6e60448201526d1bdd081899481b5bd91a599a595960921b6064820152608401610432565b63ffffffff86166000818152600460209081526040918290205482516001600160a01b0391821681529182019390935291871682820152517fb9a0fd57b76d147a2f98aed604908c2285d360c4349bf8c59e89365e39b2f9e29181900360600190a15050600160025550505050565b60008060006115d084610fcb565b63ffffffff90941660009081526004602052604090206001015493946001600160a01b039094169392505050565b6040805160a08101825263ffffffff808716600081815260046020908152858220546001600160a01b0390811686528c169085015230948401949094526060830152871660808201526116599061165490611e46565b611f7f565b6040805160008082526020820180845284905260ff88169282019290925260608101869052608081018590529192509060019060a0016020604051602081039080840390855afa1580156116b1573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166117105760405162461bcd60e51b81526020600482015260196024820152781c9958dbdd995c9959081859191c995cdcc81a5cc81b9d5b1b603a1b6044820152606401610432565b63ffffffff86166000908152600460205260409020546001600160a01b038281169116146117a65760405162461bcd60e51b815260206004820152603e60248201527f76616c6964617465456e7469746c656d656e745369676e6174757265202d2d2d60448201527f206e6f74207369676e65642062792062656e6566696369616c4f776e657200006064820152608401610432565b5050505050505050565b600063ffffffff821661038f565b6117c781610fcb565b61182d5760405162461bcd60e51b815260206004820152603160248201527f636c656172456e7469746c656d656e742d616e2061637469766520656e7469746044820152701b195b595b9d081b5d5cdd08195e1a5cdd607a1b6064820152608401610432565b63ffffffff81166000908152600460205260409020600101546001600160a01b031633146118d15760405162461bcd60e51b8152602060048201526044602482018190527f636c656172456e7469746c656d656e742d6f6e6c792074686520656e7469746c908201527f656420616464726573732063616e20636c6561722074686520656e7469746c656064820152631b595b9d60e21b608482015260a401610432565b610f8681611fa8565b63ffffffff81166000908152600460205260409020546001600160a01b039081169083168114156119615760405162461bcd60e51b815260206004820152602b60248201527f617070726f76652d617070726f76616c20746f2063757272656e742062656e6560448201526a3334b1b4b0b627bbb732b960a91b6064820152608401610432565b336001600160a01b038216146119d85760405162461bcd60e51b815260206004820152603660248201527f617070726f76652d617070726f76652063616c6c6572206973206e6f74206375604482015275393932b73a103132b732b334b1b4b0b61037bbb732b960511b6064820152608401610432565b610c168383611bf7565b6119f08686868686866115fe565b63ffffffff8416600090815260046020526040902054610449908590889088906001600160a01b03165b611a2384610fcb565b15611ab55760405162461bcd60e51b815260206004820152605660248201527f5f7265676973746572456e7469746c656d656e742d6578697374696e6720656e60448201527f7469746c656d656e74206d75737420626520636c6561726564206265666f7265606482015275207265676973746572696e672061206e6577206f6e6560501b608482015260a401610432565b428263ffffffff1611611b2d5760405162461bcd60e51b815260206004820152603a60248201527f5f7265676973746572456e7469746c656d656e742d656e7469746c656d656e74604482015279206d7573742065787069726520696e207468652066757475726560301b6064820152608401610432565b60408051606080820183526001600160a01b03848116808452878216602080860182815263ffffffff8a8116888a018181528e83166000818152600487528c90209a518b546001600160a01b031916908a16178b5593516001909a01805491519a9098166001600160c01b031990911617600160a01b9990921698909802179094558651938452830152938101929092528101919091527f595e586d44ea8c92723ac9fdd4897e8a5ee6d55e2bad6d386fe7ccbff5912b239060800160405180910390a150505050565b63ffffffff8116600081815260056020908152604080832080546001600160a01b0319166001600160a01b03888116918217909255600490935281842054915192939116917fe26b9b29b95cb706aaebdd86a3524de879b8590b0ba7e61b104f54196bab20319190a45050565b6001600160a01b038116611cd45760405162461bcd60e51b815260206004820152603160248201527f5f73657442656e6566696369616c4f776e65722d6e6577206f776e657220697360448201527020746865207a65726f206164647265737360781b6064820152608401610432565b63ffffffff8216600090815260046020526040812080546001600160a01b0319166001600160a01b038416179055611d0c9083611bf7565b7f183944454934706f9df1285ff0207d23600c244528512cdc01c94f5ec8f601b4828233604051611d3f9392919061240b565b60405180910390a15050565b600154600090610100900460ff1615611d94578160ff166001148015611d705750303b155b611d8c5760405162461bcd60e51b8152600401610432906124e1565b506000919050565b60015460ff808416911610611dbb5760405162461bcd60e51b8152600401610432906124e1565b506001805460ff191660ff9290921691909117815590565b6003546040516331a9108f60e11b815263ffffffff831660048201526000916001600160a01b031690636352211e90602401602060405180830381865afa158015611e22573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061038f9190612358565b6040516b08adce8d2e8d8cadacadce8560a31b6020820152771859191c995cdcc818995b99599a58da585b13dddb995c8b60421b602c820152701859191c995cdcc81bdc195c985d1bdc8b607a1b6044820152741859191c995cdcc81d985d5b1d1059191c995cdccb605a1b60558201526e1d5a5b9d0ccc88185cdcd95d12590b608a1b606a8201526c75696e7433322065787069727960981b6079820152602960f81b608682015260009060870160408051601f198184030181528282528051602091820120855186830151878501516060808a01516080808c0151978a01969096526001600160a01b039485169789019790975291831691870191909152169084015263ffffffff91821660a08401521660c082015260e0015b604051602081830303815290604052805190602001209050919050565b6000805460405161190160f01b6020820152602281019190915260428101839052606201611f62565b63ffffffff81166000818152600460209081526040918290206001810180546001600160c01b03191690555482519384526001600160a01b0316908301527fd7aa6a9fe8ec62f38eafaf1c43d6816defbf427c414d3b0e865cb5cdd52a647c910160405180910390a150565b60006020828403121561202657600080fd5b81356001600160e01b03198116811461203e57600080fd5b9392505050565b6001600160a01b0381168114610f8657600080fd5b803563ffffffff81168114610fc657600080fd5b60008060008060008060c0878903121561208757600080fd5b863561209281612045565b95506120a06020880161205a565b94506120ae6040880161205a565b9350606087013560ff811681146120c457600080fd5b9598949750929560808101359460a0909101359350915050565b60008083601f8401126120f057600080fd5b5081356001600160401b0381111561210757600080fd5b60208301915083602082850101111561211f57600080fd5b9250929050565b60008060008060006080868803121561213e57600080fd5b853561214981612045565b9450602086013561215981612045565b93506040860135925060608601356001600160401b0381111561217b57600080fd5b612187888289016120de565b969995985093965092949392505050565b6000602082840312156121aa57600080fd5b61203e8261205a565b600080604083850312156121c657600080fd5b6121cf8361205a565b915060208301356121df81612045565b809150509250929050565b600080604083850312156121fd57600080fd5b82356121cf81612045565b600060a0828403121561221a57600080fd5b50919050565b6000806000806060858703121561223657600080fd5b61223f8561205a565b9350602085013561224f81612045565b925060408501356001600160401b0381111561226a57600080fd5b612276878288016120de565b95989497509550505050565b6000806040838503121561229557600080fd5b82356122a081612045565b91506122ae6020840161205a565b90509250929050565b6000806000606084860312156122cc57600080fd5b83356122d781612045565b925060208401356122e781612045565b91506122f56040850161205a565b90509250925092565b6000806000806080858703121561231457600080fd5b843561231f81612045565b9350602085013561232f81612045565b925061233d6040860161205a565b9150606085013561234d81612045565b939692955090935050565b60006020828403121561236a57600080fd5b815161203e81612045565b6001600160a01b03929092168252602082015260400190565b6000602082840312156123a057600080fd5b8151801515811461203e57600080fd5b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b63ffffffff9390931683526001600160a01b03918216602084015216604082015260600190565b60006020828403121561244457600080fd5b813561203e81612045565b81546001600160a01b03908116825260019290920154918216602082015260a09190911c63ffffffff16604082015260600190565b6001600160a01b0387811682526020820187905285811660408301528416606082015260a06080820181905281018290526000828460c0840137600060c0848401015260c0601f19601f8501168301019050979650505050505050565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b60608201526080019056fe79ab5c29ad5c9805473ba77c80b4472dd5721aae58e97158a7bbc53205ee7180a264697066735822122019cacf6d4877c733e20ae34489bd7bd1259ca90a435356574a311b65270f156264736f6c634300080a0033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 27 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.