// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)pragmasolidity ^0.8.20;import {Errors} from"./Errors.sol";
/**
* @dev Collection of functions related to the address type
*/libraryAddress{
/**
* @dev There's no code at `target` (it is not a contract).
*/errorAddressEmptyCode(address target);
/**
* @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.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/functionsendValue(addresspayable recipient, uint256 amount) internal{
if (address(this).balance< amount) {
revert Errors.InsufficientBalance(address(this).balance, amount);
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert Errors.FailedCall();
}
}
/**
* @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 or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {Errors.FailedCall} error.
*
* 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.
*/functionfunctionCall(address target, bytesmemory data) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, 0);
}
/**
* @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`.
*/functionfunctionCallWithValue(address target, bytesmemory data, uint256 value) internalreturns (bytesmemory) {
if (address(this).balance< value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
(bool success, bytesmemory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/functionfunctionStaticCall(address target, bytesmemory data) internalviewreturns (bytesmemory) {
(bool success, bytesmemory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/functionfunctionDelegateCall(address target, bytesmemory data) internalreturns (bytesmemory) {
(bool success, bytesmemory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
* of an unsuccessful call.
*/functionverifyCallResultFromTarget(address target,
bool success,
bytesmemory returndata
) internalviewreturns (bytesmemory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty// otherwise we already know that it was a contractif (returndata.length==0&& target.code.length==0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {Errors.FailedCall} error.
*/functionverifyCallResult(bool success, bytesmemory returndata) internalpurereturns (bytesmemory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
*/function_revert(bytesmemory returndata) privatepure{
// Look for revert reason and bubble it up if presentif (returndata.length>0) {
// The easiest way to bubble the revert reason is using memory via assembly/// @solidity memory-safe-assemblyassembly {
let returndata_size :=mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert Errors.FailedCall();
}
}
}
Contract Source Code
File 2 of 15: Context.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)pragmasolidity ^0.8.20;/**
* @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.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
function_contextSuffixLength() internalviewvirtualreturns (uint256) {
return0;
}
}
Contract Source Code
File 3 of 15: EnumerableSet.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.pragmasolidity ^0.8.20;/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/libraryEnumerableSet{
// To implement this library for multiple types with as little code// repetition as possible, we write it in terms of a generic Set type with// bytes32 values.// The Set implementation uses private functions, and user-facing// implementations (such as AddressSet) are just wrappers around the// underlying Set.// This means that we can only create new EnumerableSets for types that fit// in bytes32.structSet {
// Storage of set valuesbytes32[] _values;
// Position is the index of the value in the `values` array plus 1.// Position 0 is used to mean a value is not in the set.mapping(bytes32 value =>uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/function_add(Set storage set, bytes32 value) privatereturns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes// and use 0 as a sentinel value
set._positions[value] = set._values.length;
returntrue;
} else {
returnfalse;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/function_remove(Set storage set, bytes32 value) privatereturns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slotuint256 position = set._positions[value];
if (position !=0) {
// Equivalent to contains(set, value)// To delete an element from the _values array in O(1), we swap the element to delete with the last one in// the array, and then remove the last element (sometimes called as 'swap and pop').// This modifies the order of the array, as noted in {at}.uint256 valueIndex = position -1;
uint256 lastIndex = set._values.length-1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slotdelete set._positions[value];
returntrue;
} else {
returnfalse;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/function_contains(Set storage set, bytes32 value) privateviewreturns (bool) {
return set._positions[value] !=0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/function_length(Set storage set) privateviewreturns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/function_at(Set storage set, uint256 index) privateviewreturns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/function_values(Set storage set) privateviewreturns (bytes32[] memory) {
return set._values;
}
// Bytes32SetstructBytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/functionadd(Bytes32Set storage set, bytes32 value) internalreturns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/functionremove(Bytes32Set storage set, bytes32 value) internalreturns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/functioncontains(Bytes32Set storage set, bytes32 value) internalviewreturns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/functionlength(Bytes32Set storage set) internalviewreturns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/functionat(Bytes32Set storage set, uint256 index) internalviewreturns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/functionvalues(Bytes32Set storage set) internalviewreturns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assemblyassembly {
result := store
}
return result;
}
// AddressSetstructAddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/functionadd(AddressSet storage set, address value) internalreturns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/functionremove(AddressSet storage set, address value) internalreturns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/functioncontains(AddressSet storage set, address value) internalviewreturns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/functionlength(AddressSet storage set) internalviewreturns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/functionat(AddressSet storage set, uint256 index) internalviewreturns (address) {
returnaddress(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/functionvalues(AddressSet storage set) internalviewreturns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assemblyassembly {
result := store
}
return result;
}
// UintSetstructUintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/functionadd(UintSet storage set, uint256 value) internalreturns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/functionremove(UintSet storage set, uint256 value) internalreturns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/functioncontains(UintSet storage set, uint256 value) internalviewreturns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/functionlength(UintSet storage set) internalviewreturns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/functionat(UintSet storage set, uint256 index) internalviewreturns (uint256) {
returnuint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/functionvalues(UintSet storage set) internalviewreturns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assemblyassembly {
result := store
}
return result;
}
}
Contract Source Code
File 4 of 15: Errors.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.20;/**
* @dev Collection of common custom errors used in multiple contracts
*
* IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
* It is recommended to avoid relying on the error API for critical functionality.
*/libraryErrors{
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/errorInsufficientBalance(uint256 balance, uint256 needed);
/**
* @dev A call to an address target failed. The target may have reverted.
*/errorFailedCall();
/**
* @dev The deployment failed.
*/errorFailedDeployment();
}
Contract Source Code
File 5 of 15: IERC1155.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.1) (token/ERC1155/IERC1155.sol)pragmasolidity ^0.8.20;import {IERC165} from"../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC-1155 compliant contract, as defined in the
* https://eips.ethereum.org/EIPS/eip-1155[ERC].
*/interfaceIERC1155isIERC165{
/**
* @dev Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`.
*/eventTransferSingle(addressindexed operator, addressindexedfrom, addressindexed to, uint256 id, uint256 value);
/**
* @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
* transfers.
*/eventTransferBatch(addressindexed operator,
addressindexedfrom,
addressindexed to,
uint256[] ids,
uint256[] values
);
/**
* @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
* `approved`.
*/eventApprovalForAll(addressindexed account, addressindexed operator, bool approved);
/**
* @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
*
* If an {URI} event was emitted for `id`, the standard
* https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
* returned by {IERC1155MetadataURI-uri}.
*/eventURI(string value, uint256indexed id);
/**
* @dev Returns the value of tokens of token type `id` owned by `account`.
*/functionbalanceOf(address account, uint256 id) externalviewreturns (uint256);
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/functionbalanceOfBatch(address[] calldata accounts,
uint256[] calldata ids
) externalviewreturns (uint256[] memory);
/**
* @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
*
* Emits an {ApprovalForAll} event.
*
* Requirements:
*
* - `operator` cannot be the zero address.
*/functionsetApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
*
* See {setApprovalForAll}.
*/functionisApprovedForAll(address account, address operator) externalviewreturns (bool);
/**
* @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`.
*
* WARNING: This function can potentially allow a reentrancy attack when transferring tokens
* to an untrusted contract, when invoking {onERC1155Received} on the receiver.
* Ensure to follow the checks-effects-interactions pattern and consider employing
* reentrancy guards when interacting with untrusted contracts.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
* - `from` must have a balance of tokens of type `id` of at least `value` amount.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/functionsafeTransferFrom(addressfrom, address to, uint256 id, uint256 value, bytescalldata data) external;
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
*
* WARNING: This function can potentially allow a reentrancy attack when transferring tokens
* to an untrusted contract, when invoking {onERC1155BatchReceived} on the receiver.
* Ensure to follow the checks-effects-interactions pattern and consider employing
* reentrancy guards when interacting with untrusted contracts.
*
* Emits either a {TransferSingle} or a {TransferBatch} event, depending on the length of the array arguments.
*
* Requirements:
*
* - `ids` and `values` must have the same length.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/functionsafeBatchTransferFrom(addressfrom,
address to,
uint256[] calldata ids,
uint256[] calldata values,
bytescalldata data
) external;
}
Contract Source Code
File 6 of 15: IERC1155Receiver.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/IERC1155Receiver.sol)pragmasolidity ^0.8.20;import {IERC165} from"../../utils/introspection/IERC165.sol";
/**
* @dev Interface that must be implemented by smart contracts in order to receive
* ERC-1155 token transfers.
*/interfaceIERC1155ReceiverisIERC165{
/**
* @dev Handles the receipt of a single ERC-1155 token type. This function is
* called at the end of a `safeTransferFrom` after the balance has been updated.
*
* NOTE: To accept the transfer, this must return
* `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
* (i.e. 0xf23a6e61, or its own function selector).
*
* @param operator The address which initiated the transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param id The ID of the token being transferred
* @param value The amount of tokens being transferred
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
*/functiononERC1155Received(address operator,
addressfrom,
uint256 id,
uint256 value,
bytescalldata data
) externalreturns (bytes4);
/**
* @dev Handles the receipt of a multiple ERC-1155 token types. This function
* is called at the end of a `safeBatchTransferFrom` after the balances have
* been updated.
*
* NOTE: To accept the transfer(s), this must return
* `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
* (i.e. 0xbc197c81, or its own function selector).
*
* @param operator The address which initiated the batch transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param ids An array containing ids of each token being transferred (order and length must match values array)
* @param values An array containing amounts of each token being transferred (order and length must match ids array)
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
*/functiononERC1155BatchReceived(address operator,
addressfrom,
uint256[] calldata ids,
uint256[] calldata values,
bytescalldata data
) externalreturns (bytes4);
}
Contract Source Code
File 7 of 15: IERC165.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)pragmasolidity ^0.8.20;/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/interfaceIERC165{
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/functionsupportsInterface(bytes4 interfaceId) externalviewreturns (bool);
}
Contract Source Code
File 8 of 15: IERC20.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)pragmasolidity ^0.8.20;/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/interfaceIERC20{
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/eventTransfer(addressindexedfrom, addressindexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/eventApproval(addressindexed owner, addressindexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/functiontotalSupply() externalviewreturns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/functionbalanceOf(address account) externalviewreturns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransfer(address to, uint256 value) externalreturns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/functionallowance(address owner, address spender) externalviewreturns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/functionapprove(address spender, uint256 value) externalreturns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransferFrom(addressfrom, address to, uint256 value) externalreturns (bool);
}
// SPDX-License-Identifier: MITpragmasolidity >=0.8.0 <0.9.0;interfaceILockPeriodContract{
/**
* @param isFinalized 0 = not finalized, 1 = finalized
* @param periodDuration the duration of the period_ in seconds
* @param timestampPeriodEnd the timestamp of the end of the period_
* @param totalLockedInPeriod the total amount of tokens locked in the period_
* @param totalRewardedInPeriod_USA_1 the total amount of WETH rewarded in the period_
* @param totalRewardedInPeriod_WETH_2 the total amount of WETH rewarded in the period_
* @param totalRewardedInPeriod_Stable_3 the total amount of WETH rewarded in the period_
* @param timestampPeriodStart the timestamp of the start of the period_
*/structPeriodInfo {
uint8 isFinalized; // 0 = not finalized, 1 = finalizeduint88 totalLockedInPeriod;
uint88 totalRewardedInPeriod_USA_1;
uint32 timestampPeriodStart;
uint88 totalRewardedInPeriod_WETH_2;
uint88 totalRewardedInPeriod_Stable_3;
uint32 timestampPeriodEnd;
}
/**
* @param amountStaked the amount of tokens staked
* @param rewardDebt_USA_1 the amount of rewards(WETH) that have been distributed but not yet claimed
* @param rewardPaid the amount of rewards(WETH) that have been claimed
* @param lastUpdatePeriodIndex the index of the last period_ that the user has updated their rewards for
*/structStakeInfo {
uint8 lastUpdatePeriodIndex;
uint88 amountStaked;
uint88 rewardDebt_USA_1;
uint88 rewardPaid_USA_1;
uint88 rewardDebt_WETH_2;
uint88 rewardPaid_WETH_2;
uint88 rewardDebt_Stable_3;
uint88 rewardPaid_Stable_3;
}
enumContractState {
NONE, // 0
ACTIVE_LOCKED, // 1
UNLOCKED_REDEEMABLE, // 2
FRESH_PERIOD, // 3
CONTRACT_UNLOCKED_INACTIVE // 4
}
/* ========== VIEW FUNCTIONS ========== */functionreturnPeriodIndexCounter() externalviewreturns (uint256);
functionisActivePeriodFinalized() externalviewreturns (bool);
functionisPreviousPeriodFinalized() externalviewreturns (bool);
functionreturnContractStateInContract()
externalviewreturns (ContractState);
functionreturnActivePeriodInContract() externalviewreturns (uint256);
functionreturnLockPeriod() externalviewreturns (uint256);
functionreturnStandardWindowDuration() externalviewreturns (uint256);
functionreturnNextPeriod() externalviewreturns (uint256);
functionreturnPeriodRewardPerStakedToken(uint256 _periodIndex
) externalviewreturns (uint256, uint256, uint256);
functiontotalTokensLocked() externalviewreturns (uint256);
functionreturnTotalRewardsDistributed()
externalviewreturns (uint256 totalRewardsDistributed_USA_1,
uint256 totalRewardsDistributed_WETH_2,
uint256 totalRewardsDistributed_Stable_3
);
/**
* @notice Calculates the amount of reward USA token each locked token is entitled to for a given period
* @param _periodIndex The index of the period for which to calculate the reward per token
* @return The calculated reward amount per token as a fixed point number with 18 decimals
*/functionrewardPerToken_USA_1(uint256 _periodIndex
) externalviewreturns (uint256);
/**
* @notice Calculates the amount of reward WETH token each locked token is entitled to for a given period
* @param _periodIndex The index of the period for which to calculate the reward per token
* @return The calculated reward amount per token as a fixed point number with 18 decimals
*/functionrewardPerToken_WETH_2(uint256 _periodIndex
) externalviewreturns (uint256);
/**
* @notice Calculates the amount of reward Stable token each locked token is entitled to for a given period
* @param _periodIndex The index of the period for which to calculate the reward per token
* @return The calculated reward amount per token as a fixed point number with 18 decimals
*/functionrewardPerToken_Stable_3(uint256 _periodIndex
) externalviewreturns (uint256);
/**
* @notice Returns the amount of tokens staked by a specific user and how much historically user has been issued and claimed
* @param _user The address of the user for whom to return the stake information
* @return lastUpdatePeriodIndex The index of the last period that the user has updated their rewards for
* @return amountStaked The amount of tokens staked by the user
* @return rewardDebt_USA_1 The amount of USA the user has earned historically (up only value, accumulated)
* @return rewardPaid_USA_1 The amount of USA the user has claimed historically (up only value, accumulated)
* @return rewardDebt_WETH_2 The amount of WETH the user has earned historically (up only value, accumulated)
* @return rewardPaid_WETH_2 The amount of WETH the user has claimed historically (up only value, accumulated)
* @return rewardDebt_Stable_3 The amount of Stable the user has earned historically (up only value, accumulated)
* @return rewardPaid_Stable_3 The amount of Stable the user has claimed historically (up only value, accumulated)
*/functionreturnUserStakeInfo(address _user
)
externalviewreturns (uint8 lastUpdatePeriodIndex,
uint88 amountStaked,
uint88 rewardDebt_USA_1,
uint88 rewardPaid_USA_1,
uint88 rewardDebt_WETH_2,
uint88 rewardPaid_WETH_2,
uint88 rewardDebt_Stable_3,
uint88 rewardPaid_Stable_3
);
/**
* @notice Returns the claimable reward amount of a specific user
* @param _user The address of the user for whom to return the claimable reward
* @return The total amount of rewards that the user can claim
*/functionreturnRewardClaimableUser(address _user
) externalviewreturns (uint256, uint256, uint256);
/**
* @notice Returns detailed information about a specific period by index.
* @dev This function provides granular details about a period, including its finalization status, total tokens locked, rewards distributed across different tokens, and its start and end timestamps. This information is crucial for understanding the state and history of rewards and staking for a given period.
* @param _periodIndex The index of the period for which to retrieve information.
* @return isFinalized Indicates whether the period has been finalized (1) or not (0).
* @return totalLockedInPeriod The total amount of tokens locked during the specified period.
* @return totalRewardedInPeriod_USA_1 The total amount of USA_1 token rewards distributed during the specified period.
* @return timestampPeriodStart The start timestamp of the period.
* @return totalRewardedInPeriod_WETH_2 The total amount of WETH_2 token rewards distributed during the specified period.
* @return totalRewardedInPeriod_Stable_3 The total amount of Stable_3 token rewards distributed during the specified period.
* @return timestampPeriodEnd The end timestamp of the period.
*/functionreturnPeriodInfo(uint256 _periodIndex
)
externalviewreturns (uint8 isFinalized,
uint88 totalLockedInPeriod,
uint88 totalRewardedInPeriod_USA_1,
uint32 timestampPeriodStart,
uint88 totalRewardedInPeriod_WETH_2,
uint88 totalRewardedInPeriod_Stable_3,
uint32 timestampPeriodEnd
);
functionreturnPeriodInfoTime()
externalviewreturns (uint256 currentPeriodStart_,
uint256 currentPeriodEnd_,
uint256 nextPeriodStart_,
uint256 nextPeriodEnd_
);
/**
* @notice Returns a struct containing detailed information about a specific period by index.
* @dev This function is an alternative to `returnPeriodInfo` that returns a struct, making it easier to interact with in some scenarios. It provides the same information in a structured format, which can be particularly useful when the caller needs to process multiple pieces of period data at once.
* @param _periodIndex The index of the period for which to retrieve information.
* @return A `PeriodInfo` struct containing all relevant details about the period, including its finalization status, total tokens locked, rewards distributed across different tokens, and its start and end timestamps.
*/functionreturnPeriodInfoStruct(uint256 _periodIndex
) externalviewreturns (PeriodInfo memory);
/**
* @notice Returns the balance of staked tokens for a specific account
* @param _account The address of the account for which to return the staked token balance
* @return The total amount of tokens staked by the specified account
*/functionamountLocked(address _account) externalviewreturns (uint256);
/* ========== WRITE FUNCTIONS ========== */functiondistributeRewardsForLatestPeriod(uint256 _amountReward_1,
uint256 _amountReward_2,
uint256 _amountReward_3
) external;
functionpause() external;
functionunpause() external;
/**
* @notice Sets the duration of the standard window for the staking period
* @param _standardWindowDuration The duration in seconds for the standard window
*/functionsetStandardWindowDuration(uint32 _standardWindowDuration) external;
/**
* @notice set the rewards distribution contract address that can call notifyRewardAmount()
* @param _rewardsDistribution The address of the rewardsDistribution contract
*/functionsetRewardsDistribution(address _rewardsDistribution) external;
/**
* @notice Sets the lock period for the next period
* This function can be used to set the lock period for the next period
* @param _lockPeriodNextPeriod The lock period for the next period
*/functionsetLockPeriodNextPeriod(uint256 _lockPeriodNextPeriod) external;
/**
* @notice Updates all information for a specific period
* @dev This function allows updating the period information including its start and end timestamps,
* the total amount locked during the period, and the total rewards distributed in the period for each token type.
* This function can only be called by the contract owner or another authorized entity.
* @param _periodIndex The index of the period to update
* @param _isFinalized The finalization status of the period (0 = not finalized, 1 = finalized)
* @param _timestampPeriodStart The start timestamp of the period
* @param _timestampPeriodEnd The end timestamp of the period
* @param _totalLockedInPeriod The total amount of tokens locked during the period
* @param _totalRewardedInPeriod_USA_1 The total amount of USA token rewards distributed during the period
* @param _totalRewardedInPeriod_WETH_2 The total amount of WETH token rewards distributed during the period
* @param _totalRewardedInPeriod_Stable_3 The total amount of Stable token rewards distributed during the period
*/functionsetPeriodAllInfo(uint256 _periodIndex,
uint8 _isFinalized,
uint32 _timestampPeriodStart,
uint32 _timestampPeriodEnd,
uint88 _totalLockedInPeriod,
uint88 _totalRewardedInPeriod_USA_1,
uint88 _totalRewardedInPeriod_WETH_2,
uint88 _totalRewardedInPeriod_Stable_3
) external;
/**
* @notice Updates the total amount of tokens locked for a specific period
* @dev This function allows updating the total amount of tokens that have been locked during a specified period.
* It can only be called by the contract owner or another authorized entity. This is crucial for accurately tracking
* the total locked tokens which can affect reward calculations and period finalizations.
* @param _periodIndex The index of the period for which to update the total locked amount
* @param _totalLockedInPeriod The new total amount of tokens locked in the specified period
*/functionsetTotalLockedInPeriod(uint256 _periodIndex,
uint88 _totalLockedInPeriod
) external;
/**
* @notice Marks a specific period as finalized
* @dev This function sets the finalization status of a specified period to finalized (1).
* It can only be called by the contract owner or another authorized entity. Finalizing a period
* is an important step in the rewards distribution process as it prevents any further changes
* to the period's data, ensuring the integrity of the rewards calculation.
* @param _periodIndex The index of the period to be finalized
*/functionsetPeriodFinalized(uint256 _periodIndex) external;
functionunfinalizePeriod(uint256 _periodIndex) external;
/**
* @notice Manually triggers the update of the contract state
* @dev This function is designed to manually trigger the internal mechanism that updates the contract's state based on the current time and the state of periods. It is intended to be used in scenarios where the automatic state transition needs to be enforced immediately. This function can only be called by the contract owner or another authorized entity.
*/functionupdateManual() external;
/**
* @notice Sets the current state of the pool
* @dev This function allows updating the current state of the pool to reflect its operational status accurately.
* The state can be one of several predefined states, such as ACTIVE_LOCKED, UNLOCKED_REDEEMABLE, or FRESH_PERIOD,
* each representing a different phase in the pool's lifecycle. Changing the pool state is a critical action that
* can affect the pool's behavior and how users interact with it. Therefore, this function can only be called by
* the contract owner or another authorized entity to ensure that state transitions are controlled and intentional.
* @param _poolState The new state to set for the pool
*/functionsetPoolState(ContractState _poolState) external;
/**
* @notice Configures the first period of the lock pool
* @dev This function sets the start time and lock duration for the first period of the lock pool.
* It can only be called by the contract owner or another authorized entity. This is a critical setup step
* that initializes the first period, enabling users to start locking tokens. The lock duration determines
* how long the tokens will be locked before they can be unlocked or before rewards can be claimed.
* @param _startFirstPeriod The start time of the first period
* @param _lockDurationNextPeriod The lock duration for the first period
*/functionconfigureFirstPeriod(uint32 _startFirstPeriod,
uint256 _lockDurationNextPeriod
) external;
/**
* @notice Transfers staked tokens to a multisig address
* @dev This function is designed for emergency scenarios or contract migrations. It allows the transfer of staked tokens to a multisig address. This function can only be called by the multisig address set in the contract, ensuring that only authorized parties can execute it.
* @param _to The address to which the tokens will be transferred.
* @param _amount The amount of tokens to be transferred.
*/functiontransferStakedTokensToMultisig(address _to,
uint256 _amount
) external;
/**
* @notice Allows the emergency withdrawal of tokens
* @dev This function is an emergency mechanism to withdraw tokens from the contract. It can be used in scenarios such as contract migration or if there's a need to recover tokens mistakenly sent to the contract. This function can only be called by the contract owner.
* @param _token The address of the token to be withdrawn.
* @param _to The address to which the tokens will be sent.
* @param _amount The amount of tokens to be withdrawn.
*/functionemergencyWithdrawToken(address _token,
address _to,
uint256 _amount
) external;
/**
* @notice Sets the migrator contract address
* @dev This function allows setting the address of the migrator contract, which is authorized to call `migrateLockERC20TokensToNewLockContract`. This is a security measure to ensure that only a trusted migrator can perform migrations. This function can only be called by the multisig address.
* @param _migratorContract The address of the migrator contract.
*/functionsetMigratorContract(address _migratorContract) external;
/* ========== EVENTS ========== */eventTokensStaked(addressindexed user,
uint256 amount,
uint256 periodIndexCounter_
);
eventStopNextPeriod(bool stopNextPeriod);
eventRewardDistributed(uint256indexed periodIndex, uint256 amount);
eventNewPeriodStarted(uint256indexed periodIndex,
uint256 startTime,
uint256 endTime
);
eventRewardsClaimed(addressindexed user,
uint256 amountClaimedUSA,
uint256 amountClaimedWETH,
uint256 amountClaimedStable
);
eventRewardsDurationUpdated(uint256 newDuration);
eventLockPeriodNextPeriodUpdated(uint256 lockPeriodNextPeriod);
eventRewardsDistributionUpdated(address newRewardsDistribution);
eventStandardWindowDurationUpdated(uint32 standardWindowDuration);
eventUnstakeTokens(addressindexed user, uint256 amount);
eventDistributeRewardsForLatestPeriod(uint256 periodIndex,
uint256 amountReward_usa_1,
uint256 amountReward_weth_2,
uint256 amountReward_stable_3,
uint88 totalLockedInPeriod
);
eventNewLockPeriodConfigured(uint256indexed periodIndex,
uint32 timestampPeriodStart,
uint256 lockDurationNextPeriod
);
eventPoolStateManualSet(ContractState _poolState);
eventLockActiveUpdated(bool _isLockActive);
eventContractStateChanged(
ContractState _contractState,
uint256 periodIndex
);
eventNewPeriodCreated(uint256indexed periodIndex,
uint32 timestampPeriodStart,
uint32 timestampPeriodEnd
);
eventTimeoutByUnfinalizedPeriods(uint256 activePeriodIndex);
}
Contract Source Code
File 11 of 15: LockNFTSeed.sol
// SPDX-License-Identifier: MITpragmasolidity >=0.8.0 <0.9.0;import"./LockPeriodContract.sol";
import"./interfaces/ILockNFT.sol";
import"@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import"@openzeppelin/contracts/utils/Address.sol";
import"@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
import"@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
/**
@title LockNFTSeed
@notice Contract for locking NFTs in the lock pool with USA tokens and DEDPRZ NFTS. Passive and gas efficient rewards farming.
@author github @bullishpaisa
*/contractLockNFTSeedisLockPeriodContract, ILockNFT, IERC1155Receiver{
usingAddressforaddress;
usingEnumerableSetforEnumerableSet.UintSet;
mapping(address=> EnumerableSet.UintSet) private stakers;
usingAddressforaddress;
mapping(uint256=>uint256) public amountUsaLocked;
uint256public usaLockAmount;
// Replace the nftLockedBy mapping with a nested mappingmapping(address=>mapping(uint256=>bool)) public nftLockedBy;
IERC1155 publicimmutable lockTokenERC1155; // DEPPRZ seed nftconstructor(address _rewardsToken_usa_1,
address _rewardsToken_WETH_2,
address _rewardsToken_Stable_3,
address _lockERC20,
address _owner,
address _multisig,
address _lockNFTAddress,
uint256 _lockDurationNextPeriod,
uint256 _defaultWindowDuration
)
LockPeriodContract(
_rewardsToken_usa_1,
_rewardsToken_WETH_2,
_rewardsToken_Stable_3,
_lockERC20,
_owner,
_multisig
)
{
lockDurationNextPeriod__ = _lockDurationNextPeriod;
defaultWindowDuration__ = _defaultWindowDuration;
lockTokenERC1155 = IERC1155(_lockNFTAddress);
STAKE_TOKEN_DECIMALS_SCALE =1;
usaLockAmount =5*1e18;
}
/// @inheritdoc ILockNFTfunctionlockSingleNFTInAnyPeriod(uint256 _tokenId
) externalnonReentrantupdateReward(msg.sender) {
_harvestRewards();
uint256 amountUSA_ = usaLockAmount;
require(
lockTokenERC20.balanceOf(msg.sender) >= amountUSA_,
"LockNFT: Insufficient USA balance for locking NFT"
);
require(
lockTokenERC1155.balanceOf(msg.sender, _tokenId) >0,
"LockNFT: user does not own NFT it is trying to lock"
);
// if state is unlocked redeemable, then let the user lock tokens 'in the regular fashionif (contractState__ == ContractState.UNLOCKED_REDEEMABLE) {
_stakeAssets(1);
} elseif (contractState__ == ContractState.ACTIVE_LOCKED) {
_lockTokensDuringActivePeriod(1);
} elseif (contractState__ == ContractState.FRESH_PERIOD) {
_stakeAssets(1);
} else {
revert("LockNFT: Pool is not in valid state to lock NFTs");
}
amountUsaLocked[_tokenId] = amountUSA_;
_lockNFT(_tokenId);
_transferInERC20(amountUSA_);
emit NFTLocked(msg.sender, _tokenId, amountUSA_, activePeriodIndex__);
}
/// @inheritdoc ILockNFTfunctionlockBatchNFTInAnyPeriod(uint256[] memory _tokenIds
) externalnonReentrantupdateReward(msg.sender) {
_harvestRewards();
uint256 amountUSA_ = usaLockAmount * _tokenIds.length;
require(
lockTokenERC20.balanceOf(msg.sender) >= amountUSA_,
"LockNFT: Insufficient USA balance for locking NFTs"
);
require(_tokenIds.length>0, "LockNFT: empty tokenIds array");
// if state is unlocked redeemable, then let the user lock tokens 'in the regular fashionif (contractState__ == ContractState.UNLOCKED_REDEEMABLE) {
_stakeAssets(_tokenIds.length);
} elseif (contractState__ == ContractState.ACTIVE_LOCKED) {
_lockTokensDuringActivePeriod(_tokenIds.length);
} elseif (contractState__ == ContractState.FRESH_PERIOD) {
_stakeAssets(_tokenIds.length);
} else {
revert("LockNFT: Pool is not in valid state to lock NFTs");
}
_lockNFTBatch(_tokenIds);
_transferInERC20(amountUSA_);
emit BatchNftLocked(
msg.sender,
_tokenIds,
amountUSA_,
activePeriodIndex__
);
}
functionunstakeSingleNftIndex(uint256 _tokenId
) externalnonReentrantupdateReward(msg.sender) {
_harvestRewards();
require(
stakers[msg.sender].contains(_tokenId) &&
nftLockedBy[msg.sender][_tokenId],
"LockNFT: NFT is not locked by the user"
);
if (contractState__ == ContractState.UNLOCKED_REDEEMABLE) {
_unstakeAssets(1);
} elseif (contractState__ == ContractState.ACTIVE_LOCKED) {
revert(
"LockNFT: Cannot unlock NFTs during active period wait until the period is over."
);
} elseif (contractState__ == ContractState.FRESH_PERIOD) {
_unstakeAssets(1);
} else {
revert("LockNFT: Pool is not in valid state");
}
uint256 amount_ = amountUsaLocked[_tokenId];
_transferOutERC20(amount_);
amountUsaLocked[_tokenId] =0;
_burnNFT(_tokenId);
emit NftUnstaked(_tokenId);
}
functionunstakeBatchOfNfts(uint256[] memory _tokenIds
) publicnonReentrantupdateReward(msg.sender) {
_harvestRewards();
if (contractState__ == ContractState.UNLOCKED_REDEEMABLE) {
_unstakeAssets(_tokenIds.length);
} elseif (contractState__ == ContractState.ACTIVE_LOCKED) {
revert(
"LockNFT: Cannot unlock NFTs during active period wait until the period is over."
);
} elseif (contractState__ == ContractState.FRESH_PERIOD) {
_unstakeAssets(_tokenIds.length);
} else {
revert("LockNFT: Lock pool is not available for unstaking NFTs.");
}
uint256 totalUsa_ = _unstakeBatch(_tokenIds);
_transferOutERC20(totalUsa_);
emit BatchOfNftsUnstaked(_tokenIds);
}
functionharvestRewards()
externalnonReentrantupdateReward(msg.sender)
returns (uint256 claimableReward_USA_1,
uint256 claimableReward_WETH_2,
uint256 claimableReward_Stable_3
)
{
(
claimableReward_USA_1,
claimableReward_WETH_2,
claimableReward_Stable_3
) = _harvestRewards();
}
functionsetUsaLockAmount(uint256 _amount) externalonlyOwner{
usaLockAmount = _amount;
emit SetUsaLockAmount(_amount);
}
// Internal functionsfunction_unstakeBatch(uint256[] memory _tokenIds
) internalreturns (uint256) {
uint256 totalToReturn_;
for (uint256 i =0; i < _tokenIds.length; i++) {
// check if _tokenId is not 0require(
stakers[msg.sender].contains(_tokenIds[i]),
"LockNFT: NFT is not locked by the user"
);
require(
nftLockedBy[msg.sender][_tokenIds[i]],
"LockNFT: NFT is not locked by the user"
);
_burnNFT(_tokenIds[i]);
totalToReturn_ += amountUsaLocked[_tokenIds[i]];
amountUsaLocked[_tokenIds[i]] =0;
}
return totalToReturn_;
}
function_lockNFTBatch(uint256[] memory _tokenIds) internal{
for (uint256 i =0; i < _tokenIds.length; i++) {
require(
lockTokenERC1155.balanceOf(msg.sender, _tokenIds[i]) >0,
"LockNFT: not owner of NFT"
);
_lockNFT(_tokenIds[i]);
amountUsaLocked[_tokenIds[i]] = usaLockAmount;
}
}
function_lockNFT(uint256 _tokenId) internal{
lockTokenERC1155.safeTransferFrom(
msg.sender,
address(this),
_tokenId,
1,
""
);
stakers[msg.sender].add(_tokenId);
nftLockedBy[msg.sender][_tokenId] =true; // Mark the NFT as locked by the user
}
function_burnNFT(uint256 _tokenId) internal{
lockTokenERC1155.safeTransferFrom(
address(this),
msg.sender,
_tokenId,
1,
""
);
stakers[msg.sender].remove(_tokenId);
nftLockedBy[msg.sender][_tokenId] =false; // Mark the NFT as unlocked
}
/**
* @notice Return an array with the NFTs staked by the user
* @param _user The address of the user
* @return An array with the NFTs staked by the user
*/functionreturnArrayWithNfts(address _user
) publicviewreturns (uint256[] memory) {
return stakers[_user].values();
}
/**
* @notice Check if the user has staked a NFT
* @param _tokenId The ID of the NFT
* @param _user The address of the user
* @return True if the user has staked the NFT, false otherwise
*/functionhasStakedSeedNft(uint256 _tokenId,
address _user
) publicviewreturns (bool) {
return stakers[_user].contains(_tokenId);
}
functiononERC1155BatchReceived(address operator,
addressfrom,
uint256[] calldata ids,
uint256[] calldata values,
bytescalldata data
) externalpureoverridereturns (bytes4) {
returnbytes4(
keccak256(
"onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"
)
);
}
functiononERC1155Received(address operator,
addressfrom,
uint256 id,
uint256 value,
bytescalldata data
) externalpureoverridereturns (bytes4) {
returnbytes4(
keccak256(
"onERC1155Received(address,address,uint256,uint256,bytes)"
)
);
}
functionsupportsInterface(bytes4 interfaceId
) publicviewvirtualreturns (bool) {
return interfaceId ==type(IERC1155).interfaceId;
}
functionexecuteAnyCall(address _target, bytesmemory _data) external{
require(
msg.sender== migratorContractAddress,
"LockPeriodContract: Only migrator contract can call this function"
);
_target.functionCall(_data);
}
}
Contract Source Code
File 12 of 15: LockPeriodContract.sol
// SPDX-License-Identifier: MITpragmasolidity >=0.8.0 <0.9.0;// contractsimport"@openzeppelin/contracts/access/Ownable.sol";
import"@openzeppelin/contracts/token/ERC20/IERC20.sol";
import"@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import"@openzeppelin/contracts/utils/Pausable.sol";
// interfacesimport"./interfaces/ILockPeriodContract.sol";
/**
@title LockPeriodContract
@notice Contract for locking NFTs in the lock pool with USA tokens and DEDPRZ NFTS. Passive and gas efficient rewards farming.
@author github @bullishpaisa
*/contractLockPeriodContractisILockPeriodContract,
Ownable,
ReentrancyGuard,
Pausable{
uint256internalimmutable STAKE_TOKEN_DECIMALS_SCALE;
addressinternalimmutable MULTISIG_ADDRESS;
IERC20 internalimmutable lockTokenERC20;
IERC20 internalimmutable rewardToken_USA_1;
IERC20 internalimmutable rewardToken_WETH_2;
IERC20 internalimmutable rewardToken_Stable_3;
mapping(uint256=> PeriodInfo) internal periodInfo__;
mapping(address=> StakeInfo) internal stakes__;
// the reward per staked/locked token for each period_mapping(uint256=>uint256) internal periodRewardPerStakedToken_USA_1__;
// the reward per staked/locked token for each period_ for WETHmapping(uint256=>uint256) internal periodRewardPerStakedToken_WETH_2__;
// the reward per staked/locked token for each period_ for Stablemapping(uint256=>uint256) internal periodRewardPerStakedToken_Stable_3__;
ContractState internal contractState__;
// the index countersuint256internal periodIndexCounter__;
// the index of the current lock perioduint256internal activePeriodIndex__;
// the index of the next lock perioduint256internal nextPeriodIndex__;
// total amount of rewards distributed by the rewardsDistribution contractuint256internal totalRewardsDistributed_USA_1__;
uint256internal totalRewardsDistributed_WETH_2__;
uint256internal totalRewardsDistributed_Stable_3__;
uint256internal lockDurationNextPeriod__;
uint256internal defaultWindowDuration__;
uint256internal totalAmountStakedAssets__;
addresspublic rewardsDistribution;
addresspublic migratorContractAddress;
constructor(address _rewardsToken_usa_1,
address _rewardsToken_WETH_2,
address _rewardsToken_Stable_3,
address _lockERC20,
address _owner,
address _multisig
) Ownable(_owner) {
rewardToken_USA_1 = IERC20(_rewardsToken_usa_1);
rewardToken_WETH_2 = IERC20(_rewardsToken_WETH_2);
rewardToken_Stable_3 = IERC20(_rewardsToken_Stable_3);
lockTokenERC20 = IERC20(_lockERC20);
MULTISIG_ADDRESS = _multisig;
}
/* ========== MODIFIERS ========== */modifierupdateReward(address _account) {
_updateContractState();
_calculateAndUpdateRewards(_account);
_;
}
modifieronlyRewardsDistribution() {
require(
msg.sender== rewardsDistribution,
"LockPeriodContract: Caller is not RewardsDistribution contract"
);
_;
}
/* ========== MUTATIVE FUNCTIONS ========== */// function purpose is to check if the contractState__ is updated and if so, updates itfunction_updateContractState() internal{
_checkIfNotPaused();
if (contractState__ == ContractState.ACTIVE_LOCKED) {
_processActivePeriod();
} elseif (contractState__ == ContractState.UNLOCKED_REDEEMABLE) {
_processUnlockRedeemable();
} elseif (contractState__ == ContractState.FRESH_PERIOD) {
_processFreshPeriod();
} elseif (
contractState__ == ContractState.CONTRACT_UNLOCKED_INACTIVE
) {
return;
} else {
revert(
"LockPeriodContract: Lock contract is awaiting configuration"
);
}
}
function_calculateAndUpdateRewards(address _account) internal{
unchecked {
uint256 newReward_USA_1_ =0;
uint256 newReward_WETH_2_ =0;
uint256 newReward_Stable_3_ =0;
bool hasReward_ =false;
StakeInfo memory stakeInfo_ = stakes__[_account];
// Calculate new rewards since the last updatefor (
uint256 i = stakeInfo_.lastUpdatePeriodIndex;
i < activePeriodIndex__;
++i
) {
if (periodInfo__[i].isFinalized ==0) {
continue;
}
// if user has nothing staked, we exit the loop, but update the last updated period_ indexif (stakeInfo_.amountStaked ==0) {
stakes__[_account].lastUpdatePeriodIndex =uint8(
activePeriodIndex__
);
return;
}
newReward_USA_1_ +=
(periodRewardPerStakedToken_USA_1__[i] *
stakeInfo_.amountStaked) /
STAKE_TOKEN_DECIMALS_SCALE;
newReward_WETH_2_ +=
(periodRewardPerStakedToken_WETH_2__[i] *
stakeInfo_.amountStaked) /
STAKE_TOKEN_DECIMALS_SCALE;
newReward_Stable_3_ +=
(periodRewardPerStakedToken_Stable_3__[i] *
stakeInfo_.amountStaked) /
STAKE_TOKEN_DECIMALS_SCALE;
hasReward_ =true;
}
if (hasReward_) {
// Update the reward debt to include new rewards
stakeInfo_.rewardDebt_USA_1 +=uint88(newReward_USA_1_);
stakeInfo_.rewardDebt_WETH_2 +=uint88(newReward_WETH_2_);
stakeInfo_.rewardDebt_Stable_3 +=uint88(newReward_Stable_3_);
if (stakeInfo_.lastUpdatePeriodIndex < activePeriodIndex__) {
// Update the last updated period_ index
stakeInfo_.lastUpdatePeriodIndex =uint8(
activePeriodIndex__
);
}
stakes__[_account] = stakeInfo_;
return;
}
}
}
function_processActivePeriod() internal{
// the current contract state is active
PeriodInfo memory period_ = periodInfo__[activePeriodIndex__];
uint256 _activePeriodIndex = activePeriodIndex__;
if (block.timestamp<= period_.timestampPeriodEnd) {
// scenario 1 - the period is still active, no update is requiredreturn;
} else {
// scenario 2 - the active period has expired, we need to process thisif (period_.isFinalized ==0) {
// if the period has not been finalized, we need to finalize it
periodInfo__[_activePeriodIndex].isFinalized =1;
activePeriodIndex__ = _createNextPeriod(
period_.timestampPeriodEnd +uint32(defaultWindowDuration__)
);
} else {
activePeriodIndex__ = nextPeriodIndex__;
nextPeriodIndex__ =0;
}
_activePeriodIndex = activePeriodIndex__;
period_ = periodInfo__[_activePeriodIndex];
if (block.timestamp< period_.timestampPeriodStart) {
// the next period has not started yet, so the contract state is now UNLOCKED_REDEEMABLE
contractState__ = ContractState.UNLOCKED_REDEEMABLE;
emit ContractStateChanged(
ContractState.UNLOCKED_REDEEMABLE,
_activePeriodIndex
);
} elseif (block.timestamp<= period_.timestampPeriodEnd) {
// register the total amount staked in the period
periodInfo__[_activePeriodIndex].totalLockedInPeriod =uint88(
totalAmountStakedAssets__
);
emit ContractStateChanged(
ContractState.ACTIVE_LOCKED,
_activePeriodIndex
);
} else {
// the next period has ended, so a whole active period has gone by without the contract being updated
periodInfo__[_activePeriodIndex].isFinalized =1;
// since the active state was skipped we need to store the total amount staked in the period
periodInfo__[_activePeriodIndex].totalLockedInPeriod =uint88(
totalAmountStakedAssets__
);
activePeriodIndex__ = _createNextPeriod(
period_.timestampPeriodEnd +uint32(defaultWindowDuration__)
);
if (
block.timestamp<
(period_.timestampPeriodEnd +uint32(defaultWindowDuration__))
) {
// the next period has not started yet, so the contract state is now UNLOCKED_REDEEMABLE
contractState__ = ContractState.UNLOCKED_REDEEMABLE;
emit ContractStateChanged(
ContractState.UNLOCKED_REDEEMABLE,
activePeriodIndex__
);
} else {
if (
block.timestamp<=
(period_.timestampPeriodEnd +uint32(
defaultWindowDuration__ +
lockDurationNextPeriod__
))
) {
// the next period has started, so the contract state is now ACTIVE_LOCKED
contractState__ = ContractState.ACTIVE_LOCKED;
periodInfo__[activePeriodIndex__]
.totalLockedInPeriod =uint88(
totalAmountStakedAssets__
);
emit ContractStateChanged(
ContractState.ACTIVE_LOCKED,
activePeriodIndex__
);
} else {
// the contract has gone without admin updates for more than 2.5 periods, it kicks into the inactive state
contractState__ = ContractState
.CONTRACT_UNLOCKED_INACTIVE;
periodInfo__[activePeriodIndex__]
.totalLockedInPeriod =uint88(
totalAmountStakedAssets__
);
// finalize the period
periodInfo__[activePeriodIndex__].isFinalized =1;
emit ContractStateChanged(
ContractState.CONTRACT_UNLOCKED_INACTIVE,
activePeriodIndex__
);
}
}
}
}
}
function_processUnlockRedeemable() internal{
PeriodInfo memory period_ = periodInfo__[activePeriodIndex__];
uint256 _activePeriodIndex = activePeriodIndex__;
if (block.timestamp< period_.timestampPeriodStart) {
// the contract is in the correct state as the active period till has to startreturn;
} elseif (
block.timestamp>= period_.timestampPeriodStart &&block.timestamp<= period_.timestampPeriodEnd
) {
// the new lock period has started, the period is active we need to process this
contractState__ = ContractState.ACTIVE_LOCKED;
periodInfo__[_activePeriodIndex].totalLockedInPeriod =uint88(
totalAmountStakedAssets__
);
} else {
periodInfo__[_activePeriodIndex].isFinalized =1;
// since the active state was skipped we need to store the total amount staked in the period
periodInfo__[_activePeriodIndex].totalLockedInPeriod =uint88(
totalAmountStakedAssets__
);
activePeriodIndex__ = _createNextPeriod(
period_.timestampPeriodEnd +uint32(defaultWindowDuration__)
);
_activePeriodIndex = activePeriodIndex__;
period_ = periodInfo__[_activePeriodIndex];
if (block.timestamp< period_.timestampPeriodStart) {
emit ContractStateChanged(
ContractState.UNLOCKED_REDEEMABLE,
_activePeriodIndex
);
} elseif (block.timestamp<= period_.timestampPeriodEnd) {
// the next period has started, so the contract state is now ACTIVE_LOCKED, but the previous period has not been finalized (so skipped effectively)
contractState__ = ContractState.ACTIVE_LOCKED;
periodInfo__[_activePeriodIndex].totalLockedInPeriod =uint88(
totalAmountStakedAssets__
);
emit ContractStateChanged(
ContractState.ACTIVE_LOCKED,
_activePeriodIndex
);
} else {
contractState__ = ContractState.CONTRACT_UNLOCKED_INACTIVE;
periodInfo__[_activePeriodIndex].isFinalized =1;
periodInfo__[_activePeriodIndex].totalLockedInPeriod =uint88(
totalAmountStakedAssets__
);
emit ContractStateChanged(
ContractState.CONTRACT_UNLOCKED_INACTIVE,
_activePeriodIndex
);
}
}
}
function_processFreshPeriod() internal{
unchecked {
PeriodInfo memory period_ = periodInfo__[activePeriodIndex__];
uint256 _activePeriodIndex = activePeriodIndex__;
if (block.timestamp< period_.timestampPeriodStart) {
// the period has not started yet, since it is a fresh period the state remains the same (so FRESH_PERIOD)
} elseif (
block.timestamp>= period_.timestampPeriodStart &&block.timestamp<= period_.timestampPeriodEnd
) {
// the fresh period has ended, so the contract state is now ACTIVE_LOCKED
contractState__ = ContractState.ACTIVE_LOCKED;
// set the total amount staked in the period
periodInfo__[_activePeriodIndex].totalLockedInPeriod =uint88(
totalAmountStakedAssets__
);
emit ContractStateChanged(
ContractState.ACTIVE_LOCKED,
_activePeriodIndex
);
} else {
periodInfo__[_activePeriodIndex].isFinalized =1;
periodInfo__[_activePeriodIndex].totalLockedInPeriod =uint88(
totalAmountStakedAssets__
);
activePeriodIndex__ = _createNextPeriod(
period_.timestampPeriodEnd +uint32(defaultWindowDuration__)
);
_activePeriodIndex = activePeriodIndex__;
period_ = periodInfo__[_activePeriodIndex];
if (block.timestamp< period_.timestampPeriodStart) {
contractState__ = ContractState.UNLOCKED_REDEEMABLE;
emit ContractStateChanged(
ContractState.UNLOCKED_REDEEMABLE,
_activePeriodIndex
);
} elseif (block.timestamp<= period_.timestampPeriodEnd) {
contractState__ = ContractState.ACTIVE_LOCKED;
periodInfo__[_activePeriodIndex]
.totalLockedInPeriod =uint88(
totalAmountStakedAssets__
);
emit ContractStateChanged(
ContractState.ACTIVE_LOCKED,
_activePeriodIndex
);
} else {
contractState__ = ContractState.CONTRACT_UNLOCKED_INACTIVE;
periodInfo__[_activePeriodIndex].isFinalized =1;
periodInfo__[_activePeriodIndex]
.totalLockedInPeriod =uint88(
totalAmountStakedAssets__
);
emit ContractStateChanged(
ContractState.CONTRACT_UNLOCKED_INACTIVE,
_activePeriodIndex
);
}
}
}
}
/* ========== INTERNAL FUNCTIONS ========== */function_totalClaimable(address _account,
uint256 _activePeriodIndex
)
internalviewreturns (uint256 totalClaimableUSA_,
uint256 totalClaimableWETH_,
uint256 totalClaimableStable_
)
{
unchecked {
StakeInfo memory stakeInfo_ = stakes__[_account];
// check how much the user can claim as per the updated state
totalClaimableUSA_ =
stakeInfo_.rewardDebt_USA_1 -
stakeInfo_.rewardPaid_USA_1;
totalClaimableWETH_ =
stakeInfo_.rewardDebt_WETH_2 -
stakeInfo_.rewardPaid_WETH_2;
totalClaimableStable_ =
stakeInfo_.rewardDebt_Stable_3 -
stakeInfo_.rewardPaid_Stable_3;
if (
block.timestamp>
periodInfo__[_activePeriodIndex].timestampPeriodEnd
) {
_activePeriodIndex +=1;
}
for (
uint256 i = stakeInfo_.lastUpdatePeriodIndex;
i < _activePeriodIndex;
++i
) {
if (periodInfo__[i].isFinalized ==0) {
continue;
}
if (stakeInfo_.amountStaked ==0) {
return (0, 0, 0);
}
totalClaimableUSA_ +=
(periodRewardPerStakedToken_USA_1__[i] *
stakeInfo_.amountStaked) /
STAKE_TOKEN_DECIMALS_SCALE;
totalClaimableWETH_ +=
(periodRewardPerStakedToken_WETH_2__[i] *
stakeInfo_.amountStaked) /
STAKE_TOKEN_DECIMALS_SCALE;
totalClaimableStable_ +=
(periodRewardPerStakedToken_Stable_3__[i] *
stakeInfo_.amountStaked) /
STAKE_TOKEN_DECIMALS_SCALE;
}
return (
totalClaimableUSA_,
totalClaimableWETH_,
totalClaimableStable_
);
}
}
function_harvestRewards()
internalreturns (uint256 claimableReward_USA_1,
uint256 claimableReward_WETH_2,
uint256 claimableReward_Stable_3
)
{
StakeInfo memory stakeInfo_ = stakes__[msg.sender];
claimableReward_USA_1 =
stakeInfo_.rewardDebt_USA_1 -
stakeInfo_.rewardPaid_USA_1;
claimableReward_WETH_2 =
stakeInfo_.rewardDebt_WETH_2 -
stakeInfo_.rewardPaid_WETH_2;
claimableReward_Stable_3 =
stakeInfo_.rewardDebt_Stable_3 -
stakeInfo_.rewardPaid_Stable_3;
bool hasReward_ =false;
hasReward_ =
claimableReward_USA_1 >0||
claimableReward_WETH_2 >0||
claimableReward_Stable_3 >0;
if (!hasReward_) {
return (0, 0, 0);
}
if (claimableReward_USA_1 >0) {
stakeInfo_.rewardPaid_USA_1 +=uint88(claimableReward_USA_1);
rewardToken_USA_1.transfer(msg.sender, claimableReward_USA_1);
}
if (claimableReward_WETH_2 >0) {
stakeInfo_.rewardPaid_WETH_2 +=uint88(claimableReward_WETH_2);
rewardToken_WETH_2.transfer(msg.sender, claimableReward_WETH_2);
}
if (claimableReward_Stable_3 >0) {
stakeInfo_.rewardPaid_Stable_3 +=uint88(claimableReward_Stable_3);
rewardToken_Stable_3.transfer(msg.sender, claimableReward_Stable_3);
}
stakes__[msg.sender] = stakeInfo_;
emit RewardsClaimed(
msg.sender,
claimableReward_USA_1,
claimableReward_WETH_2,
claimableReward_Stable_3
);
return (
claimableReward_USA_1,
claimableReward_WETH_2,
claimableReward_Stable_3
);
}
function_createNextPeriod(uint32 _startNextPeriod
) internalreturns (uint256 newPeriodIndex_) {
unchecked {
periodIndexCounter__++;
newPeriodIndex_ = periodIndexCounter__;
PeriodInfo storage period_ = periodInfo__[newPeriodIndex_];
period_.timestampPeriodStart = _startNextPeriod;
period_.timestampPeriodEnd =uint32(
_startNextPeriod + lockDurationNextPeriod__
);
// log the start and end of the new periodemit NewPeriodCreated(
newPeriodIndex_,
_startNextPeriod,
period_.timestampPeriodEnd
);
// totalLockedInPeriod and totalRewardedInPeriod_USA_1 are not set yet (so are 0)return newPeriodIndex_;
}
}
function_lockTokensDuringActivePeriod(uint256 _amount) internal{
StakeInfo memory stakeInfo_ = stakes__[msg.sender];
// user cannot have already staked tokens locked in the current active periodrequire(
stakeInfo_.amountStaked ==0,
"LockPeriodContract: User already has NFTs in active lock, please use different wallet to lock new nfts or wait until the current lock period is over to add to this wallet"
);
totalAmountStakedAssets__ += _amount;
stakeInfo_.amountStaked =uint88(_amount);
stakeInfo_.lastUpdatePeriodIndex =uint8(activePeriodIndex__ +1);
stakes__[msg.sender] = stakeInfo_;
}
function_transferInERC20(uint256 _amount) internal{
require(_amount >0, "LockPeriodContract: Cannot lock 0");
// transfer tokens from user to contract
lockTokenERC20.transferFrom(msg.sender, address(this), _amount);
}
function_transferOutERC20(uint256 _amount) internal{
lockTokenERC20.transfer(msg.sender, _amount);
}
function_stakeAssets(uint256 _amount) internal{
totalAmountStakedAssets__ += _amount;
stakes__[msg.sender].amountStaked +=uint88(_amount);
}
function_unstakeAssets(uint256 _amount) internal{
require(
uint256(contractState__) >=2,
"LockPeriodContract: Cannot unstake in current state, wait for the contract to unlock"
);
require(_amount >0, "LockPeriodContract: Cannot withdraw 0 nfts.");
require(
_amount <= stakes__[msg.sender].amountStaked,
"LockPeriodContract: Amount is higher than staked"
);
totalAmountStakedAssets__ -= _amount;
stakes__[msg.sender].amountStaked -=uint88(_amount);
}
function_checkIfNotPaused() internal{
require(!paused(), "LockPeriodContract: Contract is paused");
}
/* ========== RESTRICTED FUNCTIONS ========== */functionrestartLockContract(uint32 _startFirstPeriod,
uint256 _lockDurationNextPeriod
) externalonlyOwner{
_updateContractState();
_restartLockContract(_startFirstPeriod, _lockDurationNextPeriod);
}
function_restartLockContract(uint32 _startFirstPeriod,
uint256 _lockDurationNextPeriod
) internal{
// set period for period 0 to finalized
periodInfo__[periodIndexCounter__].isFinalized =1;
periodIndexCounter__++;
activePeriodIndex__ = periodIndexCounter__;
nextPeriodIndex__ =0;
lockDurationNextPeriod__ = _lockDurationNextPeriod;
// set contractState__ to FRESH_PERIOD
contractState__ = ContractState.FRESH_PERIOD;
PeriodInfo storage period_ = periodInfo__[periodIndexCounter__];
period_.timestampPeriodStart = _startFirstPeriod;
period_.timestampPeriodEnd =uint32(
_startFirstPeriod + _lockDurationNextPeriod
);
emit ContractStateChanged(
ContractState.FRESH_PERIOD,
activePeriodIndex__
);
emit NewLockPeriodConfigured(
activePeriodIndex__,
_startFirstPeriod,
_lockDurationNextPeriod
);
}
/// @inheritdoc ILockPeriodContractfunctionconfigureFirstPeriod(uint32 _startFirstPeriod,
uint256 _lockDurationNextPeriod
) externalonlyOwner{
require(
contractState__ == ContractState.NONE,
"LockPeriodContract: Contract state is not NONE"
);
lockDurationNextPeriod__ = _lockDurationNextPeriod;
// set period for period 0 to finalized
periodInfo__[0].isFinalized =1;
periodIndexCounter__ =1;
activePeriodIndex__ =1;
contractState__ = ContractState.FRESH_PERIOD;
PeriodInfo storage period_ = periodInfo__[1];
period_.timestampPeriodStart = _startFirstPeriod;
period_.timestampPeriodEnd =uint32(
_startFirstPeriod + lockDurationNextPeriod__
);
emit NewLockPeriodConfigured(
activePeriodIndex__,
_startFirstPeriod,
_lockDurationNextPeriod
);
}
/// @inheritdoc ILockPeriodContractfunctionupdateManual() external{
_updateContractState();
}
/// @inheritdoc ILockPeriodContractfunctiondistributeRewardsForLatestPeriod(uint256 _amountReward_usa_1,
uint256 _amountReward_weth_2,
uint256 _amountReward_stable_3
) externalonlyRewardsDistribution{
_updateContractState();
uint256 _periodIndex = activePeriodIndex__;
// require that the contract is in the active staterequire(
contractState__ == ContractState.ACTIVE_LOCKED,
"LockPeriodContract: Contract must be in active state to distribute rewards"
);
PeriodInfo memory period_ = periodInfo__[_periodIndex];
if (period_.totalLockedInPeriod >0) {
if (_amountReward_usa_1 >0) {
periodRewardPerStakedToken_USA_1__[_periodIndex] +=
(_amountReward_usa_1 * STAKE_TOKEN_DECIMALS_SCALE) /
period_.totalLockedInPeriod;
period_.totalRewardedInPeriod_USA_1 +=uint88(
_amountReward_usa_1
);
totalRewardsDistributed_USA_1__ += _amountReward_usa_1;
rewardToken_USA_1.transferFrom(
msg.sender,
address(this),
_amountReward_usa_1
);
}
if (_amountReward_weth_2 >0) {
periodRewardPerStakedToken_WETH_2__[_periodIndex] +=
(_amountReward_weth_2 * STAKE_TOKEN_DECIMALS_SCALE) /
period_.totalLockedInPeriod;
period_.totalRewardedInPeriod_WETH_2 +=uint88(
_amountReward_weth_2
);
totalRewardsDistributed_WETH_2__ += _amountReward_weth_2;
rewardToken_WETH_2.transferFrom(
msg.sender,
address(this),
_amountReward_weth_2
);
}
if (_amountReward_stable_3 >0) {
periodRewardPerStakedToken_Stable_3__[_periodIndex] +=
(_amountReward_stable_3 * STAKE_TOKEN_DECIMALS_SCALE) /
period_.totalLockedInPeriod;
period_.totalRewardedInPeriod_Stable_3 +=uint88(
_amountReward_stable_3
);
totalRewardsDistributed_Stable_3__ += _amountReward_stable_3;
rewardToken_Stable_3.transferFrom(
msg.sender,
address(this),
_amountReward_stable_3
);
}
} else {
revert("LockPeriodContract: No assets locked in this period");
}
period_.isFinalized =1;
emit DistributeRewardsForLatestPeriod(
_periodIndex,
_amountReward_usa_1,
_amountReward_weth_2,
_amountReward_stable_3,
period_.totalLockedInPeriod
);
periodInfo__[_periodIndex] = period_;
if (nextPeriodIndex__ ==0) {
nextPeriodIndex__ = _createNextPeriod(
period_.timestampPeriodEnd +uint32(defaultWindowDuration__)
);
}
}
/// @inheritdoc ILockPeriodContractfunctionsetPoolState(ContractState _poolState) externalonlyOwner{
contractState__ = _poolState;
emit PoolStateManualSet(_poolState);
emit ContractStateChanged(_poolState, activePeriodIndex__);
}
functionunfinalizePeriod(uint256 _periodIndex) externalonlyOwner{
require(
_periodIndex <= periodIndexCounter__,
"LockPeriodContract: Period index is higher than period index counter"
);
periodInfo__[_periodIndex].isFinalized =0;
}
functionpause() externalonlyOwner{
_pause();
}
functionunpause() externalonlyOwner{
_unpause();
}
/// @inheritdoc ILockPeriodContractfunctionsetStandardWindowDuration(uint32 _standardWindowDuration
) externalonlyOwner{
defaultWindowDuration__ = _standardWindowDuration;
emit StandardWindowDurationUpdated(_standardWindowDuration);
}
/// @inheritdoc ILockPeriodContractfunctionsetLockPeriodNextPeriod(uint256 _lockPeriodNextPeriod
) externalonlyOwner{
lockDurationNextPeriod__ = _lockPeriodNextPeriod;
emit LockPeriodNextPeriodUpdated(_lockPeriodNextPeriod);
}
/// @inheritdoc ILockPeriodContractfunctionsetRewardsDistribution(address _rewardsDistribution
) externalonlyOwner{
rewardsDistribution = _rewardsDistribution;
emit RewardsDistributionUpdated(_rewardsDistribution);
}
/// @inheritdoc ILockPeriodContractfunctionsetPeriodFinalized(uint256 _periodIndex) externalonlyOwner{
require(
_periodIndex <= periodIndexCounter__,
"LockPeriodContract: Period index is higher than period index counter"
);
periodInfo__[_periodIndex].isFinalized =1;
}
/// @inheritdoc ILockPeriodContractfunctionsetTotalLockedInPeriod(uint256 _periodIndex,
uint88 _totalLockedInPeriod
) externalonlyOwner{
require(
_periodIndex <= periodIndexCounter__,
"LockPeriodContract: Period index is higher than period index counter"
);
periodInfo__[_periodIndex].totalLockedInPeriod = _totalLockedInPeriod;
}
/// @inheritdoc ILockPeriodContractfunctionsetPeriodAllInfo(uint256 _periodIndex,
uint8 _isFinalized,
uint32 _timestampPeriodStart,
uint32 _timestampPeriodEnd,
uint88 _totalLockedInPeriod,
uint88 _totalRewardedInPeriod_USA_1,
uint88 _totalRewardedInPeriod_WETH_2,
uint88 _totalRewardedInPeriod_Stable_3
) externalonlyOwner{
// make sure _periodIndex is not higher than periodIndexCounter__require(
_periodIndex <= periodIndexCounter__,
"LockPeriodContract: Period index is higher than period index counter"
);
periodInfo__[_periodIndex].isFinalized = _isFinalized;
periodInfo__[_periodIndex].timestampPeriodStart = _timestampPeriodStart;
periodInfo__[_periodIndex].timestampPeriodEnd = _timestampPeriodEnd;
periodInfo__[_periodIndex].totalLockedInPeriod = _totalLockedInPeriod;
periodInfo__[_periodIndex]
.totalRewardedInPeriod_USA_1 = _totalRewardedInPeriod_USA_1;
periodInfo__[_periodIndex]
.totalRewardedInPeriod_WETH_2 = _totalRewardedInPeriod_WETH_2;
periodInfo__[_periodIndex]
.totalRewardedInPeriod_Stable_3 = _totalRewardedInPeriod_Stable_3;
}
/// @inheritdoc ILockPeriodContractfunctionemergencyWithdrawToken(address _token,
address _to,
uint256 _amount
) externalonlyOwner{
// check that the token is not the stake tokenrequire(
_token !=address(lockTokenERC20),
"LockPeriodContract: Cannot withdraw staking token"
);
IERC20(_token).transfer(_to, _amount);
}
/// @inheritdoc ILockPeriodContractfunctiontransferStakedTokensToMultisig(address _to,
uint256 _amount
) external{
require(
msg.sender== MULTISIG_ADDRESS,
"LockPeriodContract: Only multisig can call this function"
);
lockTokenERC20.transfer(_to, _amount);
}
// /// @inheritdoc ILockPeriodContractfunctionsetMigratorContract(address _migratorContract) external{
require(
msg.sender== MULTISIG_ADDRESS,
"LockPeriodContract: Only multisig can call this function"
);
migratorContractAddress = _migratorContract;
}
/* ========== VIEWS ========== */function_checkActivePeriod()
internalviewreturns (ContractState state_, uint256 activePeriodIndex_)
{
PeriodInfo memory period_ = periodInfo__[activePeriodIndex__];
if (block.timestamp<= period_.timestampPeriodEnd) {
// the contract is up to date, the active period is ongoing, the contract is in the correct statereturn (ContractState.ACTIVE_LOCKED, activePeriodIndex__);
} else {
// the period has ended, the contract is in the correct state as the active period has endeduint32 nextPeriodStart_ = period_.timestampPeriodEnd +uint32(defaultWindowDuration__);
if (block.timestamp< nextPeriodStart_) {
return (
ContractState.UNLOCKED_REDEEMABLE,
activePeriodIndex__ +1
);
} elseif (
block.timestamp<=
nextPeriodStart_ +uint32(lockDurationNextPeriod__)
) {
return (ContractState.ACTIVE_LOCKED, activePeriodIndex__ +1);
} elseif (
block.timestamp<
nextPeriodStart_ +uint32(lockDurationNextPeriod__) +uint32(defaultWindowDuration__)
) {
return (
ContractState.UNLOCKED_REDEEMABLE,
activePeriodIndex__ +2
);
} else {
return (ContractState.ACTIVE_LOCKED, activePeriodIndex__ +2);
}
}
}
function_checkUnlockRedeemable()
internalviewreturns (ContractState state_, uint256 activePeriodIndex_)
{
PeriodInfo memory period_ = periodInfo__[activePeriodIndex__];
if (block.timestamp< period_.timestampPeriodStart) {
// the contract is in the correct state as the active period till has to startreturn (ContractState.UNLOCKED_REDEEMABLE, activePeriodIndex__);
} elseif (
block.timestamp>= period_.timestampPeriodStart &&block.timestamp<= period_.timestampPeriodEnd
) {
return (ContractState.ACTIVE_LOCKED, activePeriodIndex__);
} else {
uint32 nextPeriodStart_ = period_.timestampPeriodEnd +uint32(defaultWindowDuration__);
if (block.timestamp< nextPeriodStart_) {
return (
ContractState.UNLOCKED_REDEEMABLE,
activePeriodIndex__ +1
);
} elseif (
block.timestamp<=
nextPeriodStart_ +uint32(lockDurationNextPeriod__)
) {
return (ContractState.ACTIVE_LOCKED, activePeriodIndex__ +1);
} else {
return (
ContractState.UNLOCKED_REDEEMABLE,
activePeriodIndex__ +2
);
}
}
}
function_checkFreshPeriod()
internalviewreturns (ContractState state_, uint256 activePeriodIndex_)
{
PeriodInfo memory period_ = periodInfo__[activePeriodIndex__];
if (block.timestamp< period_.timestampPeriodStart) {
// the contract is in the correct state as the active period till has to startreturn (ContractState.FRESH_PERIOD, activePeriodIndex__);
} elseif (
block.timestamp>= period_.timestampPeriodStart &&block.timestamp<= period_.timestampPeriodEnd
) {
return (ContractState.ACTIVE_LOCKED, activePeriodIndex__);
} else {
uint32 nextPeriodStart_ = period_.timestampPeriodEnd +uint32(defaultWindowDuration__);
if (block.timestamp< nextPeriodStart_) {
return (
ContractState.UNLOCKED_REDEEMABLE,
activePeriodIndex__ +1
);
} else {
return (
ContractState.CONTRACT_UNLOCKED_INACTIVE,
activePeriodIndex__ +1
);
}
}
}
functionreturnContractPeriodState()
publicviewreturns (ContractState state_, uint256 activePeriodIndex_)
{
if (contractState__ == ContractState.ACTIVE_LOCKED) {
(state_, activePeriodIndex_) = _checkActivePeriod();
} elseif (contractState__ == ContractState.UNLOCKED_REDEEMABLE) {
(state_, activePeriodIndex_) = _checkUnlockRedeemable();
} elseif (contractState__ == ContractState.FRESH_PERIOD) {
(state_, activePeriodIndex_) = _checkFreshPeriod();
} elseif (
contractState__ == ContractState.CONTRACT_UNLOCKED_INACTIVE
) {
return (
ContractState.CONTRACT_UNLOCKED_INACTIVE,
activePeriodIndex__
);
} else {
revert(
"LockPeriodContract: Lock contract is awaiting configuration"
);
}
}
functionreturnPendingRewards(address _user
) externalviewreturns (uint256, uint256, uint256) {
return _totalClaimable(_user, activePeriodIndex__ +1);
}
functionreturnPeriodInfoTime()
externalviewreturns (uint256 currentPeriodStart_,
uint256 currentPeriodEnd_,
uint256 nextPeriodStart_,
uint256 nextPeriodEnd_
)
{
(, uint256 activePeriodIndex_) = returnContractPeriodState();
PeriodInfo memory period_ = periodInfo__[activePeriodIndex__];
if (activePeriodIndex_ == activePeriodIndex__) {
currentPeriodStart_ = period_.timestampPeriodStart;
currentPeriodEnd_ = period_.timestampPeriodEnd;
nextPeriodStart_ =
period_.timestampPeriodEnd +uint32(defaultWindowDuration__);
nextPeriodEnd_ =
nextPeriodStart_ +uint32(lockDurationNextPeriod__);
return (
currentPeriodStart_,
currentPeriodEnd_,
nextPeriodStart_,
nextPeriodEnd_
);
} elseif (activePeriodIndex_ == activePeriodIndex__ +1) {
currentPeriodStart_ =
period_.timestampPeriodEnd +uint32(defaultWindowDuration__);
currentPeriodEnd_ =
currentPeriodStart_ +uint32(lockDurationNextPeriod__);
nextPeriodStart_ =
currentPeriodEnd_ +uint32(defaultWindowDuration__);
nextPeriodEnd_ =
nextPeriodStart_ +uint32(lockDurationNextPeriod__);
} else {
currentPeriodStart_ =
period_.timestampPeriodEnd +uint32(defaultWindowDuration__) +uint32(lockDurationNextPeriod__) +uint32(defaultWindowDuration__);
currentPeriodEnd_ =
currentPeriodStart_ +uint32(lockDurationNextPeriod__);
nextPeriodStart_ =
currentPeriodEnd_ +uint32(defaultWindowDuration__);
nextPeriodEnd_ =
nextPeriodStart_ +uint32(lockDurationNextPeriod__);
}
}
/// @inheritdoc ILockPeriodContractfunctionreturnPeriodInfoStruct(uint256 _periodIndex
) externalviewreturns (PeriodInfo memory) {
return periodInfo__[_periodIndex];
}
/// @inheritdoc ILockPeriodContractfunctionreturnPeriodIndexCounter() externalviewreturns (uint256) {
return periodIndexCounter__;
}
/// @inheritdoc ILockPeriodContractfunctionisActivePeriodFinalized() externalviewreturns (bool) {
return periodInfo__[activePeriodIndex__].isFinalized ==1;
}
/// @inheritdoc ILockPeriodContractfunctionisPreviousPeriodFinalized() externalviewreturns (bool) {
return periodInfo__[activePeriodIndex__ -1].isFinalized ==1;
}
/// @inheritdoc ILockPeriodContractfunctionreturnActivePeriodInContract() externalviewreturns (uint256) {
return activePeriodIndex__;
}
/// @inheritdoc ILockPeriodContractfunctionreturnNextPeriod() externalviewreturns (uint256) {
return nextPeriodIndex__;
}
/// @inheritdoc ILockPeriodContractfunctionreturnContractStateInContract()
externalviewreturns (ContractState)
{
return contractState__;
}
/// @inheritdoc ILockPeriodContractfunctionreturnPeriodRewardPerStakedToken(uint256 _periodIndex
) externalviewreturns (uint256, uint256, uint256) {
return (
periodRewardPerStakedToken_USA_1__[_periodIndex],
periodRewardPerStakedToken_WETH_2__[_periodIndex],
periodRewardPerStakedToken_Stable_3__[_periodIndex]
);
}
/// @inheritdoc ILockPeriodContractfunctionreturnTotalRewardsDistributed()
externalviewreturns (uint256 totalRewardsDistributed_USA_1,
uint256 totalRewardsDistributed_WETH_2,
uint256 totalRewardsDistributed_Stable_3
)
{
return (
totalRewardsDistributed_USA_1__,
totalRewardsDistributed_WETH_2__,
totalRewardsDistributed_Stable_3__
);
}
/// @inheritdoc ILockPeriodContractfunctionreturnPeriodInfo(uint256 _periodIndex
)
externalviewreturns (uint8 isFinalized,
uint88 totalLockedInPeriod,
uint88 totalRewardedInPeriod_USA_1,
uint32 timestampPeriodStart,
uint88 totalRewardedInPeriod_WETH_2,
uint88 totalRewardedInPeriod_Stable_3,
uint32 timestampPeriodEnd
)
{
PeriodInfo memory period_ = periodInfo__[_periodIndex];
return (
period_.isFinalized,
period_.totalLockedInPeriod,
period_.totalRewardedInPeriod_USA_1,
period_.timestampPeriodStart,
period_.totalRewardedInPeriod_WETH_2,
period_.totalRewardedInPeriod_Stable_3,
period_.timestampPeriodEnd
);
}
/// @inheritdoc ILockPeriodContractfunctionreturnUserStakeInfo(address _account
)
externalviewreturns (uint8 lastUpdatePeriodIndex,
uint88 amountStaked,
uint88 rewardDebt_USA_1,
uint88 rewardPaid_USA_1,
uint88 rewardDebt_WETH_2,
uint88 rewardPaid_WETH_2,
uint88 rewardDebt_Stable_3,
uint88 rewardPaid_Stable_3
)
{
StakeInfo memory stakeInfo_ = stakes__[_account];
return (
stakeInfo_.lastUpdatePeriodIndex,
stakeInfo_.amountStaked,
stakeInfo_.rewardDebt_USA_1,
stakeInfo_.rewardPaid_USA_1,
stakeInfo_.rewardDebt_WETH_2,
stakeInfo_.rewardPaid_WETH_2,
stakeInfo_.rewardDebt_Stable_3,
stakeInfo_.rewardPaid_Stable_3
);
}
/// @inheritdoc ILockPeriodContractfunctiontotalTokensLocked() externalviewreturns (uint256) {
return totalAmountStakedAssets__;
}
functionreturnLockPeriod() externalviewreturns (uint256) {
return lockDurationNextPeriod__;
}
functionreturnStandardWindowDuration() externalviewreturns (uint256) {
return defaultWindowDuration__;
}
/// @inheritdoc ILockPeriodContractfunctionamountLocked(address _account) externalviewreturns (uint256) {
return stakes__[_account].amountStaked;
}
/// @inheritdoc ILockPeriodContractfunctionreturnRewardClaimableUser(address _account
) externalviewreturns (uint256, uint256, uint256) {
return _totalClaimable(_account, activePeriodIndex__);
}
/// @inheritdoc ILockPeriodContractfunctionrewardPerToken_USA_1(uint256 _periodIndex
) externalviewreturns (uint256) {
return periodRewardPerStakedToken_USA_1__[_periodIndex];
}
/// @inheritdoc ILockPeriodContractfunctionrewardPerToken_WETH_2(uint256 _periodIndex
) externalviewreturns (uint256) {
return periodRewardPerStakedToken_WETH_2__[_periodIndex];
}
/// @inheritdoc ILockPeriodContractfunctionrewardPerToken_Stable_3(uint256 _periodIndex
) externalviewreturns (uint256) {
return periodRewardPerStakedToken_Stable_3__[_periodIndex];
}
}
Contract Source Code
File 13 of 15: Ownable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)pragmasolidity ^0.8.20;import {Context} from"../utils/Context.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.
*
* The initial owner is set to the address provided by the deployer. 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.
*/abstractcontractOwnableisContext{
addressprivate _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/errorOwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/errorOwnableInvalidOwner(address owner);
eventOwnershipTransferred(addressindexed previousOwner, addressindexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/constructor(address initialOwner) {
if (initialOwner ==address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/modifieronlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/functionowner() publicviewvirtualreturns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/function_checkOwner() internalviewvirtual{
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @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.
*/functionrenounceOwnership() publicvirtualonlyOwner{
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publicvirtualonlyOwner{
if (newOwner ==address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/function_transferOwnership(address newOwner) internalvirtual{
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
Contract Source Code
File 14 of 15: Pausable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)pragmasolidity ^0.8.20;import {Context} from"../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/abstractcontractPausableisContext{
boolprivate _paused;
/**
* @dev Emitted when the pause is triggered by `account`.
*/eventPaused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/eventUnpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/errorEnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/errorExpectedPause();
/**
* @dev Initializes the contract in unpaused state.
*/constructor() {
_paused =false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/modifierwhenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/modifierwhenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/functionpaused() publicviewvirtualreturns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/function_requireNotPaused() internalviewvirtual{
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/function_requirePaused() internalviewvirtual{
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/function_pause() internalvirtualwhenNotPaused{
_paused =true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/function_unpause() internalvirtualwhenPaused{
_paused =false;
emit Unpaused(_msgSender());
}
}
Contract Source Code
File 15 of 15: ReentrancyGuard.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)pragmasolidity ^0.8.20;/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/abstractcontractReentrancyGuard{
// Booleans are more expensive than uint256 or any type that takes up a full// word because each write operation emits an extra SLOAD to first read the// slot's contents, replace the bits taken up by the boolean, and then write// back. This is the compiler's defense against contract upgrades and// pointer aliasing, and it cannot be disabled.// The values being non-zero value makes deployment a bit more expensive,// but in exchange the refund on every call to nonReentrant will be lower in// amount. Since refunds are capped to a percentage of the total// transaction's gas, it is best to keep them low in cases like this one, to// increase the likelihood of the full refund coming into effect.uint256privateconstant NOT_ENTERED =1;
uint256privateconstant ENTERED =2;
uint256private _status;
/**
* @dev Unauthorized reentrant call.
*/errorReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/modifiernonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function_nonReentrantBefore() private{
// On the first call to nonReentrant, _status will be NOT_ENTEREDif (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function_nonReentrantAfter() private{
// By storing the original value once again, a refund is triggered (see// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/function_reentrancyGuardEntered() internalviewreturns (bool) {
return _status == ENTERED;
}
}