// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import { UintUtils } from './UintUtils.sol';
library AddressUtils {
using UintUtils for uint256;
error AddressUtils__InsufficientBalance();
error AddressUtils__NotContract();
error AddressUtils__SendValueFailed();
function toString(address account) internal pure returns (string memory) {
return uint256(uint160(account)).toHexString(20);
}
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
function sendValue(address payable account, uint256 amount) internal {
(bool success, ) = account.call{ value: amount }('');
if (!success) revert AddressUtils__SendValueFailed();
}
function functionCall(
address target,
bytes memory data
) internal returns (bytes memory) {
return
functionCall(target, data, 'AddressUtils: failed low-level call');
}
function functionCall(
address target,
bytes memory data,
string memory error
) internal returns (bytes memory) {
return _functionCallWithValue(target, data, 0, error);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return
functionCallWithValue(
target,
data,
value,
'AddressUtils: failed low-level call with value'
);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory error
) internal returns (bytes memory) {
if (value > address(this).balance)
revert AddressUtils__InsufficientBalance();
return _functionCallWithValue(target, data, value, error);
}
/**
* @notice execute arbitrary external call with limited gas usage and amount of copied return data
* @dev derived from https://github.com/nomad-xyz/ExcessivelySafeCall (MIT License)
* @param target recipient of call
* @param gasAmount gas allowance for call
* @param value native token value to include in call
* @param maxCopy maximum number of bytes to copy from return data
* @param data encoded call data
* @return success whether call is successful
* @return returnData copied return data
*/
function excessivelySafeCall(
address target,
uint256 gasAmount,
uint256 value,
uint16 maxCopy,
bytes memory data
) internal returns (bool success, bytes memory returnData) {
returnData = new bytes(maxCopy);
assembly {
// execute external call via assembly to avoid automatic copying of return data
success := call(
gasAmount,
target,
value,
add(data, 0x20),
mload(data),
0,
0
)
// determine whether to limit amount of data to copy
let toCopy := returndatasize()
if gt(toCopy, maxCopy) {
toCopy := maxCopy
}
// store the length of the copied bytes
mstore(returnData, toCopy)
// copy the bytes from returndata[0:toCopy]
returndatacopy(add(returnData, 0x20), 0, toCopy)
}
}
function _functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory error
) private returns (bytes memory) {
if (!isContract(target)) revert AddressUtils__NotContract();
(bool success, bytes memory returnData) = target.call{ value: value }(
data
);
if (success) {
return returnData;
} else if (returnData.length > 0) {
assembly {
let returnData_size := mload(returnData)
revert(add(32, returnData), returnData_size)
}
} else {
revert(error);
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Arrays.sol)
pragma solidity ^0.8.20;
import {StorageSlot} from "./StorageSlot.sol";
import {Math} from "./math/Math.sol";
/**
* @dev Collection of functions related to array types.
*/
library Arrays {
using StorageSlot for bytes32;
/**
* @dev Searches a sorted `array` and returns the first index that contains
* a value greater or equal to `element`. If no such index exists (i.e. all
* values in the array are strictly less than `element`), the array length is
* returned. Time complexity O(log n).
*
* `array` is expected to be sorted in ascending order, and to contain no
* repeated elements.
*/
function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds towards zero (it does integer division with truncation).
if (unsafeAccess(array, mid).value > element) {
high = mid;
} else {
low = mid + 1;
}
}
// At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
if (low > 0 && unsafeAccess(array, low - 1).value == element) {
return low - 1;
} else {
return low;
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(address[] storage arr, uint256 pos) internal pure returns (StorageSlot.AddressSlot storage) {
bytes32 slot;
// We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
// following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
/// @solidity memory-safe-assembly
assembly {
mstore(0, arr.slot)
slot := add(keccak256(0, 0x20), pos)
}
return slot.getAddressSlot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(bytes32[] storage arr, uint256 pos) internal pure returns (StorageSlot.Bytes32Slot storage) {
bytes32 slot;
// We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
// following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
/// @solidity memory-safe-assembly
assembly {
mstore(0, arr.slot)
slot := add(keccak256(0, 0x20), pos)
}
return slot.getBytes32Slot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(uint256[] storage arr, uint256 pos) internal pure returns (StorageSlot.Uint256Slot storage) {
bytes32 slot;
// We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
// following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
/// @solidity memory-safe-assembly
assembly {
mstore(0, arr.slot)
slot := add(keccak256(0, 0x20), pos)
}
return slot.getUint256Slot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(uint256[] memory arr, uint256 pos) internal pure returns (uint256 res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(address[] memory arr, uint256 pos) internal pure returns (address res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^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.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/ERC1155.sol)
pragma solidity ^0.8.20;
import { IERC1155 } from "./IERC1155.sol";
import { IERC1155Receiver } from "./IERC1155Receiver.sol";
import { IERC1155MetadataURI } from "./extensions/IERC1155MetadataURI.sol";
import { Context } from "../../utils/Context.sol";
import { IERC165, ERC165 } from "../../utils/introspection/ERC165.sol";
import { Arrays } from "../../utils/Arrays.sol";
import { IERC1155Errors } from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the basic standard multi-token.
* See https://eips.ethereum.org/EIPS/eip-1155
* Originally based on code by Enjin: https://github.com/enjin/erc-1155
*/
abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IERC1155Errors {
using Arrays for uint256[];
using Arrays for address[];
mapping(uint256 id => mapping(address account => uint256)) private _balances;
mapping(address account => mapping(address operator => bool)) private _operatorApprovals;
// Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
string private _uri;
/**
* @dev See {_setURI}.
*/
constructor(string memory uri_) {
_setURI(uri_);
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return interfaceId == type(IERC1155).interfaceId || interfaceId == type(IERC1155MetadataURI).interfaceId
|| super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC1155MetadataURI-uri}.
*
* This implementation returns the same URI for *all* token types. It relies
* on the token type ID substitution mechanism
* https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
*
* Clients calling this function must replace the `\{id\}` substring with the
* actual token type ID.
*/
function uri(uint256 /* id */ ) public view virtual returns (string memory) {
return _uri;
}
/**
* @dev See {IERC1155-balanceOf}.
*/
function balanceOf(address account, uint256 id) public view virtual returns (uint256) {
return _balances[id][account];
}
/**
* @dev See {IERC1155-balanceOfBatch}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/
function balanceOfBatch(
address[] memory accounts,
uint256[] memory ids
)
public
view
virtual
returns (uint256[] memory)
{
if (accounts.length != ids.length) {
revert ERC1155InvalidArrayLength(ids.length, accounts.length);
}
uint256[] memory batchBalances = new uint256[](accounts.length);
for (uint256 i = 0; i < accounts.length; ++i) {
batchBalances[i] = balanceOf(accounts.unsafeMemoryAccess(i), ids.unsafeMemoryAccess(i));
}
return batchBalances;
}
/**
* @dev See {IERC1155-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual {
_setApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC1155-isApprovedForAll}.
*/
function isApprovedForAll(address account, address operator) public view virtual returns (bool) {
return _operatorApprovals[account][operator];
}
/**
* @dev See {IERC1155-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes memory data) public virtual {
address sender = _msgSender();
if (from != sender && !isApprovedForAll(from, sender)) {
revert ERC1155MissingApprovalForAll(sender, from);
}
_safeTransferFrom(from, to, id, value, data);
}
/**
* @dev See {IERC1155-safeBatchTransferFrom}.
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
)
public
virtual
{
address sender = _msgSender();
if (from != sender && !isApprovedForAll(from, sender)) {
revert ERC1155MissingApprovalForAll(sender, from);
}
_safeBatchTransferFrom(from, to, ids, values, data);
}
/**
* @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`. Will mint (or burn) if `from`
* (or `to`) is the zero address.
*
* Emits a {TransferSingle} event if the arrays contain one element, and {TransferBatch} otherwise.
*
* Requirements:
*
* - If `to` refers to a smart contract, it must implement either {IERC1155Receiver-onERC1155Received}
* or {IERC1155Receiver-onERC1155BatchReceived} and return the acceptance magic value.
* - `ids` and `values` must have the same length.
*
* NOTE: The ERC-1155 acceptance check is not performed in this function. See {_updateWithAcceptanceCheck} instead.
*/
function _update(address from, address to, uint256[] memory ids, uint256[] memory values) internal virtual {
if (ids.length != values.length) {
revert ERC1155InvalidArrayLength(ids.length, values.length);
}
address operator = _msgSender();
for (uint256 i = 0; i < ids.length; ++i) {
uint256 id = ids.unsafeMemoryAccess(i);
uint256 value = values.unsafeMemoryAccess(i);
if (from != address(0)) {
uint256 fromBalance = _balances[id][from];
if (fromBalance < value) {
revert ERC1155InsufficientBalance(from, fromBalance, value, id);
}
unchecked {
// Overflow not possible: value <= fromBalance
_balances[id][from] = fromBalance - value;
}
}
if (to != address(0)) {
_balances[id][to] += value;
}
}
if (ids.length == 1) {
uint256 id = ids.unsafeMemoryAccess(0);
uint256 value = values.unsafeMemoryAccess(0);
emit TransferSingle(operator, from, to, id, value);
} else {
emit TransferBatch(operator, from, to, ids, values);
}
}
/**
* @dev Version of {_update} that performs the token acceptance check by calling
* {IERC1155Receiver-onERC1155Received} or {IERC1155Receiver-onERC1155BatchReceived} on the receiver address if it
* contains code (eg. is a smart contract at the moment of execution).
*
* IMPORTANT: Overriding this function is discouraged because it poses a reentrancy risk from the receiver. So any
* update to the contract state after this function would break the check-effect-interaction pattern. Consider
* overriding {_update} instead.
*/
function _updateWithAcceptanceCheck(
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
)
internal
virtual
{
_update(from, to, ids, values);
if (to != address(0)) {
address operator = _msgSender();
if (ids.length == 1) {
uint256 id = ids.unsafeMemoryAccess(0);
uint256 value = values.unsafeMemoryAccess(0);
_doSafeTransferAcceptanceCheck(operator, from, to, id, value, data);
} else {
_doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, values, data);
}
}
}
/**
* @dev Transfers a `value` tokens of token type `id` from `from` to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `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.
*/
function _safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes memory data) internal {
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
_updateWithAcceptanceCheck(from, to, ids, values, data);
}
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
* - `ids` and `values` must have the same length.
*/
function _safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
)
internal
{
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
_updateWithAcceptanceCheck(from, to, ids, values, data);
}
/**
* @dev Sets a new URI for all token types, by relying on the token type ID
* substitution mechanism
* https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
*
* By this mechanism, any occurrence of the `\{id\}` substring in either the
* URI or any of the values in the JSON file at said URI will be replaced by
* clients with the token type ID.
*
* For example, the `https://token-cdn-domain/\{id\}.json` URI would be
* interpreted by clients as
* `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
* for token type ID 0x4cce0.
*
* See {uri}.
*
* Because these URIs cannot be meaningfully represented by the {URI} event,
* this function emits no events.
*/
function _setURI(string memory newuri) internal virtual {
_uri = newuri;
}
/**
* @dev Creates a `value` amount of tokens of type `id`, and assigns them to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function _mint(address to, uint256 id, uint256 value, bytes memory data) internal {
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
_updateWithAcceptanceCheck(address(0), to, ids, values, data);
}
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - `ids` and `values` must have the same length.
* - `to` cannot be the zero address.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function _mintBatch(address to, uint256[] memory ids, uint256[] memory values, bytes memory data) internal {
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
_updateWithAcceptanceCheck(address(0), to, ids, values, data);
}
/**
* @dev Destroys a `value` amount of tokens of type `id` from `from`
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `from` must have at least `value` amount of tokens of type `id`.
*/
function _burn(address from, uint256 id, uint256 value) internal {
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
_updateWithAcceptanceCheck(from, address(0), ids, values, "");
}
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `from` must have at least `value` amount of tokens of type `id`.
* - `ids` and `values` must have the same length.
*/
function _burnBatch(address from, uint256[] memory ids, uint256[] memory values) internal {
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
_updateWithAcceptanceCheck(from, address(0), ids, values, "");
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Emits an {ApprovalForAll} event.
*
* Requirements:
*
* - `operator` cannot be the zero address.
*/
function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
if (operator == address(0)) {
revert ERC1155InvalidOperator(address(0));
}
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
/**
* @dev Performs an acceptance check by calling {IERC1155-onERC1155Received} on the `to` address
* if it contains code at the moment of execution.
*/
function _doSafeTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256 id,
uint256 value,
bytes memory data
)
private
{
if (to.code.length > 0) {
try IERC1155Receiver(to).onERC1155Received(operator, from, id, value, data) returns (bytes4 response) {
if (response != IERC1155Receiver.onERC1155Received.selector) {
// Tokens rejected
revert ERC1155InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
// non-ERC1155Receiver implementer
revert ERC1155InvalidReceiver(to);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
/**
* @dev Performs a batch acceptance check by calling {IERC1155-onERC1155BatchReceived} on the `to` address
* if it contains code at the moment of execution.
*/
function _doSafeBatchTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
)
private
{
if (to.code.length > 0) {
try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, values, data) returns (bytes4 response)
{
if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
// Tokens rejected
revert ERC1155InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
// non-ERC1155Receiver implementer
revert ERC1155InvalidReceiver(to);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
/**
* @dev Creates an array in memory with only one value for each of the elements provided.
*/
function _asSingletonArrays(
uint256 element1,
uint256 element2
)
private
pure
returns (uint256[] memory array1, uint256[] memory array2)
{
/// @solidity memory-safe-assembly
assembly {
// Load the free memory pointer
array1 := mload(0x40)
// Set array length to 1
mstore(array1, 1)
// Store the single element at the next word after the length (where content starts)
mstore(add(array1, 0x20), element1)
// Repeat for next array locating it right after the first array
array2 := add(array1, 0x40)
mstore(array2, 1)
mstore(add(array2, 0x20), element2)
// Update the free memory pointer by pointing after the second array
mstore(0x40, add(array2, 0x40))
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @title Contains 512-bit math functions
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of
/// precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256
/// bits
library FullMath {
/// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or
/// denominator == 0
/// @param a The multiplicand
/// @param b The multiplier
/// @param denominator The divisor
/// @return result The 256-bit result
/// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
function mulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = a * b
// Compute the product mod 2**256 and mod 2**256 - 1
// then use the Chinese Remainder Theorem to reconstruct
// the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2**256 + prod0
uint256 prod0 = a * b; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(a, b, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Make sure the result is less than 2**256.
// Also prevents denominator == 0
require(denominator > prod1);
// Handle non-overflow cases, 256 by 256 division
if (prod1 == 0) {
assembly {
result := div(prod0, denominator)
}
return result;
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0]
// Compute remainder using mulmod
uint256 remainder;
assembly {
remainder := mulmod(a, b, denominator)
}
// Subtract 256 bit number from 512 bit number
assembly {
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator
// Compute largest power of two divisor of denominator.
// Always >= 1.
uint256 twos = (0 - denominator) & denominator;
// Divide denominator by power of two
assembly {
denominator := div(denominator, twos)
}
// Divide [prod1 prod0] by the factors of two
assembly {
prod0 := div(prod0, twos)
}
// Shift in bits from prod1 into prod0. For this we need
// to flip `twos` such that it is 2**256 / twos.
// If twos is zero, then it becomes one
assembly {
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
// Invert denominator mod 2**256
// Now that denominator is an odd number, it has an inverse
// modulo 2**256 such that denominator * inv = 1 mod 2**256.
// Compute the inverse by starting with a seed that is correct
// correct for four bits. That is, denominator * inv = 1 mod 2**4
uint256 inv = (3 * denominator) ^ 2;
// Now use Newton-Raphson iteration to improve the precision.
// Thanks to Hensel's lifting lemma, this also works in modular
// arithmetic, doubling the correct bits in each step.
inv *= 2 - denominator * inv; // inverse mod 2**8
inv *= 2 - denominator * inv; // inverse mod 2**16
inv *= 2 - denominator * inv; // inverse mod 2**32
inv *= 2 - denominator * inv; // inverse mod 2**64
inv *= 2 - denominator * inv; // inverse mod 2**128
inv *= 2 - denominator * inv; // inverse mod 2**256
// Because the division is now exact we can divide by multiplying
// with the modular inverse of denominator. This will give us the
// correct result modulo 2**256. Since the preconditions guarantee
// that the outcome is less than 2**256, this is the final result.
// We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inv;
return result;
}
}
/// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or
/// denominator == 0
/// @param a The multiplicand
/// @param b The multiplier
/// @param denominator The divisor
/// @return result The 256-bit result
function mulDivRoundingUp(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
result = mulDiv(a, b, denominator);
if (mulmod(a, b, denominator) > 0) {
require(result < type(uint256).max);
result++;
}
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./GsERC1155Merkle.sol";
import "@uniswap/v2-core/contracts/interfaces/IERC20.sol";
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import "./libraries/FullMath.sol";
import "./libraries/LibGsERC20Storage.sol";
// import { console } from "forge-std/Test.sol";
error TransferPaused();
error Limited();
error TradingNotStarted();
error InsufficientAllowance();
error PERMIT_DEADLINE_EXPIRED();
error INVALID_SIGNER();
contract GsERC_1155_20 is GsERC1155Merkle, IERC20 {
bytes32 internal constant EIP712_TYPE_HASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
bytes32 internal constant EIP712_PERMIT_HASH =
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
address _owner_,
string memory _name,
string memory _symbol,
uint8 _decimals,
string memory _contractURI,
address _recipient,
uint16 _royaltyShare
)
GsERC1155Merkle(_owner_, _contractURI, _recipient, _royaltyShare)
{
LibGsERC20Storage.getStorage().name = _name;
LibGsERC20Storage.getStorage().symbol = _symbol;
LibGsERC20Storage.getStorage().packedRules = LibGsERC20Storage.packRules(address(0), true, false, _decimals);
DOMAIN_SEPARATOR();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function totalSupply() public view virtual returns (uint256) {
return LibGsERC20Storage.getStorage().totalSupply;
}
function balanceOf(address holder) public view virtual returns (uint256) {
return LibGsERC20Storage.getStorage().balanceOf[holder];
}
function allowance(address owner_, address spender) public view returns (uint256) {
return LibGsERC20Storage.getStorage().allowance[owner_][spender];
}
function name() public view returns (string memory) {
return LibGsERC20Storage.getStorage().name;
}
function symbol() public view returns (string memory) {
return LibGsERC20Storage.getStorage().symbol;
}
function decimals() public view returns (uint8) {
(,,, uint256 _decimals) = LibGsERC20Storage.getRulesFromPacked(LibGsERC20Storage.getStorage().packedRules);
return uint8(_decimals);
}
function uniswapV3Pool() public view returns (address) {
(address pool,,,) = LibGsERC20Storage.getRulesFromPacked(LibGsERC20Storage.getStorage().packedRules);
return pool;
}
function disabled_transfer() public view returns (bool) {
(, bool disable_transfer,,) = LibGsERC20Storage.getRulesFromPacked(LibGsERC20Storage.getStorage().packedRules);
return disable_transfer;
}
function enabled_pay() public view returns (bool) {
(,, bool enable_pay,) = LibGsERC20Storage.getRulesFromPacked(LibGsERC20Storage.getStorage().packedRules);
return enable_pay;
}
function nonces(address owner_) public view returns (uint256) {
return LibGsERC20Storage.getStorage().nonces[owner_];
}
function approve(address spender, uint256 amount) public virtual returns (bool) {
LibGsERC20Storage.getStorage().allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
return transfer(_msgSender(), to, amount);
}
function transfer(
address origin,
address to,
uint256 amount
)
internal
whenTransferNotPaused(origin)
returns (bool)
{
if (LibGsERC20Storage.getStorage().balanceOf[origin] < amount) revert InsufficentFunds();
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
LibGsERC20Storage.getStorage().balanceOf[origin] -= amount;
LibGsERC20Storage.getStorage().balanceOf[to] += amount;
}
emit Transfer(origin, to, amount);
return true;
}
function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
uint256 allowed = LibGsERC20Storage.getStorage().allowance[from][msg.sender]; // Saves gas for limited
// approvals.
if (allowed < amount) revert InsufficientAllowance();
if (allowed != type(uint256).max) {
LibGsERC20Storage.getStorage().allowance[from][msg.sender] = allowed - amount;
}
return transfer(from, to, amount);
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner_,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
)
public
virtual
{
if (deadline < block.timestamp) revert PERMIT_DEADLINE_EXPIRED();
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
hex"1901",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
EIP712_PERMIT_HASH,
owner_,
spender,
value,
LibGsERC20Storage.getStorage().nonces[owner_]++,
deadline
)
)
)
),
v,
r,
s
);
if (recoveredAddress == address(0)) revert INVALID_SIGNER();
if (recoveredAddress != owner_) revert INVALID_SIGNER();
LibGsERC20Storage.getStorage().allowance[recoveredAddress][spender] = value;
}
emit Approval(owner_, spender, value);
}
function DOMAIN_SEPARATOR() public virtual returns (bytes32 domainSeparator) {
domainSeparator = LibGsERC20Storage.getStorage().domainSeparators[block.chainid];
if (domainSeparator == 0x00) {
domainSeparator = computeDomainSeparator(
keccak256(bytes(LibGsERC20Storage.getStorage().name)),
0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6 // value of keccak256("1")
);
LibGsERC20Storage.getStorage().domainSeparators[block.chainid] = domainSeparator;
}
}
function computeDomainSeparator(
bytes32 nameHash,
bytes32 versionHash
)
internal
view
virtual
returns (bytes32 domainSeparator)
{
// execute EIP-712 hashStruct procedure using assembly, equavalent to:
//
// domainSeparator = keccak256(
// abi.encode(
// EIP712_TYPE_HASH,
// nameHash,
// versionHash,
// block.chainid,
// address(this)
// )
// );
bytes32 typeHash = EIP712_TYPE_HASH;
assembly {
// load free memory pointer
let pointer := mload(64)
mstore(pointer, typeHash)
mstore(add(pointer, 32), nameHash)
mstore(add(pointer, 64), versionHash)
mstore(add(pointer, 96), chainid())
mstore(add(pointer, 128), address())
domainSeparator := keccak256(pointer, 160)
}
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
LibGsERC20Storage.getStorage().totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
LibGsERC20Storage.getStorage().balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
LibGsERC20Storage.getStorage().balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
LibGsERC20Storage.getStorage().totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
/*///////////////////////////////////////////////////////////////
MODIFIERS
///////////////////////////////////////////////////////////////*/
modifier whenTransferNotPaused(address from) {
(, bool disable_transfer,,) = LibGsERC20Storage.getRulesFromPacked(LibGsERC20Storage.getStorage().packedRules);
if (disable_transfer && !(from == address(this) || from == _owner())) revert TransferPaused();
_;
}
/*///////////////////////////////////////////////////////////////
ERC20 UTILITIES
///////////////////////////////////////////////////////////////*/
function setRule(address _uniswapV3Pool, bool _disable_transfer, bool _enabled_pay) external onlyOwner {
(, bool disable_transfer,,) = LibGsERC20Storage.getRulesFromPacked(LibGsERC20Storage.getStorage().packedRules);
// Once disable_transfer is off, it can't be turned back on ==> equivalent to a renounced ownershipt for ERC20
// specific functions
LibGsERC20Storage.getStorage().packedRules =
LibGsERC20Storage.packRules(_uniswapV3Pool, disable_transfer && _disable_transfer, _enabled_pay, decimals());
}
function airdropERC20(bytes calldata recipients, uint256 amount) public virtual onlyOwner {
uint256 length = recipients.length / 20;
uint256 total = amount * length;
if (total > LibGsERC20Storage.getStorage().balanceOf[address(this)]) revert InsufficentFunds();
unchecked {
LibGsERC20Storage.getStorage().balanceOf[address(this)] -= total;
}
bytes32 position = LibGsERC20Storage.STORAGE_POSITION;
assembly {
let sign := 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
//keccak256("Transfer(address,address,uint256)")
mstore(0x80, amount)
mstore(0x60, position) // putting in 0x60 for keccak concatenation below
for { let i := 0 } lt(i, length) { i := add(i, 1) } {
let recipient := shr(96, calldataload(add(recipients.offset, mul(i, 0x14))))
mstore(0x40, recipient)
let balanceOf_slot_addr := keccak256(0x40, 0x40)
sstore(balanceOf_slot_addr, add(sload(balanceOf_slot_addr), amount))
log3(0x80, 0x20, sign, address(), recipient)
}
}
}
function getTokenPrice(uint256 amount, bool inverse) public view returns (uint256, uint8) {
IUniswapV3Pool pool = IUniswapV3Pool(uniswapV3Pool());
(uint160 sqrtRatioX96,,,,,,) = pool.slot0();
IERC20 t0 = IERC20(pool.token0());
IERC20 t1 = IERC20(pool.token1());
uint8 d0 = t0.decimals();
uint8 d1 = t1.decimals();
uint256 exp;
uint256 quoteAmount;
if (d1 <= d0) {
exp = 10 ** d0;
} else {
exp = 10 ** (18 - d1 + d0);
}
// Calculate quoteAmount with better precision if it doesn't overflow when multiplied by itself
if (sqrtRatioX96 <= type(uint128).max) {
uint256 ratioX192 = uint256(sqrtRatioX96) * sqrtRatioX96;
quoteAmount =
!inverse ? FullMath.mulDiv(ratioX192, exp, 1 << 192) : FullMath.mulDiv(1 << 192, 10 ** d1, ratioX192);
} else {
uint256 ratioX128 = FullMath.mulDiv(sqrtRatioX96, sqrtRatioX96, 1 << 64);
quoteAmount =
!inverse ? FullMath.mulDiv(ratioX128, exp, 1 << 128) : FullMath.mulDiv(1 << 128, 10 ** d1, ratioX128);
}
return (amount * quoteAmount, inverse ? d0 : d1);
}
/*///////////////////////////////////////////////////////////////
MIX ERC1155 and ERC20
///////////////////////////////////////////////////////////////*/
/**
* @notice Mints the given amount of token id to the specified receiver address
* Token 0 is the ERC20 token and is being transferred from the vault
*
* @param to the receiving wallet
* @param sft_id the token id to mint
* @param sft_amount the amount of tokens to mint
* @param ft_amount the amount of tokens to mint
* @param data some data to attach to the Tx
*/
function airdropMultiple(
address to,
uint256 sft_id,
uint256 sft_amount,
uint256 ft_amount,
bytes memory data
)
external
onlyOwner
{
if (sft_id != 0) {
(, uint256 max_supply, uint256 total_supply,,) = mint_token_data(sft_id);
if (total_supply + sft_amount > max_supply) {
revert ExceedingMaxSupply();
}
_mint(to, sft_id, sft_amount, data);
}
if (ft_amount > 0) transfer(address(this), to, ft_amount);
}
// Mint batch management - Token ID 0 is handled as the ERC20 token
// function mintBatch(
// address to,
// uint256[] memory ids,
// uint256[] memory amounts,
// bytes memory data
// )
// internal
// virtual
// whenNotPaused
// {
// (uint256[] memory _ids, uint256[] memory _amounts, bool id0, uint256 amount0) = separate_id0(ids, amounts);
// if (id0) super._mint(to, amount0);
// super._mintBatch(to, _ids, _amounts, data);
// }
// Burn batch management - Token ID 0 is handled as the ERC20 token
// function burnBatch(
// address account,
// uint256[] memory ids,
// uint256[] memory amounts
// )
// public
// virtual
// override
// whenNotPaused
// {
// // remove the id 0 from the ids and amounts lists and relay management to the parent
// (uint256[] memory _ids, uint256[] memory _amounts, bool id0, uint256 amount0) = separate_id0(ids, amounts);
// if (id0) super._burn(account, amount0);
// super.burnBatch(account, _ids, _amounts);
// }
function safe_mint(
address to,
uint256 token_id,
uint256 amount,
bool wl,
uint256 wlIndex,
bytes32[] calldata proof
)
internal
returns (uint256 price)
{
if (wl) {
price = super.elligible_claim(amount, token_id, wlIndex, proof);
} else {
price = super.elligible_mint(token_id, amount);
}
_mint(to, token_id, amount, "");
}
/*///////////////////////////////////////////////////////////////
HELPER FUNCTIONS
///////////////////////////////////////////////////////////////*/
function separate_id0(
uint256[] memory _ids,
uint256[] memory _amounts
)
internal
pure
returns (uint256[] memory ids, uint256[] memory amounts, bool id0, uint256 amount0)
{
uint256 index;
id0 = false;
for (uint256 i; i < _ids.length; ++i) {
if (_ids[i] == 0) {
index = i;
id0 = true;
}
}
if (id0) {
amount0 = _amounts[index];
_ids[index] = _ids[_ids.length - 1];
_amounts[index] = _amounts[_amounts.length - 1];
assembly {
mstore(_ids, sub(mload(_ids), 1))
mstore(_amounts, sub(mload(_amounts), 1))
}
}
ids = _ids;
amounts = _amounts;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/interfaces/IERC2981.sol";
import "./libraries/LibGsERC1155Storage.sol";
import "./interfaces/aGsERC1155.sol";
error Unburnable();
error SaleClosed();
error IndexOutOfRange();
error ListFull();
error InvalidRoyaltyShare();
error TransferFailed();
error ContractMintNotAllowed();
error ExceedingMaxSupply();
error ExceedingMintLimit();
error ZeroAddress();
error NotTokenOwnerOrAuthorised();
// For convenience to inheriting contracts
error InsufficentFunds();
/// @title A base class to create token IDs within an ERC1155 collection
/// @author www.geeks.solutions
/// @notice Each token definition carries a price, a max supply and certain mint rules
/// You can define as many tokens as you want. Once a token has been supplied the supply levels
/// and base prices are locked for this token ID.
/// You can also define pricing rules per token to have dynamic prices apply to tokens.
/// pricing rules are based on ascending triggering values and can be based on
/// token SUPPLY or wallet MINTS or a combination of both
/// @dev You can define default values that will be used when creating new tokens
/// You can use this contract as a parent for your collection and you can use
/// https://sections.geeks.solutions to get a ready frontend solution to run
/// your mint page that connects seamlessly to this contract
contract GsERC1155 is ERC1155("https://geeks.solutions"), aGsERC1155, IERC2981 {
using Arrays for uint256[];
using Arrays for address[];
/// @notice constructor to instantiate GsERC1155 contract
/// @dev The params provided to this function will define the default Token Data to use for Tokens creation
//
/// @param _baseURI the URI pointing to the base manifest routing to json for the contract and the tokens
/// (https://docs.opensea.io/docs/contract-level-metadata)
/// @param _recipient the default royalty recipient for tokens to be created
/// @param _royaltyShare the share of each sale (1% = 100) to distribute to the royalty recipient
constructor(address _owner_, string memory _baseURI, address _recipient, uint16 _royaltyShare) {
_setOwner(_owner_);
LibGsERC1155Storage.getStorage().baseURI = _baseURI;
LibGsERC1155Storage.getStorage().recipient = _recipient;
LibGsERC1155Storage.getStorage().percentage = _royaltyShare;
}
////////////////////////////////////////////////////////////////////////
/////////////////////////// GET METHODS ////////////////////////////////
////////////////////////////////////////////////////////////////////////
/**
* @dev Total amount of tokens minted with a given id.
*/
function totalSupply(uint256 id) public view virtual returns (uint256) {
(,, uint256 total_supply,,) = mint_token_data(id);
return total_supply;
}
function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
(uint256 balance,) = LibGsERC1155Storage.getBalanceDataFromPacked(
LibGsERC1155Storage.getStorage().packed_balances_mint[id][account]
);
return balance;
}
function contractURI() public view virtual returns (string memory) {
return string.concat(LibGsERC1155Storage.getStorage().baseURI, "contract.json");
}
/**
* @notice returns the metadata uri for a given id
*
* @param _token_id the token id to return metadata for
*/
function uri(uint256 _token_id)
public
view
virtual
override(ERC1155, aGsERC1155)
tokenExist(_token_id)
returns (string memory)
{
return aGsERC1155.uri(_token_id);
}
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC1155, IERC165) returns (bool) {
return (
interfaceId == type(IERC1155).interfaceId || interfaceId == type(IERC2981).interfaceId
|| super.supportsInterface(interfaceId)
);
}
/**
* @dev See {IERC1155-isApprovedForAll}.
*/
function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
return LibGsERC1155Storage.getStorage().operatorApprovals[account][operator];
}
////////////////////////////////////////////////////////////////////////
/////////////////////////// SET METHODS ////////////////////////////////
////////////////////////////////////////////////////////////////////////
/// @notice Convenience function to update the URI of the contract metadata
///
/// @param _baseURI the new URI to set for your contract/collection metadata base
function setMetaData(string calldata _baseURI, address _recipient, uint16 _royaltyShare) external onlyOwner {
LibGsERC1155Storage.getStorage().baseURI = _baseURI;
LibGsERC1155Storage.getStorage().recipient = _recipient;
LibGsERC1155Storage.getStorage().percentage = _royaltyShare;
}
function enableEditor(address editor) external onlyOwner {
LibGsERC1155Storage.getStorage()._editors[editor] = true;
}
function disableEditor(address editor) external onlyOwner {
LibGsERC1155Storage.getStorage()._editors[editor] = false;
}
function pause() external onlyOwner {
_pause();
}
function unpause() external onlyOwner {
_unpause();
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Emits an {ApprovalForAll} event.
*
* Requirements:
*
* - `operator` cannot be the zero address.
*/
function _setApprovalForAll(address owner_, address operator, bool approved) internal virtual override {
if (operator == address(0)) {
revert ERC1155InvalidOperator(address(0));
}
LibGsERC1155Storage.getStorage().operatorApprovals[owner_][operator] = approved;
emit ApprovalForAll(owner_, operator, approved);
}
////////////////////////////////////////////////////////////////////////
/////////////////////////// MODIFIERS //////////////////////////////////
////////////////////////////////////////////////////////////////////////
function isDynamicPricer() public pure virtual returns (bool) {
return false;
}
////////////////////////////////////////////////////////////////////////
/////////////////////////// TOKEN DATA /////////////////////////////////
////////////////////////////////////////////////////////////////////////
// IN FACET
// /// @notice Adds a new token without reading default token values
// /// Updates the token if it's been declared but not yet supplied. Rejects if the token id has been supplied
// already
// /// @dev can only be called by the owner.
// //
// /// @param _tokenId The ID to use for the new token to create
// /// @param _tokenPublicSale Allow the token to be publicly minted
// /// @param _tokenBurnable Allow the token to be burned by its holder
// /// @param _tokenMaxSupply The maximum Supply for this token
// /// @param _tokenMaxMintsPerWallet The maximum amount of token a wallet can hold to mint more
// /// @param price_in_wei The price of the token in wei (https://eth-converter.com/)
// function addNewToken(
// uint256 _tokenId,
// bool _tokenPublicSale,
// bool _tokenBurnable,
// uint32 _tokenMaxSupply,
// uint16 _tokenMaxMintsPerWallet,
// uint128 price_in_wei
// )
// external
// editors
// {
// if (_tokenId == 0) revert ReservedToken();
// {
// (
// uint256 base_price,
// bool public_sale,
// bool burnable,
// uint256 max_supply,
// uint256 total_supply,
// uint256 max_per_wallet,
// ,
// ) =
// LibGsERC1155Storage.getTokenDataFromPacked(LibGsERC1155Storage.getStorage().packed_token_data[_tokenId]);
// if (total_supply > 0) revert ExistingToken();
// base_price = price_in_wei;
// public_sale = _tokenPublicSale;
// burnable = _tokenBurnable;
// max_supply = _tokenMaxSupply;
// max_per_wallet = _tokenMaxMintsPerWallet;
// LibGsERC1155Storage.getStorage().packed_token_data[_tokenId] = LibGsERC1155Storage.packTokenFromRawData(
// base_price, public_sale, burnable, max_supply, total_supply, max_per_wallet, true, false
// );
// }
// }
// /// @notice Updates a given token Supply and price only if it hasn't been supplied
// //
// /// @param _tokenId The token ID to update
// /// @param _maxSupply The new maximum Supply to define for this token
// /// @param _maxPerWallet The maximum number of token a wallet can mint
// /// @param _price_in_wei The new price in GWEI (https://eth-converter.com/)
// function editTokenMeta(
// uint256 _tokenId,
// uint32 _maxSupply,
// uint16 _maxPerWallet,
// uint128 _price_in_wei
// )
// external
// editors
// {
// if (_tokenId == 0) revert ReservedToken();
// {
// (
// uint256 base_price,
// bool public_sale,
// bool burnable,
// uint256 max_supply,
// uint256 total_supply,
// uint256 max_per_wallet,
// ,
// ) =
// LibGsERC1155Storage.getTokenDataFromPacked(LibGsERC1155Storage.getStorage().packed_token_data[_tokenId]);
// if (total_supply > 0) revert TokenLockedBySupply();
// base_price = _price_in_wei;
// max_supply = _maxSupply;
// max_per_wallet = _maxPerWallet;
// LibGsERC1155Storage.getStorage().packed_token_data[_tokenId] = LibGsERC1155Storage.packTokenFromRawData(
// base_price, public_sale, burnable, max_supply, total_supply, max_per_wallet, true, false
// );
// }
// }
////////////////////////////////////////////////////////////////////////
/////////////////////////////// LOGIC //////////////////////////////////
////////////////////////////////////////////////////////////////////////
// IN FACET
// function getMintTotalPrice(
// address account,
// uint256 token_id,
// uint256 amount
// )
// external
// view
// virtual
// tokenExist(token_id)
// returns (uint256 price)
// {
// (uint256 base_price,,,,) = mint_token_data(token_id);
// return base_price * amount;
// }
function initFront(
address account,
uint256 token_id
)
public
view
virtual
tokenExist(token_id)
returns (ReturnData memory)
{
(
uint256 base_price,
bool public_sale,
bool burnable,
uint256 max_supply,
uint256 total_supply,
uint256 max_per_wallet,
,
bool use_dynamic_price
) = LibGsERC1155Storage.getTokenDataFromPacked(LibGsERC1155Storage.getStorage().packed_token_data[token_id]);
(uint256 balance, uint256 mints_count) = LibGsERC1155Storage.getBalanceDataFromPacked(
LibGsERC1155Storage.getStorage().packed_balances_mint[token_id][account]
);
return ReturnData({
base_price: base_price,
royalty_share: LibGsERC1155Storage.getStorage().percentage,
active: public_sale,
burnable: burnable,
max_supply: max_supply,
total_supply: total_supply,
max_per_wallet: max_per_wallet,
use_dynamic_price: use_dynamic_price,
royalty_recipient: LibGsERC1155Storage.getStorage().recipient,
uri: uri(token_id),
isPaused: _paused(),
mints_count: mints_count,
balance: balance
});
}
/// @notice Calculates the amount of royalty to send to the recipient based on the sale price
///
/// @param _token_id the Id of the token to compute royalty for
/// @param _salePrice the price the token was sold for
/// @return receiver
/// @return royaltyAmount
function royaltyInfo(
uint256 _token_id,
uint256 _salePrice
)
external
view
override
returns (address receiver, uint256 royaltyAmount)
{
return (
LibGsERC1155Storage.getStorage().recipient,
(_salePrice * LibGsERC1155Storage.getStorage().percentage) / 10_000
);
}
/**
* @notice Taken from ERC1155Burnable burn method to let the contract define the burnability of tokens
*
* @param account the address to burn from
* @param id the id of the token to burn
* @param amount the amount of tokens to burn
*/
function burn(address account, uint256 id, uint256 amount) public virtual whenNotPaused {
(,, bool burnable,,,,,) =
LibGsERC1155Storage.getTokenDataFromPacked(LibGsERC1155Storage.getStorage().packed_token_data[id]);
if (!burnable) revert Unburnable();
if (!(account == _msgSender() || isApprovedForAll(account, _msgSender()))) revert NotTokenOwnerOrAuthorised();
_burn(account, id, amount);
}
/**
* @notice Taken from ERC1155Burnable burnBatch method to let the contract define the burnability of tokens
*
* @param account the address to burn from
* @param ids an array of ids of tokens to burn
* @param amounts an array of amounts of tokens to burn
*
* ids and amounts must be of the same length
*/
function burnBatch(address account, uint256[] memory ids, uint256[] memory amounts) public virtual whenNotPaused {
for (uint256 i; i < ids.length; i++) {
(,, bool burnable,,,,,) =
LibGsERC1155Storage.getTokenDataFromPacked(LibGsERC1155Storage.getStorage().packed_token_data[ids[i]]);
if (!burnable) revert Unburnable();
}
if (!(account == _msgSender() || isApprovedForAll(account, _msgSender()))) revert NotTokenOwnerOrAuthorised();
_burnBatch(account, ids, amounts);
}
/// @notice Allow the owner to withdraw funds from the contract to the owner's wallet
function withdraw() external onlyOwner {
uint256 balance = address(this).balance;
(bool succ,) = payable(msg.sender).call{ value: balance }("");
if (!succ) revert TransferFailed();
}
/**
* @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`. Will mint (or burn) if `from`
* (or `to`) is the zero address.
*
* Emits a {TransferSingle} event if the arrays contain one element, and {TransferBatch} otherwise.
*
* Requirements:
*
* - If `to` refers to a smart contract, it must implement either {IERC1155Receiver-onERC1155Received}
* or {IERC1155Receiver-onERC1155BatchReceived} and return the acceptance magic value.
* - `ids` and `values` must have the same length.
*
* NOTE: The ERC-1155 acceptance check is not performed in this function. See {_updateWithAcceptanceCheck} instead.
*/
function _update(
address from,
address to,
uint256[] memory ids,
uint256[] memory values
)
internal
virtual
override
whenNotPaused
{
if (ids.length != values.length) {
revert ERC1155InvalidArrayLength(ids.length, values.length);
}
address operator = _msgSender();
for (uint256 i = 0; i < ids.length; ++i) {
uint256 id = ids.unsafeMemoryAccess(i);
uint256 value = values.unsafeMemoryAccess(i);
if (from != address(0)) {
(uint256 fromBalance, uint256 mints_count) = LibGsERC1155Storage.getBalanceDataFromPacked(
LibGsERC1155Storage.getStorage().packed_balances_mint[id][from]
);
if (fromBalance < value) {
revert ERC1155InsufficientBalance(from, fromBalance, value, id);
}
unchecked {
// Overflow not possible: value <= fromBalance
LibGsERC1155Storage.getStorage().packed_balances_mint[id][from] =
LibGsERC1155Storage.packBalanceData(fromBalance - value, mints_count);
}
}
if (to != address(0)) {
(uint256 balance, uint256 mints_count) = LibGsERC1155Storage.getBalanceDataFromPacked(
LibGsERC1155Storage.getStorage().packed_balances_mint[id][to]
);
balance += value;
if (from == address(0)) {
unchecked {
mints_count += value;
(,, uint256 total_supply,,) = mint_token_data(ids[i]);
LibGsERC1155Storage.getStorage().packed_token_data[ids[i]] = LibGsERC1155Storage
.updatePackedTokenData(
LibGsERC1155Storage.getStorage().packed_token_data[ids[i]],
total_supply + value,
LibGsERC1155Storage.TokenField.total_supply
);
}
}
LibGsERC1155Storage.getStorage().packed_balances_mint[id][to] =
LibGsERC1155Storage.packBalanceData(balance, mints_count);
}
/**
* GS Specific Logic
*/
if (to == address(0)) {
(,, uint256 total_supply,,) = mint_token_data(ids[i]);
if (value > total_supply) revert ExceedingMaxSupply();
LibGsERC1155Storage.getStorage().packed_token_data[ids[i]] = LibGsERC1155Storage.updatePackedTokenData(
LibGsERC1155Storage.getStorage().packed_token_data[ids[i]],
total_supply - value,
LibGsERC1155Storage.TokenField.total_supply
);
}
}
if (ids.length == 1) {
uint256 id = ids.unsafeMemoryAccess(0);
uint256 value = values.unsafeMemoryAccess(0);
emit TransferSingle(operator, from, to, id, value);
} else {
emit TransferBatch(operator, from, to, ids, values);
}
}
//************ SUPPORT FUNCTIONS ************//
function elligible_mint(uint256 id, uint256 amount) internal view virtual returns (uint256 dynamicPrice) {
(uint256 base_price, uint256 max_supply, uint256 total_supply, uint256 max_per_wallet, bool public_sale) =
mint_token_data(id);
return elligible_mint(id, amount, total_supply, max_supply, false, max_per_wallet, base_price, public_sale);
}
// Called from child contract in charge to relay checks, enforcing active sales
function elligible_mint(
uint256 _token_id,
uint256 _amount,
bool _load_dynamic_price,
uint256 _wallet_max,
uint256 _default_price
)
internal
view
virtual
returns (uint256 dynamicPrice)
{
(, uint256 max_supply, uint256 total_supply,,) = mint_token_data(_token_id);
return elligible_mint(
_token_id, _amount, total_supply, max_supply, _load_dynamic_price, _wallet_max, _default_price, true
);
}
/// @notice Verifies the elligibility for a mint by a given wallet
///
/// @param _token_id The id of the token to mint
/// @param _amount The amount of tokens to mint
/// @param _total_supply The amount of tokens minted
/// @param _max_supply The maximum amount of tokens available
/// @param _load_dynamic_price whether or not it is necessary to compute a dynamic price or simply return the
/// standard price
/// @param _wallet_max the max_per_wallet condition to check (could come from a whitelist entry)
/// @param _default_price the price to apply in case no dynamic price were found or it is disabled
function elligible_mint(
uint256 _token_id,
uint256 _amount,
uint256 _total_supply,
uint256 _max_supply,
bool _load_dynamic_price,
uint256 _wallet_max,
uint256 _default_price,
bool public_sale
)
internal
view
virtual
tokenExist(_token_id)
returns (uint256 dynamicPrice)
{
if (!public_sale) revert SaleClosed();
if (tx.origin != _msgSender()) revert ContractMintNotAllowed();
if (_total_supply + _amount > _max_supply) revert ExceedingMaxSupply();
(, uint256 mints_count) = LibGsERC1155Storage.getBalanceDataFromPacked(
LibGsERC1155Storage.getStorage().packed_balances_mint[_token_id][_msgSender()]
);
if (mints_count + _amount > _wallet_max) revert ExceedingMintLimit();
return _default_price * _amount;
}
function extractTotalPrice(
uint256 _token_id,
uint256 _base_price,
uint256 total_supply,
uint256 mintsCount,
uint256 amount
)
internal
view
virtual
returns (uint256)
{ }
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "./GsERC1155.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import "./libraries/LibGsMerkleStorage.sol";
error WrongRoot();
error InactiveWhitelist();
error InvalidProof();
/// @notice This contract adds a Merkle Tree based Whitelist to the GsERC1155 contract.
/// Multiple whitelists can exist for a given token ID
/// @author www.geeks.solutions
/// @dev You can use this contract as a parent for your collection and you can use
/// https://sections.geeks.solutions to get a ready frontend solution to run
/// your mint page that connects seamlessly to this contract
contract GsERC1155Merkle is GsERC1155 {
/// @notice Builds a collection that can be whitelist gated
constructor(
address _owner_,
string memory _contractURI,
address _recipient,
uint16 _royaltyShare
)
GsERC1155(_owner_, _contractURI, _recipient, _royaltyShare)
{ }
function whitelist_datas(uint256 tokenId) internal view returns (LibGsMerkleStorage.WhitelistData[] storage) {
return LibGsMerkleStorage.getStorage().whitelist_datas[tokenId];
}
modifier whitelistExist(uint256 whitelistIndex, uint256 tokenId) {
_whiteListExist(whitelistIndex, tokenId);
_;
}
function _whiteListExist(uint256 whitelistIndex, uint256 tokenId) internal view virtual {
if (whitelistIndex >= whitelist_datas(tokenId).length) {
revert IndexOutOfRange();
}
}
/// @notice Adds a new whitelist for a given token ID. The token must be defined. Whitelist index starts at 0
///
/// @param _token_id The Id of the token to create a whitelist for
/// @param _wl_price_in_wei The price in GWEI (https://eth-converter.com/) of the token when minting with this
/// whitelist
/// @param _merkle_root The merkle root to grant access to the whitelist
/// @param _wl_max_per_wallet The maximum amount of tokens one can hold to mint using this whitelist
/// @param _active Whether or not this whitelist is active and can be used to mint the token
/// @param _use_dynamic_price a boolean to indicate if we should use the dynamic price or not
/// @return whitelist_index
function addNewWhitelist(
uint256 _token_id,
uint128 _wl_price_in_wei,
bytes32 _merkle_root,
uint8 _wl_max_per_wallet,
bool _active,
bool _use_dynamic_price
)
external
editors
tokenExist(_token_id)
returns (uint256)
{
whitelist_datas(_token_id).push(
LibGsMerkleStorage.WhitelistData({
token_price: _wl_price_in_wei,
active: _active,
merkle_root: _merkle_root,
max_per_wallet: _wl_max_per_wallet,
use_dynamic_price: _use_dynamic_price
})
);
return whitelist_datas(_token_id).length - 1;
}
/// @notice Fully edit the whitelist for a token id at a given index
///
/// @param _token_id The id of the token to update the whitelist for
/// @param _whitelist_index The index of the whitelist to edit
/// @param _merkle_root The new merkle root to store for this whitelist
/// @param _active Whether or not this whitelist is active and can be used to mint the token
/// @param _wl_price_in_wei the price in GWEI (https://eth-converter.com/)
/// @param _wl_max_per_wallet The maximum amount of tokens one can hold to mint using this whitelist
/// @param _use_dynamic_price a boolean to indicate if we should use the dynamic price or not
function editWhitelistFull(
uint256 _whitelist_index,
uint256 _token_id,
uint128 _wl_price_in_wei,
bytes32 _merkle_root,
uint8 _wl_max_per_wallet,
bool _active,
bool _use_dynamic_price
)
external
editors
whitelistExist(_whitelist_index, _token_id)
{
if (_merkle_root == "") revert WrongRoot();
LibGsMerkleStorage.WhitelistData memory _whitelist = whitelist_datas(_token_id)[_whitelist_index];
_whitelist.merkle_root = _merkle_root;
_whitelist.active = _active;
_whitelist.token_price = _wl_price_in_wei;
_whitelist.max_per_wallet = _wl_max_per_wallet;
_whitelist.use_dynamic_price = _use_dynamic_price;
whitelist_datas(_token_id)[_whitelist_index] = _whitelist;
}
/// @notice Gets a whitelist for token id at a specific index
///
/// @param _token_id the id of the token to get the whitelist for
/// @param _whitelist_index the index of the whitelist to get
/// @return whitelist
function getWhiteListAtIndex(
uint256 _token_id,
uint256 _whitelist_index
)
public
view
whitelistExist(_whitelist_index, _token_id)
returns (LibGsMerkleStorage.WhitelistData memory)
{
return whitelist_datas(_token_id)[_whitelist_index];
}
/// @notice Returns the number of whitelists defined for a given token id
///
/// @param _token_id The id of the token to get the count for
/// @return length
function getWhiteListLengthForToken(uint256 _token_id) external view returns (uint256) {
return whitelist_datas(_token_id).length;
}
/// @notice Returns the total price for a mint based on a token ID the amount of token,
/// the whitelist and the wallet requesting it. Price can be dynamic or static,
/// whitelist can be set to override dynamic pricing
///
/// @param account the address to compute the price for
/// @param token_id the token id to get the price for
/// @param whitelist_index the whitelist to check the price for
/// @param amount the amount of token to compute the price for
///
/// @return price
function getMintTotalPrice(
address account,
uint256 token_id,
uint256 whitelist_index,
uint256 amount
)
external
view
whitelistExist(whitelist_index, token_id)
returns (uint256 price)
{
LibGsMerkleStorage.WhitelistData memory _whitelist = getWhiteListAtIndex(token_id, whitelist_index);
if (super.isDynamicPricer() && _whitelist.use_dynamic_price) {
(,, uint256 total_supply,,) = mint_token_data(token_id);
return super.extractTotalPrice(
token_id,
_whitelist.token_price,
total_supply,
0, // TODO udate token_datas(token_id).mints_count[account],
amount
);
}
return _whitelist.token_price * amount;
}
function initFront(
address account,
uint256 token_id,
uint256 whitelist_index
)
public
view
whitelistExist(whitelist_index, token_id)
returns (ReturnData memory rd)
{
rd = super.initFront(account, token_id);
// whitelist overriding
LibGsMerkleStorage.WhitelistData memory wl = getWhiteListAtIndex(token_id, whitelist_index);
rd.max_per_wallet = wl.max_per_wallet;
rd.base_price = wl.token_price;
rd.active = wl.active;
rd.use_dynamic_price = wl.use_dynamic_price;
}
//************ SUPPORT FUNCTIONS ************//
/**
* @notice This function checks for the elligibility for a user to mint a given amount of token id based on the
* proof
* of a given whitelist index
* @param _amount the amount of token to check mint elligibility for
* @param _token_id the token id to check
* @param _whitelist_index the whitelist index to check for
* @param _merkleProof the proof of the Merkle tree provided by the user
* @return total
*/
function elligible_claim(
uint256 _amount,
uint256 _token_id,
uint256 _whitelist_index,
bytes32[] calldata _merkleProof
)
internal
view
returns (uint256 total)
{
LibGsMerkleStorage.WhitelistData storage whitelist = whitelist_datas(_token_id)[_whitelist_index];
if (!whitelist.active) revert InactiveWhitelist();
if (!MerkleProof.verifyCalldata(_merkleProof, whitelist.merkle_root, keccak256(abi.encodePacked(msg.sender)))) {
revert InvalidProof();
}
return super.elligible_mint(
_token_id, _amount, whitelist.use_dynamic_price, whitelist.max_per_wallet, whitelist.token_price
);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/*
* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
*/
interface IDiamondCut {
enum FacetCutAction {
Add,
Replace,
Remove
}
// Add=0, Replace=1, Remove=2
struct FacetCut {
address facetAddress;
FacetCutAction action;
bytes4[] functionSelectors;
}
/// @notice Add/replace/remove any number of functions and optionally execute
/// a function with delegatecall
/// @param _diamondCut Contains the facet addresses and function selectors
/// @param _init The address of the contract or facet to execute _calldata
/// @param _calldata A function call, including function selector and arguments
/// _calldata is executed with delegatecall on _init
function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;
event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (token/ERC1155/IERC1155.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC1155 compliant contract, as defined in the
* https://eips.ethereum.org/EIPS/eip-1155[EIP].
*/
interface IERC1155 is IERC165 {
/**
* @dev Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`.
*/
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
/**
* @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
* transfers.
*/
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
/**
* @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
* `approved`.
*/
event ApprovalForAll(address indexed account, address indexed 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}.
*/
event URI(string value, uint256 indexed id);
/**
* @dev Returns the value of tokens of token type `id` owned by `account`.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function balanceOf(address account, uint256 id) external view returns (uint256);
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/
function balanceOfBatch(
address[] calldata accounts,
uint256[] calldata ids
) external view returns (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 caller.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
*
* See {setApprovalForAll}.
*/
function isApprovedForAll(address account, address operator) external view returns (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.
*/
function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes calldata 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.
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/extensions/IERC1155MetadataURI.sol)
pragma solidity ^0.8.20;
import {IERC1155} from "../IERC1155.sol";
/**
* @dev Interface of the optional ERC1155MetadataExtension interface, as defined
* in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
*/
interface IERC1155MetadataURI is IERC1155 {
/**
* @dev Returns the URI for token type `id`.
*
* If the `\{id\}` substring is present in the URI, it must be replaced by
* clients with the actual token type ID.
*/
function uri(uint256 id) external view returns (string memory);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/IERC1155Receiver.sol)
pragma solidity ^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.
*/
interface IERC1155Receiver is IERC165 {
/**
* @dev Handles the receipt of a single ERC1155 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
*/
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
/**
* @dev Handles the receipt of a multiple ERC1155 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
*/
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import { IERC173Internal } from './IERC173Internal.sol';
/**
* @title Contract ownership standard interface
* @dev see https://eips.ethereum.org/EIPS/eip-173
*/
interface IERC173 is IERC173Internal {
/**
* @notice get the ERC173 contract owner
* @return contract owner
*/
function owner() external view returns (address);
/**
* @notice transfer contract ownership to new account
* @param account address of new owner
*/
function transferOwnership(address account) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
/**
* @title Partial ERC173 interface needed by internal functions
*/
interface IERC173Internal {
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
}
pragma solidity >=0.5.0;
interface IERC20 {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC2981.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";
/**
* @dev Interface for the NFT Royalty Standard.
*
* A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
* support for royalty payments across all NFT marketplaces and ecosystem participants.
*/
interface IERC2981 is IERC165 {
/**
* @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
* exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
*/
function royaltyInfo(
uint256 tokenId,
uint256 salePrice
) external view returns (address receiver, uint256 royaltyAmount);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import { IERC173 } from '../../interfaces/IERC173.sol';
import { IOwnableInternal } from './IOwnableInternal.sol';
interface IOwnable is IOwnableInternal, IERC173 {}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import { IERC173Internal } from '../../interfaces/IERC173Internal.sol';
interface IOwnableInternal is IERC173Internal {
error Ownable__NotOwner();
error Ownable__NotTransitiveOwner();
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import { IPausableInternal } from './IPausableInternal.sol';
interface IPausable is IPausableInternal {
/**
* @notice query whether contract is paused
* @return status whether contract is paused
*/
function paused() external view returns (bool status);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
interface IPausableInternal {
error Pausable__Paused();
error Pausable__NotPaused();
event Paused(address account);
event Unpaused(address account);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
import './pool/IUniswapV3PoolImmutables.sol';
import './pool/IUniswapV3PoolState.sol';
import './pool/IUniswapV3PoolDerivedState.sol';
import './pool/IUniswapV3PoolActions.sol';
import './pool/IUniswapV3PoolOwnerActions.sol';
import './pool/IUniswapV3PoolEvents.sol';
/// @title The interface for a Uniswap V3 Pool
/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform
/// to the ERC20 specification
/// @dev The pool interface is broken up into many smaller pieces
interface IUniswapV3Pool is
IUniswapV3PoolImmutables,
IUniswapV3PoolState,
IUniswapV3PoolDerivedState,
IUniswapV3PoolActions,
IUniswapV3PoolOwnerActions,
IUniswapV3PoolEvents
{
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Permissionless pool actions
/// @notice Contains pool methods that can be called by anyone
interface IUniswapV3PoolActions {
/// @notice Sets the initial price for the pool
/// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value
/// @param sqrtPriceX96 the initial sqrt price of the pool as a Q64.96
function initialize(uint160 sqrtPriceX96) external;
/// @notice Adds liquidity for the given recipient/tickLower/tickUpper position
/// @dev The caller of this method receives a callback in the form of IUniswapV3MintCallback#uniswapV3MintCallback
/// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends
/// on tickLower, tickUpper, the amount of liquidity, and the current price.
/// @param recipient The address for which the liquidity will be created
/// @param tickLower The lower tick of the position in which to add liquidity
/// @param tickUpper The upper tick of the position in which to add liquidity
/// @param amount The amount of liquidity to mint
/// @param data Any data that should be passed through to the callback
/// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback
/// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback
function mint(
address recipient,
int24 tickLower,
int24 tickUpper,
uint128 amount,
bytes calldata data
) external returns (uint256 amount0, uint256 amount1);
/// @notice Collects tokens owed to a position
/// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity.
/// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or
/// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the
/// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity.
/// @param recipient The address which should receive the fees collected
/// @param tickLower The lower tick of the position for which to collect fees
/// @param tickUpper The upper tick of the position for which to collect fees
/// @param amount0Requested How much token0 should be withdrawn from the fees owed
/// @param amount1Requested How much token1 should be withdrawn from the fees owed
/// @return amount0 The amount of fees collected in token0
/// @return amount1 The amount of fees collected in token1
function collect(
address recipient,
int24 tickLower,
int24 tickUpper,
uint128 amount0Requested,
uint128 amount1Requested
) external returns (uint128 amount0, uint128 amount1);
/// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position
/// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0
/// @dev Fees must be collected separately via a call to #collect
/// @param tickLower The lower tick of the position for which to burn liquidity
/// @param tickUpper The upper tick of the position for which to burn liquidity
/// @param amount How much liquidity to burn
/// @return amount0 The amount of token0 sent to the recipient
/// @return amount1 The amount of token1 sent to the recipient
function burn(
int24 tickLower,
int24 tickUpper,
uint128 amount
) external returns (uint256 amount0, uint256 amount1);
/// @notice Swap token0 for token1, or token1 for token0
/// @dev The caller of this method receives a callback in the form of IUniswapV3SwapCallback#uniswapV3SwapCallback
/// @param recipient The address to receive the output of the swap
/// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0
/// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative)
/// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this
/// value after the swap. If one for zero, the price cannot be greater than this value after the swap
/// @param data Any data to be passed through to the callback
/// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive
/// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive
function swap(
address recipient,
bool zeroForOne,
int256 amountSpecified,
uint160 sqrtPriceLimitX96,
bytes calldata data
) external returns (int256 amount0, int256 amount1);
/// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback
/// @dev The caller of this method receives a callback in the form of IUniswapV3FlashCallback#uniswapV3FlashCallback
/// @dev Can be used to donate underlying tokens pro-rata to currently in-range liquidity providers by calling
/// with 0 amount{0,1} and sending the donation amount(s) from the callback
/// @param recipient The address which will receive the token0 and token1 amounts
/// @param amount0 The amount of token0 to send
/// @param amount1 The amount of token1 to send
/// @param data Any data to be passed through to the callback
function flash(
address recipient,
uint256 amount0,
uint256 amount1,
bytes calldata data
) external;
/// @notice Increase the maximum number of price and liquidity observations that this pool will store
/// @dev This method is no-op if the pool already has an observationCardinalityNext greater than or equal to
/// the input observationCardinalityNext.
/// @param observationCardinalityNext The desired minimum number of observations for the pool to store
function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external;
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Pool state that is not stored
/// @notice Contains view functions to provide information about the pool that is computed rather than stored on the
/// blockchain. The functions here may have variable gas costs.
interface IUniswapV3PoolDerivedState {
/// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp
/// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing
/// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick,
/// you must call it with secondsAgos = [3600, 0].
/// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in
/// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio.
/// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned
/// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp
/// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block
/// timestamp
function observe(uint32[] calldata secondsAgos)
external
view
returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);
/// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range
/// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed.
/// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first
/// snapshot is taken and the second snapshot is taken.
/// @param tickLower The lower tick of the range
/// @param tickUpper The upper tick of the range
/// @return tickCumulativeInside The snapshot of the tick accumulator for the range
/// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range
/// @return secondsInside The snapshot of seconds per liquidity for the range
function snapshotCumulativesInside(int24 tickLower, int24 tickUpper)
external
view
returns (
int56 tickCumulativeInside,
uint160 secondsPerLiquidityInsideX128,
uint32 secondsInside
);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Events emitted by a pool
/// @notice Contains all events emitted by the pool
interface IUniswapV3PoolEvents {
/// @notice Emitted exactly once by a pool when #initialize is first called on the pool
/// @dev Mint/Burn/Swap cannot be emitted by the pool before Initialize
/// @param sqrtPriceX96 The initial sqrt price of the pool, as a Q64.96
/// @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool
event Initialize(uint160 sqrtPriceX96, int24 tick);
/// @notice Emitted when liquidity is minted for a given position
/// @param sender The address that minted the liquidity
/// @param owner The owner of the position and recipient of any minted liquidity
/// @param tickLower The lower tick of the position
/// @param tickUpper The upper tick of the position
/// @param amount The amount of liquidity minted to the position range
/// @param amount0 How much token0 was required for the minted liquidity
/// @param amount1 How much token1 was required for the minted liquidity
event Mint(
address sender,
address indexed owner,
int24 indexed tickLower,
int24 indexed tickUpper,
uint128 amount,
uint256 amount0,
uint256 amount1
);
/// @notice Emitted when fees are collected by the owner of a position
/// @dev Collect events may be emitted with zero amount0 and amount1 when the caller chooses not to collect fees
/// @param owner The owner of the position for which fees are collected
/// @param tickLower The lower tick of the position
/// @param tickUpper The upper tick of the position
/// @param amount0 The amount of token0 fees collected
/// @param amount1 The amount of token1 fees collected
event Collect(
address indexed owner,
address recipient,
int24 indexed tickLower,
int24 indexed tickUpper,
uint128 amount0,
uint128 amount1
);
/// @notice Emitted when a position's liquidity is removed
/// @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect
/// @param owner The owner of the position for which liquidity is removed
/// @param tickLower The lower tick of the position
/// @param tickUpper The upper tick of the position
/// @param amount The amount of liquidity to remove
/// @param amount0 The amount of token0 withdrawn
/// @param amount1 The amount of token1 withdrawn
event Burn(
address indexed owner,
int24 indexed tickLower,
int24 indexed tickUpper,
uint128 amount,
uint256 amount0,
uint256 amount1
);
/// @notice Emitted by the pool for any swaps between token0 and token1
/// @param sender The address that initiated the swap call, and that received the callback
/// @param recipient The address that received the output of the swap
/// @param amount0 The delta of the token0 balance of the pool
/// @param amount1 The delta of the token1 balance of the pool
/// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96
/// @param liquidity The liquidity of the pool after the swap
/// @param tick The log base 1.0001 of price of the pool after the swap
event Swap(
address indexed sender,
address indexed recipient,
int256 amount0,
int256 amount1,
uint160 sqrtPriceX96,
uint128 liquidity,
int24 tick
);
/// @notice Emitted by the pool for any flashes of token0/token1
/// @param sender The address that initiated the swap call, and that received the callback
/// @param recipient The address that received the tokens from flash
/// @param amount0 The amount of token0 that was flashed
/// @param amount1 The amount of token1 that was flashed
/// @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee
/// @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee
event Flash(
address indexed sender,
address indexed recipient,
uint256 amount0,
uint256 amount1,
uint256 paid0,
uint256 paid1
);
/// @notice Emitted by the pool for increases to the number of observations that can be stored
/// @dev observationCardinalityNext is not the observation cardinality until an observation is written at the index
/// just before a mint/swap/burn.
/// @param observationCardinalityNextOld The previous value of the next observation cardinality
/// @param observationCardinalityNextNew The updated value of the next observation cardinality
event IncreaseObservationCardinalityNext(
uint16 observationCardinalityNextOld,
uint16 observationCardinalityNextNew
);
/// @notice Emitted when the protocol fee is changed by the pool
/// @param feeProtocol0Old The previous value of the token0 protocol fee
/// @param feeProtocol1Old The previous value of the token1 protocol fee
/// @param feeProtocol0New The updated value of the token0 protocol fee
/// @param feeProtocol1New The updated value of the token1 protocol fee
event SetFeeProtocol(uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New);
/// @notice Emitted when the collected protocol fees are withdrawn by the factory owner
/// @param sender The address that collects the protocol fees
/// @param recipient The address that receives the collected protocol fees
/// @param amount0 The amount of token0 protocol fees that is withdrawn
/// @param amount0 The amount of token1 protocol fees that is withdrawn
event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Pool state that never changes
/// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values
interface IUniswapV3PoolImmutables {
/// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface
/// @return The contract address
function factory() external view returns (address);
/// @notice The first of the two tokens of the pool, sorted by address
/// @return The token contract address
function token0() external view returns (address);
/// @notice The second of the two tokens of the pool, sorted by address
/// @return The token contract address
function token1() external view returns (address);
/// @notice The pool's fee in hundredths of a bip, i.e. 1e-6
/// @return The fee
function fee() external view returns (uint24);
/// @notice The pool tick spacing
/// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive
/// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ...
/// This value is an int24 to avoid casting even though it is always positive.
/// @return The tick spacing
function tickSpacing() external view returns (int24);
/// @notice The maximum amount of position liquidity that can use any tick in the range
/// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and
/// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool
/// @return The max amount of liquidity per tick
function maxLiquidityPerTick() external view returns (uint128);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Permissioned pool actions
/// @notice Contains pool methods that may only be called by the factory owner
interface IUniswapV3PoolOwnerActions {
/// @notice Set the denominator of the protocol's % share of the fees
/// @param feeProtocol0 new protocol fee for token0 of the pool
/// @param feeProtocol1 new protocol fee for token1 of the pool
function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external;
/// @notice Collect the protocol fee accrued to the pool
/// @param recipient The address to which collected protocol fees should be sent
/// @param amount0Requested The maximum amount of token0 to send, can be 0 to collect fees in only token1
/// @param amount1Requested The maximum amount of token1 to send, can be 0 to collect fees in only token0
/// @return amount0 The protocol fee collected in token0
/// @return amount1 The protocol fee collected in token1
function collectProtocol(
address recipient,
uint128 amount0Requested,
uint128 amount1Requested
) external returns (uint128 amount0, uint128 amount1);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Pool state that can change
/// @notice These methods compose the pool's state, and can change with any frequency including multiple times
/// per transaction
interface IUniswapV3PoolState {
/// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas
/// when accessed externally.
/// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value
/// tick The current tick of the pool, i.e. according to the last tick transition that was run.
/// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick
/// boundary.
/// observationIndex The index of the last oracle observation that was written,
/// observationCardinality The current maximum number of observations stored in the pool,
/// observationCardinalityNext The next maximum number of observations, to be updated when the observation.
/// feeProtocol The protocol fee for both tokens of the pool.
/// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0
/// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee.
/// unlocked Whether the pool is currently locked to reentrancy
function slot0()
external
view
returns (
uint160 sqrtPriceX96,
int24 tick,
uint16 observationIndex,
uint16 observationCardinality,
uint16 observationCardinalityNext,
uint8 feeProtocol,
bool unlocked
);
/// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool
/// @dev This value can overflow the uint256
function feeGrowthGlobal0X128() external view returns (uint256);
/// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool
/// @dev This value can overflow the uint256
function feeGrowthGlobal1X128() external view returns (uint256);
/// @notice The amounts of token0 and token1 that are owed to the protocol
/// @dev Protocol fees will never exceed uint128 max in either token
function protocolFees() external view returns (uint128 token0, uint128 token1);
/// @notice The currently in range liquidity available to the pool
/// @dev This value has no relationship to the total liquidity across all ticks
function liquidity() external view returns (uint128);
/// @notice Look up information about a specific tick in the pool
/// @param tick The tick to look up
/// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or
/// tick upper,
/// liquidityNet how much liquidity changes when the pool price crosses the tick,
/// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,
/// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,
/// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick
/// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick,
/// secondsOutside the seconds spent on the other side of the tick from the current tick,
/// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false.
/// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.
/// In addition, these values are only relative and must be used only in comparison to previous snapshots for
/// a specific position.
function ticks(int24 tick)
external
view
returns (
uint128 liquidityGross,
int128 liquidityNet,
uint256 feeGrowthOutside0X128,
uint256 feeGrowthOutside1X128,
int56 tickCumulativeOutside,
uint160 secondsPerLiquidityOutsideX128,
uint32 secondsOutside,
bool initialized
);
/// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information
function tickBitmap(int16 wordPosition) external view returns (uint256);
/// @notice Returns the information about a position by the position's key
/// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper
/// @return _liquidity The amount of liquidity in the position,
/// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke,
/// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke,
/// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke,
/// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke
function positions(bytes32 key)
external
view
returns (
uint128 _liquidity,
uint256 feeGrowthInside0LastX128,
uint256 feeGrowthInside1LastX128,
uint128 tokensOwed0,
uint128 tokensOwed1
);
/// @notice Returns data about a specific observation index
/// @param index The element of the observations array to fetch
/// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time
/// ago, rather than at a specific index in the array.
/// @return blockTimestamp The timestamp of the observation,
/// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp,
/// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp,
/// Returns initialized whether the observation has been initialized and the values are safe to use
function observations(uint256 index)
external
view
returns (
uint32 blockTimestamp,
int56 tickCumulative,
uint160 secondsPerLiquidityCumulativeX128,
bool initialized
);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
*/
import { IDiamondCut } from "../interfaces/IDiamondCut.sol";
// Remember to add the loupe functions from DiamondLoupeFacet to the diamond.
// The loupe functions are required by the EIP2535 Diamonds standard
error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata);
library LibDiamond {
bytes32 constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage");
struct FacetAddressAndPosition {
address facetAddress;
uint96 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array
}
struct FacetFunctionSelectors {
bytes4[] functionSelectors;
uint256 facetAddressPosition; // position of facetAddress in facetAddresses array
}
struct DiamondStorage {
// maps function selector to the facet address and
// the position of the selector in the facetFunctionSelectors.selectors array
mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition;
// maps facet addresses to function selectors
mapping(address => FacetFunctionSelectors) facetFunctionSelectors;
// facet addresses
address[] facetAddresses;
// Used to query if a contract implements an interface.
// Used to implement ERC-165.
mapping(bytes4 => bool) supportedInterfaces;
// owner of the contract
address contractOwner;
}
function diamondStorage() internal pure returns (DiamondStorage storage ds) {
bytes32 position = DIAMOND_STORAGE_POSITION;
assembly {
ds.slot := position
}
}
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function setContractOwner(address _newOwner) internal {
DiamondStorage storage ds = diamondStorage();
address previousOwner = ds.contractOwner;
ds.contractOwner = _newOwner;
emit OwnershipTransferred(previousOwner, _newOwner);
}
function contractOwner() internal view returns (address contractOwner_) {
contractOwner_ = diamondStorage().contractOwner;
}
function enforceIsContractOwner() internal view {
require(msg.sender == diamondStorage().contractOwner, "LibDiamond: Must be contract owner");
}
event DiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata);
// Internal function version of diamondCut
function diamondCut(IDiamondCut.FacetCut[] memory _diamondCut, address _init, bytes memory _calldata) internal {
for (uint256 facetIndex; facetIndex < _diamondCut.length; facetIndex++) {
IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action;
if (action == IDiamondCut.FacetCutAction.Add) {
addFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
} else if (action == IDiamondCut.FacetCutAction.Replace) {
replaceFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
} else if (action == IDiamondCut.FacetCutAction.Remove) {
removeFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
} else {
revert("LibDiamondCut: Incorrect FacetCutAction");
}
}
emit DiamondCut(_diamondCut, _init, _calldata);
initializeDiamondCut(_init, _calldata);
}
function addFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut");
DiamondStorage storage ds = diamondStorage();
require(_facetAddress != address(0), "LibDiamondCut: Add facet can't be address(0)");
uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length);
// add new facet address if it does not exist
if (selectorPosition == 0) {
addFacet(ds, _facetAddress);
}
for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) {
bytes4 selector = _functionSelectors[selectorIndex];
address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
require(oldFacetAddress == address(0), "LibDiamondCut: Can't add function that already exists");
addFunction(ds, selector, selectorPosition, _facetAddress);
selectorPosition++;
}
}
function replaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut");
DiamondStorage storage ds = diamondStorage();
require(_facetAddress != address(0), "LibDiamondCut: Add facet can't be address(0)");
uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length);
// add new facet address if it does not exist
if (selectorPosition == 0) {
addFacet(ds, _facetAddress);
}
for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) {
bytes4 selector = _functionSelectors[selectorIndex];
address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
require(oldFacetAddress != _facetAddress, "LibDiamondCut: Can't replace function with same function");
removeFunction(ds, oldFacetAddress, selector);
addFunction(ds, selector, selectorPosition, _facetAddress);
selectorPosition++;
}
}
function removeFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut");
DiamondStorage storage ds = diamondStorage();
// if function does not exist then do nothing and return
require(_facetAddress == address(0), "LibDiamondCut: Remove facet address must be address(0)");
for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) {
bytes4 selector = _functionSelectors[selectorIndex];
address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
removeFunction(ds, oldFacetAddress, selector);
}
}
function addFacet(DiamondStorage storage ds, address _facetAddress) internal {
enforceHasContractCode(_facetAddress, "LibDiamondCut: New facet has no code");
ds.facetFunctionSelectors[_facetAddress].facetAddressPosition = ds.facetAddresses.length;
ds.facetAddresses.push(_facetAddress);
}
function addFunction(
DiamondStorage storage ds,
bytes4 _selector,
uint96 _selectorPosition,
address _facetAddress
)
internal
{
ds.selectorToFacetAndPosition[_selector].functionSelectorPosition = _selectorPosition;
ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(_selector);
ds.selectorToFacetAndPosition[_selector].facetAddress = _facetAddress;
}
function removeFunction(DiamondStorage storage ds, address _facetAddress, bytes4 _selector) internal {
require(_facetAddress != address(0), "LibDiamondCut: Can't remove function that doesn't exist");
// an immutable function is a function defined directly in a diamond
require(_facetAddress != address(this), "LibDiamondCut: Can't remove immutable function");
// replace selector with last selector, then delete last selector
uint256 selectorPosition = ds.selectorToFacetAndPosition[_selector].functionSelectorPosition;
uint256 lastSelectorPosition = ds.facetFunctionSelectors[_facetAddress].functionSelectors.length - 1;
// if not the same then replace _selector with lastSelector
if (selectorPosition != lastSelectorPosition) {
bytes4 lastSelector = ds.facetFunctionSelectors[_facetAddress].functionSelectors[lastSelectorPosition];
ds.facetFunctionSelectors[_facetAddress].functionSelectors[selectorPosition] = lastSelector;
ds.selectorToFacetAndPosition[lastSelector].functionSelectorPosition = uint96(selectorPosition);
}
// delete the last selector
ds.facetFunctionSelectors[_facetAddress].functionSelectors.pop();
delete ds.selectorToFacetAndPosition[_selector];
// if no more selectors for facet address then delete the facet address
if (lastSelectorPosition == 0) {
// replace facet address with last facet address and delete last facet address
uint256 lastFacetAddressPosition = ds.facetAddresses.length - 1;
uint256 facetAddressPosition = ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;
if (facetAddressPosition != lastFacetAddressPosition) {
address lastFacetAddress = ds.facetAddresses[lastFacetAddressPosition];
ds.facetAddresses[facetAddressPosition] = lastFacetAddress;
ds.facetFunctionSelectors[lastFacetAddress].facetAddressPosition = facetAddressPosition;
}
ds.facetAddresses.pop();
delete ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;
}
}
function initializeDiamondCut(address _init, bytes memory _calldata) internal {
if (_init == address(0)) {
return;
}
enforceHasContractCode(_init, "LibDiamondCut: _init address has no code");
(bool success, bytes memory error) = _init.delegatecall(_calldata);
if (!success) {
if (error.length > 0) {
// bubble up error
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(error)
revert(add(32, error), returndata_size)
}
} else {
revert InitializationFunctionReverted(_init, _calldata);
}
}
}
function enforceHasContractCode(address _contract, string memory _errorMessage) internal view {
uint256 contractSize;
assembly {
contractSize := extcodesize(_contract)
}
require(contractSize > 0, _errorMessage);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./LibPacking.sol";
/**
* @author @0xJacool from Geeks Solutions for Space Hunters
* @notice Library holding all storage layout
*/
library LibGsERC1155Storage {
bytes32 constant STORAGE_POSITION = keccak256("geeks.solutions.erc1155.storage");
enum TokenField {
base_price,
public_sale,
burnable,
max_supply,
total_supply,
max_per_wallet,
exist,
use_dynamic_price,
uri
}
struct GsERC1155Storage {
// Metadata
address recipient;
uint16 percentage;
string baseURI;
// data
mapping(uint256 id => uint256) packed_token_data;
mapping(uint256 id => mapping(address account => uint256)) packed_balances_mint;
mapping(address account => mapping(address operator => bool)) operatorApprovals;
mapping(address account => bool) _editors;
}
function getStorage() internal pure returns (GsERC1155Storage storage s) {
bytes32 position = STORAGE_POSITION;
assembly {
s.slot := position
}
}
function packTokenFromRawData(
uint256 base_price,
bool public_sale,
bool burnable,
uint256 max_supply,
uint256 total_supply,
uint256 max_per_wallet,
bool exist,
bool use_dynamic_price
)
internal
pure
returns (uint256 packedData)
{
packedData = base_price;
packedData |= (public_sale ? 1 << 128 : 0);
packedData |= (burnable ? 1 << 132 : 0);
packedData |= (exist ? 1 << 136 : 0);
packedData |= (use_dynamic_price ? 1 << 140 : 0);
packedData |= max_supply << 144;
packedData |= total_supply << 176;
packedData |= max_per_wallet << 208;
}
function getTokenDataFromPacked(uint256 packedData)
internal
pure
returns (
uint256 base_price,
bool public_sale,
bool burnable,
uint256 max_supply,
uint256 total_supply,
uint256 max_per_wallet,
bool exist,
bool use_dynamic_price
)
{
base_price = uint256(uint128(packedData));
public_sale = LibPacking.getBooleanFromUint(packedData >> 128);
burnable = LibPacking.getBooleanFromUint(packedData >> 132);
exist = LibPacking.getBooleanFromUint(packedData >> 136);
use_dynamic_price = LibPacking.getBooleanFromUint(packedData >> 140);
max_supply = uint256(uint32(packedData >> 144));
total_supply = uint256(uint32(packedData >> 176));
max_per_wallet = uint256(uint16(packedData >> 208));
}
function updatePackedTokenData(uint256 data, uint256 value, TokenField field) internal pure returns (uint256) {
uint256 mask = type(uint256).max;
uint256 filter;
uint256 offset;
//define size and offsets
if (field == TokenField.total_supply) {
offset = 176;
filter = uint256(type(uint32).max) << 176;
} else if (field == TokenField.public_sale) {
offset = 128;
filter = 1 << 128;
} else if ((field == TokenField.burnable)) {
offset = 132;
filter = 1 << 132;
} else if ((field == TokenField.use_dynamic_price)) {
offset = 140;
filter = 1 << 140;
} else {
return data;
}
mask -= filter;
data &= mask;
data |= value << offset;
return data;
}
function packBalanceData(uint256 balance, uint256 mint_count) internal pure returns (uint256 packedData) {
packedData = balance;
packedData |= mint_count << 128;
}
function getBalanceDataFromPacked(uint256 packedData) internal pure returns (uint256 balance, uint256 mint_count) {
balance = uint256(uint128(packedData));
mint_count = uint256(uint128(packedData >> 128));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./LibPacking.sol";
/**
* @author @0xJacool from Geeks Solutions for Space Hunters
* @notice Library holding all storage layout
*/
library LibGsERC20Storage {
bytes32 constant STORAGE_POSITION = keccak256("geeks.solutions.erc20.storage");
struct GsERC20Storage {
mapping(address => uint256) balanceOf;
// Metadata
string name;
string symbol;
// data
uint256 packedRules;
uint256 totalSupply;
mapping(address => mapping(address => uint256)) allowance;
//EIP-2612
mapping(address => uint256) nonces;
mapping(uint256 => bytes32) domainSeparators;
}
function getStorage() internal pure returns (GsERC20Storage storage s) {
bytes32 position = STORAGE_POSITION;
assembly {
s.slot := position
}
}
function packRules(
address pool,
bool disable_transfer,
bool enabled_pay,
uint256 decimals
)
internal
pure
returns (uint256 packedRules)
{
packedRules = uint160(pool);
packedRules |= (disable_transfer ? 1 << 160 : 0);
packedRules |= (enabled_pay ? 1 << 164 : 0);
packedRules |= decimals << 168;
}
function getRulesFromPacked(uint256 packedRules)
internal
pure
returns (address pool, bool disable_transfer, bool enabled_pay, uint256 decimals)
{
pool = address(uint160(packedRules));
disable_transfer = LibPacking.getBooleanFromUint(packedRules >> 160);
enabled_pay = LibPacking.getBooleanFromUint(packedRules >> 164);
decimals = uint256(uint8(packedRules >> 168));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @author @0xJacool from Geeks Solutions for Space Hunters
* @notice Library holding all storage layout
*/
library LibGsMerkleStorage {
bytes32 constant STORAGE_POSITION = keccak256("geeks.solutions.merkle.storage");
struct GsERCMerkleStorage {
// data
mapping(uint256 => WhitelistData[]) whitelist_datas;
}
struct WhitelistData {
bytes32 merkle_root;
uint128 token_price;
uint8 max_per_wallet;
bool active;
bool use_dynamic_price;
}
function getStorage() internal pure returns (GsERCMerkleStorage storage s) {
bytes32 position = STORAGE_POSITION;
assembly {
s.slot := position
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @author @0xJacool from Geeks Solutions for Space Hunters
* @notice Library holding all storage layout
*/
library LibPacking {
function getBooleanFromUint(uint256 data) internal pure returns (bool) {
return ((data << 252 >> 252) == 1 ? true : false);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol)
pragma solidity ^0.8.20;
/**
* @dev These functions deal with verification of Merkle Tree proofs.
*
* The tree and the proofs can be generated using our
* https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
* You will find a quickstart guide in the readme.
*
* WARNING: You should avoid using leaf values that are 64 bytes long prior to
* hashing, or use a hash function other than keccak256 for hashing leaves.
* This is because the concatenation of a sorted pair of internal nodes in
* the Merkle tree could be reinterpreted as a leaf value.
* OpenZeppelin's JavaScript library generates Merkle trees that are safe
* against this attack out of the box.
*/
library MerkleProof {
/**
*@dev The multiproof provided is not valid.
*/
error MerkleProofInvalidMultiproof();
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*/
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
/**
* @dev Calldata version of {verify}
*/
function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProofCalldata(proof, leaf) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leafs & pre-images are assumed to be sorted.
*/
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Calldata version of {processProof}
*/
function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
* `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function multiProofVerify(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProof(proof, proofFlags, leaves) == root;
}
/**
* @dev Calldata version of {multiProofVerify}
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function multiProofVerifyCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProofCalldata(proof, proofFlags, leaves) == root;
}
/**
* @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
* proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
* leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
* respectively.
*
* CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
* is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
* tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
*/
function processMultiProof(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
if (leavesLen + proofLen != totalHashes + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
if (proofPos != proofLen) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Calldata version of {processMultiProof}.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function processMultiProofCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
if (leavesLen + proofLen != totalHashes + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
if (proofPos != proofLen) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Sorts the pair (a, b) and hashes the result.
*/
function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
}
/**
* @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
*/
function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import { IERC173 } from '../../interfaces/IERC173.sol';
import { IOwnable } from './IOwnable.sol';
import { OwnableInternal } from './OwnableInternal.sol';
/**
* @title Ownership access control based on ERC173
*/
abstract contract Ownable is IOwnable, OwnableInternal {
/**
* @inheritdoc IERC173
*/
function owner() public view virtual returns (address) {
return _owner();
}
/**
* @inheritdoc IERC173
*/
function transferOwnership(address account) public virtual onlyOwner {
_transferOwnership(account);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import { IERC173 } from "../../interfaces/IERC173.sol";
import { AddressUtils } from "../../utils/AddressUtils.sol";
import { IOwnableInternal } from "./IOwnableInternal.sol";
import { OwnableStorage } from "./OwnableStorage.sol";
abstract contract OwnableInternal is IOwnableInternal {
using AddressUtils for address;
modifier onlyOwner() {
if (msg.sender != _owner()) revert Ownable__NotOwner();
_;
}
modifier onlyTransitiveOwner() {
if (msg.sender != _transitiveOwner()) {
revert Ownable__NotTransitiveOwner();
}
_;
}
function _owner() internal view virtual returns (address) {
return OwnableStorage.layout().owner;
}
function _transitiveOwner() internal view virtual returns (address owner) {
owner = _owner();
while (owner.isContract()) {
try IERC173(owner).owner() returns (address transitiveOwner) {
owner = transitiveOwner;
} catch {
break;
}
}
}
function _transferOwnership(address account) internal virtual {
_setOwner(account);
}
function _setOwner(address account) internal virtual {
OwnableStorage.Layout storage l = OwnableStorage.layout();
emit OwnershipTransferred(l.owner, account);
l.owner = account;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
library OwnableStorage {
struct Layout {
address owner;
}
bytes32 internal constant STORAGE_SLOT =
keccak256('solidstate.contracts.storage.Ownable');
function layout() internal pure returns (Layout storage l) {
bytes32 slot = STORAGE_SLOT;
assembly {
l.slot := slot
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import { IPausable } from './IPausable.sol';
import { PausableInternal } from './PausableInternal.sol';
/**
* @title Pausable security control module.
*/
abstract contract Pausable is IPausable, PausableInternal {
/**
* @inheritdoc IPausable
*/
function paused() external view virtual returns (bool status) {
status = _paused();
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import { IPausableInternal } from './IPausableInternal.sol';
import { PausableStorage } from './PausableStorage.sol';
/**
* @title Internal functions for Pausable security control module.
*/
abstract contract PausableInternal is IPausableInternal {
modifier whenNotPaused() {
if (_paused()) revert Pausable__Paused();
_;
}
modifier whenPaused() {
if (!_paused()) revert Pausable__NotPaused();
_;
}
/**
* @notice query whether contract is paused
* @return status whether contract is paused
*/
function _paused() internal view virtual returns (bool status) {
status = PausableStorage.layout().paused;
}
/**
* @notice Triggers paused state, when contract is unpaused.
*/
function _pause() internal virtual whenNotPaused {
PausableStorage.layout().paused = true;
emit Paused(msg.sender);
}
/**
* @notice Triggers unpaused state, when contract is paused.
*/
function _unpause() internal virtual whenPaused {
delete PausableStorage.layout().paused;
emit Unpaused(msg.sender);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
library PausableStorage {
struct Layout {
bool paused;
}
bytes32 internal constant STORAGE_SLOT =
keccak256('solidstate.contracts.storage.Pausable');
function layout() internal pure returns (Layout storage l) {
bytes32 slot = STORAGE_SLOT;
assembly {
l.slot := slot
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
/**
* @title utility functions for uint256 operations
* @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license)
*/
library UintUtils {
error UintUtils__InsufficientPadding();
error UintUtils__InvalidBase();
bytes16 private constant HEX_SYMBOLS = '0123456789abcdef';
function add(uint256 a, int256 b) internal pure returns (uint256) {
return b < 0 ? sub(a, -b) : a + uint256(b);
}
function sub(uint256 a, int256 b) internal pure returns (uint256) {
return b < 0 ? add(a, -b) : a - uint256(b);
}
/**
* @notice output the string representation of a number in a given radix
* @dev radix must be between 2 and 36 (inclusive)
* @param value number to format as string
* @param radix numerical base to use
* @return output formatted string
*/
function toString(
uint256 value,
uint256 radix
) internal pure returns (string memory output) {
// this check is repeated in the internal call to #toString(uint256,uint256,uint256)
// but is still needed here to avoid zero division (radix = 0) or infinite loop (radix = 1)
if (radix < 2) {
revert UintUtils__InvalidBase();
}
uint256 length;
uint256 temp = value;
do {
unchecked {
length++;
}
temp /= radix;
} while (temp != 0);
output = toString(value, radix, length);
}
/**
* @notice output the string representation of a number in a given radix and padded to given length
* @dev radix must be between 2 and 36 (inclusive)
* @param value number to format as string
* @param radix numerical base to use
* @param length size to which output should be zero padded
* @return output formatted string
*/
function toString(
uint256 value,
uint256 radix,
uint256 length
) internal pure returns (string memory output) {
if (radix < 2 || radix > 36) {
revert UintUtils__InvalidBase();
}
bytes memory buffer = new bytes(length);
while (length != 0) {
unchecked {
length--;
}
uint256 char = value % radix;
if (char < 10) {
// for numeral characters, shift 48 places through ASCII character set
// 48 can be added using bitwise-or because its binary is 00110000
char |= 48;
} else {
// for alphabetical characters, shift 87 places through ASCII character set
unchecked {
char += 87;
}
}
buffer[length] = bytes1(uint8(char));
value /= radix;
}
if (value != 0) revert UintUtils__InsufficientPadding();
output = string(buffer);
}
/**
* @notice output the 0b-prefixed binary string representation of a number
* @param value number to format as string
* @return output formatted string
*/
function toBinString(
uint256 value
) internal pure returns (string memory output) {
uint256 length;
uint256 temp = value;
do {
unchecked {
length++;
}
temp >>= 1;
} while (temp != 0);
output = toBinString(value, length);
}
/**
* @notice output the 0b-prefixed binary string representation of a number padded to given length
* @param value number to format as string
* @param length size to which output should be zero padded (not including prefix)
* @return output formatted string
*/
function toBinString(
uint256 value,
uint256 length
) internal pure returns (string memory output) {
// add two to length for the leading "0b"
length += 2;
bytes memory buffer = new bytes(length);
buffer[0] = '0';
buffer[1] = 'b';
while (length > 2) {
unchecked {
length--;
}
buffer[length] = HEX_SYMBOLS[value & 1];
value >>= 1;
}
if (value != 0) revert UintUtils__InsufficientPadding();
output = string(buffer);
}
/**
* @notice output the 0o-prefixed octal string representation of a number
* @param value number to format as string
* @return output formatted string
*/
function toOctString(
uint256 value
) internal pure returns (string memory output) {
uint256 length;
uint256 temp = value;
do {
unchecked {
length++;
}
temp >>= 3;
} while (temp != 0);
output = toOctString(value, length);
}
/**
* @notice output the 0o-prefixed octal string representation of a number padded to given length
* @param value number to format as string
* @param length size to which output should be zero padded (not including prefix)
* @return output formatted string
*/
function toOctString(
uint256 value,
uint256 length
) internal pure returns (string memory output) {
// add two to length for the leading "0o"
length += 2;
bytes memory buffer = new bytes(length);
buffer[0] = '0';
buffer[1] = 'o';
while (length > 2) {
unchecked {
length--;
}
buffer[length] = HEX_SYMBOLS[value & 7];
value >>= 3;
}
if (value != 0) revert UintUtils__InsufficientPadding();
output = string(buffer);
}
/**
* @notice output the decimal string representation of a number
* @param value number to format as string
* @return output formatted string
*/
function toDecString(
uint256 value
) internal pure returns (string memory output) {
output = toString(value, 10);
}
/**
* @notice output the decimal string representation of a number padded to given length
* @param value number to format as string
* @param length size to which output should be zero padded
* @return output formatted string
*/
function toDecString(
uint256 value,
uint256 length
) internal pure returns (string memory output) {
output = toString(value, 10, length);
}
/**
* @notice output the 0x-prefixed hexadecimal string representation of a number
* @dev calculated string length will always be even to prevent splitting of bytes
* @param value number to format as string
* @return output formatted string
*/
function toHexString(
uint256 value
) internal pure returns (string memory output) {
uint256 length;
uint256 temp = value;
do {
unchecked {
length++;
}
temp >>= 8;
} while (temp != 0);
output = toHexString(value, length);
}
/**
* @notice output the 0x-prefixed hexadecimal string representation of a number padded to given length
* @dev calculated string length will always be even to prevent splitting of bytes
* @param value number to format as string
* @param length size (in bytes) to which output should be zero padded (not including prefix)
* @return output formatted string
*/
function toHexString(
uint256 value,
uint256 length
) internal pure returns (string memory output) {
// convert byte length to character length and add two to length for the leading "0x"
unchecked {
length = (length << 1) + 2;
}
bytes memory buffer = new bytes(length);
buffer[0] = '0';
buffer[1] = 'x';
while (length > 2) {
unchecked {
length--;
}
buffer[length] = HEX_SYMBOLS[value & 15];
value >>= 4;
}
if (value != 0) revert UintUtils__InsufficientPadding();
output = string(buffer);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
error CallForwardNotContractAddress(address);
error CallForwardFailedCall();
error CallForwardInsufficentFunds();
/**
* @author @0xJacool from Geeks Solutions for Space Hunters
* @notice Abstract contract to forward calls to other contracts
*/
abstract contract aGsCallForwarder {
function _forward_call(
address contract_address,
bytes calldata data,
uint256 value
)
internal
virtual
returns (bytes memory)
{
if (value > address(this).balance) revert CallForwardInsufficentFunds();
uint32 size;
assembly {
size := extcodesize(contract_address)
}
if (size == 0) revert CallForwardNotContractAddress(contract_address);
(bool status, bytes memory return_data) = contract_address.call{ value: value }(data);
if (!status) revert CallForwardFailedCall();
return return_data;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@solidstate/contracts/access/ownable/Ownable.sol";
import "@solidstate/contracts/security/pausable/Pausable.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "../libraries/LibGsERC1155Storage.sol";
error UnknownModifier();
error Unauthorized(address user);
error InvalidArraysLength();
error TokenNotFound();
error ExistingToken();
error ReservedToken();
error TokenLockedBySupply();
abstract contract aGsERC1155 is Ownable, Pausable {
using Strings for uint256;
struct ReturnData {
uint256 base_price;
uint16 royalty_share;
bool active;
bool burnable;
bool use_dynamic_price;
bool isPaused;
uint256 max_supply;
uint256 total_supply;
uint256 max_per_wallet;
address royalty_recipient;
string uri;
uint256 mints_count;
uint256 balance;
}
modifier editors() {
_checkEditors();
_;
}
function _checkEditors() internal view virtual {
if (!((owner() == msg.sender) || LibGsERC1155Storage.getStorage()._editors[msg.sender])) {
revert Unauthorized(msg.sender);
}
}
modifier tokenExist(uint256 tokenId) {
_tokenExist(tokenId);
_;
}
function _tokenExist(uint256 tokenId) internal view virtual {
(,,,,,, bool exist,) =
LibGsERC1155Storage.getTokenDataFromPacked(LibGsERC1155Storage.getStorage().packed_token_data[tokenId]);
if (!exist) revert TokenNotFound();
}
/**
* @notice returns the metadata uri for a given id
*
* @param _token_id the token id to return metadata for
*/
function uri(uint256 _token_id) public view virtual tokenExist(_token_id) returns (string memory) {
return string.concat(LibGsERC1155Storage.getStorage().baseURI, _token_id.toString());
}
function mint_token_data(uint256 id) internal view returns (uint256, uint256, uint256, uint256, bool) {
(uint256 base_price, bool public_sale,, uint256 max_supply, uint256 total_supply, uint256 max_per_wallet,,) =
// (0, false, 0, 0, 0, 0, 0, 0);
LibGsERC1155Storage.getTokenDataFromPacked(LibGsERC1155Storage.getStorage().packed_token_data[id]);
return (base_price, max_supply, total_supply, max_per_wallet, public_sale);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./GsERC1155-20.sol";
import "./interfaces/aGsCallForwarder.sol";
import "./libraries/LibDiamond.sol";
error ShotPayDisabled();
/// @title SPACE HUNTERS ORIGINAL TOKEN - An editionable ERC1155 attached to an ERC20 token
/// @author 0xJacool (https://www.twitter.com/0xjacool) - (https://warpcast.com/jacool)
/// @notice This contract provides the ability to generate an ERC1155 Edition, tied to a fungible token
/// all user facing interaction is ummutable, some administrative tasks are offloaded to facets of a diamond, this
/// enables smaller contract sizes and gas fees optimizations for key user facing Txns
contract SHOT is GsERC_1155_20, aGsCallForwarder {
uint32 public multiplier; // multiplied by 10^18
uint8 public shot_pay_discount; // divide this number by 100 and multiply by price to get final amount to pay
constructor(
address _owner_,
address founder,
address diamondCutFacet
)
GsERC_1155_20(
_owner_,
"Space Hunters Original",
"SHOT",
18,
"https://arweave.net/m4H3QHhMchj5ufXks8XE8RnWM-SV4x2dRXvu9HMyztY/og/",
founder,
1500
)
{
multiplier = 11_111_111;
shot_pay_discount = 80;
// We mint the total Supply of 16 180 339 887 SHOT tokens (using ether as SHOT is also 18 decimals)
_mint(address(this), 13_680_339_887 ether);
_mint(founder, 2_500_000_000 ether);
// Diamond Specific logic
LibDiamond.setContractOwner(_owner_);
// Add the diamondCut external function from the diamondCutFacet
IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1);
bytes4[] memory functionSelectors = new bytes4[](1);
functionSelectors[0] = IDiamondCut.diamondCut.selector;
cut[0] = IDiamondCut.FacetCut({
facetAddress: diamondCutFacet,
action: IDiamondCut.FacetCutAction.Add,
functionSelectors: functionSelectors
});
LibDiamond.diamondCut(cut, address(0), "");
}
/*///////////////////////////////////////////////////////////////
TOKENS FUNCTIONS
///////////////////////////////////////////////////////////////*/
function set_params(uint32 m, uint8 d) external onlyOwner {
multiplier = m;
shot_pay_discount = d;
}
/**
* @notice Mints a token following a given whitelist conditions
*
* @param token_id The token id to mint
* @param wlIndex the index of the whitelist to use for this mint
* @param amount the amount of tokens to mint
* @param proof the proof to grant access to this whitelist
*/
function mint(
uint256 token_id,
uint256 amount,
bool wl,
bool shot,
uint256 wlIndex,
bytes32[] calldata proof
)
external
payable
{
uint256 price = safe_mint(_msgSender(), token_id, amount, wl, wlIndex, proof);
if (shot && enabled_pay()) {
if (shot_pay_discount > 0) {
(uint256 shot_price,) = getTokenPrice(price, false);
transfer(_msgSender(), address(this), (shot_price * shot_pay_discount) / 100);
}
} else if (shot) {
revert ShotPayDisabled();
} else {
if (msg.value < price) revert InsufficentFunds();
if (multiplier > 0) {
// Send SHOTs for each token minted with ETH
transfer(address(this), _msgSender(), amount * multiplier * (10 ** decimals()));
}
}
}
function rewards(address[] calldata _spenders, uint256[] calldata _allowances) external editors {
uint256 length = _spenders.length;
if (length != _allowances.length) revert IndexOutOfRange();
uint256 i;
do {
LibGsERC20Storage.getStorage().allowance[address(this)][_spenders[i]] += _allowances[i];
emit Approval(address(this), _spenders[i], _allowances[i]);
unchecked {
++i;
}
} while (i < length);
}
// Interactions with other Contracts
function forward_call(
address contract_address,
bytes calldata data,
uint256 value
)
external
onlyOwner
returns (bytes memory)
{
return _forward_call(contract_address, data, value);
}
function onERC1155Received(address, address, uint256, uint256, bytes memory) public virtual returns (bytes4) {
return this.onERC1155Received.selector;
}
function onERC1155BatchReceived(
address,
address,
uint256[] memory,
uint256[] memory,
bytes memory
)
public
virtual
returns (bytes4)
{
return this.onERC1155BatchReceived.selector;
}
function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) {
return this.onERC721Received.selector;
}
// DIAMOND FUNCTIONS
// Find facet for function that is called and execute the
// function if a facet is found and return any value.
fallback() external payable whenNotPaused {
LibDiamond.DiamondStorage storage ds;
bytes32 position = LibDiamond.DIAMOND_STORAGE_POSITION;
// get diamond storage
assembly {
ds.slot := position
}
// get facet from function selector
address facet = ds.selectorToFacetAndPosition[msg.sig].facetAddress;
require(facet != address(0), "Diamond: Function does not exist");
// Execute external function from facet using delegatecall and return any value.
assembly {
// copy function selector and any arguments
calldatacopy(0, 0, calldatasize())
// execute function call using the facet
let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0)
// get any return value
returndatacopy(0, 0, returndatasize())
// return any return value or error back to the caller
switch result
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
receive() external payable { }
function supportsInterface(bytes4 interfaceId) public view virtual override(GsERC1155) returns (bool) {
LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();
return (
interfaceId == 0x01ffc9a7 // for IERC165
|| interfaceId == 0xd9b67a26 // for IERC1155
|| interfaceId == 0x2a55205a // for IERC2981
|| interfaceId == 0x4e2312e0 // For IERC1155Receiver
|| super.supportsInterface(interfaceId) || ds.supportedInterfaces[interfaceId]
);
}
}
{
"compilationTarget": {
"src/shot.sol": "SHOT"
},
"evmVersion": "cancun",
"libraries": {},
"metadata": {
"bytecodeHash": "none"
},
"optimizer": {
"enabled": true,
"runs": 1234
},
"remappings": [
":@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/",
":@solidstate/contracts/=node_modules/@solidstate/contracts/",
":@uniswap/v2-core/=node_modules/@uniswap/v2-core/",
":@uniswap/v3-core/=node_modules/@uniswap/v3-core/",
":@uniswap/v3-periphery/=node_modules/@uniswap/v3-periphery/",
":base64-sol/=node_modules/base64-sol/",
":forge-std/=node_modules/forge-std/src/",
":solmate/contracts/=node_modules/solmate/src/"
]
}
[{"inputs":[{"internalType":"address","name":"_owner_","type":"address"},{"internalType":"address","name":"founder","type":"address"},{"internalType":"address","name":"diamondCutFacet","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CallForwardFailedCall","type":"error"},{"inputs":[],"name":"CallForwardInsufficentFunds","type":"error"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"CallForwardNotContractAddress","type":"error"},{"inputs":[],"name":"ContractMintNotAllowed","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC1155InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC1155InvalidApprover","type":"error"},{"inputs":[{"internalType":"uint256","name":"idsLength","type":"uint256"},{"internalType":"uint256","name":"valuesLength","type":"uint256"}],"name":"ERC1155InvalidArrayLength","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"ERC1155InvalidOperator","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC1155InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC1155InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"ERC1155MissingApprovalForAll","type":"error"},{"inputs":[],"name":"ExceedingMaxSupply","type":"error"},{"inputs":[],"name":"ExceedingMintLimit","type":"error"},{"inputs":[],"name":"INVALID_SIGNER","type":"error"},{"inputs":[],"name":"InactiveWhitelist","type":"error"},{"inputs":[],"name":"IndexOutOfRange","type":"error"},{"inputs":[{"internalType":"address","name":"_initializationContractAddress","type":"address"},{"internalType":"bytes","name":"_calldata","type":"bytes"}],"name":"InitializationFunctionReverted","type":"error"},{"inputs":[],"name":"InsufficentFunds","type":"error"},{"inputs":[],"name":"InsufficientAllowance","type":"error"},{"inputs":[],"name":"InvalidProof","type":"error"},{"inputs":[],"name":"NotTokenOwnerOrAuthorised","type":"error"},{"inputs":[],"name":"Ownable__NotOwner","type":"error"},{"inputs":[],"name":"Ownable__NotTransitiveOwner","type":"error"},{"inputs":[],"name":"PERMIT_DEADLINE_EXPIRED","type":"error"},{"inputs":[],"name":"Pausable__NotPaused","type":"error"},{"inputs":[],"name":"Pausable__Paused","type":"error"},{"inputs":[],"name":"SaleClosed","type":"error"},{"inputs":[],"name":"ShotPayDisabled","type":"error"},{"inputs":[],"name":"TokenNotFound","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"TransferPaused","type":"error"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"Unburnable","type":"error"},{"inputs":[],"name":"WrongRoot","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"facetAddress","type":"address"},{"internalType":"enum IDiamondCut.FacetCutAction","name":"action","type":"uint8"},{"internalType":"bytes4[]","name":"functionSelectors","type":"bytes4[]"}],"indexed":false,"internalType":"struct IDiamondCut.FacetCut[]","name":"_diamondCut","type":"tuple[]"},{"indexed":false,"internalType":"address","name":"_init","type":"address"},{"indexed":false,"internalType":"bytes","name":"_calldata","type":"bytes"}],"name":"DiamondCut","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"TransferBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"TransferSingle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"value","type":"string"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"URI","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"domainSeparator","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_token_id","type":"uint256"},{"internalType":"uint128","name":"_wl_price_in_wei","type":"uint128"},{"internalType":"bytes32","name":"_merkle_root","type":"bytes32"},{"internalType":"uint8","name":"_wl_max_per_wallet","type":"uint8"},{"internalType":"bool","name":"_active","type":"bool"},{"internalType":"bool","name":"_use_dynamic_price","type":"bool"}],"name":"addNewWhitelist","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"recipients","type":"bytes"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"airdropERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"sft_id","type":"uint256"},{"internalType":"uint256","name":"sft_amount","type":"uint256"},{"internalType":"uint256","name":"ft_amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"airdropMultiple","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"balanceOfBatch","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"burnBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"contractURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"editor","type":"address"}],"name":"disableEditor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"disabled_transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_whitelist_index","type":"uint256"},{"internalType":"uint256","name":"_token_id","type":"uint256"},{"internalType":"uint128","name":"_wl_price_in_wei","type":"uint128"},{"internalType":"bytes32","name":"_merkle_root","type":"bytes32"},{"internalType":"uint8","name":"_wl_max_per_wallet","type":"uint8"},{"internalType":"bool","name":"_active","type":"bool"},{"internalType":"bool","name":"_use_dynamic_price","type":"bool"}],"name":"editWhitelistFull","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"editor","type":"address"}],"name":"enableEditor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"enabled_pay","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"contract_address","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"forward_call","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"token_id","type":"uint256"},{"internalType":"uint256","name":"whitelist_index","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getMintTotalPrice","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"inverse","type":"bool"}],"name":"getTokenPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_token_id","type":"uint256"},{"internalType":"uint256","name":"_whitelist_index","type":"uint256"}],"name":"getWhiteListAtIndex","outputs":[{"components":[{"internalType":"bytes32","name":"merkle_root","type":"bytes32"},{"internalType":"uint128","name":"token_price","type":"uint128"},{"internalType":"uint8","name":"max_per_wallet","type":"uint8"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"bool","name":"use_dynamic_price","type":"bool"}],"internalType":"struct LibGsMerkleStorage.WhitelistData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_token_id","type":"uint256"}],"name":"getWhiteListLengthForToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"token_id","type":"uint256"}],"name":"initFront","outputs":[{"components":[{"internalType":"uint256","name":"base_price","type":"uint256"},{"internalType":"uint16","name":"royalty_share","type":"uint16"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"bool","name":"burnable","type":"bool"},{"internalType":"bool","name":"use_dynamic_price","type":"bool"},{"internalType":"bool","name":"isPaused","type":"bool"},{"internalType":"uint256","name":"max_supply","type":"uint256"},{"internalType":"uint256","name":"total_supply","type":"uint256"},{"internalType":"uint256","name":"max_per_wallet","type":"uint256"},{"internalType":"address","name":"royalty_recipient","type":"address"},{"internalType":"string","name":"uri","type":"string"},{"internalType":"uint256","name":"mints_count","type":"uint256"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct aGsERC1155.ReturnData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"token_id","type":"uint256"},{"internalType":"uint256","name":"whitelist_index","type":"uint256"}],"name":"initFront","outputs":[{"components":[{"internalType":"uint256","name":"base_price","type":"uint256"},{"internalType":"uint16","name":"royalty_share","type":"uint16"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"bool","name":"burnable","type":"bool"},{"internalType":"bool","name":"use_dynamic_price","type":"bool"},{"internalType":"bool","name":"isPaused","type":"bool"},{"internalType":"uint256","name":"max_supply","type":"uint256"},{"internalType":"uint256","name":"total_supply","type":"uint256"},{"internalType":"uint256","name":"max_per_wallet","type":"uint256"},{"internalType":"address","name":"royalty_recipient","type":"address"},{"internalType":"string","name":"uri","type":"string"},{"internalType":"uint256","name":"mints_count","type":"uint256"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct aGsERC1155.ReturnData","name":"rd","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isDynamicPricer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"token_id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"wl","type":"bool"},{"internalType":"bool","name":"shot","type":"bool"},{"internalType":"uint256","name":"wlIndex","type":"uint256"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"multiplier","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"status","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_spenders","type":"address[]"},{"internalType":"uint256[]","name":"_allowances","type":"uint256[]"}],"name":"rewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_token_id","type":"uint256"},{"internalType":"uint256","name":"_salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"royaltyAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeBatchTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_baseURI","type":"string"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint16","name":"_royaltyShare","type":"uint16"}],"name":"setMetaData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_uniswapV3Pool","type":"address"},{"internalType":"bool","name":"_disable_transfer","type":"bool"},{"internalType":"bool","name":"_enabled_pay","type":"bool"}],"name":"setRule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"m","type":"uint32"},{"internalType":"uint8","name":"d","type":"uint8"}],"name":"set_params","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"shot_pay_discount","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"uniswapV3Pool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_token_id","type":"uint256"}],"name":"uri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]