// Sources flattened with hardhat v2.18.3 https://hardhat.org
// SPDX-License-Identifier: MIT
pragma experimental ABIEncoderV2;
// File contracts/interfaces/IMintableERC20.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.5.17 <0.9.0;
interface IMintableERC20 {
function mint(address account, uint256 amount) external returns (bool);
function transfer(address recipient, uint256 amount) external returns (bool);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function decimals() external view returns (uint8);
}
// File contracts/interfaces/IPublicLock.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.5.17 <0.9.0;
// Original pragma directive: pragma experimental ABIEncoderV2
/**
* @title The PublicLock Interface
*/
interface IPublicLock {
/// Functions
function initialize(
address _lockCreator,
uint _expirationDuration,
address _tokenAddress,
uint _keyPrice,
uint _maxNumberOfKeys,
string calldata _lockName
) external;
// default role from OpenZeppelin
function DEFAULT_ADMIN_ROLE() external view returns (bytes32 role);
/**
* @notice The version number of the current implementation on this network.
* @return The current version number.
*/
function publicLockVersion() external pure returns (uint16);
/**
* @dev Called by lock manager to withdraw all funds from the lock
* @param _tokenAddress specifies the token address to withdraw or 0 for ETH. This is usually
* the same as `tokenAddress` in MixinFunds.
* @param _recipient specifies the address that will receive the tokens
* @param _amount specifies the max amount to withdraw, which may be reduced when
* considering the available balance. Set to 0 or MAX_UINT to withdraw everything.
* -- however be wary of draining funds as it breaks the `cancelAndRefund` and `expireAndRefundFor` use cases.
*/
function withdraw(
address _tokenAddress,
address payable _recipient,
uint _amount
) external;
/**
* A function which lets a Lock manager of the lock to change the price for future purchases.
* @dev Throws if called by other than a Lock manager
* @dev Throws if lock has been disabled
* @dev Throws if _tokenAddress is not a valid token
* @param _keyPrice The new price to set for keys
* @param _tokenAddress The address of the erc20 token to use for pricing the keys,
* or 0 to use ETH
*/
function updateKeyPricing(uint _keyPrice, address _tokenAddress) external;
/**
* Update the main key properties for the entire lock:
*
* - default duration of each key
* - the maximum number of keys the lock can edit
* - the maximum number of keys a single address can hold
*
* @notice keys previously bought are unaffected by this changes in expiration duration (i.e.
* existing keys timestamps are not recalculated/updated)
* @param _newExpirationDuration the new amount of time for each key purchased or type(uint).max for a non-expiring key
* @param _maxKeysPerAcccount the maximum amount of key a single user can own
* @param _maxNumberOfKeys uint the maximum number of keys
* @dev _maxNumberOfKeys Can't be smaller than the existing supply
*/
function updateLockConfig(
uint _newExpirationDuration,
uint _maxNumberOfKeys,
uint _maxKeysPerAcccount
) external;
/**
* Checks if the user has a non-expired key.
* @param _user The address of the key owner
*/
function getHasValidKey(address _user) external view returns (bool);
/**
* @dev Returns the key's ExpirationTimestamp field for a given owner.
* @param _tokenId the id of the key
* @dev Returns 0 if the owner has never owned a key for this lock
*/
function keyExpirationTimestampFor(
uint _tokenId
) external view returns (uint timestamp);
/**
* Public function which returns the total number of unique owners (both expired
* and valid). This may be larger than totalSupply.
*/
function numberOfOwners() external view returns (uint);
/**
* Allows the Lock owner to assign
* @param _lockName a descriptive name for this Lock.
* @param _lockSymbol a Symbol for this Lock (default to KEY).
* @param _baseTokenURI the baseTokenURI for this Lock
*/
function setLockMetadata(
string calldata _lockName,
string calldata _lockSymbol,
string calldata _baseTokenURI
) external;
/**
* @dev Gets the token symbol
* @return string representing the token symbol
*/
function symbol() external view returns (string memory);
/** @notice A distinct Uniform Resource Identifier (URI) for a given asset.
* @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
* 3986. The URI may point to a JSON file that conforms to the "ERC721
* Metadata JSON Schema".
* https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
* @param _tokenId The tokenID we're inquiring about
* @return String representing the URI for the requested token
*/
function tokenURI(uint256 _tokenId) external view returns (string memory);
/**
* Allows a Lock manager to add or remove an event hook
* @param _onKeyPurchaseHook Hook called when the `purchase` function is called
* @param _onKeyCancelHook Hook called when the internal `_cancelAndRefund` function is called
* @param _onValidKeyHook Hook called to determine if the contract should overide the status for a given address
* @param _onTokenURIHook Hook called to generate a data URI used for NFT metadata
* @param _onKeyTransferHook Hook called when a key is transfered
* @param _onKeyExtendHook Hook called when a key is extended or renewed
* @param _onKeyGrantHook Hook called when a key is granted
*/
function setEventHooks(
address _onKeyPurchaseHook,
address _onKeyCancelHook,
address _onValidKeyHook,
address _onTokenURIHook,
address _onKeyTransferHook,
address _onKeyExtendHook,
address _onKeyGrantHook
) external;
/**
* Allows a Lock manager to give a collection of users a key with no charge.
* Each key may be assigned a different expiration date.
* @dev Throws if called by other than a Lock manager
* @param _recipients An array of receiving addresses
* @param _expirationTimestamps An array of expiration Timestamps for the keys being granted
* @return the ids of the granted tokens
*/
function grantKeys(
address[] calldata _recipients,
uint[] calldata _expirationTimestamps,
address[] calldata _keyManagers
) external returns (uint256[] memory);
/**
* Allows the Lock owner to extend an existing keys with no charge.
* @param _tokenId The id of the token to extend
* @param _duration The duration in secondes to add ot the key
* @dev set `_duration` to 0 to use the default duration of the lock
*/
function grantKeyExtension(uint _tokenId, uint _duration) external;
/**
* @dev Purchase function
* @param _values array of tokens amount to pay for this purchase >= the current keyPrice - any applicable discount
* (_values is ignored when using ETH)
* @param _recipients array of addresses of the recipients of the purchased key
* @param _referrers array of addresses of the users making the referral
* @param _keyManagers optional array of addresses to grant managing rights to a specific address on creation
* @param _data array of arbitrary data populated by the front-end which initiated the sale
* @notice when called for an existing and non-expired key, the `_keyManager` param will be ignored
* @dev Setting _value to keyPrice exactly doubles as a security feature. That way if the lock owner increases the
* price while my transaction is pending I can't be charged more than I expected (only applicable to ERC-20 when more
* than keyPrice is approved for spending).
* @return tokenIds the ids of the created tokens
*/
function purchase(
uint256[] calldata _values,
address[] calldata _recipients,
address[] calldata _referrers,
address[] calldata _keyManagers,
bytes[] calldata _data
) external payable returns (uint256[] memory tokenIds);
/**
* @dev Extend function
* @param _value the number of tokens to pay for this purchase >= the current keyPrice - any applicable discount
* (_value is ignored when using ETH)
* @param _tokenId the id of the key to extend
* @param _referrer address of the user making the referral
* @param _data arbitrary data populated by the front-end which initiated the sale
* @dev Throws if lock is disabled or key does not exist for _recipient. Throws if _recipient == address(0).
*/
function extend(
uint _value,
uint _tokenId,
address _referrer,
bytes calldata _data
) external payable;
/**
* Returns the percentage of the keyPrice to be sent to the referrer (in basis points)
* @param _referrer the address of the referrer
* @return referrerFee the percentage of the keyPrice to be sent to the referrer (in basis points)
*/
function referrerFees(
address _referrer
) external view returns (uint referrerFee);
/**
* Set a specific percentage of the keyPrice to be sent to the referrer while purchasing,
* extending or renewing a key.
* @param _referrer the address of the referrer
* @param _feeBasisPoint the percentage of the price to be used for this
* specific referrer (in basis points)
* @dev To send a fixed percentage of the key price to all referrers, sett a percentage to `address(0)`
*/
function setReferrerFee(address _referrer, uint _feeBasisPoint) external;
/**
* Merge existing keys
* @param _tokenIdFrom the id of the token to substract time from
* @param _tokenIdTo the id of the destination token to add time
* @param _amount the amount of time to transfer (in seconds)
*/
function mergeKeys(uint _tokenIdFrom, uint _tokenIdTo, uint _amount) external;
/**
* Deactivate an existing key
* @param _tokenId the id of token to burn
* @notice the key will be expired and ownership records will be destroyed
*/
function burn(uint _tokenId) external;
/**
* @param _gasRefundValue price in wei or token in smallest price unit
* @dev Set the value to be refunded to the sender on purchase
*/
function setGasRefundValue(uint256 _gasRefundValue) external;
/**
* _gasRefundValue price in wei or token in smallest price unit
* @dev Returns the value/price to be refunded to the sender on purchase
*/
function gasRefundValue() external view returns (uint256 _gasRefundValue);
/**
* @notice returns the minimum price paid for a purchase with these params.
* @dev this considers any discount from Unlock or the OnKeyPurchase hook.
*/
function purchasePriceFor(
address _recipient,
address _referrer,
bytes calldata _data
) external view returns (uint);
/**
* Allow a Lock manager to change the transfer fee.
* @dev Throws if called by other than a Lock manager
* @param _transferFeeBasisPoints The new transfer fee in basis-points(bps).
* Ex: 200 bps = 2%
*/
function updateTransferFee(uint _transferFeeBasisPoints) external;
/**
* Determines how much of a fee would need to be paid in order to
* transfer to another account. This is pro-rated so the fee goes
* down overtime.
* @dev Throws if _tokenId does not have a valid key
* @param _tokenId The id of the key check the transfer fee for.
* @param _time The amount of time to calculate the fee for.
* @return The transfer fee in seconds.
*/
function getTransferFee(
uint _tokenId,
uint _time
) external view returns (uint);
/**
* @dev Invoked by a Lock manager to expire the user's key
* and perform a refund and cancellation of the key
* @param _tokenId The key id we wish to refund to
* @param _amount The amount to refund to the key-owner
* @dev Throws if called by other than a Lock manager
* @dev Throws if _keyOwner does not have a valid key
*/
function expireAndRefundFor(uint _tokenId, uint _amount) external;
/**
* @dev allows the key manager to expire a given tokenId
* and send a refund to the keyOwner based on the amount of time remaining.
* @param _tokenId The id of the key to cancel.
* @notice cancel is enabled with a 10% penalty by default on all Locks.
*/
function cancelAndRefund(uint _tokenId) external;
/**
* Allow a Lock manager to change the refund penalty.
* @dev Throws if called by other than a Lock manager
* @param _freeTrialLength The new duration of free trials for this lock
* @param _refundPenaltyBasisPoints The new refund penaly in basis-points(bps)
*/
function updateRefundPenalty(
uint _freeTrialLength,
uint _refundPenaltyBasisPoints
) external;
/**
* @dev Determines how much of a refund a key owner would receive if they issued
* @param _tokenId the id of the token to get the refund value for.
* @notice Due to the time required to mine a tx, the actual refund amount will be lower
* than what the user reads from this call.
* @return refund the amount of tokens refunded
*/
function getCancelAndRefundValue(
uint _tokenId
) external view returns (uint refund);
function addLockManager(address account) external;
function isLockManager(address account) external view returns (bool);
/**
* Returns the address of the `onKeyPurchaseHook` hook.
* @return hookAddress address of the hook
*/
function onKeyPurchaseHook() external view returns (address hookAddress);
/**
* Returns the address of the `onKeyCancelHook` hook.
* @return hookAddress address of the hook
*/
function onKeyCancelHook() external view returns (address hookAddress);
/**
* Returns the address of the `onValidKeyHook` hook.
* @return hookAddress address of the hook
*/
function onValidKeyHook() external view returns (address hookAddress);
/**
* Returns the address of the `onTokenURIHook` hook.
* @return hookAddress address of the hook
*/
function onTokenURIHook() external view returns (address hookAddress);
/**
* Returns the address of the `onKeyTransferHook` hook.
* @return hookAddress address of the hook
*/
function onKeyTransferHook() external view returns (address hookAddress);
/**
* Returns the address of the `onKeyExtendHook` hook.
* @return hookAddress the address ok the hook
*/
function onKeyExtendHook() external view returns (address hookAddress);
/**
* Returns the address of the `onKeyGrantHook` hook.
* @return hookAddress the address ok the hook
*/
function onKeyGrantHook() external view returns (address hookAddress);
function renounceLockManager() external;
/**
* @return the maximum number of key allowed for a single address
*/
function maxKeysPerAddress() external view returns (uint);
function expirationDuration() external view returns (uint256);
function freeTrialLength() external view returns (uint256);
function keyPrice() external view returns (uint256);
function maxNumberOfKeys() external view returns (uint256);
function refundPenaltyBasisPoints() external view returns (uint256);
function tokenAddress() external view returns (address);
function transferFeeBasisPoints() external view returns (uint256);
function unlockProtocol() external view returns (address);
function keyManagerOf(uint) external view returns (address);
///===================================================================
/**
* @notice Allows the key owner to safely share their key (parent key) by
* transferring a portion of the remaining time to a new key (child key).
* @dev Throws if key is not valid.
* @dev Throws if `_to` is the zero address
* @param _to The recipient of the shared key
* @param _tokenId the key to share
* @param _timeShared The amount of time shared
* checks if `_to` is a smart contract (code size > 0). If so, it calls
* `onERC721Received` on `_to` and throws if the return value is not
* `bytes4(keccak256('onERC721Received(address,address,uint,bytes)'))`.
* @dev Emit Transfer event
*/
function shareKey(address _to, uint _tokenId, uint _timeShared) external;
/**
* @notice Update transfer and cancel rights for a given key
* @param _tokenId The id of the key to assign rights for
* @param _keyManager The address to assign the rights to for the given key
*/
function setKeyManagerOf(uint _tokenId, address _keyManager) external;
/**
* Check if a certain key is valid
* @param _tokenId the id of the key to check validity
* @notice this makes use of the onValidKeyHook if it is set
*/
function isValidKey(uint _tokenId) external view returns (bool);
/**
* Returns the number of keys owned by `_keyOwner` (expired or not)
* @param _keyOwner address for which we are retrieving the total number of keys
* @return numberOfKeys total number of keys owned by the address
*/
function totalKeys(
address _keyOwner
) external view returns (uint numberOfKeys);
/// @notice A descriptive name for a collection of NFTs in this contract
function name() external view returns (string memory _name);
///===================================================================
/// From ERC165.sol
function supportsInterface(bytes4 interfaceId) external view returns (bool);
///===================================================================
/// From ERC-721
/**
* In the specific case of a Lock, `balanceOf` returns only the tokens with a valid expiration timerange
* @return balance The number of valid keys owned by `_keyOwner`
*/
function balanceOf(address _owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the NFT specified by `tokenId`.
*/
function ownerOf(uint256 tokenId) external view returns (address _owner);
/**
* @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
* another (`to`).
*
* Requirements:
* - `from`, `to` cannot be zero.
* - `tokenId` must be owned by `from`.
* - If the caller is not `from`, it must be have been allowed to move this
* NFT by either `approve` or `setApprovalForAll`.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* an ERC721-like function to transfer a token from one account to another.
* @param from the owner of token to transfer
* @param to the address that will receive the token
* @param tokenId the id of the token
* @dev Requirements: if the caller is not `from`, it must be approved to move this token by
* either `approve` or `setApprovalForAll`.
* The key manager will be reset to address zero after the transfer
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* Lending a key allows you to transfer the token while retaining the
* ownerships right by setting yourself as a key manager first.
* @param from the owner of token to transfer
* @param to the address that will receive the token
* @param tokenId the id of the token
* @notice This function can only be called by 1) the key owner when no key manager is set or 2) the key manager.
* After calling the function, the `_recipent` will be the new owner, and the sender of the tx
* will become the key manager.
*/
function lendKey(address from, address to, uint tokenId) external;
/**
* Unlend is called when you have lent a key and want to claim its full ownership back.
* @param _recipient the address that will receive the token ownership
* @param _tokenId the id of the token
* @dev Only the key manager of the token can call this function
*/
function unlendKey(address _recipient, uint _tokenId) external;
function approve(address to, uint256 tokenId) external;
/**
* @notice Get the approved address for a single NFT
* @dev Throws if `_tokenId` is not a valid NFT.
* @param _tokenId The NFT to find the approved address for
* @return operator The approved address for this NFT, or the zero address if there is none
*/
function getApproved(
uint256 _tokenId
) external view returns (address operator);
/**
* @dev Sets or unsets the approval of a given operator
* An operator is allowed to transfer all tokens of the sender on their behalf
* @param _operator operator address to set the approval
* @param _approved representing the status of the approval to be set
* @notice disabled when transfers are disabled
*/
function setApprovalForAll(address _operator, bool _approved) external;
/**
* @dev Tells whether an operator is approved by a given keyManager
* @param _owner owner address which you want to query the approval of
* @param _operator operator address which you want to query the approval of
* @return bool whether the given operator is approved by the given owner
*/
function isApprovedForAll(
address _owner,
address _operator
) external view returns (bool);
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
/**
* Returns the total number of keys, including non-valid ones
* @return _totalKeysCreated the total number of keys, valid or not
*/
function totalSupply() external view returns (uint256 _totalKeysCreated);
function tokenOfOwnerByIndex(
address _owner,
uint256 index
) external view returns (uint256 tokenId);
function tokenByIndex(uint256 index) external view returns (uint256);
/**
* Innherited from Open Zeppelin AccessControl.sol
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
function grantRole(bytes32 role, address account) external;
function revokeRole(bytes32 role, address account) external;
function renounceRole(bytes32 role, address account) external;
function hasRole(bytes32 role, address account) external view returns (bool);
/** `owner()` is provided as an helper to mimick the `Ownable` contract ABI.
* The `Ownable` logic is used by many 3rd party services to determine
* contract ownership - e.g. who is allowed to edit metadata on Opensea.
*
* @notice This logic is NOT used internally by the Unlock Protocol and is made
* available only as a convenience helper.
*/
function owner() external view returns (address owner);
function setOwner(address account) external;
function isOwner(address account) external view returns (bool isOwner);
/**
* Migrate data from the previous single owner => key mapping to
* the new data structure w multiple tokens.
* @param _calldata an ABI-encoded representation of the params (v10: the number of records to migrate as `uint`)
* @dev when all record schemas are sucessfully upgraded, this function will update the `schemaVersion`
* variable to the latest/current lock version
*/
function migrate(bytes calldata _calldata) external;
/**
* Returns the version number of the data schema currently used by the lock
* @notice if this is different from `publicLockVersion`, then the ability to purchase, grant
* or extend keys is disabled.
* @dev will return 0 if no ;igration has ever been run
*/
function schemaVersion() external view returns (uint);
/**
* Set the schema version to the latest
* @notice only lock manager call call this
*/
function updateSchemaVersion() external;
/**
* Renew a given token
* @notice only works for non-free, expiring, ERC20 locks
* @param _tokenId the ID fo the token to renew
* @param _referrer the address of the person to be granted UDT
*/
function renewMembershipFor(uint _tokenId, address _referrer) external;
/**
* Set the expiration of a key
* @notice only a lock manager can call this function
* @param _tokenId the id of the key
* @param _newExpiration the new timestamp to use
*/
function setKeyExpiration(uint _tokenId, uint _newExpiration) external;
/**
* @dev helper to check if a key is currently renewable
* it will revert if the pricing or duration of the lock have been modified
* unfavorably since the key was bought(price increase or duration decrease).
* It will also revert if a lock is not renewable or if the key is not ready for renewal yet
* (at least 90% expired).
* @param tokenId the id of the token to check
* @param referrer the address where to send the referrer fee
* @return true if the terms has changed
*/
function isRenewable(
uint256 tokenId,
address referrer
) external view returns (bool);
}
// File contracts/interfaces/ISwapBurner.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
interface ISwapBurner {
function swapAndBurn(
address tokenAddress,
uint24 poolFee
) external payable returns (uint amount);
}
// File contracts/interfaces/IUniswapOracleV3.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.5.0;
interface IUniswapOracleV3 {
function PERIOD() external returns (uint256);
function factory() external returns (address);
function update(address _tokenIn, address _tokenOut) external;
function consult(
address _tokenIn,
uint256 _amountIn,
address _tokenOut
) external view returns (uint256 _amountOut);
function updateAndConsult(
address _tokenIn,
uint256 _amountIn,
address _tokenOut
) external returns (uint256 _amountOut);
}
// File contracts/interfaces/IUnlock.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.5.17 <0.9.0;
/**
* @title The Unlock Interface
**/
interface IUnlock {
// Use initialize instead of a constructor to support proxies(for upgradeability via zos).
function initialize(address _unlockOwner) external;
/**
* @dev deploy a ProxyAdmin contract used to upgrade locks
*/
function initializeProxyAdmin() external;
/**
* Retrieve the contract address of the proxy admin that manages the locks
* @return _proxyAdminAddress the address of the ProxyAdmin instance
*/
function proxyAdminAddress()
external
view
returns (address _proxyAdminAddress);
/**
* @notice Create lock (legacy)
* This deploys a lock for a creator. It also keeps track of the deployed lock.
* @param _expirationDuration the duration of the lock (pass 0 for unlimited duration)
* @param _tokenAddress set to the ERC20 token address, or 0 for ETH.
* @param _keyPrice the price of each key
* @param _maxNumberOfKeys the maximum nimbers of keys to be edited
* @param _lockName the name of the lock
* param _salt [deprec] -- kept only for backwards copatibility
* This may be implemented as a sequence ID or with RNG. It's used with `create2`
* to know the lock's address before the transaction is mined.
* @dev internally call `createUpgradeableLock`
*/
function createLock(
uint _expirationDuration,
address _tokenAddress,
uint _keyPrice,
uint _maxNumberOfKeys,
string calldata _lockName,
bytes12 // _salt
) external returns (address);
/**
* @notice Create lock (default)
* This deploys a lock for a creator. It also keeps track of the deployed lock.
* @param data bytes containing the call to initialize the lock template
* @dev this call is passed as encoded function - for instance:
* bytes memory data = abi.encodeWithSignature(
* 'initialize(address,uint256,address,uint256,uint256,string)',
* msg.sender,
* _expirationDuration,
* _tokenAddress,
* _keyPrice,
* _maxNumberOfKeys,
* _lockName
* );
* @return address of the create lock
*/
function createUpgradeableLock(bytes memory data) external returns (address);
/**
* Create an upgradeable lock using a specific PublicLock version
* @param data bytes containing the call to initialize the lock template
* (refer to createUpgradeableLock for more details)
* @param _lockVersion the version of the lock to use
*/
function createUpgradeableLockAtVersion(
bytes memory data,
uint16 _lockVersion
) external returns (address);
/**
* @notice Upgrade a lock to a specific version
* @dev only available for publicLockVersion > 10 (proxyAdmin /required)
* @param lockAddress the existing lock address
* @param version the version number you are targeting
* Likely implemented with OpenZeppelin TransparentProxy contract
*/
function upgradeLock(
address payable lockAddress,
uint16 version
) external returns (address);
/**
* This function keeps track of the added GDP, as well as grants of discount tokens
* to the referrer, if applicable.
* The number of discount tokens granted is based on the value of the referal,
* the current growth rate and the lock's discount token distribution rate
* This function is invoked by a previously deployed lock only.
*/
function recordKeyPurchase(
uint _value,
address _referrer // solhint-disable-line no-unused-vars
) external;
/**
* @notice [DEPRECATED] Call to this function has been removed from PublicLock > v9.
* @dev [DEPRECATED] Kept for backwards compatibility
* This function will keep track of consumed discounts by a given user.
* It will also grant discount tokens to the creator who is granting the discount based on the
* amount of discount and compensation rate.
* This function is invoked by a previously deployed lock only.
*/
function recordConsumedDiscount(
uint _discount,
uint _tokens // solhint-disable-line no-unused-vars
) external view;
/**
* @notice [DEPRECATED] Call to this function has been removed from PublicLock > v9.
* @dev [DEPRECATED] Kept for backwards compatibility
* This function returns the discount available for a user, when purchasing a
* a key from a lock.
* This does not modify the state. It returns both the discount and the number of tokens
* consumed to grant that discount.
*/
function computeAvailableDiscountFor(
address _purchaser, // solhint-disable-line no-unused-vars
uint _keyPrice // solhint-disable-line no-unused-vars
) external pure returns (uint discount, uint tokens);
// Function to read the globalTokenURI field.
function globalBaseTokenURI() external view returns (string memory);
/**
* @dev Redundant with globalBaseTokenURI() for backwards compatibility with v3 & v4 locks.
*/
function getGlobalBaseTokenURI() external view returns (string memory);
// Function to read the globalTokenSymbol field.
function globalTokenSymbol() external view returns (string memory);
// Function to read the chainId field.
function chainId() external view returns (uint);
/**
* @dev Redundant with globalTokenSymbol() for backwards compatibility with v3 & v4 locks.
*/
function getGlobalTokenSymbol() external view returns (string memory);
/**
* @notice Allows the owner to update configuration variables
*/
function configUnlock(
address _udt,
address _weth,
uint _estimatedGasForPurchase,
string calldata _symbol,
string calldata _URI,
uint _chainId
) external;
/**
* @notice Add a PublicLock template to be used for future calls to `createLock`.
* @dev This is used to upgrade conytract per version number
*/
function addLockTemplate(address impl, uint16 version) external;
/**
* Match lock templates addresses with version numbers
* @param _version the number of the version of the template
* @return _implAddress address of the lock templates
*/
function publicLockImpls(
uint16 _version
) external view returns (address _implAddress);
/**
* Match version numbers with lock templates addresses
* @param _impl the address of the deployed template contract (PublicLock)
* @return number of the version corresponding to this address
*/
function publicLockVersions(address _impl) external view returns (uint16);
/**
* Retrive the latest existing lock template version
* @return _version the version number of the latest template (used to deploy contracts)
*/
function publicLockLatestVersion() external view returns (uint16 _version);
/**
* @notice Upgrade the PublicLock template used for future calls to `createLock`.
* @dev This will initialize the template and revokeOwnership.
*/
function setLockTemplate(address payable _publicLockAddress) external;
// Allows the owner to change the value tracking variables as needed.
function resetTrackedValue(
uint _grossNetworkProduct,
uint _totalDiscountGranted
) external;
function grossNetworkProduct() external view returns (uint);
function totalDiscountGranted() external view returns (uint);
function locks(
address
)
external
view
returns (bool deployed, uint totalSales, uint yieldedDiscountTokens);
// The address of the public lock template, used when `createLock` is called
function publicLockAddress() external view returns (address);
// Map token address to exchange contract address if the token is supported
// Used for GDP calculations
function uniswapOracles(address) external view returns (address);
// The WETH token address, used for value calculations
function weth() external view returns (address);
// The UDT token address, used to mint tokens on referral
function udt() external view returns (address);
// The approx amount of gas required to purchase a key
function estimatedGasForPurchase() external view returns (uint);
/**
* Helper to get the network mining basefee as introduced in EIP-1559
* @dev this helper can be wrapped in try/catch statement to avoid
* revert in networks where EIP-1559 is not implemented
*/
function networkBaseFee() external view returns (uint);
// The version number of the current Unlock implementation on this network
function unlockVersion() external pure returns (uint16);
/**
* @notice allows the owner to set the oracle address to use for value conversions
* setting the _oracleAddress to address(0) removes support for the token
* @dev This will also call update to ensure at least one datapoint has been recorded.
*/
function setOracle(address _tokenAddress, address _oracleAddress) external;
// Initialize the Ownable contract, granting contract ownership to the specified sender
function __initializeOwnable(address sender) external;
/**
* @dev Returns true if the caller is the current owner.
*/
function isOwner() external view returns (bool);
/**
* @dev Returns the address of the current owner.
*/
function owner() external view returns (address);
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() external;
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) external;
/**
* Set the fee collected by the protocol
* @param _protocolFee fee (in basis points)
*/
function setProtocolFee(uint _protocolFee) external;
/**
* The fee (in basis points) collected by the protocol on each purchase /
extension / renewal of a key
* @return the protocol fee in basic point
*/
function protocolFee() external view returns (uint);
/**
* Returns the ProxyAdmin contract address that manage upgrades for
* the current Unlock contract.
* @dev this reads the address directly from storage, at the slot `_ADMIN_SLOT`
* defined by Open Zeppelin's EIP1967 Proxy implementation which corresponds
* to the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1
*/
function getAdmin() external view returns (address);
/**
* Call executed by a lock after its version upgrade triggred by `upgradeLock`
* - PublicLock v12 > v13 (mainnet): migrate an existing Lock to another instance
* of the Unlock contract
* @dev The `msg.sender` will be the upgraded lock
*/
function postLockUpgrade() external;
/**
* Functions which transfers tokens held by the contract
* It handles both ERC20 and the base currency.
* @dev This function is onlyOwner
* @param token the address of the token to transfer (pass the 0x0 address for the base currency)
* @param to the address to transfer the tokens to
* @param amount the amount of tokens to transfer
*/
function transferTokens(address token, address to, uint256 amount) external;
/**
* Removes a lock from the list of locks. This will prevent the lock from being able to receive governance tokens.
* The lock will still be able to sell its memberships.
* @dev This function is onlyOwner
* @param lock address of the lock to remove
*/
function removeLock(address lock) external;
/**
* Send tokens held by this contract to the UDT SwapBurner contract. The tokens sent to the
* contract are then swapped for UDT and UDT itself will be sent to a burner address.
* @notice This function can be called by anyone (not only the contract owner) as a way to ensure decentralization.
* @param token the address of the token (zero address for native) to swap and burn
* @param amount the amount of tokens to swap and burn
* @param poolFee the poolFee of the token <> WETH/Wrapped Native asset to use for the swap
*/
function swapAndBurn(address token, uint256 amount, uint24 poolFee) external;
/**
* Set the UDT Swap and Burn contract address
* @param _swapBurnerAddress the address of the SwapBurner contract instance
*/
function setSwapBurner(address _swapBurnerAddress) external;
/**
* The address of the UDT Swap and Burn contract
*/
function swapBurnerAddress() external view returns (address);
}
// File @openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol@v4.9.5
// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [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://consensys.net/diligence/blog/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.8.0/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 functionCallWithValue(target, data, 0, "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");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or 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 {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// 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
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
// File contracts/utils/UnlockInitializable.sol
// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/utils/Initializable.sol)
pragma solidity ^0.8.0;
/**
* @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.
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the
* initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() initializer {}
* ```
* ====
*/
abstract contract UnlockInitializable {
/**
* @dev Indicates that the contract has been initialized.
*/
bool private initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private initializing;
/**
* @dev Modifier to protect an initializer function from being invoked twice.
*/
modifier initializer() {
// If the contract is initializing we ignore whether initialized is set in order to support multiple
// inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
// contract may have been reentered.
require(
initializing ? _isConstructor() : !initialized,
"ALREADY_INITIALIZED"
);
bool isTopLevelCall = !initializing;
if (isTopLevelCall) {
initializing = true;
initialized = true;
}
_;
if (isTopLevelCall) {
initializing = false;
}
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} modifier, directly or indirectly.
*/
modifier onlyInitializing() {
require(initializing, "NOT_INITIALIZING");
_;
}
function _isConstructor() private view returns (bool) {
return !AddressUpgradeable.isContract(address(this));
}
}
// File contracts/utils/UnlockContextUpgradeable.sol
// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract UnlockContextUpgradeable is UnlockInitializable {
function __Context_init() internal onlyInitializing {
__Context_init_unchained();
}
function __Context_init_unchained() internal onlyInitializing {}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
uint256[50] private ______gap;
}
// File contracts/utils/UnlockOwnable.sol
// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts v4.3.2 (access/Ownable.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be aplied to your functions to restrict their use to
* the owner.
*
* This contract was originally part of openzeppelin/contracts-ethereum-package
* but had to be included (instead of using the one in openzeppelin/contracts-upgradeable )
* because the ______gap array length was 49 instead of 50
*/
abstract contract UnlockOwnable is
UnlockInitializable,
UnlockContextUpgradeable
{
address private _owner;
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function __initializeOwnable(address sender) public initializer {
_owner = sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner(), "ONLY_OWNER");
_;
}
/**
* @dev Returns true if the caller is the current owner.
*/
function isOwner() public view returns (bool) {
return _msgSender() == _owner;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* > Note: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0), "INVALID_OWNER");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
*/
function _transferOwnership(address newOwner) internal {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
uint256[50] private ______gap;
}
// File contracts/utils/UnlockProxyAdmin.sol
/* solhint-disable no-inline-assembly */
// Sources flattened with hardhat v2.18.3 https://hardhat.org
// Original license: SPDX_License_Identifier: MIT
pragma solidity ^0.8.21;
// File @openzeppelin/contracts/utils/Context.sol@v4.9.3
// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
// File @openzeppelin/contracts/access/Ownable.sol@v4.9.3
// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
// File @openzeppelin/contracts/interfaces/draft-IERC1822.sol@v4.9.3
// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
/**
* @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
* proxy whose upgrades are fully controlled by the current implementation.
*/
interface IERC1822Proxiable {
/**
* @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
* address.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy.
*/
function proxiableUUID() external view returns (bytes32);
}
// File @openzeppelin/contracts/interfaces/IERC1967.sol@v4.9.3
// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC1967.sol)
/**
* @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
*
* _Available since v4.8.3._
*/
interface IERC1967 {
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Emitted when the beacon is changed.
*/
event BeaconUpgraded(address indexed beacon);
}
// File @openzeppelin/contracts/proxy/beacon/IBeacon.sol@v4.9.3
// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {BeaconProxy} will check that this address is a contract.
*/
function implementation() external view returns (address);
}
// File @openzeppelin/contracts/utils/Address.sol@v4.9.3
// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
/**
* @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
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [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://consensys.net/diligence/blog/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.8.0/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
functionCallWithValue(target, data, 0, "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"
);
(bool success, bytes memory returndata) = target.call{value: value}(data);
return
verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.staticcall(data);
return
verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return
verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or 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 {
_revert(returndata, errorMessage);
}
}
function _revert(
bytes memory returndata,
string memory errorMessage
) private pure {
// 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
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
// File @openzeppelin/contracts/utils/StorageSlot.sol@v4.9.3
// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
* _Available since v4.9 for `string`, `bytes`._
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(
bytes32 slot
) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(
bytes32 slot
) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(
bytes32 slot
) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(
bytes32 slot
) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(
bytes32 slot
) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(
string storage store
) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(
bytes32 slot
) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(
bytes storage store
) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}
// File @openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol@v4.9.3
// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/ERC1967/ERC1967Upgrade.sol)
/**
* @dev This abstract contract provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
*
* _Available since v4.1._
*/
abstract contract ERC1967Upgrade is IERC1967 {
// This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
bytes32 private constant _ROLLBACK_SLOT =
0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 internal constant _IMPLEMENTATION_SLOT =
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev Returns the current implementation address.
*/
function _getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
require(
Address.isContract(newImplementation),
"ERC1967: new implementation is not a contract"
);
StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Perform implementation upgrade
*
* Emits an {Upgraded} event.
*/
function _upgradeTo(address newImplementation) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
}
/**
* @dev Perform implementation upgrade with additional setup call.
*
* Emits an {Upgraded} event.
*/
function _upgradeToAndCall(
address newImplementation,
bytes memory data,
bool forceCall
) internal {
_upgradeTo(newImplementation);
if (data.length > 0 || forceCall) {
Address.functionDelegateCall(newImplementation, data);
}
}
/**
* @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
*
* Emits an {Upgraded} event.
*/
function _upgradeToAndCallUUPS(
address newImplementation,
bytes memory data,
bool forceCall
) internal {
// Upgrades from old implementations will perform a rollback test. This test requires the new
// implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
// this special case will break upgrade paths from old UUPS implementation to new ones.
if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
_setImplementation(newImplementation);
} else {
try IERC1822Proxiable(newImplementation).proxiableUUID() returns (
bytes32 slot
) {
require(
slot == _IMPLEMENTATION_SLOT,
"ERC1967Upgrade: unsupported proxiableUUID"
);
} catch {
revert("ERC1967Upgrade: new implementation is not UUPS");
}
_upgradeToAndCall(newImplementation, data, forceCall);
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 internal constant _ADMIN_SLOT =
0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Returns the current admin.
*/
function _getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
require(newAdmin != address(0), "ERC1967: new admin is the zero address");
StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {AdminChanged} event.
*/
function _changeAdmin(address newAdmin) internal {
emit AdminChanged(_getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
*/
bytes32 internal constant _BEACON_SLOT =
0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Returns the current beacon.
*/
function _getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the EIP1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
require(
Address.isContract(newBeacon),
"ERC1967: new beacon is not a contract"
);
require(
Address.isContract(IBeacon(newBeacon).implementation()),
"ERC1967: beacon implementation is not a contract"
);
StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
}
/**
* @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
* not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
*
* Emits a {BeaconUpgraded} event.
*/
function _upgradeBeaconToAndCall(
address newBeacon,
bytes memory data,
bool forceCall
) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length > 0 || forceCall) {
Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
}
}
}
// File @openzeppelin/contracts/proxy/Proxy.sol@v4.9.3
// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)
/**
* @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
* instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
* be specified by overriding the virtual {_implementation} function.
*
* Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
* different contract through the {_delegate} function.
*
* The success and return data of the delegated call will be returned back to the caller of the proxy.
*/
abstract contract Proxy {
/**
* @dev Delegates the current call to `implementation`.
*
* This function does not return to its internal call site, it will return directly to the external caller.
*/
function _delegate(address implementation) internal virtual {
assembly {
// Copy msg.data. We take full control of memory in this inline assembly
// block because it will not return to Solidity code. We overwrite the
// Solidity scratch pad at memory position 0.
calldatacopy(0, 0, calldatasize())
// Call the implementation.
// out and outsize are 0 because we don't know the size yet.
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
// Copy the returned data.
returndatacopy(0, 0, returndatasize())
switch result
// delegatecall returns 0 on error.
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
/**
* @dev This is a virtual function that should be overridden so it returns the address to which the fallback function
* and {_fallback} should delegate.
*/
function _implementation() internal view virtual returns (address);
/**
* @dev Delegates the current call to the address returned by `_implementation()`.
*
* This function does not return to its internal call site, it will return directly to the external caller.
*/
function _fallback() internal virtual {
_beforeFallback();
_delegate(_implementation());
}
/**
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
* function in the contract matches the call data.
*/
fallback() external payable virtual {
_fallback();
}
/**
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
* is empty.
*/
receive() external payable virtual {
_fallback();
}
/**
* @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
* call, or as part of the Solidity `fallback` or `receive` functions.
*
* If overridden should call `super._beforeFallback()`.
*/
function _beforeFallback() internal virtual {}
}
// File @openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol@v4.9.3
// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (proxy/ERC1967/ERC1967Proxy.sol)
/**
* @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
* implementation address that can be changed. This address is stored in storage in the location specified by
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
* implementation behind the proxy.
*/
contract ERC1967Proxy is Proxy, ERC1967Upgrade {
/**
* @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
*
* If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
* function call, and allows initializing the storage of the proxy like a Solidity constructor.
*/
constructor(address _logic, bytes memory _data) payable {
_upgradeToAndCall(_logic, _data, false);
}
/**
* @dev Returns the current implementation address.
*/
function _implementation()
internal
view
virtual
override
returns (address impl)
{
return ERC1967Upgrade._getImplementation();
}
}
// File @openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol@v4.9.3
// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/transparent/TransparentUpgradeableProxy.sol)
/**
* @dev Interface for {TransparentUpgradeableProxy}. In order to implement transparency, {TransparentUpgradeableProxy}
* does not implement this interface directly, and some of its functions are implemented by an internal dispatch
* mechanism. The compiler is unaware that these functions are implemented by {TransparentUpgradeableProxy} and will not
* include them in the ABI so this interface must be used to interact with it.
*/
interface ITransparentUpgradeableProxy is IERC1967 {
function admin() external view returns (address);
function implementation() external view returns (address);
function changeAdmin(address) external;
function upgradeTo(address) external;
function upgradeToAndCall(address, bytes memory) external payable;
}
/**
* @dev This contract implements a proxy that is upgradeable by an admin.
*
* To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
* clashing], which can potentially be used in an attack, this contract uses the
* https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
* things that go hand in hand:
*
* 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
* that call matches one of the admin functions exposed by the proxy itself.
* 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
* implementation. If the admin tries to call a function on the implementation it will fail with an error that says
* "admin cannot fallback to proxy target".
*
* These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
* the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
* to sudden errors when trying to call a function from the proxy implementation.
*
* Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
* you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
*
* NOTE: The real interface of this proxy is that defined in `ITransparentUpgradeableProxy`. This contract does not
* inherit from that interface, and instead the admin functions are implicitly implemented using a custom dispatch
* mechanism in `_fallback`. Consequently, the compiler will not produce an ABI for this contract. This is necessary to
* fully implement transparency without decoding reverts caused by selector clashes between the proxy and the
* implementation.
*
* WARNING: It is not recommended to extend this contract to add additional external functions. If you do so, the compiler
* will not check that there are no selector conflicts, due to the note above. A selector clash between any new function
* and the functions declared in {ITransparentUpgradeableProxy} will be resolved in favor of the new one. This could
* render the admin operations inaccessible, which could prevent upgradeability. Transparency may also be compromised.
*/
contract TransparentUpgradeableProxy is ERC1967Proxy {
/**
* @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
* optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
*/
constructor(
address _logic,
address admin_,
bytes memory _data
) payable ERC1967Proxy(_logic, _data) {
_changeAdmin(admin_);
}
/**
* @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
*
* CAUTION: This modifier is deprecated, as it could cause issues if the modified function has arguments, and the
* implementation provides a function with the same selector.
*/
modifier ifAdmin() {
if (msg.sender == _getAdmin()) {
_;
} else {
_fallback();
}
}
/**
* @dev If caller is the admin process the call internally, otherwise transparently fallback to the proxy behavior
*/
function _fallback() internal virtual override {
if (msg.sender == _getAdmin()) {
bytes memory ret;
bytes4 selector = msg.sig;
if (selector == ITransparentUpgradeableProxy.upgradeTo.selector) {
ret = _dispatchUpgradeTo();
} else if (
selector == ITransparentUpgradeableProxy.upgradeToAndCall.selector
) {
ret = _dispatchUpgradeToAndCall();
} else if (
selector == ITransparentUpgradeableProxy.changeAdmin.selector
) {
ret = _dispatchChangeAdmin();
} else if (selector == ITransparentUpgradeableProxy.admin.selector) {
ret = _dispatchAdmin();
} else if (
selector == ITransparentUpgradeableProxy.implementation.selector
) {
ret = _dispatchImplementation();
} else {
revert(
"TransparentUpgradeableProxy: admin cannot fallback to proxy target"
);
}
assembly {
return(add(ret, 0x20), mload(ret))
}
} else {
super._fallback();
}
}
/**
* @dev Returns the current admin.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
* https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
*/
function _dispatchAdmin() private returns (bytes memory) {
_requireZeroValue();
address admin = _getAdmin();
return abi.encode(admin);
}
/**
* @dev Returns the current implementation.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
* https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
*/
function _dispatchImplementation() private returns (bytes memory) {
_requireZeroValue();
address implementation = _implementation();
return abi.encode(implementation);
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {AdminChanged} event.
*/
function _dispatchChangeAdmin() private returns (bytes memory) {
_requireZeroValue();
address newAdmin = abi.decode(msg.data[4:], (address));
_changeAdmin(newAdmin);
return "";
}
/**
* @dev Upgrade the implementation of the proxy.
*/
function _dispatchUpgradeTo() private returns (bytes memory) {
_requireZeroValue();
address newImplementation = abi.decode(msg.data[4:], (address));
_upgradeToAndCall(newImplementation, bytes(""), false);
return "";
}
/**
* @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
* by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
* proxied contract.
*/
function _dispatchUpgradeToAndCall() private returns (bytes memory) {
(address newImplementation, bytes memory data) = abi.decode(
msg.data[4:],
(address, bytes)
);
_upgradeToAndCall(newImplementation, data, true);
return "";
}
/**
* @dev Returns the current admin.
*
* CAUTION: This function is deprecated. Use {ERC1967Upgrade-_getAdmin} instead.
*/
function _admin() internal view virtual returns (address) {
return _getAdmin();
}
/**
* @dev To keep this contract fully transparent, all `ifAdmin` functions must be payable. This helper is here to
* emulate some proxy functions being non-payable while still allowing value to pass through.
*/
function _requireZeroValue() private {
require(msg.value == 0);
}
}
// File @openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol@v4.9.3
// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.3) (proxy/transparent/ProxyAdmin.sol)
/**
* @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
* explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
*/
contract ProxyAdmin is Ownable {
/**
* @dev Returns the current implementation of `proxy`.
*
* Requirements:
*
* - This contract must be the admin of `proxy`.
*/
function getProxyImplementation(
ITransparentUpgradeableProxy proxy
) public view virtual returns (address) {
// We need to manually run the static call since the getter cannot be flagged as view
// bytes4(keccak256("implementation()")) == 0x5c60da1b
(bool success, bytes memory returndata) = address(proxy).staticcall(
hex"5c60da1b"
);
require(success);
return abi.decode(returndata, (address));
}
/**
* @dev Returns the current admin of `proxy`.
*
* Requirements:
*
* - This contract must be the admin of `proxy`.
*/
function getProxyAdmin(
ITransparentUpgradeableProxy proxy
) public view virtual returns (address) {
// We need to manually run the static call since the getter cannot be flagged as view
// bytes4(keccak256("admin()")) == 0xf851a440
(bool success, bytes memory returndata) = address(proxy).staticcall(
hex"f851a440"
);
require(success);
return abi.decode(returndata, (address));
}
/**
* @dev Changes the admin of `proxy` to `newAdmin`.
*
* Requirements:
*
* - This contract must be the current admin of `proxy`.
*/
function changeProxyAdmin(
ITransparentUpgradeableProxy proxy,
address newAdmin
) public virtual onlyOwner {
proxy.changeAdmin(newAdmin);
}
/**
* @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.
*
* Requirements:
*
* - This contract must be the admin of `proxy`.
*/
function upgrade(
ITransparentUpgradeableProxy proxy,
address implementation
) public virtual onlyOwner {
proxy.upgradeTo(implementation);
}
/**
* @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See
* {TransparentUpgradeableProxy-upgradeToAndCall}.
*
* Requirements:
*
* - This contract must be the admin of `proxy`.
*/
function upgradeAndCall(
ITransparentUpgradeableProxy proxy,
address implementation,
bytes memory data
) public payable virtual onlyOwner {
proxy.upgradeToAndCall{value: msg.value}(implementation, data);
}
}
// File contracts/Unlock.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity 0.8.21;
/**
* @title The Unlock contract
* @author Julien Genestoux (unlock-protocol.com)
* This smart contract has 3 main roles:
* 1. Distribute discounts to discount token holders
* 2. Grant dicount tokens to users making referrals and/or publishers granting discounts.
* 3. Create & deploy Public Lock contracts.
* In order to achieve these 3 elements, it keeps track of several things such as
* a. Deployed locks addresses and balances of discount tokens granted by each lock.
* b. The total network product (sum of all key sales, net of discounts)
* c. Total of discounts granted
* d. Balances of discount tokens, including 'frozen' tokens (which have been used to claim
* discounts and cannot be used/transferred for a given period)
* e. Growth rate of Network Product
* f. Growth rate of Discount tokens supply
* The smart contract has an owner who only can perform the following
* - Upgrades
* - Change in golden rules (20% of GDP available in discounts, and supply growth rate is at most
* 50% of GNP growth rate)
* NOTE: This smart contract is partially implemented for now until enough Locks are deployed and
* in the wild.
* The partial implementation includes the following features:
* a. Keeping track of deployed locks
* b. Keeping track of GNP
*/
/// @dev Must list the direct base contracts in the order from “most base-like” to “most derived”.
/// https://solidity.readthedocs.io/en/latest/contracts.html#multiple-inheritance-and-linearization
contract Unlock is UnlockInitializable, UnlockOwnable {
/**
* The struct for a lock
* We use deployed to keep track of deployments.
* This is required because both totalSales and yieldedDiscountTokens are 0 when initialized,
* which would be the same values when the lock is not set.
*/
struct LockBalances {
bool deployed;
uint totalSales; // This is in wei
uint yieldedDiscountTokens;
}
modifier onlyFromDeployedLock() {
require(locks[msg.sender].deployed, "ONLY_LOCKS");
_;
}
uint public grossNetworkProduct;
uint public totalDiscountGranted;
// We keep track of deployed locks to ensure that callers are all deployed locks.
mapping(address => LockBalances) public locks;
// global base token URI
// Used by locks where the owner has not set a custom base URI.
string public globalBaseTokenURI;
// global base token symbol
// Used by locks where the owner has not set a custom symbol
string public globalTokenSymbol;
// The address of the latest public lock template, used by default when `createLock` is called
address public publicLockAddress;
// Map token address to oracle contract address if the token is supported
// Used for GDP calculations
mapping(address => IUniswapOracleV3) public uniswapOracles;
// The WETH token address, used for value calculations
address public weth;
// The UDT token address, used to mint tokens on referral
address public udt;
// The approx amount of gas required to purchase a key
uint public estimatedGasForPurchase;
// Blockchain ID the network id on which this version of Unlock is operating
uint public chainId;
// store proxy admin
address public proxyAdminAddress;
ProxyAdmin private proxyAdmin;
// publicLock templates
mapping(address => uint16) private _publicLockVersions;
mapping(uint16 => address) private _publicLockImpls;
uint16 public publicLockLatestVersion;
// protocol fee
uint public protocolFee;
// UDT SwapBurner contract address
address public swapBurnerAddress;
// errors
error Unlock__MANAGER_ONLY();
error Unlock__VERSION_TOO_HIGH();
error Unlock__MISSING_TEMPLATE();
error Unlock__ALREADY_DEPLOYED();
error Unlock__MISSING_PROXY_ADMIN();
error Unlock__MISSING_LOCK_TEMPLATE();
error Unlock__MISSING_LOCK(address lockAddress);
error Unlock__INVALID_AMOUNT();
error Unlock__INVALID_TOKEN();
// Events
event NewLock(address indexed lockOwner, address indexed newLockAddress);
event LockUpgraded(address lockAddress, uint16 version);
event ConfigUnlock(
address udt,
address weth,
uint estimatedGasForPurchase,
string globalTokenSymbol,
string globalTokenURI,
uint chainId
);
event SetLockTemplate(address publicLockAddress);
event GNPChanged(
uint grossNetworkProduct,
uint _valueInETH,
address tokenAddress,
uint value,
address lockAddress
);
event ResetTrackedValue(uint grossNetworkProduct, uint totalDiscountGranted);
event UnlockTemplateAdded(address indexed impl, uint16 indexed version);
event SwapBurnerChanged(
address indexed oldAddress,
address indexed newAddress
);
// Use initialize instead of a constructor to support proxies (for upgradeability via OZ).
function initialize(address _unlockOwner) public initializer {
// We must manually initialize Ownable
UnlockOwnable.__initializeOwnable(_unlockOwner);
// add a proxy admin on deployment
_deployProxyAdmin();
}
function initializeProxyAdmin() public onlyOwner {
if (proxyAdminAddress != address(0)) {
revert Unlock__ALREADY_DEPLOYED();
}
_deployProxyAdmin();
}
/**
* @dev Deploy the ProxyAdmin contract that will manage lock templates upgrades
* This deploys an instance of ProxyAdmin used by PublicLock transparent proxies.
*/
function _deployProxyAdmin() private returns (address) {
proxyAdmin = new ProxyAdmin();
proxyAdminAddress = address(proxyAdmin);
return address(proxyAdmin);
}
/**
* @dev Helper to get the version number of a template from his address
*/
function publicLockVersions(address _impl) external view returns (uint16) {
return _publicLockVersions[_impl];
}
/**
* @dev Helper to get the address of a template based on its version number
*/
function publicLockImpls(uint16 _version) external view returns (address) {
return _publicLockImpls[_version];
}
/**
* @dev Registers a new PublicLock template immplementation
* The template is identified by a version number
* Once registered, the template can be used to upgrade an existing Lock
* @dev This will initialize the template and revokeOwnership.
*/
function addLockTemplate(address impl, uint16 version) public onlyOwner {
// if the template has not been initialized,
// claim the template so that no-one else could
try IPublicLock(impl).initialize(address(this), 0, address(0), 0, 0, "") {
// renounce the lock manager role that was added during initialization
IPublicLock(impl).renounceLockManager();
} catch {
// failure means that the template is already initialized
}
_publicLockVersions[impl] = version;
_publicLockImpls[version] = impl;
emit UnlockTemplateAdded(impl, version);
}
/**
* @notice Create lock (legacy)
* This deploys a lock for a creator. It also keeps track of the deployed lock.
* @param _expirationDuration the duration of the lock (pass type(uint).max for unlimited duration)
* @param _tokenAddress set to the ERC20 token address, or 0 for ETH.
* @param _keyPrice the price of each key
* @param _maxNumberOfKeys the maximum nimbers of keys to be edited
* @param _lockName the name of the lock
* param _salt [deprec] -- kept only for backwards copatibility
* This may be implemented as a sequence ID or with RNG. It's used with `create2`
* to know the lock's address before the transaction is mined.
* @dev internally call `createUpgradeableLock`
*/
function createLock(
uint _expirationDuration,
address _tokenAddress,
uint _keyPrice,
uint _maxNumberOfKeys,
string calldata _lockName,
bytes12 // _salt
) public returns (address) {
bytes memory data = abi.encodeWithSignature(
"initialize(address,uint256,address,uint256,uint256,string)",
msg.sender,
_expirationDuration,
_tokenAddress,
_keyPrice,
_maxNumberOfKeys,
_lockName
);
return createUpgradeableLock(data);
}
/**
* @notice Create upgradeable lock
* This deploys a lock for a creator. It also keeps track of the deployed lock.
* @param data bytes containing the call to initialize the lock template
* @dev this call is passed as encoded function - for instance:
* bytes memory data = abi.encodeWithSignature(
* 'initialize(address,uint256,address,uint256,uint256,string)',
* msg.sender,
* _expirationDuration,
* _tokenAddress,
* _keyPrice,
* _maxNumberOfKeys,
* _lockName
* );
* @return address of the create lock
*/
function createUpgradeableLock(bytes memory data) public returns (address) {
address newLock = createUpgradeableLockAtVersion(
data,
publicLockLatestVersion
);
return newLock;
}
/**
* Create an upgradeable lock using a specific PublicLock version
* @param data bytes containing the call to initialize the lock template
* (refer to createUpgradeableLock for more details)
* @param _lockVersion the version of the lock to use
*/
function createUpgradeableLockAtVersion(
bytes memory data,
uint16 _lockVersion
) public returns (address) {
if (proxyAdminAddress == address(0)) {
revert Unlock__MISSING_PROXY_ADMIN();
}
// get lock version
address publicLockImpl = _publicLockImpls[_lockVersion];
if (publicLockImpl == address(0)) {
revert Unlock__MISSING_LOCK_TEMPLATE();
}
// deploy a proxy pointing to impl
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
publicLockImpl,
proxyAdminAddress,
data
);
address payable newLock = payable(address(proxy));
// assign the new Lock
locks[newLock] = LockBalances({
deployed: true,
totalSales: 0,
yieldedDiscountTokens: 0
});
// trigger event
emit NewLock(msg.sender, newLock);
return newLock;
}
/**
* @dev Upgrade a Lock template implementation
* @param lockAddress the address of the lock to be upgraded
* @param version the version number of the template
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function upgradeLock(
address payable lockAddress,
uint16 version
) external returns (address) {
if (proxyAdminAddress == address(0)) {
revert Unlock__MISSING_PROXY_ADMIN();
}
// check perms
if (_isLockManager(lockAddress, msg.sender) != true) {
revert Unlock__MANAGER_ONLY();
}
// check version
IPublicLock lock = IPublicLock(lockAddress);
uint16 currentVersion = lock.publicLockVersion();
if (version != currentVersion + 1) {
revert Unlock__VERSION_TOO_HIGH();
}
// make our upgrade
address impl = _publicLockImpls[version];
if (impl == address(0)) {
revert Unlock__MISSING_TEMPLATE();
}
ITransparentUpgradeableProxy proxy = ITransparentUpgradeableProxy(
lockAddress
);
proxyAdmin.upgrade(proxy, impl);
// let's upgrade the data schema
// the function is called with empty bytes as migration behaviour is set by the lock in accordance to data version
lock.migrate("0x");
emit LockUpgraded(lockAddress, version);
return lockAddress;
}
function _isLockManager(
address lockAddress,
address _sender
) private view returns (bool isManager) {
IPublicLock lock = IPublicLock(lockAddress);
return lock.isLockManager(_sender);
}
/**
* @notice [DEPRECATED] Call to this function has been removed from PublicLock > v9.
* @dev [DEPRECATED] Kept for backwards compatibility
*/
function computeAvailableDiscountFor(
address /* _purchaser */,
uint /* _keyPrice */
) public pure returns (uint discount, uint tokens) {
return (0, 0);
}
/**
* Helper to get the network mining basefee as introduced in EIP-1559
* @dev this helper can be wrapped in try/catch statement to avoid
* revert in networks where EIP-1559 is not implemented
*/
function networkBaseFee() external view returns (uint) {
return block.basefee;
}
/**
* This function keeps track of the added GDP, as well as grants of discount tokens
* to the referrer, if applicable.
* The number of discount tokens granted is based on the value of the referal,
* the current growth rate and the lock's discount token distribution rate
* This function is invoked by a previously deployed lock only.
*/
function recordKeyPurchase(
uint _value,
address _referrer
) public onlyFromDeployedLock {
if (_value > 0) {
uint valueInETH;
address tokenAddress = IPublicLock(msg.sender).tokenAddress();
if (tokenAddress != address(0) && tokenAddress != weth) {
// If priced in an ERC-20 token, find the supported uniswap oracle
IUniswapOracleV3 oracle = uniswapOracles[tokenAddress];
if (address(oracle) != address(0)) {
valueInETH = oracle.updateAndConsult(tokenAddress, _value, weth);
}
} else {
// If priced in ETH (or value is 0), no conversion is required
valueInETH = _value;
}
updateGrossNetworkProduct(
valueInETH,
tokenAddress,
_value,
msg.sender // lockAddress
);
// If GNP does not overflow, the lock totalSales should be safe
locks[msg.sender].totalSales += valueInETH;
// Distribute UDT
// version 13 is the first version for which locks can be paying the fee.
// Prior versions should not distribute UDT if they don't "pay" the fee.
if (
_referrer != address(0) &&
IPublicLock(msg.sender).publicLockVersion() >= 13
) {
IUniswapOracleV3 udtOracle = uniswapOracles[udt];
if (address(udtOracle) != address(0)) {
// Get the value of 1 UDT (w/ 18 decimals) in ETH
uint udtPrice = udtOracle.updateAndConsult(udt, 10 ** 18, weth);
uint balance = IMintableERC20(udt).balanceOf(address(this));
// base fee default to 100 GWEI for chains that does
uint baseFee;
try this.networkBaseFee() returns (uint _basefee) {
// no assigned value
if (_basefee == 0) {
baseFee = 100;
} else {
baseFee = _basefee;
}
} catch {
// block.basefee not supported
baseFee = 100;
}
// tokensToDistribute is either == to the gas cost
uint tokensToDistribute = ((estimatedGasForPurchase * baseFee) *
(10 ** 18)) / udtPrice;
// or tokensToDistribute is capped by network GDP growth
// we distribute tokens using asymptotic curve between 0 and 0.5
uint maxTokens = (balance * valueInETH) /
(2 + (2 * valueInETH) / grossNetworkProduct) /
grossNetworkProduct;
// cap to GDP growth!
if (tokensToDistribute > maxTokens) {
tokensToDistribute = maxTokens;
}
if (tokensToDistribute > 0) {
if (balance > tokensToDistribute) {
// Only distribute if there are enough tokens
IMintableERC20(udt).transfer(_referrer, tokensToDistribute);
}
}
}
}
}
}
/**
* Update the GNP by a new value.
* Emits an event to simply tracking
*/
function updateGrossNetworkProduct(
uint _valueInETH,
address _tokenAddress,
uint _value,
address _lock
) internal {
// increase GNP
grossNetworkProduct = grossNetworkProduct + _valueInETH;
emit GNPChanged(
grossNetworkProduct,
_valueInETH,
_tokenAddress,
_value,
_lock
);
}
/**
* @notice [DEPRECATED] Call to this function has been removed from PublicLock > v9.
* @dev [DEPRECATED] only Kept for backwards compatibility
*/
function recordConsumedDiscount(
uint /* _discount */,
uint /* _tokens */
) public view onlyFromDeployedLock {
return;
}
// The version number of the current Unlock implementation on this network
function unlockVersion() external pure returns (uint16) {
return 13;
}
/**
* Set the fee used by the protocol
* @param _protocolFee fee in basic point
*/
function setProtocolFee(uint _protocolFee) external onlyOwner {
protocolFee = _protocolFee;
}
/**
* Set the UDT Swap and Burn contract address
* @param _swapBurnerAddress the address of the SwapBurner contract instance
*/
function setSwapBurner(address _swapBurnerAddress) external onlyOwner {
address prevSwapBurnerAddress = swapBurnerAddress;
swapBurnerAddress = _swapBurnerAddress;
emit SwapBurnerChanged(prevSwapBurnerAddress, swapBurnerAddress);
}
/**
* @notice Allows the owner to update configuration variables
*/
function configUnlock(
address _udt,
address _weth,
uint _estimatedGasForPurchase,
string calldata _symbol,
string calldata _URI,
uint _chainId
) external onlyOwner {
udt = _udt;
weth = _weth;
estimatedGasForPurchase = _estimatedGasForPurchase;
globalTokenSymbol = _symbol;
globalBaseTokenURI = _URI;
chainId = _chainId;
emit ConfigUnlock(
_udt,
_weth,
_estimatedGasForPurchase,
_symbol,
_URI,
_chainId
);
}
/**
* @notice Set the default PublicLock template to use when creating locks
*/
function setLockTemplate(address _publicLockAddress) external onlyOwner {
if (_publicLockVersions[_publicLockAddress] == 0) {
revert Unlock__MISSING_LOCK_TEMPLATE();
}
// set latest version
publicLockLatestVersion = _publicLockVersions[_publicLockAddress];
// set corresponding template
publicLockAddress = _publicLockAddress;
emit SetLockTemplate(_publicLockAddress);
}
/**
* @notice allows the owner to set the oracle address to use for value conversions
* setting the _oracleAddress to address(0) removes support for the token
* @dev This will also call update to ensure at least one datapoint has been recorded.
*/
function setOracle(
address _tokenAddress,
address _oracleAddress
) external onlyOwner {
uniswapOracles[_tokenAddress] = IUniswapOracleV3(_oracleAddress);
if (_oracleAddress != address(0)) {
IUniswapOracleV3(_oracleAddress).update(_tokenAddress, weth);
}
}
// Allows the owner to change the value tracking variables as needed.
function resetTrackedValue(
uint _grossNetworkProduct,
uint _totalDiscountGranted
) external onlyOwner {
grossNetworkProduct = _grossNetworkProduct;
totalDiscountGranted = _totalDiscountGranted;
emit ResetTrackedValue(_grossNetworkProduct, _totalDiscountGranted);
}
/**
* @dev Redundant with globalBaseTokenURI() for backwards compatibility with v3 & v4 locks.
*/
function getGlobalBaseTokenURI() external view returns (string memory) {
return globalBaseTokenURI;
}
/**
* @dev Redundant with globalTokenSymbol() for backwards compatibility with v3 & v4 locks.
*/
function getGlobalTokenSymbol() external view returns (string memory) {
return globalTokenSymbol;
}
// for doc, see IUnlock.sol
function getAdmin() public view returns (address) {
bytes32 _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
}
// for doc, see IUnlock.sol
function postLockUpgrade() public {
// check if lock hasnot already been deployed here and version is correct
if (
locks[msg.sender].deployed == false &&
IPublicLock(msg.sender).publicLockVersion() == 13 &&
block.chainid == 1 &&
IPublicLock(msg.sender).unlockProtocol() ==
0x3d5409CcE1d45233dE1D4eBDEe74b8E004abDD13 // hardcoded address of previous Unlock
) {
IUnlock previousUnlock = IUnlock(
0x3d5409CcE1d45233dE1D4eBDEe74b8E004abDD13
);
(
bool deployed,
uint totalSales,
uint yieldedDiscountTokens
) = previousUnlock.locks(msg.sender);
// record lock from old Unlock in this one
if (deployed) {
locks[msg.sender] = LockBalances(
deployed,
totalSales,
yieldedDiscountTokens
);
} else {
revert Unlock__MISSING_LOCK(msg.sender);
}
}
}
/**
* Internal helper to transfer token held by this contract
*/
function _transfer(address token, address to, uint256 amount) internal {
if (token != address(0)) {
IMintableERC20(token).transfer(to, amount);
} else {
payable(to).transfer(amount);
}
}
/**
* Functions which transfers tokens held by the contract
* It handles both ERC20 and the base currency.
* @dev This function is onlyOwner
* @param token the address of the token to transfer (pass the 0x0 address for the base currency)
* @param to the address to transfer the tokens to
* @param amount the amount of tokens to transfer
*/
function transferTokens(
address token,
address to,
uint256 amount
) public onlyOwner {
_transfer(token, to, amount);
}
/**
* Send tokens held by this contract to the UDT SwapBurner contract. The tokens sent to the
* contract are then swapped for UDT and UDT itself will be sent to a burner address.
* @notice This function can be called by anyone (not only the contract owner) as a way to ensure decentralization.
* @param token the address of the token (zero address for native) to swap and burn
* @param amount the amount of tokens to swap and burn
*/
function swapAndBurn(address token, uint256 amount, uint24 poolFee) public {
if (token == udt) {
revert Unlock__INVALID_TOKEN();
}
_transfer(token, swapBurnerAddress, amount);
ISwapBurner(swapBurnerAddress).swapAndBurn(token, poolFee);
}
/**
* Removes a lock from the list of locks. This will prevent the lock from being able to receive governance tokens.
* The lock will still be able to sell its memberships.
* @dev This function is onlyOwner
* @param lock address of the lock to remove
*/
function removeLock(address lock) external onlyOwner {
delete locks[lock];
}
// required to receive ETH / withdraw ETH
receive() external payable {
if (msg.value <= 0) {
revert Unlock__INVALID_AMOUNT();
}
}
}
{
"compilationTarget": {
"contracts/past-versions/UnlockV13.sol": "TransparentUpgradeableProxy"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 80
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_logic","type":"address"},{"internalType":"address","name":"admin_","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"}],"stateMutability":"payable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beacon","type":"address"}],"name":"BeaconUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"stateMutability":"payable","type":"fallback"},{"stateMutability":"payable","type":"receive"}]