Contract Name:
ERC20FactoryByMetadropV1
Contract Source Code:
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC1271 standard signature validation method for
* contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
*
* _Available since v4.1._
*/
interface IERC1271 {
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param hash Hash of the data to be signed
* @param signature Signature byte array associated with _data
*/
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.0;
import "../Strings.sol";
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV // Deprecated in v4.8
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32")
mstore(0x1c, hash)
message := keccak256(0x00, 0x3c)
}
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, "\x19\x01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
data := keccak256(ptr, 0x42)
}
}
/**
* @dev Returns an Ethereum Signed Data with intended validator, created from a
* `validator` and `data` according to the version 0 of EIP-191.
*
* See {recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x00", validator, data));
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/SignatureChecker.sol)
pragma solidity ^0.8.0;
import "./ECDSA.sol";
import "../../interfaces/IERC1271.sol";
/**
* @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
* signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
* Argent and Gnosis Safe.
*
* _Available since v4.1._
*/
library SignatureChecker {
/**
* @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
* signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/
function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
(address recovered, ECDSA.RecoverError error) = ECDSA.tryRecover(hash, signature);
return
(error == ECDSA.RecoverError.NoError && recovered == signer) ||
isValidERC1271SignatureNow(signer, hash, signature);
}
/**
* @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
* against the signer smart contract using ERC1271.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/
function isValidERC1271SignatureNow(
address signer,
bytes32 hash,
bytes memory signature
) internal view returns (bool) {
(bool success, bytes memory result) = signer.staticcall(
abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature)
);
return (success &&
result.length >= 32 &&
abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @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 up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (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; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
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.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 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.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
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 (rounding == Rounding.Up && 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 down.
*
* 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 + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* 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 + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* 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 + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* 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 + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @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 v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/Math.sol";
import "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @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), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(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) {
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] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
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 keccak256(bytes(a)) == keccak256(bytes(b));
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableMap.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableMap.js.
pragma solidity ^0.8.0;
import "./EnumerableSet.sol";
/**
* @dev Library for managing an enumerable variant of Solidity's
* https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
* type.
*
* Maps have the following properties:
*
* - Entries are added, removed, and checked for existence in constant time
* (O(1)).
* - Entries are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableMap for EnumerableMap.UintToAddressMap;
*
* // Declare a set state variable
* EnumerableMap.UintToAddressMap private myMap;
* }
* ```
*
* The following map types are supported:
*
* - `uint256 -> address` (`UintToAddressMap`) since v3.0.0
* - `address -> uint256` (`AddressToUintMap`) since v4.6.0
* - `bytes32 -> bytes32` (`Bytes32ToBytes32Map`) since v4.6.0
* - `uint256 -> uint256` (`UintToUintMap`) since v4.7.0
* - `bytes32 -> uint256` (`Bytes32ToUintMap`) since v4.7.0
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableMap, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableMap.
* ====
*/
library EnumerableMap {
using EnumerableSet for EnumerableSet.Bytes32Set;
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Map type with
// bytes32 keys and values.
// The Map implementation uses private functions, and user-facing
// implementations (such as Uint256ToAddressMap) are just wrappers around
// the underlying Map.
// This means that we can only create new EnumerableMaps for types that fit
// in bytes32.
struct Bytes32ToBytes32Map {
// Storage of keys
EnumerableSet.Bytes32Set _keys;
mapping(bytes32 => bytes32) _values;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(Bytes32ToBytes32Map storage map, bytes32 key, bytes32 value) internal returns (bool) {
map._values[key] = value;
return map._keys.add(key);
}
/**
* @dev Removes a key-value pair from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(Bytes32ToBytes32Map storage map, bytes32 key) internal returns (bool) {
delete map._values[key];
return map._keys.remove(key);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool) {
return map._keys.contains(key);
}
/**
* @dev Returns the number of key-value pairs in the map. O(1).
*/
function length(Bytes32ToBytes32Map storage map) internal view returns (uint256) {
return map._keys.length();
}
/**
* @dev Returns the key-value pair stored at position `index` in the map. O(1).
*
* Note that there are no guarantees on the ordering of entries inside the
* array, and it may change when more entries are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32ToBytes32Map storage map, uint256 index) internal view returns (bytes32, bytes32) {
bytes32 key = map._keys.at(index);
return (key, map._values[key]);
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool, bytes32) {
bytes32 value = map._values[key];
if (value == bytes32(0)) {
return (contains(map, key), bytes32(0));
} else {
return (true, value);
}
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) {
bytes32 value = map._values[key];
require(value != 0 || contains(map, key), "EnumerableMap: nonexistent key");
return value;
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/
function get(
Bytes32ToBytes32Map storage map,
bytes32 key,
string memory errorMessage
) internal view returns (bytes32) {
bytes32 value = map._values[key];
require(value != 0 || contains(map, key), errorMessage);
return value;
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(Bytes32ToBytes32Map storage map) internal view returns (bytes32[] memory) {
return map._keys.values();
}
// UintToUintMap
struct UintToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(UintToUintMap storage map, uint256 key, uint256 value) internal returns (bool) {
return set(map._inner, bytes32(key), bytes32(value));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(UintToUintMap storage map, uint256 key) internal returns (bool) {
return remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(UintToUintMap storage map, uint256 key) internal view returns (bool) {
return contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(UintToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintToUintMap storage map, uint256 index) internal view returns (uint256, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (uint256(key), uint256(value));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(UintToUintMap storage map, uint256 key) internal view returns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
return (success, uint256(value));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(UintToUintMap storage map, uint256 key) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(key)));
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/
function get(UintToUintMap storage map, uint256 key, string memory errorMessage) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(key), errorMessage));
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(UintToUintMap storage map) internal view returns (uint256[] memory) {
bytes32[] memory store = keys(map._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintToAddressMap
struct UintToAddressMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) {
return set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
return remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
return contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(UintToAddressMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (uint256(key), address(uint160(uint256(value))));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
return (success, address(uint160(uint256(value))));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
return address(uint160(uint256(get(map._inner, bytes32(key)))));
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/
function get(
UintToAddressMap storage map,
uint256 key,
string memory errorMessage
) internal view returns (address) {
return address(uint160(uint256(get(map._inner, bytes32(key), errorMessage))));
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(UintToAddressMap storage map) internal view returns (uint256[] memory) {
bytes32[] memory store = keys(map._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressToUintMap
struct AddressToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(AddressToUintMap storage map, address key, uint256 value) internal returns (bool) {
return set(map._inner, bytes32(uint256(uint160(key))), bytes32(value));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(AddressToUintMap storage map, address key) internal returns (bool) {
return remove(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(AddressToUintMap storage map, address key) internal view returns (bool) {
return contains(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(AddressToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressToUintMap storage map, uint256 index) internal view returns (address, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (address(uint160(uint256(key))), uint256(value));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(AddressToUintMap storage map, address key) internal view returns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(uint256(uint160(key))));
return (success, uint256(value));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(AddressToUintMap storage map, address key) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(uint256(uint160(key)))));
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/
function get(
AddressToUintMap storage map,
address key,
string memory errorMessage
) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(uint256(uint160(key))), errorMessage));
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(AddressToUintMap storage map) internal view returns (address[] memory) {
bytes32[] memory store = keys(map._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// Bytes32ToUintMap
struct Bytes32ToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(Bytes32ToUintMap storage map, bytes32 key, uint256 value) internal returns (bool) {
return set(map._inner, key, bytes32(value));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(Bytes32ToUintMap storage map, bytes32 key) internal returns (bool) {
return remove(map._inner, key);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool) {
return contains(map._inner, key);
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(Bytes32ToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32ToUintMap storage map, uint256 index) internal view returns (bytes32, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (key, uint256(value));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, key);
return (success, uint256(value));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(Bytes32ToUintMap storage map, bytes32 key) internal view returns (uint256) {
return uint256(get(map._inner, key));
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/
function get(
Bytes32ToUintMap storage map,
bytes32 key,
string memory errorMessage
) internal view returns (uint256) {
return uint256(get(map._inner, key, errorMessage));
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(Bytes32ToUintMap storage map) internal view returns (bytes32[] memory) {
bytes32[] memory store = keys(map._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}
pragma solidity >=0.5.0;
interface IUniswapV2Factory {
event PairCreated(address indexed token0, address indexed token1, address pair, uint);
function feeTo() external view returns (address);
function feeToSetter() external view returns (address);
function getPair(address tokenA, address tokenB) external view returns (address pair);
function allPairs(uint) external view returns (address pair);
function allPairsLength() external view returns (uint);
function createPair(address tokenA, address tokenB) external returns (address pair);
function setFeeTo(address) external;
function setFeeToSetter(address) external;
}
pragma solidity >=0.6.2;
interface IUniswapV2Router01 {
function factory() external pure returns (address);
function WETH() external pure returns (address);
function addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB, uint liquidity);
function addLiquidityETH(
address token,
uint amountTokenDesired,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external payable returns (uint amountToken, uint amountETH, uint liquidity);
function removeLiquidity(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB);
function removeLiquidityETH(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external returns (uint amountToken, uint amountETH);
function removeLiquidityWithPermit(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountA, uint amountB);
function removeLiquidityETHWithPermit(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountToken, uint amountETH);
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapTokensForExactTokens(
uint amountOut,
uint amountInMax,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
external
payable
returns (uint[] memory amounts);
function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
external
returns (uint[] memory amounts);
function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
external
returns (uint[] memory amounts);
function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
external
payable
returns (uint[] memory amounts);
function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}
pragma solidity >=0.6.2;
import './IUniswapV2Router01.sol';
interface IUniswapV2Router02 is IUniswapV2Router01 {
function removeLiquidityETHSupportingFeeOnTransferTokens(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external returns (uint amountETH);
function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountETH);
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
function swapExactETHForTokensSupportingFeeOnTransferTokens(
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external payable;
function swapExactTokensForETHSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
}
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MM MM
// MM ^ ##### ##### #### # ###### #### ##### # # # # # # #### MM
// MM | # # # # # # # # # # # # # ## # # # # MM
// MM | # # # # # # # ##### # # # # # # # #### #### MM
// MM ^ ##### ##### # # # # # # # # # # # # # # MM
// MM | # # # # # # # # # # # # # # ## # # # # MM
// MM | # # # #### #### ###### #### # ###### # # # # # #### MM
// MM ^ MM
// MM | MM
// MM |________ Click 'Read Contract', then select: MM
// MM * _1___website MM
// MM * _2___twitter (or X...) MM
// MM * _3___telegram MM
// MM * _4___discord MM
// MM MM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWNNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXKXWMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMN0dccdKMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMN0d:,;dXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKxc,.,xNMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXOo;.. .oWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWNOo;.. .kMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMN0d:.. ;KMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXkl,. .lNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXkl,.. .kMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNOo;.. ;KMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKxc'. .lNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXkl,. .kMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXkl,. ;KMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMN0d:'. .lNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKxc'. .kMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKxc'. ;KMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMN0d:.. .lNMMMMMMMMMMMMMMMMMMMMMMMMMMN0d:.. .kMMMMMMMMMMMMMMMMMMMMMMMMMWKxc'. ;KMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMWXOo;.. .lNMMMMMMMMMMMMMMMMMMMMMMNOxo;.. .kMMMMMMMMMMMMMMMMMMMMMMN0d:.. ;KMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMWXkl,. .lNMMMMMMMMMMMMMMMMMMWXOo,... .kMMMMMMMMMMMMMMMMMMMNOo;.. ;KMMMM
// MMMMMMMMMMMMMMMMMMMMMMWKxc'. .lNMMMMMMMMMMMMMMMWXkl,. .kMMMMMMMMMMMMMMMWXkl,. ;KMMMM
// MMMMMMMMMMMMMMMMMMMW0d:'. .lNMMMMMMMMMMMMWKxc'. .kMMMMMMMMMMMMWKkc'. ;KMMMM
// MMMMMMMMMMMMMMMMN0d:.. .cXMMMMMMMMMN0d:.. .oWMMMMMMMMWKxc'. ;KMMMM
// MMMMMMMMMMMMWNOo;.. .lKWMMMWNOo;.. .oKWMMWN0d:.. ;KMMMM
// MMMMMMMMMWXkl,. .'codol,.. ..;clc,.. ;KMMMM
// MMMMMMWKxc'. :XMMMM
// MMMMXxc'. 'kWMMMM
// MMW0:. ..:OWMMMMM
// MMK:. ..;o0NMMMMMMM
// MMO' ..... ..''.. ..:d0NMMMMMMMMMM
// MMO' ..,lxO0Oxc.. .'cx0XXKkc.. .'cxKWMMMMMMMMMMMMM
// MMO' ..;oOXWMMMMMWO, ..,lkXWMMMMMMWx. .,lkXWMMMMMMMMMMMMMMMM
// MMO' ..:d0NMMMMMMMMMMNo. ..;oONWMMMMMMMMMMK; .,oOXWMMMMMMMMMMMMMMMMMMM
// MMO' ..:d0NMMMMMMMMMMMMMWo. ..:d0NMMMMMMMMMMMMMMX: ..;oONMMMMMMMMMMMMMMMMMMMMMMM
// MMO' .'cxKWMMMMMMMMMMMMMMMMWo. .';cxKWMMMMMMMMMMMMMMMMMX: ..:d0NMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMO' .,lkXWMMMMMMMMMMMMMMMMMMMWo. .,ckKNWMMMMMMMMMMMMMMMMMMMMX: .'cxKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMO' ..;oOXMMMMMMMMMMMMMMMMMMMMMMMWo. ..,lkXWMMMMMMMMMMMMMMMMMMMMMMMMX: .,lkXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMO' ..:d0NMMMMMMMMMMMMMMMMMMMMMMMMMMWo. ..;oONWMMMMMMMMMMMMMMMMMMMMMMMMMMMX: ..,oOXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMO' ..:d0NMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWo. ..:d0NMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMX: ..;oONMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMO' .'cxKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWo. .'cxKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMX: ..:d0NMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMO' .,lkXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWo. .'ckKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMX: .'cxKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MM0, ..;oOXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWk'. .,lkXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNd...,lkXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMW0dld0NMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMW0xxONWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKO0XWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMWX00000XWMMMMMMWX00000KWMNK000000000000KWNK0000000000000KXWMMWX0000KNMMMMMMMNKK00000000KKNWMMMMMWX000000000KKXWMMMMMMMMMWXK0OO0KXWMMMMMMNK000000000KXNMMMMMMM
// MMMK:..''.;OMMMMMMK:..'..;0M0;............:00;..............oNMWO;.''..lXMMMMMWd'..........',:d0WMMNl...........':dKMMMMNOo;'......';oONMMMO;..........';lOWMMMM
// MMMK; .;;..cXMMMMNo..,;. 'OMO' .,looooooookXXxooooc. .,looodOWM0;..:o' .oNMMMMWo.. .:oooooo:'. .lXMXc. .cdddddo:. .;0MW0:...;lddddl;...:OWMO' .,loooool,. .dNMMM
// MMMK; :d, .xWMMMO, .dc. 'OMO' .lNMMMMMMMMMMMMMMMMO' .lNMMMMMMXc. 'kNo. .xWMMMWo.. .kMMMMMMWKl. .lNXc. ,0MMMMMMNo. .xWO,..;kNMMMMMMNk;. 'kWO' .lNMMMMMM0, ;KMMM
// MMMK; :0o. ;KMMXc..cKl. 'OMO' .'looooooo0MMMMMMMMO' .lNMMMMMNo. .dWMK:. 'OMMMWo.. .kMMMMMMMMX:. ,OXc. 'xXKXXK0d,..:0Nl. 'kMMMMMMMMMMO' .cXO' .cKXXXXX0o. .lXMMM
// MMMK; :XK: .oNWx. ,ONl. 'OMO' .''''''',xWMMMMMMMO' .lNMMMMWx. .:0NNNk' .;0MMWo.. .kMMMMMMMMNl. 'OXc. ..........:xXMXc. ,0MMMMMMMMMM0; :XO' .'''''....'oXMMMM
// MMMK; :XWx. ,OK:..oNNl. 'OMO' .cKXXXXXXXWMMMMMMMMO' .lNMMMWO, ..,,,,,. .cXMWo.. .kMMMMMMMWk, .:KXc. .:ooool:..;oKMWd. .lXMMMMMMMMNo. .oWO' .,looooooxOXWMMMMM
// MMMK; :KMXl..co..:KMNl. 'OMO' .:0KKKKKKKKXWMMMMMMO' .lNMMMK:. .:ccccccc,. .oNWo.. .oKKKKK0kl'..,OWXc. ,0MMMMMNx. .cXMXo. .;d0XXXX0x:. .lXMO' .lNMMMMMMMMMMMMMMM
// MMMK; :XMMO, ....kWMNl. 'OMO' ..........:KMMMMMMO' .lNMMXc. .oNMMMMMMMK:. .xNo.. ...........,oKWMXc. ,0MMMMMMXc. .dNMWOc'....''....'cONMMk' .lNMMMMMMMMMMMMMMM
// MMMNkookNMMWOooookNMMWOooxXMXxooooooooooooxXMMMMMMXxooOWMMKdooxXMMMMMMMMM0dookN0doloooooooodx0NWMMMWOooxXMMMMMMMKdookNMMMWXOxolcclodOXWMMMMXdooOWMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// SPDX-License-Identifier: BUSL 1.0
// Metadrop Contracts (v2.1.0)
pragma solidity 0.8.19;
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol";
import {ERC20Spendable} from "../../ThirdParty/omnus/ERC20Spendable/ERC20Spendable.sol";
import {IERC20ByMetadropV1} from "./IERC20ByMetadropV1.sol";
import {IERC20FactoryByMetadropV1} from "../ERC20Factory/IERC20FactoryByMetadropV1.sol";
import {IUniswapV2Router02} from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import {IUniswapV2Factory} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";
import {IUniswapV2Locker} from "../../ThirdParty/Unicrypt/IUniswapV2Locker.sol";
import {Ownable} from "../../Global/OZ/Ownable.sol";
import {Revert} from "../../Global/Revert.sol";
import {SafeERC20, IERC20} from "../../Global/OZ/SafeERC20.sol";
import {TaxAdmin} from "../../Global/TaxAdmin.sol";
/**
* @dev Metadrop core ERC-20 contract
*
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20ByMetadropV1 is
Context,
ERC20Spendable,
IERC20ByMetadropV1,
Ownable,
TaxAdmin
{
using EnumerableSet for EnumerableSet.AddressSet;
using SafeERC20 for IERC20;
uint256 public constant version = 100010001000000000;
uint256 internal constant BP_DENOM = 10000;
uint256 internal constant ROUND_DEC = 100000000000;
IUniswapV2Locker internal immutable unicryptLocker;
IUniswapV2Router02 internal immutable uniswapRouter;
uint256 public immutable lpSupply;
uint256 public immutable projectSupply;
uint256 public immutable maxTokensPerTransaction;
uint256 public immutable maxTokensPerWallet;
uint256 public immutable maxProjectBuyTaxBasisPoints;
uint256 public immutable maxProjectSellTaxBasisPoints;
uint256 public immutable maxMetadropBuyTaxBasisPoints;
uint256 public immutable maxMetadropSellTaxBasisPoints;
uint256 public immutable lpLockupInDays;
address public immutable metadropTreasury;
address public immutable uniswapV2Pair;
address internal immutable metadropV1Factory;
uint256 internal immutable metadropTaxPeriodInDays;
bool internal immutable tokenHasTax;
bool internal immutable tokenHasLimits;
/** @dev {Storage Slot 1} Vars read / written as part of transfers packed to a single
* slot for warm reads / writes.
* Slot 1:
* 128
* 32
* 16 * 5
* 8 * 2
* ------
* 256
* ------ */
uint128 public projectTaxPendingSwap;
uint32 public fundedDate;
uint16 public projectBuyTaxBasisPoints;
uint16 public projectSellTaxBasisPoints;
uint16 public metadropBuyTaxBasisPoints;
uint16 public metadropSellTaxBasisPoints;
uint16 public swapThresholdBasisPoints;
/** @dev {autoSwapEnabled} We start with {autoSwapEnabled} OFF, as we don't want to
* call this when processing initial liquidity from this address. We turn this on when
* liquidity has been loaded, and use this bool to control processing during auto-swaps
* from that point onwards. */
bool private autoSwapEnabled = false;
/** @dev {limitsEnabled} Similarly, we don't validate txn limits during liquidity loading. This is
* automatically set to true when liquidity loading is complete.
*
* This can be set back to false by the tax admin. This is a neccesary fallback
* position where on-chain actions cannot meet the validation and need to occur.
* For example, someone may stake token into a staking contract over a number
* of transactions until the staked balance is higher than the per txn limit.
* If the developer of the staking contract has not considered the per txn
* limit, and the 'unstake' method simply remits the entire user balance in
* one txn, then this token would now be trapped in the staking contract. Under
* these circumstances the limit can be turned off to allow the transaction to
* proceed.
*
* After a given amount of time any successful token will have
* become distributed such that a per transaction limit is less relevant. */
bool private limitsEnabled = false;
/** @dev {Storage Slot 2} Not read in transfers etc:
* 160
* ------
* 160
* ------ */
address public projectTreasury;
/** @dev {Storage Slot 3} Only written to if metadrop tax applies, otherwise no writes occur.
* Note that although a uint128 supply limit applies to this contract we store this
* as a uint256. As the evm uses uint256 for all vars we save the gas cost of the
* implicit cast by using uint256, given there are no other vars we can sensibly
* pack with. For the project tax we pack a uint128 with other vars read and written
* as part of transfer processing, allowing a warm write.
* Slot 3:
* 256
* ------
* 256
* ------ */
uint256 public metadropTaxPendingSwap;
/** @dev {Storage Slot 4} Not read as part of transfers etc.
* 256
* ------
* 256
* ------ */
uint256 private _totalSupply;
/** @dev {Storage Slot 5 to n} Not read as part of transfers etc. */
string private _name;
string private _symbol;
/** @dev {Social Links} These shouldn't have _ prefix as they are public, but we want these at the top */
string public _1___website;
string public _2___twitter;
string public _3___telegram;
string public _4___discord;
/** @dev {_balances} Addresses balances */
mapping(address => uint256) private _balances;
/** @dev {_allowances} Addresses allocance details */
mapping(address => mapping(address => uint256)) private _allowances;
/** @dev {_liquidityPools} Enumerable set for liquidity pool addresses */
EnumerableSet.AddressSet private _liquidityPools;
/** @dev {_liquidityPools} Enumerable set for addresses where limits do not apply */
EnumerableSet.AddressSet private _unlimited;
/**
* @dev {constructor}
*
* @param integrationAddresses_ The project owner, uniswap router and unicrypt locker.
* @param baseParams_ configuration of this ERC20.
* @param supplyParams_ Supply configuration of this ERC20.
* @param taxParams_ Tax configuration of this ERC20
*/
constructor(
address[3] memory integrationAddresses_,
bytes memory baseParams_,
bytes memory supplyParams_,
bytes memory taxParams_
) {
_decodeBaseParams(integrationAddresses_[0], baseParams_);
uniswapRouter = IUniswapV2Router02(integrationAddresses_[1]);
unicryptLocker = IUniswapV2Locker(integrationAddresses_[2]);
ERC20SupplyParameters memory supplyParams = abi.decode(
supplyParams_,
(ERC20SupplyParameters)
);
ERC20TaxParameters memory taxParams = abi.decode(
taxParams_,
(ERC20TaxParameters)
);
tokenHasLimits = _processSupplyParams(supplyParams);
projectTreasury = supplyParams.projectTreasury;
metadropTreasury = supplyParams.metadropTreasury;
lpSupply = supplyParams.lpSupply * (10 ** decimals());
projectSupply = supplyParams.projectSupply * (10 ** decimals());
maxTokensPerWallet = supplyParams.maxTokensPerWallet * (10 ** decimals());
maxTokensPerTransaction = supplyParams.maxTokensPerTxn * (10 ** decimals());
lpLockupInDays = supplyParams.lpLockupInDays;
tokenHasTax = _processTaxParams(taxParams);
maxProjectBuyTaxBasisPoints = taxParams.maxProjectBuyTaxBasisPoints;
maxProjectSellTaxBasisPoints = taxParams.maxProjectSellTaxBasisPoints;
maxMetadropBuyTaxBasisPoints = taxParams.maxMetadropBuyTaxBasisPoints;
maxMetadropSellTaxBasisPoints = taxParams.maxMetadropSellTaxBasisPoints;
metadropTaxPeriodInDays = taxParams.metadropTaxPeriodInDays;
swapThresholdBasisPoints = uint16(taxParams.taxSwapThresholdBasisPoints);
metadropV1Factory = _msgSender();
_mintBalances(lpSupply, projectSupply);
uniswapV2Pair = _createPair();
}
/**
* @dev {onlyOwnerOrFactory}
*
* Throws if called by any account other than the owner OR factory.
*/
modifier onlyOwnerOrFactory() {
if (metadropV1Factory != _msgSender() && owner() != _msgSender()) {
_revert(CallerIsNotFactoryOrProjectOwner.selector);
}
_;
}
/**
* @dev function {_decodeBaseParams}
*
* Decode NFT Parameters
*
* @param projectOwner_ The owner of this contract
* @param encodedBaseParams_ The base params encoded into a bytes array
*/
function _decodeBaseParams(
address projectOwner_,
bytes memory encodedBaseParams_
) internal {
_transferOwnership(projectOwner_);
_transferTaxAdmin(projectOwner_);
(
_name,
_symbol,
_1___website,
_2___twitter,
_3___telegram,
_4___discord
) = abi.decode(
encodedBaseParams_,
(string, string, string, string, string, string)
);
}
/**
* @dev function {_processSupplyParams}
*
* Process provided supply params
*
* @param erc20SupplyParameters_ The supply params encoded into a bytes array
*/
function _processSupplyParams(
ERC20SupplyParameters memory erc20SupplyParameters_
) internal returns (bool tokenHasLimits_) {
if (
erc20SupplyParameters_.maxSupply !=
(erc20SupplyParameters_.lpSupply + erc20SupplyParameters_.projectSupply)
) {
_revert(SupplyTotalMismatch.selector);
}
if (erc20SupplyParameters_.maxSupply > type(uint128).max) {
_revert(MaxSupplyTooHigh.selector);
}
if (erc20SupplyParameters_.lpLockupInDays > type(uint96).max) {
_revert(LPLockUpMustFitUint96.selector);
}
_unlimited.add(erc20SupplyParameters_.projectTreasury);
_unlimited.add(address(this));
_unlimited.add(address(0));
if (
erc20SupplyParameters_.maxTokensPerTxn == 0 &&
erc20SupplyParameters_.maxTokensPerWallet == 0
) {
return false;
} else {
return true;
}
}
/**
* @dev function {_processTaxParams}
*
* Process provided tax params
*
* @param erc20TaxParameters_ The tax params encoded into a bytes array
*/
function _processTaxParams(
ERC20TaxParameters memory erc20TaxParameters_
) internal returns (bool tokenHasTax_) {
/**
* @dev We use the immutable var {tokenHasTax} to avoid unneccesary storage writes and reads. If this
* token does NOT have tax applied then there is no need to store or read these parameters, and we can
* avoid this simply by checking the immutable var. Pass back the value for this var from this method.
*/
if (
erc20TaxParameters_.projectBuyTaxBasisPoints == 0 &&
erc20TaxParameters_.projectSellTaxBasisPoints == 0 &&
erc20TaxParameters_.metadropBuyTaxBasisPoints == 0 &&
erc20TaxParameters_.metadropSellTaxBasisPoints == 0 &&
erc20TaxParameters_.maxProjectBuyTaxBasisPoints == 0 &&
erc20TaxParameters_.maxProjectSellTaxBasisPoints == 0 &&
erc20TaxParameters_.maxMetadropBuyTaxBasisPoints == 0 &&
erc20TaxParameters_.maxMetadropSellTaxBasisPoints == 0
) {
return false;
} else {
projectBuyTaxBasisPoints = uint16(
erc20TaxParameters_.projectBuyTaxBasisPoints
);
projectSellTaxBasisPoints = uint16(
erc20TaxParameters_.projectSellTaxBasisPoints
);
metadropBuyTaxBasisPoints = uint16(
erc20TaxParameters_.metadropBuyTaxBasisPoints
);
metadropSellTaxBasisPoints = uint16(
erc20TaxParameters_.metadropSellTaxBasisPoints
);
return true;
}
}
/**
* @dev function {_mintBalances}
*
* Mint initial balances
*
* @param lpMint_ The number of tokens for liquidity
* @param projectMint_ The number of tokens for the project treasury
*/
function _mintBalances(uint256 lpMint_, uint256 projectMint_) internal {
if (lpMint_ > 0) {
_mint(address(this), lpMint_);
}
if (projectMint_ > 0) {
_mint(projectTreasury, projectMint_);
}
}
/**
* @dev function {_createPair}
*
* Create the uniswap pair
*
* @return uniswapV2Pair_ The pair address
*/
function _createPair() internal returns (address uniswapV2Pair_) {
if (_totalSupply > 0) {
uniswapV2Pair_ = IUniswapV2Factory(uniswapRouter.factory()).createPair(
address(this),
uniswapRouter.WETH()
);
_liquidityPools.add(uniswapV2Pair_);
emit LiquidityPoolCreated(uniswapV2Pair_);
}
_unlimited.add(address(uniswapRouter));
_unlimited.add(uniswapV2Pair_);
return (uniswapV2Pair_);
}
/**
* @dev function {addInitialLiquidity}
*
* Add initial liquidity to the uniswap pair
*
* @param lockerFee_ The locker fee in wei. This must match the required fee from the external locker contract.
*/
function addInitialLiquidity(
uint256 lockerFee_
) public payable onlyOwnerOrFactory {
if (lockerFee_ >= msg.value) {
// The amount of ETH MUST exceed the locker fee, otherwise what liquidity are we adding?
_revert(NoETHForLiquidityPair.selector);
}
_addInitialLiquidity((msg.value - lockerFee_), lockerFee_);
}
/**
* @dev function {_addInitialLiquidity}
*
* Add initial liquidity to the uniswap pair (internal function that does the work)
*
* @param ethAmount_ The amount of ETH passed into the call
* @param lockerFee_ The locker fee in wei. This must match the required fee from the external locker contract.
*/
function _addInitialLiquidity(
uint256 ethAmount_,
uint256 lockerFee_
) internal {
// Funded date is the date of first funding. We can only add initial liquidity once. If this date is set,
// we cannot proceed
if (fundedDate == 0) {
fundedDate = uint32(block.timestamp);
} else {
_revert(InitialLiquidityAlreadyAdded.selector);
}
// Can only do this if this contract holds tokens:
if (balanceOf(address(this)) == 0) {
_revert(NoTokenForLiquidityPair.selector);
}
// Approve the uniswap router for an inifinite amount (max uint256)
// This means that we don't need to worry about later incrememtal
// approvals on tax swaps, as the uniswap router allowance will never
// be decreased (see code in decreaseAllowance for reference)
_approve(address(this), address(uniswapRouter), type(uint256).max);
// Add the liquidity:
(uint256 amountA, uint256 amountB, uint256 lpTokens) = uniswapRouter
.addLiquidityETH{value: ethAmount_}(
address(this),
balanceOf(address(this)),
0,
0,
address(this),
block.timestamp
);
emit InitialLiquidityAdded(amountA, amountB, lpTokens);
autoSwapEnabled = true;
limitsEnabled = true;
// Lock the liqidity:
_lockInitialLiquidity(lockerFee_, lpTokens);
}
/**
* @dev function {_lockInitialLiquidity}
*
* Lock initial liquidity on locker contract
*
* @param lockerFee_ The locker fee in wei. This must match the required fee from the external locker contract.
* @param lpTokens_ The amount of LP tokens to be locked
*/
function _lockInitialLiquidity(
uint256 lockerFee_,
uint256 lpTokens_
) internal {
IERC20(uniswapV2Pair).approve(address(unicryptLocker), lpTokens_);
unicryptLocker.lockLPToken{value: lockerFee_}(
uniswapV2Pair,
IERC20(uniswapV2Pair).balanceOf(address(this)),
block.timestamp + (lpLockupInDays * 1 days),
payable(address(0)),
true,
payable(projectTreasury)
);
emit LiquidityLocked();
}
/**
* @dev function {isLiquidityPool}
*
* Return if an address is a liquidity pool
*
* @param queryAddress_ The address being queried
* @return bool The address is / isn't a liquidity pool
*/
function isLiquidityPool(address queryAddress_) public view returns (bool) {
/** @dev We check the uniswapV2Pair address first as this is an immutable variable and therefore does not need
* to be fetched from storage, saving gas if this address IS the uniswapV2Pool. We also add this address
* to the enumerated set for ease of reference (for example it is returned in the getter), and it does
* not add gas to any other calls, that still complete in 0(1) time.
*/
return (queryAddress_ == uniswapV2Pair ||
_liquidityPools.contains(queryAddress_));
}
/**
* @dev function {addLiquidityPool} onlyTaxAdmin
*
* Allows the tax admin to add a liquidity pool to the pool enumerable set
*
* @param newLiquidityPool_ The address of the new liquidity pool
*/
function addLiquidityPool(address newLiquidityPool_) public onlyTaxAdmin {
// Don't allow calls that didn't pass an address:
if (newLiquidityPool_ == address(0)) {
_revert(LiquidityPoolCannotBeAddressZero.selector);
}
// Only allow smart contract addresses to be added, as only these can be pools:
if (newLiquidityPool_.code.length == 0) {
_revert(LiquidityPoolMustBeAContractAddress.selector);
}
// Add this to the enumerated list:
_liquidityPools.add(newLiquidityPool_);
emit LiquidityPoolAdded(newLiquidityPool_);
}
/**
* @dev function {removeLiquidityPool} onlyTaxAdmin
*
* Allows the tax admin to remove a liquidity pool
*
* @param removedLiquidityPool_ The address of the old removed liquidity pool
*/
function removeLiquidityPool(
address removedLiquidityPool_
) public onlyTaxAdmin {
// Remove this from the enumerated list:
_liquidityPools.remove(removedLiquidityPool_);
emit LiquidityPoolRemoved(removedLiquidityPool_);
}
/**
* @dev function {isUnlimited}
*
* Return if an address is unlimited (is not subject to per txn and per wallet limits)
*
* @param queryAddress_ The address being queried
* @return bool The address is / isn't unlimited
*/
function isUnlimited(address queryAddress_) public view returns (bool) {
return (_unlimited.contains(queryAddress_));
}
/**
* @dev function {addUnlimited} onlyTaxAdmin
*
* Allows the tax admin to add an unlimited address
*
* @param newUnlimited_ The address of the new unlimited address
*/
function addUnlimited(address newUnlimited_) public onlyTaxAdmin {
// Add this to the enumerated list:
_unlimited.add(newUnlimited_);
emit UnlimitedAddressAdded(newUnlimited_);
}
/**
* @dev function {removeUnlimited} onlyTaxAdmin
*
* Allows the tax admin to remove an unlimited address
*
* @param removedUnlimited_ The address of the old removed unlimited address
*/
function removeUnlimited(address removedUnlimited_) public onlyTaxAdmin {
// Remove this from the enumerated list:
_unlimited.remove(removedUnlimited_);
emit UnlimitedAddressRemoved(removedUnlimited_);
}
/**
* @dev function {setLimitsEnabledStatus} onlyTaxAdmin
*
* Allows the tax admin to enable / disable tokens per txn and per holder validation.
*
* @param enabled_ Should limits be on?
*/
function setLimitsEnabledStatus(bool enabled_) public onlyTaxAdmin {
limitsEnabled = enabled_;
emit SetLimitsEnabled(enabled_);
}
/**
* @dev function {setProjectTreasury} onlyTaxAdmin
*
* Allows the tax admin to set the treasury address
*
* @param projectTreasury_ New treasury address
*/
function setProjectTreasury(address projectTreasury_) public onlyTaxAdmin {
projectTreasury = projectTreasury_;
emit TreasuryUpdated(projectTreasury_);
}
/**
* @dev function {setSwapThresholdBasisPoints} onlyTaxAdmin
*
* Allows the tax admin to set the autoswap threshold
*
* @param swapThresholdBasisPoints_ New swap threshold in basis points
*/
function setSwapThresholdBasisPoints(
uint16 swapThresholdBasisPoints_
) public onlyTaxAdmin {
uint256 oldswapThresholdBasisPoints = swapThresholdBasisPoints;
swapThresholdBasisPoints = swapThresholdBasisPoints_;
emit AutoSwapThresholdUpdated(
oldswapThresholdBasisPoints,
swapThresholdBasisPoints_
);
}
/**
* @dev function {withdrawETH} onlyOwner
*
* Allows the owner to withdraw ETH
*
* @param amount_ The amount to withdraw
*/
function withdrawETH(uint256 amount_) external onlyOwner {
(bool success, ) = owner().call{value: amount_}("");
if (!success) {
_revert(TransferFailed.selector);
}
}
/**
* @dev function {withdrawERC20} onlyOwner
*
* A withdraw function to allow ERC20s to be withdrawn.
*
* @param token_ The address of the token being withdrawn
* @param amount_ The amount to withdraw
*/
function withdrawERC20(IERC20 token_, uint256 amount_) external onlyOwner {
token_.safeTransfer(owner(), amount_);
}
/**
* @dev function {setProjectTaxRates} onlyTaxAdmin
*
* Change the tax rates, subject to max rate
*
* @param newProjectBuyTaxBasisPoints_ The new buy tax rate
* @param newProjectSellTaxBasisPoints_ The new sell tax rate
*/
function setProjectTaxRates(
uint16 newProjectBuyTaxBasisPoints_,
uint16 newProjectSellTaxBasisPoints_
) external onlyTaxAdmin {
// Cannot increase above the maximum:
if (newProjectBuyTaxBasisPoints_ > maxProjectBuyTaxBasisPoints) {
_revert(NewBuyTaxBasisPointsExceedsMaximum.selector);
}
// Cannot increase above the maximum:
if (newProjectSellTaxBasisPoints_ > maxProjectSellTaxBasisPoints) {
_revert(NewSellTaxBasisPointsExceedsMaximum.selector);
}
uint16 oldBuyTaxBasisPoints = projectBuyTaxBasisPoints;
projectBuyTaxBasisPoints = newProjectBuyTaxBasisPoints_;
uint16 oldSellTaxBasisPoints = projectSellTaxBasisPoints;
projectSellTaxBasisPoints = newProjectSellTaxBasisPoints_;
emit ProjectTaxBasisPointsChanged(
oldBuyTaxBasisPoints,
newProjectBuyTaxBasisPoints_,
oldSellTaxBasisPoints,
newProjectSellTaxBasisPoints_
);
}
/**
* @dev function {setMetadropTaxRates} onlyTaxAdmin
*
* Change the tax rates, subject to max rate and minimum tax period.
*
* @param newMetadropBuyTaxBasisPoints_ The new buy tax rate
* @param newMetadropSellTaxBasisPoints_ The new sell tax rate
*/
function setMetadropTaxRates(
uint16 newMetadropBuyTaxBasisPoints_,
uint16 newMetadropSellTaxBasisPoints_
) external onlyTaxAdmin {
// Cannot increase above the maximum:
if (newMetadropBuyTaxBasisPoints_ > maxMetadropBuyTaxBasisPoints) {
_revert(NewBuyTaxBasisPointsExceedsMaximum.selector);
}
// Cannot increase above the maximum:
if (newMetadropSellTaxBasisPoints_ > maxMetadropSellTaxBasisPoints) {
_revert(NewSellTaxBasisPointsExceedsMaximum.selector);
}
// Reducing the basis points can only occur after the tax period:
if (
(newMetadropBuyTaxBasisPoints_ < metadropBuyTaxBasisPoints ||
newMetadropSellTaxBasisPoints_ < metadropSellTaxBasisPoints) &&
block.timestamp < (fundedDate + (metadropTaxPeriodInDays * 1 days))
) {
_revert(TaxPeriodStillInForce.selector);
}
uint16 oldBuyTaxBasisPoints = metadropBuyTaxBasisPoints;
uint16 oldSellTaxBasisPoints = metadropSellTaxBasisPoints;
metadropBuyTaxBasisPoints = newMetadropBuyTaxBasisPoints_;
metadropSellTaxBasisPoints = newMetadropSellTaxBasisPoints_;
emit MetadropTaxBasisPointsChanged(
oldBuyTaxBasisPoints,
newMetadropBuyTaxBasisPoints_,
oldSellTaxBasisPoints,
newMetadropSellTaxBasisPoints_
);
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev Provide easy to view tax total:
*/
function _totalBuyTaxBasisPoints() internal view returns (uint256) {
return projectBuyTaxBasisPoints + metadropBuyTaxBasisPoints;
}
/**
* @dev Provide easy to view tax total:
*/
function _totalSellTaxBasisPoints() internal view returns (uint256) {
return projectSellTaxBasisPoints + metadropSellTaxBasisPoints;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(
address account
) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(
address to,
uint256 amount
) public virtual override(ERC20Spendable, IERC20) returns (bool) {
address owner = _msgSender();
_transfer(
owner,
to,
amount,
(isLiquidityPool(owner) || isLiquidityPool(to))
);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(
address owner,
address spender
) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(
address spender,
uint256 amount
) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount, (isLiquidityPool(from) || isLiquidityPool(to)));
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(
address spender,
uint256 addedValue
) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(
address spender,
uint256 subtractedValue
) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance < subtractedValue) {
_revert(AllowanceDecreasedBelowZero.selector);
}
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(
address from,
address to,
uint256 amount,
bool applyTax
) internal virtual {
_beforeTokenTransfer(from, to, amount);
// Perform pre-tax validation (e.g. amount doesn't exceed balance, max txn amount)
uint256 fromBalance = _pretaxValidationAndLimits(from, to, amount);
// Perform autoswap if eligible
_autoSwap(from, to);
// Process taxes
uint256 amountMinusTax = _taxProcessing(applyTax, to, from, amount);
// Perform post-tax validation (e.g. total balance after post-tax amount applied)
_posttaxValidationAndLimits(from, to, amountMinusTax);
_balances[from] = fromBalance - amount;
_balances[to] += amountMinusTax;
emit Transfer(from, to, amountMinusTax);
_afterTokenTransfer(from, to, amount);
}
/**
* @dev function {_pretaxValidationAndLimits}
*
* Perform validation on pre-tax amounts
*
* @param from_ From address for the transaction
* @param to_ To address for the transaction
* @param amount_ Amount of the transaction
*/
function _pretaxValidationAndLimits(
address from_,
address to_,
uint256 amount_
) internal view returns (uint256 fromBalance_) {
if (from_ == address(0)) {
_revert(TransferFromZeroAddress.selector);
}
if (to_ == address(0)) {
_revert(TransferToZeroAddress.selector);
}
fromBalance_ = _balances[from_];
if (fromBalance_ < amount_) {
_revert(TransferAmountExceedsBalance.selector);
}
// Liquidity pools aren't always going to round cleanly. This can (and does)
// mean that a limit of 5,000 tokens (for example) will trigger on a transfer
// of 5,000 tokens, as the transfer is actually for 5,000.00000000000000213.
// While 4,999 will work fine, it isn't hugely user friendly. So we buffer
// the limit with rounding decimals, which in all cases are considerably less
// than one whole token:
uint256 roundedLimited;
unchecked {
roundedLimited = maxTokensPerTransaction + ROUND_DEC;
}
if (
tokenHasLimits &&
limitsEnabled &&
(maxTokensPerTransaction != 0) &&
(amount_ > roundedLimited) &&
((isLiquidityPool(from_) && !isUnlimited(to_)) ||
(isLiquidityPool(to_) && !isUnlimited(from_)))
) {
_revert(MaxTokensPerTxnExceeded.selector);
}
return (fromBalance_);
}
/**
* @dev function {_posttaxValidationAndLimits}
*
* Perform validation on post-tax amounts
*
* @param to_ To address for the transaction
* @param amount_ Amount of the transaction
*/
function _posttaxValidationAndLimits(
address from_,
address to_,
uint256 amount_
) internal view returns (uint256 fromBalance_) {
// Liquidity pools aren't always going to round cleanly. This can (and does)
// mean that a limit of 5,000 tokens (for example) will trigger on a max holding
// of 5,000 tokens, as the transfer to achieve that is actually for
// 5,000.00000000000000213. While 4,999 will work fine, it isn't hugely user friendly.
// So we buffer the limit with rounding decimals, which in all cases are considerably
// less than one whole token:
uint256 roundedLimited;
unchecked {
roundedLimited = maxTokensPerWallet + ROUND_DEC;
}
if (
tokenHasLimits &&
limitsEnabled &&
(maxTokensPerWallet != 0) &&
(amount_ + balanceOf(to_) > roundedLimited) &&
// If this is a buy (from a liquidity pool), we apply if the to_
// address isn't noted as unlimited:
(isLiquidityPool(from_) && !isUnlimited(to_))
) {
_revert(MaxTokensPerWalletExceeded.selector);
}
return (fromBalance_);
}
/**
* @dev function {_taxProcessing}
*
* Perform tax processing
*
* @param applyTax_ Do we apply tax to this transaction?
* @param to_ The reciever of the token
* @param from_ The sender of the token
* @param sentAmount_ The amount being send
* @return amountLessTax_ The amount that will be recieved, i.e. the send amount minus tax
*/
function _taxProcessing(
bool applyTax_,
address to_,
address from_,
uint256 sentAmount_
) internal returns (uint256 amountLessTax_) {
amountLessTax_ = sentAmount_;
unchecked {
if (tokenHasTax && applyTax_ && autoSwapEnabled) {
uint256 tax;
// on sell
if (isLiquidityPool(to_) && _totalSellTaxBasisPoints() > 0) {
if (projectSellTaxBasisPoints > 0) {
uint256 projectTax = ((sentAmount_ * projectSellTaxBasisPoints) /
BP_DENOM);
projectTaxPendingSwap += uint128(projectTax);
tax += projectTax;
}
if (metadropSellTaxBasisPoints > 0) {
uint256 metadropTax = ((sentAmount_ * metadropSellTaxBasisPoints) /
BP_DENOM);
metadropTaxPendingSwap += uint128(metadropTax);
tax += metadropTax;
}
}
// on buy
else if (isLiquidityPool(from_) && _totalBuyTaxBasisPoints() > 0) {
if (projectBuyTaxBasisPoints > 0) {
uint256 projectTax = ((sentAmount_ * projectBuyTaxBasisPoints) /
BP_DENOM);
projectTaxPendingSwap += uint128(projectTax);
tax += projectTax;
}
if (metadropBuyTaxBasisPoints > 0) {
uint256 metadropTax = ((sentAmount_ * metadropBuyTaxBasisPoints) /
BP_DENOM);
metadropTaxPendingSwap += uint128(metadropTax);
tax += metadropTax;
}
}
if (tax > 0) {
_balances[address(this)] += tax;
emit Transfer(from_, address(this), tax);
amountLessTax_ -= tax;
}
}
}
return (amountLessTax_);
}
/**
* @dev function {_autoSwap}
*
* Automate the swap of accumulated tax fees to native token
*
* @param from_ The sender of the token
*/
function _autoSwap(address from_, address to_) internal {
if (tokenHasTax) {
uint256 taxBalance = balanceOf(address(this));
if (_eligibleForSwap(from_, to_, taxBalance)) {
// Store that a swap back is in progress:
autoSwapEnabled = false;
// Perform the auto swap to native token:
_swapTaxForNative(taxBalance);
// Flag that the autoswap is complete:
autoSwapEnabled = true;
}
}
}
/**
* @dev function {_eligibleForSwap}
*
* Is the current transfer eligible for autoswap
*
* @param from_ The sender of the token
* @param taxBalance_ The current accumulated tax balance
*/
function _eligibleForSwap(
address from_,
address to_,
uint256 taxBalance_
) internal view returns (bool) {
return (taxBalance_ >=
((_totalSupply * swapThresholdBasisPoints) / BP_DENOM) &&
autoSwapEnabled &&
!isLiquidityPool(from_) &&
from_ != address(uniswapRouter) &&
to_ != address(uniswapRouter));
}
/**
* @dev function {_swapTaxForNative}
*
* Swap tokens taken as tax for native token
*
* @param taxBalance_ The current accumulated tax balance
*/
function _swapTaxForNative(uint256 taxBalance_) internal {
uint256 preSwapBalance = address(this).balance;
address[] memory path = new address[](2);
path[0] = address(this);
path[1] = uniswapRouter.WETH();
uniswapRouter.swapExactTokensForETHSupportingFeeOnTransferTokens(
taxBalance_,
0,
path,
address(this),
block.timestamp
);
uint256 postSwapBalance = address(this).balance;
uint256 balanceToDistribute = postSwapBalance - preSwapBalance;
uint256 projectBalanceToDistribute = (balanceToDistribute *
projectTaxPendingSwap) / (projectTaxPendingSwap + metadropTaxPendingSwap);
uint256 metadropBalanceToDistribute = (balanceToDistribute *
metadropTaxPendingSwap) /
(projectTaxPendingSwap + metadropTaxPendingSwap);
(projectTaxPendingSwap, metadropTaxPendingSwap) = (0, 0);
// Distribute to treasuries:
bool success;
(success, ) = projectTreasury.call{value: projectBalanceToDistribute}("");
if (!success) {
_revert(TransferFailed.selector);
}
(success, ) = metadropTreasury.call{value: metadropBalanceToDistribute}("");
if (!success) {
_revert(TransferFailed.selector);
}
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
if (account == address(0)) {
_revert(MintToZeroAddress.selector);
}
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
if (account == address(0)) {
_revert(BurnFromTheZeroAddress.selector);
}
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
if (accountBalance < amount) {
_revert(BurnExceedsBalance.selector);
}
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
if (owner == address(0)) {
_revert(ApproveFromTheZeroAddress.selector);
}
if (spender == address(0)) {
_revert(ApproveToTheZeroAddress.selector);
}
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(
address owner,
address spender,
uint256 amount
) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
if (currentAllowance < amount) {
_revert(InsufficientAllowance.selector);
}
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Destroys a `value` amount of tokens from the caller.
*
* See {ERC20-_burn}.
*/
function burn(uint256 value) public virtual {
_burn(_msgSender(), value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, deducting from
* the caller's allowance.
*
* See {ERC20-_burn} and {ERC20-allowance}.
*
* Requirements:
*
* - the caller must have allowance for ``accounts``'s tokens of at least
* `value`.
*/
function burnFrom(address account, uint256 value) public virtual {
_spendAllowance(account, _msgSender(), value);
_burn(account, value);
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
receive() external payable {}
}
// SPDX-License-Identifier: MIT
// Metadrop Contracts (v2.1.0)
pragma solidity 0.8.19;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IConfigStructures} from "../../Global/IConfigStructures.sol";
import {IERC20ConfigByMetadropV1} from "./IERC20ConfigByMetadropV1.sol";
/**
* @dev Metadrop core ERC-20 contract, interface
*/
interface IERC20ByMetadropV1 is
IERC20,
IERC20ConfigByMetadropV1,
IERC20Metadata,
IConfigStructures
{
struct SocialLinks {
string linkType;
string link;
}
event AutoSwapThresholdUpdated(uint256 oldThreshold, uint256 newThreshold);
event ProjectTaxBasisPointsChanged(
uint256 oldBuyBasisPoints,
uint256 newBuyBasisPoints,
uint256 oldSellBasisPoints,
uint256 newSellBasisPoints
);
event MetadropTaxBasisPointsChanged(
uint256 oldBuyBasisPoints,
uint256 newBuyBasisPoints,
uint256 oldSellBasisPoints,
uint256 newSellBasisPoints
);
event LiquidityPoolCreated(address addedPool);
event LiquidityPoolAdded(address addedPool);
event LiquidityPoolRemoved(address removedPool);
event InitialLiquidityAdded(uint256 tokenA, uint256 tokenB, uint256 lpToken);
event LiquidityLocked();
event RevenueAutoSwap();
event SetLimitsEnabled(bool enabled);
event TreasuryUpdated(address treasury);
event UnlimitedAddressAdded(address addedUnlimted);
event UnlimitedAddressRemoved(address removedUnlimted);
/**
* @dev function {addInitialLiquidity}
*
* Add initial liquidity to the uniswap pair
*
* @param lockerFee_ The locker fee in wei. This must match the required fee from the external locker contract.
*/
function addInitialLiquidity(uint256 lockerFee_) external payable;
/**
* @dev function {isLiquidityPool}
*
* Return if an address is a liquidity pool
*
* @param queryAddress_ The address being queried
* @return bool The address is / isn't a liquidity pool
*/
function isLiquidityPool(address queryAddress_) external view returns (bool);
/**
* @dev function {addLiquidityPool} onlyTaxAdmin
*
* Allows the tax admin to add a liquidity pool to the pool enumerable set
*
* @param newLiquidityPool_ The address of the new liquidity pool
*/
function addLiquidityPool(address newLiquidityPool_) external;
/**
* @dev function {removeLiquidityPool} onlyTaxAdmin
*
* Allows the tax admin to remove a liquidity pool
*
* @param removedLiquidityPool_ The address of the old removed liquidity pool
*/
function removeLiquidityPool(address removedLiquidityPool_) external;
/**
* @dev function {isUnlimited}
*
* Return if an address is unlimited (is not subject to per txn and per wallet limits)
*
* @param queryAddress_ The address being queried
* @return bool The address is / isn't unlimited
*/
function isUnlimited(address queryAddress_) external view returns (bool);
/**
* @dev function {addUnlimited} onlyTaxAdmin
*
* Allows the tax admin to add an unlimited address
*
* @param newUnlimited_ The address of the new unlimited address
*/
function addUnlimited(address newUnlimited_) external;
/**
* @dev function {removeUnlimited} onlyTaxAdmin
*
* Allows the tax admin to remove an unlimited address
*
* @param removedUnlimited_ The address of the old removed unlimited address
*/
function removeUnlimited(address removedUnlimited_) external;
/**
* @dev function {setLimitsEnabledStatus} onlyTaxAdmin
*
* Allows the tax admin to enable / disable tokens per txn and per holder validation.
*
* @param enabled_ Should limits be on?
*/
function setLimitsEnabledStatus(bool enabled_) external;
/**
* @dev function {setProjectTreasury} onlyTaxAdmin
*
* Allows the tax admin to set the treasury address
*
* @param projectTreasury_ New treasury address
*/
function setProjectTreasury(address projectTreasury_) external;
/**
* @dev function {setSwapThresholdBasisPoints} onlyTaxAdmin
*
* Allows the tax admin to set the autoswap threshold
*
* @param swapThresholdBasisPoints_ New swap threshold in basis points
*/
function setSwapThresholdBasisPoints(
uint16 swapThresholdBasisPoints_
) external;
/**
* @dev function {withdrawETH} onlyOwner
*
* Allows the owner to withdraw ETH
*
* @param amount_ The amount to withdraw
*/
function withdrawETH(uint256 amount_) external;
/**
* @dev function {withdrawERC20} onlyOwner
*
* A withdraw function to allow ERC20s to be withdrawn.
*
* @param token_ The address of the token being withdrawn
* @param amount_ The amount to withdraw
*/
function withdrawERC20(IERC20 token_, uint256 amount_) external;
/**
* @dev function {setProjectTaxRates} onlyTaxAdmin
*
* Change the tax rates, subject to max rate
*
* @param newProjectBuyTaxBasisPoints_ The new buy tax rate
* @param newProjectSellTaxBasisPoints_ The new sell tax rate
*/
function setProjectTaxRates(
uint16 newProjectBuyTaxBasisPoints_,
uint16 newProjectSellTaxBasisPoints_
) external;
/**
* @dev function {setMetadropTaxRates} onlyTaxAdmin
*
* Change the tax rates, subject to max rate and minimum tax period.
*
* @param newMetadropBuyTaxBasisPoints_ The new buy tax rate
* @param newMetadropSellTaxBasisPoints_ The new sell tax rate
*/
function setMetadropTaxRates(
uint16 newMetadropBuyTaxBasisPoints_,
uint16 newMetadropSellTaxBasisPoints_
) external;
}
// SPDX-License-Identifier: MIT
// Metadrop Contracts (v2.1.0)
/**
*
* @title IERC20ByMetadrop.sol. Interface for metadrop ERC20 standard
*
* @author metadrop https://metadrop.com/
*
*/
pragma solidity 0.8.19;
interface IERC20ConfigByMetadropV1 {
struct ERC20Config {
bytes baseParameters;
bytes supplyParameters;
bytes taxParameters;
}
struct ERC20BaseParameters {
string name;
string symbol;
string website;
string twitter;
string telegram;
string otherSocials;
}
struct ERC20SupplyParameters {
uint256 maxSupply;
uint256 lpSupply;
uint256 projectSupply;
uint256 maxTokensPerWallet;
uint256 maxTokensPerTxn;
uint256 lpLockupInDays;
bool addLiquidityOnCreate;
address projectTreasury;
address metadropTreasury;
}
struct ERC20TaxParameters {
uint256 projectBuyTaxBasisPoints;
uint256 projectSellTaxBasisPoints;
uint256 maxProjectBuyTaxBasisPoints;
uint256 maxProjectSellTaxBasisPoints;
uint256 taxSwapThresholdBasisPoints;
uint256 metadropBuyTaxBasisPoints;
uint256 metadropSellTaxBasisPoints;
uint256 maxMetadropBuyTaxBasisPoints;
uint256 maxMetadropSellTaxBasisPoints;
uint256 metadropTaxPeriodInDays;
}
}
// SPDX-License-Identifier: BUSL 1.0
// Metadrop Contracts (v2.1.0)
pragma solidity 0.8.19;
import {AuthorityModel} from "../../Global/AuthorityModel.sol";
import {SafeERC20, IERC20} from "../../Global/OZ/SafeERC20.sol";
import {IERC20FactoryByMetadropV1} from "./IERC20FactoryByMetadropV1.sol";
import {IERC20ByMetadropV1} from "../ERC20/IERC20ByMetadropV1.sol";
import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {IERC20ByMetadropV1, ERC20ByMetadropV1} from "../ERC20/ERC20ByMetadropV1.sol";
/**
* @dev Metadrop ERC-20 factory
*/
contract ERC20FactoryByMetadropV1 is
Context,
IERC20FactoryByMetadropV1,
AuthorityModel
{
using SafeERC20 for IERC20;
// Uniswap router address
address public immutable uniswapRouter;
// Unicrypt locker address
address public immutable unicryptLocker;
// Metadrop trusted oracle address
address public metadropOracleAddress;
// Address for all platform fee payments
address public platformTreasury;
// The oracle signed message validity period:
// Note that maximum is 65,535, therefore 18.2 hours (which seems plenty)
uint16 private messageValidityInSeconds = 30 minutes;
/**
* @dev {constructor}
*
* @param superAdmin_ The address that can add and remove user authority roles. Will also be added as the
* first platform admin.
* @param platformAdmins_ The address(es) for the platform admin(s)
* @param platformTreasury_ The address of the platform treasury. This will be used on primary vesting
* for the platform share of funds and on the royalty payment splitter for the platform share.
* @param metadropOracleAddress_ The address of the metadrop oracle signer
* @param uniswapRouter_ The address of the uniswap router
* @param unicryptLocker_ The address of the unicrypt locker
*/
constructor(
address superAdmin_,
address[] memory platformAdmins_,
address platformTreasury_,
address metadropOracleAddress_,
address uniswapRouter_,
address unicryptLocker_
) {
// The initial instance owner is set as the Ownable owner on all cloned contracts:
if (superAdmin_ == address(0)) {
_revert(SuperAdminCannotBeAddressZero.selector);
}
// superAdmin can grant and revoke all other roles. This address MUST be secured.
// For the duration of this constructor only the super admin is the deployer.
// This is so the deployer can set initial authorities.
// We set to the configured super admin address at the end of the constructor.
superAdmin = _msgSender();
// Grant platform admin to the deployer for the duration of the constructor:
grantPlatformAdmin(_msgSender());
// By default we will revoke the temporary authority for the deployer, BUT,
// if the deployer is in the platform admin array then we want to keep that
// authority, as it has been explicitly set. We handle that situation using
// a bool:
bool revokeDeployerPlatformAdmin = true;
grantPlatformAdmin(superAdmin_);
for (uint256 i = 0; i < platformAdmins_.length; ) {
// Check if the address we are granting for is the deployer. If it is,
// then the deployer address already IS a platform admin and it would be
// a waste of gas to grant again. Instead, we update the bool to show that
// we DON'T want to revoke this permission at the end of this method:
if (platformAdmins_[i] == _msgSender()) {
revokeDeployerPlatformAdmin = false;
} else {
grantPlatformAdmin(platformAdmins_[i]);
}
unchecked {
i++;
}
}
// Set platform treasury:
if (platformTreasury_ == address(0)) {
_revert(PlatformTreasuryCannotBeAddressZero.selector);
}
platformTreasury = platformTreasury_;
if (metadropOracleAddress_ == address(0)) {
_revert(MetadropOracleCannotBeAddressZero.selector);
}
metadropOracleAddress = metadropOracleAddress_;
uniswapRouter = uniswapRouter_;
unicryptLocker = unicryptLocker_;
// This is the factory
factory = address(this);
// Revoke platform admin status of the deployer and transfer superAdmin
// and ownable owner to the superAdmin_.
// Revoke platform admin based on the bool flag set earlier (see above
// for an explanation of how this flag is set)
if (revokeDeployerPlatformAdmin) {
revokePlatformAdmin(_msgSender());
}
if (superAdmin_ != _msgSender()) {
transferSuperAdmin(superAdmin_);
}
}
/**
* @dev function {setMetadropOracleAddress} onlyPlatformAdmin
*
* Set the metadrop trusted oracle address
*
* @param metadropOracleAddress_ Trusted metadrop oracle address
*/
function setMetadropOracleAddress(
address metadropOracleAddress_
) external onlyPlatformAdmin {
if (metadropOracleAddress_ == address(0)) {
_revert(MetadropOracleCannotBeAddressZero.selector);
}
metadropOracleAddress = metadropOracleAddress_;
}
/**
* @dev function {setMessageValidityInSeconds} onlyPlatformAdmin
*
* Set the validity period of signed messages
*
* @param messageValidityInSeconds_ Validity period in seconds for messages signed by the trusted oracle
*/
function setMessageValidityInSeconds(
uint256 messageValidityInSeconds_
) external onlyPlatformAdmin {
messageValidityInSeconds = uint16(messageValidityInSeconds_);
}
/**
* @dev function {setPlatformTreasury} onlySuperAdmin
*
* Set the address that platform fees will be paid to / can be withdrawn to.
* Note that this is restricted to the highest authority level, the super
* admin. Platform admins can trigger a withdrawal to the treasury, but only
* the default admin can set or alter the treasury address. It is recommended
* that the default admin is highly secured and restrited e.g. a multi-sig.
*
* @param platformTreasury_ New treasury address
*/
function setPlatformTreasury(
address platformTreasury_
) external onlySuperAdmin {
if (platformTreasury_ == address(0)) {
_revert(PlatformTreasuryCannotBeAddressZero.selector);
}
platformTreasury = platformTreasury_;
}
/**
* @dev function {withdrawETH} onlyPlatformAdmin
*
* A withdraw function to allow ETH to be withdrawn to the treasury
*
* @param amount_ The amount to withdraw
*/
function withdrawETH(uint256 amount_) external onlyPlatformAdmin {
(bool success, ) = platformTreasury.call{value: amount_}("");
if (!success) {
_revert(TransferFailed.selector);
}
}
/**
* @dev function {withdrawERC20} onlyPlatformAdmin
*
* A withdraw function to allow ERC20s to be withdrawn to the treasury
*
* @param token_ The contract address of the token being withdrawn
* @param amount_ The amount to withdraw
*/
function withdrawERC20(
IERC20 token_,
uint256 amount_
) external onlyPlatformAdmin {
token_.safeTransfer(platformTreasury, amount_);
}
/**
* @dev function {createERC20}
*
* Create an ERC-20
*
* @param metaId_ The drop Id being approved
* @param salt_ Salt for create2
* @param erc20Config_ ERC20 configuration
* @param signedMessage_ The signed message object
* @param lockerFee_ The fee for the unicrypt locker
* @param deploymentFee_ The fee for deployment, if any
* @return deployedAddress_ The deployed ERC20 contract address
*/
function createERC20(
string calldata metaId_,
bytes32 salt_,
ERC20Config calldata erc20Config_,
SignedDropMessageDetails calldata signedMessage_,
uint256 lockerFee_,
uint256 deploymentFee_
) external payable returns (address deployedAddress_) {
// Check the signed message origin and time:
_verifyMessage(signedMessage_);
// We can only proceed if the hash of the passed configuration matches the hash
// signed by our oracle signer:
if (
!_configHashMatches(
metaId_,
salt_,
erc20Config_,
signedMessage_,
lockerFee_,
deploymentFee_,
_msgSender()
)
) {
_revert(PassedConfigDoesNotMatchApproved.selector);
}
(, , , , , , bool addLiquidityOnCreate, , ) = abi.decode(
erc20Config_.supplyParameters,
(
uint256,
uint256,
uint256,
uint256,
uint256,
uint256,
bool,
address,
address
)
);
address[3] memory integrationAddresses = [
msg.sender,
uniswapRouter,
unicryptLocker
];
bytes memory deploymentData = abi.encodePacked(
type(ERC20ByMetadropV1).creationCode,
abi.encode(
integrationAddresses,
erc20Config_.baseParameters,
erc20Config_.supplyParameters,
erc20Config_.taxParameters
)
);
address newERC20;
assembly {
newERC20 := create2(
0,
add(deploymentData, 0x20),
mload(deploymentData),
salt_
)
if iszero(extcodesize(newERC20)) {
revert(0, 0)
}
}
if (addLiquidityOnCreate) {
// Check the fee, we must have enough ETH for the fees, plus at least ONE wei if adding liquidity:
if (msg.value < (lockerFee_ + deploymentFee_)) {
_revert(IncorrectPayment.selector);
}
// Value to pass on (for locking fee plus liquidity, if any) is the sent
// amount minus the deployment fee (if any)
IERC20ByMetadropV1(newERC20).addInitialLiquidity{
value: msg.value - deploymentFee_
}(lockerFee_);
} else {
// Check the fee, we must have ETH for ONLY the deployment fee
if (msg.value != deploymentFee_) {
_revert(IncorrectPayment.selector);
}
}
(string memory tokenName, string memory tokenSymbol) = _getNameAndSymbol(
erc20Config_.baseParameters
);
emit ERC20Created(metaId_, msg.sender, newERC20, tokenName, tokenSymbol);
return (newERC20);
}
/**
* @dev function {_getNameAndSymbol} Create an ERC-20
*
* Decode the name and symbol
*
* @param encodedBaseParams_ Base ERC20 params
* @return name_ The name
* @return symbol_ The symbol
*/
function _getNameAndSymbol(
bytes memory encodedBaseParams_
) internal pure returns (string memory name_, string memory symbol_) {
(name_, symbol_, , , , ) = abi.decode(
encodedBaseParams_,
(string, string, string, string, string, string)
);
return (name_, symbol_);
}
/**
* @dev function {_verifyMessage}
*
* Check the signature and expiry of the passed message
*
* @param signedMessage_ The signed message object
*/
function _verifyMessage(
SignedDropMessageDetails calldata signedMessage_
) internal view {
// Check that this signature is from the oracle signer:
if (
!_validSignature(
signedMessage_.messageHash,
signedMessage_.messageSignature
)
) {
_revert(InvalidOracleSignature.selector);
}
// Check that the signature has not expired:
unchecked {
if (
(signedMessage_.messageTimeStamp + messageValidityInSeconds) <
block.timestamp
) {
_revert(OracleSignatureHasExpired.selector);
}
}
}
/**
* @dev function {_validSignature}
*
* Checks the the signature on the signed message is from the metadrop oracle
*
* @param messageHash_ The message hash signed by the trusted oracle signer. This will be the
* keccack256 hash of received data about this token.
* @param messageSignature_ The signed message from the backend oracle signer for validation.
*/
function _validSignature(
bytes32 messageHash_,
bytes memory messageSignature_
) internal view returns (bool) {
bytes32 ethSignedMessageHash = keccak256(
abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash_)
);
// Check the signature is valid:
return (
SignatureChecker.isValidSignatureNow(
metadropOracleAddress,
ethSignedMessageHash,
messageSignature_
)
);
}
/**
* @dev function {_configHashMatches}
*
* Check the passed config against the stored config hash
*
* @param metaId_ The drop Id being approved
* @param salt_ Salt for create2
* @param erc20Config_ ERC20 configuration
* @param signedMessage_ The signed message object
* @param lockerFee_ The fee for the unicrypt locker
* @param deploymentFee_ The fee for deployment, if any
* @param deployer_ Address performing the deployment
* @return matches_ Whether the hash matches (true) or not (false)
*/
function _configHashMatches(
string calldata metaId_,
bytes32 salt_,
ERC20Config calldata erc20Config_,
SignedDropMessageDetails calldata signedMessage_,
uint256 lockerFee_,
uint256 deploymentFee_,
address deployer_
) internal pure returns (bool matches_) {
// Create the hash of the passed data for comparison:
bytes32 passedConfigHash = createConfigHash(
metaId_,
salt_,
erc20Config_,
signedMessage_.messageTimeStamp,
lockerFee_,
deploymentFee_,
deployer_
);
// Must equal the stored hash:
return (passedConfigHash == signedMessage_.messageHash);
}
/**
* @dev function {createConfigHash}
*
* Create the config hash
*
* @param metaId_ The drop Id being approved
* @param salt_ Salt for create2
* @param erc20Config_ ERC20 configuration
* @param messageTimeStamp_ When the message for this config hash was signed
* @param lockerFee_ The fee for the unicrypt locker
* @param deploymentFee_ The fee for deployment, if any
* @param deployer_ Address performing the deployment
* @return configHash_ The bytes32 config hash
*/
function createConfigHash(
string calldata metaId_,
bytes32 salt_,
ERC20Config calldata erc20Config_,
uint256 messageTimeStamp_,
uint256 lockerFee_,
uint256 deploymentFee_,
address deployer_
) public pure returns (bytes32 configHash_) {
configHash_ = keccak256(
abi.encodePacked(
metaId_,
salt_,
erc20Config_.baseParameters,
erc20Config_.supplyParameters,
erc20Config_.taxParameters,
messageTimeStamp_,
lockerFee_,
deploymentFee_,
deployer_
)
);
return (configHash_);
}
}
// SPDX-License-Identifier: MIT
// Metadrop Contracts (v2.1.0)
pragma solidity 0.8.19;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IConfigStructures} from "../../Global/IConfigStructures.sol";
import {IErrors} from "../../Global/IErrors.sol";
import {IERC20ConfigByMetadropV1} from "../ERC20/IERC20ConfigByMetadropV1.sol";
/**
* @dev Metadrop ERC-20 factory, interface
*/
interface IERC20FactoryByMetadropV1 is
IConfigStructures,
IErrors,
IERC20ConfigByMetadropV1
{
event ERC20Created(
string metaId,
address indexed deployer,
address contractInstance,
string symbol,
string name
);
/**
* @dev function {setMetadropOracleAddress} onlyPlatformAdmin
*
* Set the metadrop trusted oracle address
*
* @param metadropOracleAddress_ Trusted metadrop oracle address
*/
function setMetadropOracleAddress(address metadropOracleAddress_) external;
/**
* @dev function {setMessageValidityInSeconds} onlyPlatformAdmin
*
* Set the validity period of signed messages
*
* @param messageValidityInSeconds_ Validity period in seconds for messages signed by the trusted oracle
*/
function setMessageValidityInSeconds(
uint256 messageValidityInSeconds_
) external;
/**
* @dev function {setPlatformTreasury} onlySuperAdmin
*
* Set the address that platform fees will be paid to / can be withdrawn to.
* Note that this is restricted to the highest authority level, the super
* admin. Platform admins can trigger a withdrawal to the treasury, but only
* the default admin can set or alter the treasury address. It is recommended
* that the default admin is highly secured and restrited e.g. a multi-sig.
*
* @param platformTreasury_ New treasury address
*/
function setPlatformTreasury(address platformTreasury_) external;
/**
* @dev function {withdrawETH} onlyPlatformAdmin
*
* A withdraw function to allow ETH to be withdrawn to the treasury
*
* @param amount_ The amount to withdraw
*/
function withdrawETH(uint256 amount_) external;
/**
* @dev function {withdrawERC20} onlyPlatformAdmin
*
* A withdraw function to allow ERC20s to be withdrawn to the treasury
*
* @param token_ The contract address of the token being withdrawn
* @param amount_ The amount to withdraw
*/
function withdrawERC20(IERC20 token_, uint256 amount_) external;
/**
* @dev function {createERC20}
*
* Create an ERC-20
*
* @param metaId_ The drop Id being approved
* @param salt_ Salt for create2
* @param erc20Config_ ERC20 configuration
* @param signedMessage_ The signed message object
* @param lockerFee_ The fee for the unicrypt locker
* @param deploymentFee_ The fee for deployment, if any
* @return deployedAddress_ The deployed ERC20 contract address
*/
function createERC20(
string calldata metaId_,
bytes32 salt_,
ERC20Config calldata erc20Config_,
SignedDropMessageDetails calldata signedMessage_,
uint256 lockerFee_,
uint256 deploymentFee_
) external payable returns (address deployedAddress_);
/**
* @dev function {createConfigHash}
*
* Create the config hash
*
* @param metaId_ The drop Id being approved
* @param salt_ Salt for create2
* @param erc20Config_ ERC20 configuration
* @param messageTimeStamp_ When the message for this config hash was signed
* @param lockerFee_ The fee for the unicrypt locker
* @param deploymentFee_ The fee for deployment, if any
* @param deployer_ Address performing the deployment
* @return configHash_ The bytes32 config hash
*/
function createConfigHash(
string calldata metaId_,
bytes32 salt_,
ERC20Config calldata erc20Config_,
uint256 messageTimeStamp_,
uint256 lockerFee_,
uint256 deploymentFee_,
address deployer_
) external pure returns (bytes32 configHash_);
}
// SPDX-License-Identifier: MIT
// Metadrop Contracts (v2.1.0)
/**
*
* @title AuthorityModel.sol. Library for global authority components
*
* @author metadrop https://metadrop.com/
*
*/
pragma solidity 0.8.19;
/**
*
* @dev Inheritance details:
* EnumerableSet OZ enumerable mapping sets
* IErrors Interface for platform error definitions
*
*/
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {IErrors} from "./IErrors.sol";
import {Revert} from "./Revert.sol";
contract AuthorityModel is IErrors, Revert {
using EnumerableSet for EnumerableSet.AddressSet;
event SuperAdminTransferred(address oldSuperAdmin, address newSuperAdmin);
event PlatformAdminAdded(address platformAdmin);
event PlatformAdminRevoked(address platformAdmin);
// Address for the factory:
address public factory;
// The super admin can grant and revoke roles
address public superAdmin;
// Enumerable set to store platform admins:
EnumerableSet.AddressSet private _platformAdmins;
/** ====================================================================================================================
* MODIFIERS
* =====================================================================================================================
*/
/** ____________________________________________________________________________________________________________________
* -->ACCESS CONTROL
* @dev (modifier) onlySuperAdmin. The associated action can only be taken by the super admin (an address with the
* default admin role).
*
* _____________________________________________________________________________________________________________________
*/
modifier onlySuperAdmin() {
if (!isSuperAdmin(msg.sender)) revert CallerIsNotSuperAdmin(msg.sender);
_;
}
/** ____________________________________________________________________________________________________________________
* -->ACCESS CONTROL
* @dev (modifier) onlyPlatformAdmin. The associated action can only be taken by an address with the
* platform admin role.
*
* _____________________________________________________________________________________________________________________
*/
modifier onlyPlatformAdmin() {
if (!isPlatformAdmin(msg.sender))
revert CallerIsNotPlatformAdmin(msg.sender);
_;
}
/** ____________________________________________________________________________________________________________________
* -->GETTER
* @dev (function) isSuperAdmin check if an address is the super admin
*
* ---------------------------------------------------------------------------------------------------------------------
* @return bool
* ---------------------------------------------------------------------------------------------------------------------
* _____________________________________________________________________________________________________________________
*/
function isSuperAdmin(address queryAddress_) public view returns (bool) {
return (superAdmin == queryAddress_);
}
/** ____________________________________________________________________________________________________________________
* -->GETTER
* @dev (function) isPlatformAdmin check if an address is a platform admin
*
* ---------------------------------------------------------------------------------------------------------------------
* @return bool
* ---------------------------------------------------------------------------------------------------------------------
* _____________________________________________________________________________________________________________________
*/
function isPlatformAdmin(address queryAddress_) public view returns (bool) {
return (_platformAdmins.contains(queryAddress_));
}
/** ____________________________________________________________________________________________________________________
* -->ACCESS CONTROL
* @dev (function) grantPlatformAdmin Allows the super user Default Admin to add an address to the platform admin group
*
* ---------------------------------------------------------------------------------------------------------------------
* @param newPlatformAdmin_ The address of the new platform admin
* ---------------------------------------------------------------------------------------------------------------------
* _____________________________________________________________________________________________________________________
*/
function grantPlatformAdmin(address newPlatformAdmin_) public onlySuperAdmin {
if (newPlatformAdmin_ == address(0)) {
_revert(PlatformAdminCannotBeAddressZero.selector);
}
// Add this to the enumerated list:
_platformAdmins.add(newPlatformAdmin_);
emit PlatformAdminAdded(newPlatformAdmin_);
}
/** ____________________________________________________________________________________________________________________
* -->ACCESS CONTROL
* @dev (function) revokePlatformAdmin Allows the super user Default Admin to revoke from the platform admin group
*
* ---------------------------------------------------------------------------------------------------------------------
* @param oldPlatformAdmin_ The address of the old platform admin
* ---------------------------------------------------------------------------------------------------------------------
* _____________________________________________________________________________________________________________________
*/
function revokePlatformAdmin(
address oldPlatformAdmin_
) public onlySuperAdmin {
// Remove this from the enumerated list:
_platformAdmins.remove(oldPlatformAdmin_);
emit PlatformAdminRevoked(oldPlatformAdmin_);
}
/** ____________________________________________________________________________________________________________________
* -->ACCESS CONTROL
* @dev (function) transferSuperAdmin Allows the super user Default Admin to transfer this right to another address
*
* ---------------------------------------------------------------------------------------------------------------------
* @param newSuperAdmin_ The address of the new default admin
* ---------------------------------------------------------------------------------------------------------------------
* _____________________________________________________________________________________________________________________
*/
function transferSuperAdmin(address newSuperAdmin_) public onlySuperAdmin {
address oldSuperAdmin = superAdmin;
// Update storage of this address:
superAdmin = newSuperAdmin_;
emit SuperAdminTransferred(oldSuperAdmin, newSuperAdmin_);
}
}
// SPDX-License-Identifier: MIT
// Metadrop Contracts (v2.1.0)
/**
*
* @title IConfigStructures.sol. Interface for common config structures used accross the platform
*
* @author metadrop https://metadrop.com/
*
*/
pragma solidity 0.8.19;
interface IConfigStructures {
enum DropStatus {
approved,
deployed,
cancelled
}
enum TemplateStatus {
live,
terminated
}
// The current status of the mint:
// - notEnabled: This type of mint is not part of this drop
// - notYetOpen: This type of mint is part of the drop, but it hasn't started yet
// - open: it's ready for ya, get in there.
// - finished: been and gone.
// - unknown: theoretically impossible.
enum MintStatus {
notEnabled,
notYetOpen,
open,
finished,
unknown
}
struct SubListConfig {
uint256 start;
uint256 end;
uint256 phaseMaxSupply;
}
struct PrimarySaleModuleInstance {
address instanceAddress;
string instanceDescription;
}
struct NFTModuleConfig {
uint256 templateId;
bytes configData;
bytes vestingData;
}
struct PrimarySaleModuleConfig {
uint256 templateId;
bytes configData;
}
struct ProjectBeneficiary {
address payable payeeAddress;
uint256 payeeShares;
}
struct VestingConfig {
uint256 start;
uint256 projectUpFrontShare;
uint256 projectVestedShare;
uint256 vestingPeriodInDays;
uint256 vestingCliff;
ProjectBeneficiary[] projectPayees;
}
struct RoyaltySplitterModuleConfig {
uint256 templateId;
bytes configData;
}
struct InLifeModuleConfig {
uint256 templateId;
bytes configData;
}
struct InLifeModules {
InLifeModuleConfig[] modules;
}
struct NFTConfig {
uint256 supply;
string name;
string symbol;
bytes32 positionProof;
bool includePriorPhasesInMintTracking;
bool singleMetadataCollection;
uint256 reservedAllocation;
uint256 assistanceRequestWindowInSeconds;
}
struct Template {
TemplateStatus status;
uint16 templateNumber;
uint32 loadedDate;
address payable templateAddress;
string templateDescription;
}
struct RoyaltyDetails {
address newRoyaltyPaymentSplitterInstance;
uint96 royaltyFromSalesInBasisPoints;
}
struct SignedDropMessageDetails {
uint256 messageTimeStamp;
bytes32 messageHash;
bytes messageSignature;
}
}
// SPDX-License-Identifier: MIT
// Metadrop Contracts (v2.1.0)
/**
*
* @title IErrors.sol. Interface for error definitions used across the platform
*
* @author metadrop https://metadrop.com/
*
*/
pragma solidity 0.8.19;
interface IErrors {
enum BondingCurveErrorType {
OK, // No error
INVALID_NUMITEMS, // The numItem value is 0
SPOT_PRICE_OVERFLOW // The updated spot price doesn't fit into 128 bits
}
error AdapterParamsMustBeEmpty(); // The adapter parameters on this LZ call must be empty.
error AddressAlreadySet(); // The address being set can only be set once, and is already non-0.
error AllowanceDecreasedBelowZero(); // You cannot decrease the allowance below zero.
error AlreadyInitialised(); // The contract is already initialised: it cannot be initialised twice!
error ApprovalCallerNotOwnerNorApproved(); // The caller must own the token or be an approved operator.
error ApproveFromTheZeroAddress(); // Approval cannot be called from the zero address (indeed, how have you??).
error ApproveToTheZeroAddress(); // Approval cannot be given to the zero address.
error ApprovalQueryForNonexistentToken(); // The token does not exist.
error AuctionStatusIsNotEnded(); // Throw if the action required the auction to be closed, and it isn't.
error AuctionStatusIsNotOpen(); // Throw if the action requires the auction to be open, and it isn't.
error AuxCallFailed(
address[] modules,
uint256 value,
bytes data,
uint256 txGas
); // An auxilliary call from the drop factory failed.
error BalanceQueryForZeroAddress(); // Cannot query the balance for the zero address.
error BidMustBeBelowTheFloorWhenReducingQuantity(); // Only bids that are below the floor can reduce the quantity of the bid.
error BidMustBeBelowTheFloorForRefundDuringAuction(); // Only bids that are below the floor can be refunded during the auction.
error BondingCurveError(BondingCurveErrorType error); // An error of the type specified has occured in bonding curve processing.
error BurnExceedsBalance(); // The amount you have selected to burn exceeds the addresses balance.
error BurnFromTheZeroAddress(); // Tokens cannot be burned from the zero address. (Also, how have you called this!?!)
error CallerIsNotFactory(); // The caller of this function must match the factory address in storage.
error CallerIsNotFactoryOrProjectOwner(); // The caller of this function must match the factory address OR project owner address.
error CallerIsNotTheOwner(); // The caller is not the owner of this contract.
error CallerIsNotTheTaxAdmin(); // The caller is not the tax admin of this contract.
error CallerMustBeLzApp(); // The caller must be an LZ application.
error CallerIsNotPlatformAdmin(address caller); // The caller of this function must be part of the platformAdmin group.
error CallerIsNotSuperAdmin(address caller); // The caller of this function must match the superAdmin address in storage.
error CannotSetNewOwnerToTheZeroAddress(); // You can't set the owner of this contract to the zero address (address(0)).
error CannotSetToZeroAddress(); // The corresponding address cannot be set to the zero address (address(0)).
error CannotSetNewTaxAdminToTheZeroAddress(); // Cannot transfer the tax admin to the zero address (address(0)).
error CollectionAlreadyRevealed(); // The collection is already revealed; you cannot call reveal again.
error ContractIsPaused(); // The call requires the contract to be unpaused, and it is paused.
error ContractIsNotPaused(); // The call required the contract to be paused, and it is NOT paused.
error DecreasedAllowanceBelowZero(); // The request would decrease the allowance below zero, and that is not allowed.
error DestinationIsNotTrustedSource(); // The destination that is being called through LZ has not been set as trusted.
error GasLimitIsTooLow(); // The gas limit for the LayerZero call is too low.
error IncorrectConfirmationValue(); // You need to enter the right confirmation value to call this funtion (usually 69420).
error IncorrectPayment(); // The function call did not include passing the correct payment.
error InitialLiquidityAlreadyAdded(); // Initial liquidity has already been added. You can't do it again.
error InsufficientAllowance(); // There is not a high enough allowance for this operation.
error InvalidAdapterParams(); // The current adapter params for LayerZero on this contract won't work :(.
error InvalidAddress(); // An address being processed in the function is not valid.
error InvalidEndpointCaller(); // The calling address is not a valid LZ endpoint. The LZ endpoint was set at contract creation
// and cannot be altered after. Check the address LZ endpoint address on the contract.
error InvalidMinGas(); // The minimum gas setting for LZ in invalid.
error InvalidOracleSignature(); // The signature provided with the contract call is not valid, either in format or signer.
error InvalidPayload(); // The LZ payload is invalid
error InvalidReceiver(); // The address used as a target for funds is not valid.
error InvalidSourceSendingContract(); // The LZ message is being related from a source contract on another chain that is NOT trusted.
error InvalidTotalShares(); // Total shares must equal 100 percent in basis points.
error ListLengthMismatch(); // Two or more lists were compared and they did not match length.
error LiquidityPoolMustBeAContractAddress(); // Cannot add a non-contract as a liquidity pool.
error LiquidityPoolCannotBeAddressZero(); // Cannot add a liquidity pool from the zero address.
error LPLockUpMustFitUint96(); // LP lockup is held in a uint96, so must fit.
error NoTrustedPathRecord(); // LZ needs a trusted path record for this to work. What's that, you ask?
error MaxBidQuantityIs255(); // Validation: as we use a uint8 array to track bid positions the max bid quantity is 255.
error MaxPublicMintAllowanceExceeded(
uint256 requested,
uint256 alreadyMinted,
uint256 maxAllowance
); // The calling address has requested a quantity that would exceed the max allowance.
error MaxSupplyTooHigh(); // Max supply must fit in a uint128.
error MaxTokensPerWalletExceeded(); // The transfer would exceed the max tokens per wallet limit.
error MaxTokensPerTxnExceeded(); // The transfer would exceed the max tokens per transaction limit.
error MetadataIsLocked(); // The metadata on this contract is locked; it cannot be altered!
error MetadropFactoryOnlyOncePerReveal(); // This function can only be called (a) by the factory and, (b) just one time!
error MetadropModulesOnly(); // Can only be called from a metadrop contract.
error MetadropOracleCannotBeAddressZero(); // The metadrop Oracle cannot be the zero address (address(0)).
error MinGasLimitNotSet(); // The minimum gas limit for LayerZero has not been set.
error MintERC2309QuantityExceedsLimit(); // The `quantity` minted with ERC2309 exceeds the safety limit.
error MintingIsClosedForever(); // Minting is, as the error suggests, so over (and locked forever).
error MintToZeroAddress(); // Cannot mint to the zero address.
error MintZeroQuantity(); // The quantity of tokens minted must be more than zero.
error NewBuyTaxBasisPointsExceedsMaximum(); // Project owner trying to set the tax rate too high.
error NewSellTaxBasisPointsExceedsMaximum(); // Project owner trying to set the tax rate too high.
error NoETHForLiquidityPair(); // No ETH has been provided for the liquidity pair.
error TaxPeriodStillInForce(); // The minimum tax period has not yet expired.
error NoPaymentDue(); // No payment is due for this address.
error NoRefundForCaller(); // Error thrown when the calling address has no refund owed.
error NoStoredMessage(); // There is no stored message matching the passed parameters.
error NoTokenForLiquidityPair(); // There is no token to add to the LP.
error OperationDidNotSucceed(); // The operation failed (vague much?).
error OracleSignatureHasExpired(); // A signature has been provided but it is too old.
error OwnershipNotInitializedForExtraData(); // The `extraData` cannot be set on an uninitialized ownership slot.
error OwnerQueryForNonexistentToken(); // The token does not exist.
error ParametersDoNotMatchSignedMessage(); // The parameters passed with the signed message do not match the message itself.
error PassedConfigDoesNotMatchApproved(); // The config provided on the call does not match the approved config.
error PauseCutOffHasPassed(); // The time period in which we can pause has passed; this contract can no longer be paused.
error PaymentMustCoverPerMintFee(); // The payment passed must at least cover the per mint fee for the quantity requested.
error PermitDidNotSucceed(); // The safeERC20 permit failed.
error PlatformAdminCannotBeAddressZero(); // We cannot use the zero address (address(0)) as a platformAdmin.
error PlatformTreasuryCannotBeAddressZero(); // The treasury address cannot be set to the zero address.
error ProjectOwnerCannotBeAddressZero(); // The project owner has to be a non zero address.
error ProofInvalid(); // The provided proof is not valid with the provided arguments.
error QuantityExceedsRemainingCollectionSupply(); // The requested quantity would breach the collection supply.
error QuantityExceedsRemainingPhaseSupply(); // The requested quantity would breach the phase supply.
error QuantityExceedsMaxPossibleCollectionSupply(); // The requested quantity would breach the maximum trackable supply
error ReferralIdAlreadyUsed(); // This referral ID has already been used; they are one use only.
error RequestingMoreThanRemainingAllocation(
uint256 previouslyMinted,
uint256 requested,
uint256 remainingAllocation
); // Number of tokens requested for this mint exceeds the remaining allocation (taking the
// original allocation from the list and deducting minted tokens).
error RoyaltyFeeWillExceedSalePrice(); // The ERC2981 royalty specified will exceed the sale price.
error ShareTotalCannotBeZero(); // The total of all the shares cannot be nothing.
error SliceOutOfBounds(); // The bytes slice operation was out of bounds.
error SliceOverflow(); // The bytes slice operation overlowed.
error SuperAdminCannotBeAddressZero(); // The superAdmin cannot be the sero address (address(0)).
error SupplyTotalMismatch(); // The sum of the team supply and lp supply does not match.
error SupportWindowIsNotOpen(); // The project owner has not requested support within the support request expiry window.
error TaxFreeAddressCannotBeAddressZero(); // A tax free address cannot be address(0)
error TemplateCannotBeAddressZero(); // The address for a template cannot be address zero (address(0)).
error TemplateNotFound(); // There is no template that matches the passed template Id.
error ThisMintIsClosed(); // It's over (well, this mint is, anyway).
error TotalSharesMustMatchDenominator(); // The total of all shares must equal the denominator value.
error TransferAmountExceedsBalance(); // The transfer amount exceeds the accounts available balance.
error TransferCallerNotOwnerNorApproved(); // The caller must own the token or be an approved operator.
error TransferFailed(); // The transfer has failed.
error TransferFromIncorrectOwner(); // The token must be owned by `from`.
error TransferToNonERC721ReceiverImplementer(); // Cannot safely transfer to a contract that does not implement the ERC721Receiver interface.
error TransferFromZeroAddress(); // Cannot transfer from the zero address. Indeed, this surely is impossible, and likely a waste to check??
error TransferToZeroAddress(); // Cannot transfer to the zero address.
error UnrecognisedVRFMode(); // Currently supported VRF modes are 0: chainlink and 1: arrng
error URIQueryForNonexistentToken(); // The token does not exist.
error ValueExceedsMaximum(); // The value sent exceeds the maximum allowed (super useful explanation huh?).
error VRFCoordinatorCannotBeAddressZero(); // The VRF coordinator cannot be the zero address (address(0)).
}
// SPDX-License-Identifier: MIT
// Metadrop Contracts (v2.1.0)
// Metadrop based on OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity 0.8.19;
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {IErrors} from "../IErrors.sol";
import {Revert} from "../Revert.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is IErrors, Revert, Context {
address private _owner;
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
_revert(CallerIsNotTheOwner.selector);
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
_revert(CannotSetNewOwnerToTheZeroAddress.selector);
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
// SPDX-License-Identifier: MIT
// Metadrop Contracts (v2.1.0)
// Metadrop based on OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity 0.8.19;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {IErrors} from "../IErrors.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(
token,
abi.encodeCall(token.transferFrom, (from, to, value))
);
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
if (oldAllowance < value) {
revert IErrors.DecreasedAllowanceBelowZero();
}
forceApprove(token, spender, oldAllowance - value);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Compatible with tokens that require the approval to be set to
* 0 before setting it to a non-zero value.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
if (nonceAfter != (nonceBefore + 1)) {
revert IErrors.PermitDidNotSucceed();
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "call fail");
if ((returndata.length != 0) && !abi.decode(returndata, (bool))) {
revert IErrors.OperationDidNotSucceed();
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(
IERC20 token,
bytes memory data
) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success &&
(returndata.length == 0 || abi.decode(returndata, (bool))) &&
address(token).code.length > 0;
}
}
// SPDX-License-Identifier: MIT
// Metadrop Contracts (v2.1.0)
/**
*
* @title Revert.sol. For efficient reverts
*
* @author metadrop https://metadrop.com/
*
*/
pragma solidity 0.8.19;
abstract contract Revert {
/**
* @dev For more efficient reverts.
*/
function _revert(bytes4 errorSelector) internal pure {
assembly {
mstore(0x00, errorSelector)
revert(0x00, 0x04)
}
}
}
// SPDX-License-Identifier: MIT
// Metadrop Contracts (v2.1.0)
// Metadrop based on OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity 0.8.19;
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {IErrors} from "./IErrors.sol";
import {Revert} from "./Revert.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract TaxAdmin is IErrors, Revert, Context {
address private _taxAdmin;
event TaxAdminTransferred(
address indexed previousTaxAdmin,
address indexed newTaxAdmin
);
constructor() {}
/**
* @dev Throws if called by any account other than the tax admin.
*/
modifier onlyTaxAdmin() {
_checkTaxAdmin();
_;
}
/**
* @dev Returns the address of the current tax admin.
*/
function taxAdmin() public view virtual returns (address) {
return _taxAdmin;
}
/**
* @dev Throws if the sender is not the tax admin.
*/
function _checkTaxAdmin() internal view virtual {
if (taxAdmin() != _msgSender()) {
_revert(CallerIsNotTheTaxAdmin.selector);
}
}
/**
* @dev Leaves the contract without a tax admin. It will not be possible to call
* `onlyTaxAdmin` functions. Can only be called by the current tax admin.
*
* NOTE: Renouncing taxAdmin will leave the contract without an tax admim,
* thereby disabling any functionality that is only available to the tax admin.
*/
function renounceTaxAdmin() public virtual onlyTaxAdmin {
_transferTaxAdmin(address(0));
}
/**
* @dev Transfers the tax admin of the contract to a new account (`newTaxAdmin`).
* Can only be called by the current tax admin.
*/
function transferTaxAdmin(address newTaxAdmin) public virtual onlyTaxAdmin {
if (newTaxAdmin == address(0)) {
_revert(CannotSetNewTaxAdminToTheZeroAddress.selector);
}
_transferTaxAdmin(newTaxAdmin);
}
/**
* @dev Transfers the tax admin of the contract to a new account (`newTaxAdmin`).
* Internal function without access restriction.
*/
function _transferTaxAdmin(address newTaxAdmin) internal virtual {
address oldTaxAdmin = _taxAdmin;
_taxAdmin = newTaxAdmin;
emit TaxAdminTransferred(oldTaxAdmin, newTaxAdmin);
}
}
// SPDX-License-Identifier: CC0-1.0
// Omnus Contracts v3
// https://omn.us/spendable
// https://github.com/omnus/ERC20Spendable
// npm: @omnus/ERC20Spendable
pragma solidity 0.8.19;
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {IERC20Spendable} from "./IERC20Spendable.sol";
import {IERC20SpendableReceiver} from "./IERC20SpendableReceiver.sol";
/**
* @title ERC-7492 ERC20Spendable.sol
*
* @author omnus
* https://omn.us
*
* @dev Implementation of {ERC20Spendable}.
*
* {ERC-7492 ERC20Spendable} allows ERC20s to operate as 'spendable' items, i.e. an ERC20 token that
* can trigger an action on another contract at the same time as being transfered. Similar to ERC677
* and the hooks in ERC777, but with more of an empasis on interoperability (returned values) than
* ERC677 and specifically scoped interaction rather than the general hooks of ERC777.
*
* For more detailed notes please see our guide https://omn.us/how-to-implement-erc20-spendable
*/
abstract contract ERC20Spendable is Context, IERC20Spendable {
/**
* @dev {spend} Allows the transfer of the owners token to the receiver, a call on the receiver,
* and then the return of information from the receiver back up the call stack.
*
* Overloaded method - call this if you are not specifying any arguments.
*
* @param receiver_ The receiving address for this token spend. Contracts must implement
* ERCSpendableReceiver to receive spendadle tokens. For more detail see {ERC20SpendableReceiver}.
* @param spent_ The amount of token being spent. This will be transfered as part of this call and
* provided as an argument on the call to {onERC20SpendableReceived} on the {ERC20SpendableReceiver}.
*/
function spend(address receiver_, uint256 spent_) public virtual {
spend(receiver_, spent_, "");
}
/**
* @dev {spend} Allows the transfer of the owners token to the receiver, a call on the receiver, and
* the return of information from the receiver back up the call stack.
*
* Overloaded method - call this to specify a bytes argument.
*
* @param receiver_ The receiving address for this token spend. Contracts must implement
* ERCSpendableReceiver to receive spendadle tokens. For more detail see {ERC20SpendableReceiver}.
* @param spent_ The amount of token being spent. This will be transfered as part of this call and
* provided as an argument on the call to {onERC20SpendableReceived} on the {ERC20SpendableReceiver}.
* @param arguments_ Bytes argument to send with the call. See {mock} contracts for details on encoding
* and decoding arguments from bytes.
*/
function spend(
address receiver_,
uint256 spent_,
bytes memory arguments_
) public virtual {
/**
* @dev Transfer tokens to the receiver contract IF this is a non-0 amount. Don't try and transfer 0,
* which leavesopen the possibility that the call is free. If not, the function call after will fail
* and revert. Why would a {spend} method call ever be free? For example, a service provider may be
* taking their ERC20 token as payment for a service. But they want to offer it for free, perhaps for a
* limited time. Under this situation the spend callcan be used in all cases, but sending 0 token while
* it is free, removing the need for different interfaces.
*
* We use the standard ERC20 public transfer method for the transfer, which means two things:
* 1) This can only be called by the token owner (but that is the entire point!)
* 2) We inherit all of the security checks in this method (e.g. owner has sufficient balance etc.)
*/
if (spent_ != 0) {
transfer(receiver_, spent_);
}
/**
* @dev Perform actions on the receiver and return arguments back up the callstack. In addition to allowing
* the execution of the hook within the receiver, this call provides the same feature as onERC721Received
* in the ERC721 standard.
*/
if (receiver_.code.length > 0) {
try
IERC20SpendableReceiver(receiver_).onERC20SpendableReceived(
_msgSender(),
spent_,
arguments_
)
returns (bytes4 retval, bytes memory returnedArguments) {
if (
retval != IERC20SpendableReceiver.onERC20SpendableReceived.selector
) {
revert ERC20SpendableInvalidReveiver(receiver_);
}
emit SpendReceipt(
_msgSender(),
receiver_,
spent_,
arguments_,
returnedArguments
);
/// @dev Handle returned values. Specify an override {_handleReceipt} method in your ERC20 contract if
/// you wish to handle returned arguments.
_handleReceipt(returnedArguments);
} catch (bytes memory reason) {
if (reason.length == 0) {
revert ERC20SpendableInvalidReveiver(receiver_);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
/**
* @dev {_handleReceipt} Internal function called on completion of a call to {onERC20SpendableReceived}
* on the {ERC20SpendableReceiver}.
*
* When making a token {ERC20Spendable} if you wish to process receipts you need to override
* {_handleReceipt} in your contract. For an example, see {mock} contract {MockSpendableERC20ReturnedArgs}.
*
* @param returnedArguments_ Bytes argument to returned from the call. See {mock} contracts for details on
* encoding and decoding arguments from bytes.
*/
function _handleReceipt(bytes memory returnedArguments_) internal virtual {}
/**
* @dev See {IERC165-supportsInterface}. This can be used to determine if an ERC20 is ERC20Spendable. For
* example, a DEX may check this value, and make use of a single {spend} transaction (rather than the current
* model of [approve -> pull]) if the ERC20Spendable interface is supported.
*
* @param interfaceId_ The bytes4 interface identifier being checked.
*/
function supportsInterface(
bytes4 interfaceId_
) public view virtual returns (bool) {
// The interface IDs are constants representing the first 4 bytes
// of the XOR of all function selectors in the interface.
// See: [ERC165](https://eips.ethereum.org/EIPS/eip-165)
// (e.g. `bytes4(i.functionA.selector ^ i.functionB.selector ^ ...)`)
return interfaceId_ == type(IERC20Spendable).interfaceId;
}
/**
* @dev override this in your implementation
*
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual returns (bool) {}
}
// SPDX-License-Identifier: CC0-1.0
// Omnus Contracts v3
// https://omn.us/spendable
// https://github.com/omnus/ERC20Spendable
// npm: @omnus/ERC20Spendable
pragma solidity ^0.8.19;
/**
* @title ERC-7492 IERC20Spendable.sol
*
* @author omnus
* https://omn.us
*
* @dev Implementation of {IERC20Spendable} interface.
*
* {ERC-7492 ERC20Spendable} allows ERC20s to operate as 'spendable' items, i.e. an ERC20 token that
* can trigger an action on another contract at the same time as being transfered. Similar to ERC677
* and the hooks in ERC777, but with more of an empasis on interoperability (returned values) than
* ERC677 and specifically scoped interaction rather than the general hooks of ERC777.
*
* For more detailed notes please see our guide https://omn.us/how-to-implement-erc20-spendable
*/
interface IERC20Spendable {
/// @dev Error {ERC20SpendableInvalidReveiver} The called contract does not support ERC20Spendable.
error ERC20SpendableInvalidReveiver(address receiver);
/// @dev Event {SpendReceipt} issued on successful return from the {ERC20SpendableReceiver} call.
event SpendReceipt(
address spender,
address receiver,
uint256 amount,
bytes sentArguments,
bytes returnedArguments
);
/**
* @dev {spend} Allows the transfer of the owners token to the receiver, a call on the receiver,
* and then the return of information from the receiver back up the call stack.
*
* Overloaded method - call this if you are not specifying any arguments.
*
* @param receiver_ The receiving address for this token spend. Contracts must implement
* ERCSpendableReceiver to receive spendadle tokens. For more detail see {ERC20SpendableReceiver}.
* @param spent_ The amount of token being spent. This will be transfered as part of this call and
* provided as an argument on the call to {onERC20SpendableReceived} on the {ERC20SpendableReceiver}.
*/
function spend(address receiver_, uint256 spent_) external;
/**
* @dev {spend} Allows the transfer of the owners token to the receiver, a call on the receiver, and
* the return of information from the receiver back up the call stack.
*
* Overloaded method - call this to specify a bytes argument.
*
* @param receiver_ The receiving address for this token spend. Contracts must implement
* ERCSpendableReceiver to receive spendadle tokens. For more detail see {ERC20SpendableReceiver}.
* @param spent_ The amount of token being spent. This will be transfered as part of this call and
* provided as an argument on the call to {onERC20SpendableReceived} on the {ERC20SpendableReceiver}.
* @param arguments_ Bytes argument to send with the call. See {mock} contracts for details on encoding
* and decoding arguments from bytes.
*/
function spend(
address receiver_,
uint256 spent_,
bytes memory arguments_
) external;
}
// SPDX-License-Identifier: CC0-1.0
// Omnus Contracts v3
// https://omn.us/spendable
// https://github.com/omnus/ERC20Spendable
// npm: @omnus/ERC20Spendable
pragma solidity ^0.8.19;
/**
* @title ERC-7492 IERC20SpendableReceiver.sol
*
* @author omnus
* https://omn.us
*
* @dev Implementation of {IERC20SpendableReceiver} interface.
*
* {ERC-7492 ERC20Spendable} allows ERC20s to operate as 'spendable' items, i.e. an ERC20 token that
* can trigger an action on another contract at the same time as being transfered. Similar to ERC677
* and the hooks in ERC777, but with more of an empasis on interoperability (returned values) than
* ERC677 and specifically scoped interaction rather than the general hooks of ERC777.
*
* For more detailed notes please see our guide https://omn.us/how-to-implement-erc20-spendable
*/
interface IERC20SpendableReceiver {
/// @dev Error {CallMustBeFromSpendableToken}. The call to this method can only be from a designated spendable token.
error CallMustBeFromSpendableToken();
/**
* @dev {onERC20SpendableReceived} External function called by ERC20SpendableTokens. This
* validates that the token is valid and then calls the internal {_handleSpend} method.
* You must overried {_handleSpend} in your contract to perform processing you wish to occur
* on token spend.
*
* This method will pass back the valid bytes4 selector and any bytes argument passed from
* {_handleSpend}.
*
* @param spender_ The address spending the ERC20Spendable
* @param spent_ The amount of token spent
* @param arguments_ Bytes sent with the call
*/
function onERC20SpendableReceived(
address spender_,
uint256 spent_,
bytes memory arguments_
) external returns (bytes4 retval_, bytes memory returnArguments_);
}
// Interface definition for UniswapV2Locker.sol
pragma solidity 0.8.19;
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
interface IERCBurn {
function burn(uint256 _amount) external;
function approve(address spender, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external returns (uint256);
function balanceOf(address account) external view returns (uint256);
}
interface IMigrator {
function migrate(
address lpToken,
uint256 amount,
uint256 unlockDate,
address owner
) external returns (bool);
}
interface IUniswapV2Locker {
struct UserInfo {
EnumerableSet.AddressSet lockedTokens; // records all tokens the user has locked
mapping(address => uint256[]) locksForToken; // map erc20 address to lock id for that token
}
struct TokenLock {
uint256 lockDate; // the date the token was locked
uint256 amount; // the amount of tokens still locked (initialAmount minus withdrawls)
uint256 initialAmount; // the initial lock amount
uint256 unlockDate; // the date the token can be withdrawn
uint256 lockID; // lockID nonce per uni pair
address owner;
}
struct FeeStruct {
uint256 ethFee; // Small eth fee to prevent spam on the platform
IERCBurn secondaryFeeToken; // UNCX or UNCL
uint256 secondaryTokenFee; // optional, UNCX or UNCL
uint256 secondaryTokenDiscount; // discount on liquidity fee for burning secondaryToken
uint256 liquidityFee; // fee on univ2 liquidity tokens
uint256 referralPercent; // fee for referrals
IERCBurn referralToken; // token the refferer must hold to qualify as a referrer
uint256 referralHold; // balance the referrer must hold to qualify as a referrer
uint256 referralDiscount; // discount on flatrate fees for using a valid referral address
}
function setDev(address payable _devaddr) external;
/**
* @notice set the migrator contract which allows locked lp tokens to be migrated to uniswap v3
*/
function setMigrator(IMigrator _migrator) external;
function setSecondaryFeeToken(address _secondaryFeeToken) external;
/**
* @notice referrers need to hold the specified token and hold amount to be elegible for referral fees
*/
function setReferralTokenAndHold(
IERCBurn _referralToken,
uint256 _hold
) external;
function setFees(
uint256 _referralPercent,
uint256 _referralDiscount,
uint256 _ethFee,
uint256 _secondaryTokenFee,
uint256 _secondaryTokenDiscount,
uint256 _liquidityFee
) external;
/**
* @notice whitelisted accounts dont pay flatrate fees on locking
*/
function whitelistFeeAccount(address _user, bool _add) external;
/**
* @notice Creates a new lock
* @param _lpToken the univ2 token address
* @param _amount amount of LP tokens to lock
* @param _unlock_date the unix timestamp (in seconds) until unlock
* @param _referral the referrer address if any or address(0) for none
* @param _fee_in_eth fees can be paid in eth or in a secondary token such as UNCX with a discount on univ2 tokens
* @param _withdrawer the user who can withdraw liquidity once the lock expires.
*/
function lockLPToken(
address _lpToken,
uint256 _amount,
uint256 _unlock_date,
address payable _referral,
bool _fee_in_eth,
address payable _withdrawer
) external payable;
/**
* @notice extend a lock with a new unlock date, _index and _lockID ensure the correct lock is changed
* this prevents errors when a user performs multiple tx per block possibly with varying gas prices
*/
function relock(
address _lpToken,
uint256 _index,
uint256 _lockID,
uint256 _unlock_date
) external;
/**
* @notice withdraw a specified amount from a lock. _index and _lockID ensure the correct lock is changed
* this prevents errors when a user performs multiple tx per block possibly with varying gas prices
*/
function withdraw(
address _lpToken,
uint256 _index,
uint256 _lockID,
uint256 _amount
) external;
/**
* @notice increase the amount of tokens per a specific lock, this is preferable to creating a new lock, less fees, and faster loading on our live block explorer
*/
function incrementLock(
address _lpToken,
uint256 _index,
uint256 _lockID,
uint256 _amount
) external;
/**
* @notice split a lock into two seperate locks, useful when a lock is about to expire and youd like to relock a portion
* and withdraw a smaller portion
*/
function splitLock(
address _lpToken,
uint256 _index,
uint256 _lockID,
uint256 _amount
) external payable;
/**
* @notice transfer a lock to a new owner, e.g. presale project -> project owner
*/
function transferLockOwnership(
address _lpToken,
uint256 _index,
uint256 _lockID,
address payable _newOwner
) external;
/**
* @notice migrates liquidity to uniswap v3
*/
function migrate(
address _lpToken,
uint256 _index,
uint256 _lockID,
uint256 _amount
) external;
function getNumLocksForToken(
address _lpToken
) external view returns (uint256);
function getNumLockedTokens() external view returns (uint256);
function getLockedTokenAtIndex(
uint256 _index
) external view returns (address);
// user functions
function getUserNumLockedTokens(
address _user
) external view returns (uint256);
function getUserLockedTokenAtIndex(
address _user,
uint256 _index
) external view returns (address);
function getUserNumLocksForToken(
address _user,
address _lpToken
) external view returns (uint256);
function getUserLockForTokenAtIndex(
address _user,
address _lpToken,
uint256 _index
)
external
view
returns (uint256, uint256, uint256, uint256, uint256, address);
// whitelist
function getWhitelistedUsersLength() external view returns (uint256);
function getWhitelistedUserAtIndex(
uint256 _index
) external view returns (address);
function getUserWhitelistStatus(address _user) external view returns (bool);
}