// SPDX-License-Identifier: MIT
// Sources flattened with hardhat v2.16.1 https://hardhat.org
// File @openzeppelin/contracts/utils/Context.sol@v4.7.0
// 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;
}
}
// File @openzeppelin/contracts/access/Ownable.sol@v4.7.0
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
// File @openzeppelin/contracts/token/ERC20/IERC20.sol@v4.7.0
// OpenZeppelin Contracts (last updated v4.6.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);
}
// File @openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol@v4.7.0
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
/**
* @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);
}
// File @openzeppelin/contracts/token/ERC20/ERC20.sol@v4.7.0
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
/**
* @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.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* 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 ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* The default value of {decimals} is 18. To select a different value for
* {decimals} you should overload it.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @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 value {ERC20} uses, unless this function is
* 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 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 returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
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);
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);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
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
) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
}
_balances[to] += amount;
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @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 {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
_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 {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
}
_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 {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_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) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @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 {}
}
// File @openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol@v4.7.0
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-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);
}
// File @openzeppelin/contracts/utils/Address.sol@v4.7.0
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
// File @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol@v4.7.0
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
/**
* @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;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
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);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @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, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
// File @openzeppelin/contracts/utils/cryptography/MerkleProof.sol@v4.7.0
// OpenZeppelin Contracts (last updated v4.7.0) (utils/cryptography/MerkleProof.sol)
pragma solidity ^0.8.0;
/**
* @dev These functions deal with verification of Merkle Tree proofs.
*
* The proofs can be generated using the JavaScript library
* https://github.com/miguelmota/merkletreejs[merkletreejs].
* Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
*
* See `test/utils/cryptography/MerkleProof.test.js` for some examples.
*
* WARNING: You should avoid using leaf values that are 64 bytes long prior to
* hashing, or use a hash function other than keccak256 for hashing leaves.
* This is because the concatenation of a sorted pair of internal nodes in
* the merkle tree could be reinterpreted as a leaf value.
*/
library MerkleProof {
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*/
function verify(
bytes32[] memory proof,
bytes32 root,
bytes32 leaf
) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
/**
* @dev Calldata version of {verify}
*
* _Available since v4.7._
*/
function verifyCalldata(
bytes32[] calldata proof,
bytes32 root,
bytes32 leaf
) internal pure returns (bool) {
return processProofCalldata(proof, leaf) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leafs & pre-images are assumed to be sorted.
*
* _Available since v4.4._
*/
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Calldata version of {processProof}
*
* _Available since v4.7._
*/
function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Returns true if the `leaves` can be proved to be a part of a Merkle tree defined by
* `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
*
* _Available since v4.7._
*/
function multiProofVerify(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProof(proof, proofFlags, leaves) == root;
}
/**
* @dev Calldata version of {multiProofVerify}
*
* _Available since v4.7._
*/
function multiProofVerifyCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProofCalldata(proof, proofFlags, leaves) == root;
}
/**
* @dev Returns the root of a tree reconstructed from `leaves` and the sibling nodes in `proof`,
* consuming from one or the other at each step according to the instructions given by
* `proofFlags`.
*
* _Available since v4.7._
*/
function processMultiProof(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the merkle tree.
uint256 leavesLen = leaves.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
return hashes[totalHashes - 1];
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Calldata version of {processMultiProof}
*
* _Available since v4.7._
*/
function processMultiProofCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the merkle tree.
uint256 leavesLen = leaves.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
return hashes[totalHashes - 1];
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
}
function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}
// File @openzeppelin/contracts/security/ReentrancyGuard.sol@v4.7.0
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
// File contracts/IFFundable.sol
pragma solidity 0.8.9;
/**
@title Abstract contract providing funder related functions in a sale
@notice To be implemented by IFSale.
*/
abstract contract IFFundable is Ownable, ReentrancyGuard {
using SafeERC20 for ERC20;
// --- CONSTANTS
// number of decimals of sale price
uint64 constant SALE_PRICE_DECIMALS = 10**18;
uint64 private constant ONE_HOUR = 3600;
uint64 private constant ONE_YEAR = 31556926;
uint64 private constant FIVE_YEARS = 157784630;
uint64 private constant TEN_YEARS = 315742060;
// --- OPERATOR ADDRESSES
address public funder;
// optional casher (settable by owner)
address public casher;
// --- SALE INFO
// start timestamp when sale is active (inclusive)
uint256 public immutable startTime;
// end timestamp when sale is active (inclusive)
uint256 public immutable endTime;
// payment token
ERC20 private immutable paymentToken;
// sale token
ERC20 private immutable saleToken;
// withdraw/cash delay timestamp (inclusive)
uint24 public withdrawDelay;
// tracks whether user has already successfully withdrawn
mapping(address => bool) public hasWithdrawn;
// --- STATS
// amount of sale token to sell
uint256 public saleAmount;
// tracks whether sale has been cashed
bool public hasCashed;
// total payment received for sale
uint256 public totalPaymentReceived;
// counter of unique withdrawers (doesn't count "cash"ing)
uint32 public withdrawerCount;
// --- CONSTRUCTOR
constructor(
ERC20 _paymentToken,
ERC20 _saleToken,
uint256 _startTime,
uint256 _endTime,
address _funder
) {
// saleToken shouldn't be the same as paymentToken
require(_saleToken != _paymentToken, 'saleToken = paymentToken');
// when salePrice != 0, paymentToken and maxTotalPayment shouldn't be 0
// sale token cannot be 0
require(address(_saleToken) != address(0), '0x0 saleToken');
// start timestamp must be in future
require(block.timestamp < _startTime, 'start timestamp too early');
require(_startTime - ONE_YEAR < block.timestamp, 'start time has to be within 1 year');
// end timestamp must be after start timestamp
require(_startTime < _endTime, 'end timestamp must be after start timestamp');
require(_endTime - TEN_YEARS < _startTime, 'end time has to be within 10 years');
require(_funder != address(0), '0x0 funder');
funder = _funder;
paymentToken = _paymentToken; // can be 0 (for giveaway)
saleToken = _saleToken;
startTime = _startTime;
endTime = _endTime;
}
// --- MODIFIERS
// Throws if called by any account other than the funder.
modifier onlyFunder() {
require(_msgSender() == funder, 'caller not funder');
_;
}
// Throws if called by any account other than the casher.
modifier onlyCasherOrOwner() {
require(
_msgSender() == casher || _msgSender() == owner(),
'caller not casher or owner'
);
_;
}
// Throws if called during or after sale
modifier onlyBeforeSale() {
require(block.timestamp < startTime, 'sale already started');
_;
}
// Throws if called outside of claim period
modifier onlyAfterSale {
require(block.timestamp > endTime + withdrawDelay, "can't withdraw before claim is started");
_;
}
// Throws if called outside of sale period
modifier onlyDuringSale {
require(startTime <= block.timestamp, 'sale has not begun');
require(block.timestamp <= endTime, 'sale over');
_;
}
// --- EVENTS
event SetCasher(address indexed casher);
event SetFunder(address indexed funder);
event Fund(address indexed sender, uint256 amount);
event SetWithdrawDelay(uint24 indexed withdrawDelay);
event Cash(
address indexed sender,
uint256 paymentTokenBalance,
uint256 saleTokenBalance
);
event EmergencyTokenRetrieve(address indexed sender, uint256 amount);
event Withdraw(address indexed sender, uint256 amount);
// --- SETTER
// Function for owner to set an optional, separate casher
function setCasher(address _casher) public onlyOwner {
casher = _casher;
emit SetCasher(_casher);
}
function setFunder(address _funder) public onlyOwner {
require(_funder != address(0), '0x0 funder');
funder = _funder;
emit SetFunder(_funder);
}
// Function for owner to set a withdraw delay
function setWithdrawDelay(uint24 _withdrawDelay) virtual public onlyOwner onlyBeforeSale{
require(_withdrawDelay < FIVE_YEARS, "withdrawDelay has to be within 5 years");
withdrawDelay = _withdrawDelay;
emit SetWithdrawDelay(_withdrawDelay);
}
// --- FUNDER'S LOGIC
// Virtual function to be implemented by IFSale.
// To calculate the amount of cashable tokens.
function getSaleTokensSold() internal virtual returns (uint256 amount);
// Function for funding sale with sale token (called by project team)
function fund(uint256 amount) public onlyFunder onlyBeforeSale{
// transfer specified amount from funder to this contract
saleToken.safeTransferFrom(_msgSender(), address(this), amount);
// increase tracked sale amount
saleAmount += amount;
emit Fund(_msgSender(), amount);
}
// Function for funder to cash in payment token and unsold sale token
function cash() external onlyCasherOrOwner onlyAfterSale {
// prevent repeat cash
require(!hasCashed, 'already cashed');
hasCashed = true;
// get amount of payment token received
uint256 paymentTokenBal = paymentToken.balanceOf(address(this));
// transfer all
paymentToken.safeTransfer(_msgSender(), paymentTokenBal);
// get amount of sale token on contract
uint256 saleTokenBal = saleToken.balanceOf(address(this));
// get amount of sold token
uint256 totalTokensSold = getSaleTokensSold();
// get principal (whichever is bigger between sale amount or amount on contract)
uint256 principal = saleAmount < saleTokenBal
? saleTokenBal
: saleAmount;
// calculate amount of unsold sale token
uint256 amountUnsold = principal - totalTokensSold;
// transfer unsold
saleToken.safeTransfer(_msgSender(), amountUnsold);
emit Cash(_msgSender(), paymentTokenBal, amountUnsold);
}
function cashPaymentToken(uint256 amount) external onlyCasherOrOwner {
// Get amount of payment token received
uint256 paymentTokenBal = paymentToken.balanceOf(address(this));
// Ensure there's enough payment tokens to cash
require(paymentTokenBal >= amount, "No enough payment tokens to cash");
// Transfer payment tokens to the caller
paymentToken.safeTransfer(_msgSender(), amount);
// Emit an event for this cashing
emit Cash(_msgSender(), amount, 0);
}
// Retrieve tokens erroneously sent in to this address
function emergencyTokenRetrieve(address token) public onlyOwner onlyAfterSale {
// cannot be sale tokens
require(token != address(saleToken));
uint256 tokenBalance = ERC20(token).balanceOf(address(this));
// transfer all
ERC20(token).safeTransfer(_msgSender(), tokenBalance);
emit EmergencyTokenRetrieve(_msgSender(), tokenBalance);
}
// Function for withdrawing purchased sale token after sale end
function withdraw() virtual public nonReentrant {}
function _withdraw(uint256 saleTokenOwed) virtual internal {
require(saleTokenOwed != 0, 'no token to be withdrawn');
// increment withdrawer count
if (!hasWithdrawn[_msgSender()]) {
withdrawerCount += 1;
// set withdrawn to true
hasWithdrawn[_msgSender()] = true;
}
saleToken.safeTransfer(_msgSender(), saleTokenOwed);
emit Withdraw(_msgSender(), saleTokenOwed);
}
}
// File contracts/IFPurchasable.sol
pragma solidity 0.8.9;
/**
@dev Abstract contract containing sale logics.
To be implemented by IFSale.
@notice Include virtual functions for regular and whitelisted purchase
@notice Include virtual functions for whitelisted free token giveaway
@notice Include sale state variables
@notice Implemneted sale state variables changes on purchase and withdraw
*/
abstract contract IFPurchasable is Ownable, ReentrancyGuard {
using SafeERC20 for ERC20;
// --- SALE INFO
// payment token
ERC20 public immutable paymentToken;
// price of the sale token
uint256 public salePrice;
// max for payment token amount
uint256 public maxTotalPayment;
// current purchased amount
uint256 public saleTokenPurchased;
// optional min for payment token amount
uint256 public minTotalPayment;
// optional max for total purchasable amount, default is 0 if there's no limit
// assuming all users buy the token on the same price
uint256 public maxTotalPurchasable;
// halt purchase if true. default is false
bool public isPurchaseHalted = false;
// --- USER INFO
// tracks amount purchased by each address
// user address => purchased amount
mapping(address => uint256) public paymentReceived;
// track amount purchased with code by each address
// user address => purchased amount with code
mapping(address => uint256) public paymentReceivedWithCode;
// track amount purchased with each code by each address
// user address => code => purchased amount with each code
mapping(address => mapping(string => uint256)) public paymentReceivedWithEachCode;
// track promo code used by each address
// user address => promo codes
mapping(address => string[]) public promoCodesPerUser;
// track if a promo code is used by an address
// user address => promo code => bool
mapping(address => mapping(string => bool)) public hasUsedCode;
// -- PROMO CODE
// all promo codes
string[] public codes;
// track if a promo code is stored
mapping(string => bool) isCodeStored;
// amount received per promo code
mapping(string => uint256) public amountPerCode;
// unique use per promo code
mapping(string => uint256) public uniqueUsePerCode;
// max amount of promo code per user
uint256 public maxPromoCodePerUser = 50;
// --- STAT
// counter of unique purchasers
uint32 public purchaserCount;
event Purchase(address indexed sender, uint256 paymentAmount);
event PurchaseWithCode(address indexed sender, uint256 paymentAmount, string code);
event SetMinTotalPayment(uint256 indexed minTotalPayment);
event SetMaxTotalPurchasable(uint256 indexed _maxTotalPurchasable);
event SetIsPurchaseHalted(bool indexed isPurchaseHalted);
constructor(
ERC20 _paymentToken,
uint256 _salePrice,
uint256 _maxTotalPayment
) {
require(
_salePrice == 0 ||
(_salePrice != 0 &&
address(_paymentToken) != address(0) &&
_maxTotalPayment >= _salePrice),
'paymentToken or maxTotalPayment should not be 0 when salePrice is not 0'
);
salePrice = _salePrice; // can be 0 (for giveaway)
paymentToken = _paymentToken; // can be 0 (for giveaway)
maxTotalPayment = _maxTotalPayment; // can be 0 (for giveaway)
}
function setIsPurchaseHalted(bool _isPurchaseHalted) public onlyOwner {
isPurchaseHalted = _isPurchaseHalted;
emit SetIsPurchaseHalted(_isPurchaseHalted);
}
// Function for owner to set an optional, minTotalPayment
function setMinTotalPayment(uint256 _minTotalPayment) public onlyOwner {
minTotalPayment = _minTotalPayment;
emit SetMinTotalPayment(_minTotalPayment);
}
// Function for owner to set an optional, maxTotalPurchasable
// The amount is calculated on salePrice.
function setMaxTotalPurchasable(uint256 _maxTotalPurchasable) virtual public onlyOwner {
maxTotalPurchasable = _maxTotalPurchasable * salePrice;
require(maxTotalPurchasable >= saleTokenPurchased, 'Max purchasable should not be lower than the amount of token purchased');
emit SetMaxTotalPurchasable(_maxTotalPurchasable);
}
// --- PURCHASE
function purchase(uint256 paymentAmount) virtual public {}
// Internal function for making purchase
// Used by public functions `purchase`
function _purchase(uint256 paymentAmount, uint256 remaining) virtual internal nonReentrant {
require(!isPurchaseHalted, 'purchase is halted');
require(salePrice > 0, 'sale price is zero');
// amount must be greater than minTotalPayment
// by default, minTotalPayment is 0 unless otherwise set
require(paymentAmount >= minTotalPayment, 'amount below min');
// payment must not exceed remaining
require(paymentAmount <= remaining, 'exceeds max payment');
require(paymentAmount != 0, 'zero payment amount');
saleTokenPurchased += paymentAmount;
require(maxTotalPurchasable == 0 || maxTotalPurchasable >= saleTokenPurchased, 'exceed max purchasable');
// if user is paying for the first time to this contract, increase counter
if (paymentReceived[_msgSender()] == 0) purchaserCount += 1;
// increase payment received amount
paymentReceived[_msgSender()] += paymentAmount;
// transfer specified amount from user to this contract
paymentToken.safeTransferFrom(_msgSender(), address(this), paymentAmount);
emit Purchase(_msgSender(), paymentAmount);
}
function _purchaseWithCode(uint256 paymentAmount, uint256 remaining, string memory code) virtual internal {
// check if code is not empty
require(bytes(code).length > 0, 'code is empty');
// check if code is too long
require(bytes(code).length <= 64, 'code is too long');
// This needs to be before anything else
// ===
_purchase(paymentAmount, remaining);
// ====
if (!isCodeStored[code]) {
isCodeStored[code] = true;
codes.push(code);
}
if (!hasUsedCode[_msgSender()][code]) {
require(promoCodesPerUser[_msgSender()].length < maxPromoCodePerUser, 'max promo code per user reached');
hasUsedCode[_msgSender()][code] = true;
promoCodesPerUser[_msgSender()].push(code);
}
amountPerCode[code] += paymentAmount;
if (paymentReceivedWithEachCode[_msgSender()][code] == 0) {
uniqueUsePerCode[code] += 1;
}
paymentReceivedWithCode[_msgSender()] += paymentAmount;
paymentReceivedWithEachCode[_msgSender()][code] += paymentAmount;
emit PurchaseWithCode(_msgSender(), paymentAmount, code);
}
}
// File @openzeppelin/contracts/utils/math/Math.sol@v4.7.0
// OpenZeppelin Contracts (last updated v4.7.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) {
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1);
///////////////////////////////////////////////
// 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. It 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)`.
// We also know that `k`, the position of the most significant bit, is such that `msb(a) = 2**k`.
// This gives `2**k < a <= 2**(k+1)` → `2**(k/2) <= sqrt(a) < 2 ** (k/2+1)`.
// Using an algorithm similar to the msb conmputation, we are able to compute `result = 2**(k/2)` which is a
// good first aproximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1;
uint256 x = a;
if (x >> 128 > 0) {
x >>= 128;
result <<= 64;
}
if (x >> 64 > 0) {
x >>= 64;
result <<= 32;
}
if (x >> 32 > 0) {
x >>= 32;
result <<= 16;
}
if (x >> 16 > 0) {
x >>= 16;
result <<= 8;
}
if (x >> 8 > 0) {
x >>= 8;
result <<= 4;
}
if (x >> 4 > 0) {
x >>= 4;
result <<= 2;
}
if (x >> 2 > 0) {
result <<= 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) {
uint256 result = sqrt(a);
if (rounding == Rounding.Up && result * result < a) {
result += 1;
}
return result;
}
}
// File contracts/IFVestable.sol
pragma solidity 0.8.9;
/**
@dev Abstract contract containing vesting logics.
To be implemented by IFSale.
@notice There are two vesting types: linear and cliff
@notice Can only set one vesting type
@notice Once one of the vesting type is set, another one will be reset
@notice Linear vesting unlocks tokens at a linear scale. Calculated by vesting end time
@notice Cliff vesting unlocks tokens at a series of specific time. According to cliff period
*/
abstract contract IFVestable is Ownable {
uint64 private constant TEN_YEARS = 315742060;
// --- VESTING
// Allow vesting to be editable after sale
bool public vestingEditableOverride;
// withdraw/cash delay timestamp (inclusive)
uint256 public withdrawTime;
// the most recent time the user claimed the saleToken
mapping(address => uint256) public latestClaimTime;
event OptInBuyback(address indexed user);
// --- LINEAR VESTING
// the time where the user can take all of the vested saleToken
uint256 public linearVestingEndTime;
event SetLinearVestingEndTime(uint256 indexed linearVestingEndTime);
// --- CLIFF VESTING
// store how many percentage of the token can be claimed at a certain cliff date
struct Cliff {
// the date when the percentage of token can be claimed
uint256 claimTime;
// the percentage token that can be claimed
uint8 pct;
}
// cliff vesting time and percentage
Cliff[] public cliffPeriod;
event SetCliffVestingPeriod(Cliff[] indexed cliffPeriod);
function getCliffPeriod() public view returns (Cliff[] memory){
return cliffPeriod;
}
// --- CONSTRUCTOR
constructor(
uint256 _withdrawTime
) {
// withdrawal delay is by default 0
// it can be set using setWithdrawDelay
withdrawTime = _withdrawTime;
}
// --- SETTER
function setVestingEditable(bool _vestingEditableOverride) public onlyOwner {
vestingEditableOverride = _vestingEditableOverride;
}
function setWithdrawTime(uint256 _withdrawTime) internal {
withdrawTime = _withdrawTime;
}
// Function for owner to set a vesting end time
function setLinearVestingEndTime(uint256 _linearVestingEndTime) virtual public onlyOwner {
require(vestingEditableOverride || block.timestamp < withdrawTime, "Can't edit vesting after sale");
require(_linearVestingEndTime > withdrawTime, "vesting end time has to be after withdrawal start time");
require(withdrawTime > _linearVestingEndTime - TEN_YEARS, "vesting end time has to be within 10 years");
linearVestingEndTime = _linearVestingEndTime;
// unset cliff vesting
delete cliffPeriod;
emit SetLinearVestingEndTime(_linearVestingEndTime);
}
function setCliffPeriod(uint256[] calldata claimTimes, uint8[] calldata pct) virtual public onlyOwner {
require(vestingEditableOverride || block.timestamp < withdrawTime, "Can't edit vesting after sale");
require(claimTimes.length == pct.length, "dates and pct doesn't match");
require(claimTimes.length > 0, "input is empty");
require(claimTimes.length <= 100, "input length cannot exceed 100");
// clear the past entry
delete cliffPeriod;
uint256 maxDate;
uint8 totalPct;
require(claimTimes[0] > withdrawTime, "first claim time is before end time + withdraw delay");
for (uint i = 0; i < claimTimes.length; i++) {
require(maxDate < claimTimes[i], "dates not in ascending order");
maxDate = claimTimes[i];
totalPct += pct[i];
cliffPeriod.push(Cliff(claimTimes[i], pct[i]));
}
require(withdrawTime > maxDate - TEN_YEARS, "vesting end time has to be within 10 years");
// pct is the release percentage, with a precision of 1%. Thus, the sum of all elements of pct must be equal to 100
require(totalPct == 100, "total input percentage doesn't equal to 100");
// unset linear vesting
linearVestingEndTime = 0;
}
// --- VESTING LOGIC
/**
@notice Get the amount of token unlocked
@param totalPurchased Total tokens purchased
@param user Address of the user claiming the tokens
*/
function getUnlockedToken(uint256 totalPurchased, uint256 claimable, address user) virtual public view returns (uint256) {
// linear vesting
if (linearVestingEndTime > block.timestamp) {
// current claimable = total purchased * (now - last claimed time) / (total vesting time)
return totalPurchased * (block.timestamp - Math.max(latestClaimTime[user], withdrawTime)) / (linearVestingEndTime - withdrawTime);
}
// cliff vesting
uint256 cliffPeriodLength = cliffPeriod.length;
if (cliffPeriodLength != 0 && (cliffPeriod[cliffPeriodLength - 1].claimTime > block.timestamp)) {
uint8 claimablePct;
for (uint8 i; i < cliffPeriodLength; i++) {
// if the cliff timestamp has been passed, add the claimable percentage
if (cliffPeriod[i].claimTime > block.timestamp) { break; }
if (latestClaimTime[user] < cliffPeriod[i].claimTime) {
claimablePct += cliffPeriod[i].pct;
}
}
// current claimable = total * claimiable percentage
if (claimablePct == 0) {
return 0;
}
return totalPurchased * claimablePct / 100;
}
// When vesting end, claim all of the remaining tokens.
// Since all of the above calculations return a lower rounded number,
// users will get a little bit less tokens.
// Keeping track and returning the total remaining claimable makes sure the users will get the exact amount.
return claimable;
}
}
// File contracts/IFWhitelistable.sol
pragma solidity 0.8.9;
abstract contract IFWhitelistable is Ownable, ReentrancyGuard {
// optional whitelist setter (settable by owner)
address public whitelistSetter;
// whitelist merkle root; if not set, then sale is open to everyone that has allocation
bytes32 public whitelistRootHash;
event SetWhitelistSetter(address indexed whitelistSetter);
event SetWhitelist(bytes32 indexed whitelistRootHash);
constructor() {
whitelistSetter = _msgSender();
}
// Throws if called by any account other than the whitelist setter.
modifier onlyWhitelistSetterOrOwner() {
require(
_msgSender() == whitelistSetter || _msgSender() == owner(),
'caller not whitelist setter or owner'
);
_;
}
// Function for owner to set an optional, separate whitelist setter
function setWhitelistSetter(address _whitelistSetter) public onlyOwner {
whitelistSetter = _whitelistSetter;
emit SetWhitelistSetter(_whitelistSetter);
}
// Function for owner or whitelist setter to set a whitelist; if not set, then everyone allowed
function setWhitelist(bytes32 _whitelistRootHash)
public
onlyWhitelistSetterOrOwner
{
whitelistRootHash = _whitelistRootHash;
emit SetWhitelist(_whitelistRootHash);
}
// purchase function when there is a whitelist
function whitelistedPurchase(uint256 paymentAmount, bytes32[] calldata merkleProof) virtual public {}
function withdrawGiveaway(bytes32[] calldata merkleProof) virtual public nonReentrant {}
}
// File contracts/IFSale.sol
pragma solidity 0.8.9;
/**
@dev Vanilla Sale contract compositing vesting, funding, and whitelisting functions.
@notice Features:
@notice 1. Funder related actions like fund and cash
@notice 2. Regular and whitelisted purchase
@notice 3. Whitelisted free token giveaway
@notice 4. Vest tokens in linear or cliff mode
*/
contract IFSale is IFPurchasable, IFVestable, IFFundable, IFWhitelistable {
// tracks amount of tokens owed to each address
mapping(address => uint256) public claimable;
// tracks amount of tokens purchased by each address
mapping(address => uint256) public totalPurchased;
// flag to enable integer sale
bool public isIntegerSale = false;
// --- CONSTRUCTOR
constructor(
address _funder,
uint256 _salePrice,
ERC20 _paymentToken,
ERC20 _saleToken,
uint256 _startTime,
uint256 _endTime,
uint256 _maxTotalPayment
)
IFPurchasable(_paymentToken, _salePrice, _maxTotalPayment)
IFVestable(_endTime)
IFFundable(_paymentToken, _saleToken, _startTime, _endTime, _funder)
IFWhitelistable()
{}
// --- SETTERS
function setWithdrawDelay(uint24 _withdrawDelay) override public onlyOwner onlyBeforeSale {
setWithdrawTime(endTime + _withdrawDelay);
super.setWithdrawDelay(_withdrawDelay);
}
function setLinearVestingEndTime(uint256 _vestingEndTime) override public onlyOwner onlyBeforeSale {
super.setLinearVestingEndTime(_vestingEndTime);
}
function setCliffPeriod(uint256[] calldata claimTimes, uint8[] calldata pct) override public onlyOwner onlyBeforeSale {
super.setCliffPeriod(claimTimes, pct);
}
function setIsIntegerSale(bool _isIntegerSale) public onlyOwner onlyBeforeSale {
isIntegerSale = _isIntegerSale;
}
// --- PURCHASE
function purchase(uint256 paymentAmount) virtual override public onlyDuringSale {
require(whitelistRootHash == 0, 'use whitelistedPurchase');
_purchase(paymentAmount, maxTotalPayment);
}
// purchase function when there is a whitelist
function whitelistedPurchase(
uint256 paymentAmount,
bytes32[] calldata merkleProof
) virtual override public onlyDuringSale {
// the user has to be whitelisted
require(checkWhitelist(_msgSender(), merkleProof), 'proof invalid');
_purchase(paymentAmount, maxTotalPayment);
}
// --- WITHDRAW
function withdraw() virtual override public onlyAfterSale nonReentrant {
// must not be a zero price sale
require(salePrice != 0, 'use withdrawGiveaway');
address user = _msgSender();
uint256 tokenOwed = getCurrentClaimableToken(user);
require(tokenOwed != 0, 'no token to be withdrawn');
// send token and update states
_withdraw(tokenOwed);
}
// Function to withdraw (redeem) tokens from a zero cost "giveaway" sale
function withdrawGiveaway(bytes32[] calldata merkleProof) virtual override public onlyAfterSale nonReentrant
{
revert("Not implemented");
}
// --- UPDATE SALE STATES
function _purchase(uint256 paymentAmount, uint256 remaining) override internal {
if (isIntegerSale) {
require(isIntegerPayment(paymentAmount), 'can only buy integer amount of sale tokens');
}
totalPaymentReceived += paymentAmount;
super._purchase(paymentAmount, remaining);
// Update vesting variables
uint256 tokenPurchased = (paymentReceived[_msgSender()] * SALE_PRICE_DECIMALS) / salePrice;
totalPurchased[_msgSender()] = tokenPurchased;
claimable[_msgSender()] = tokenPurchased;
}
function _withdraw(uint256 tokenOwed) override internal {
// Update vesting variables
latestClaimTime[_msgSender()] = block.timestamp;
claimable[_msgSender()] -= tokenOwed;
super._withdraw(tokenOwed);
}
// --- HELPER FUNCTIONS
function getSaleTokensSold() override internal view returns (uint256 amount) {
// if salePrice is 0, no tokens will be sold
if (salePrice == 0) {
return 0;
} else {
return totalPaymentReceived * SALE_PRICE_DECIMALS / salePrice;
}
}
// A helper function to get the amount of unlocked token by providing user's address
function getCurrentClaimableToken (address user) public view returns (uint256) {
return getUnlockedToken(totalPurchased[user], claimable[user], user);
}
// Returns true if user is on whitelist, otherwise false
function checkWhitelist(address user, bytes32[] calldata merkleProof) virtual public view returns (bool)
{
// compute merkle leaf from input
bytes32 leaf = keccak256(abi.encodePacked(user));
// verify merkle proof
return MerkleProof.verify(merkleProof, whitelistRootHash, leaf);
}
// a function to check if the payment amount can buy integer amount of sale tokens, accounting the token decimals
function isIntegerPayment(uint256 paymentAmount) public view returns (bool) {
return (
// has to at least purchase 1 token
paymentAmount % salePrice == 0
&& paymentAmount >= salePrice
);
}
// Override the renounceOwnership function to disable it
function renounceOwnership() public pure override{
revert("ownership renunciation is disabled");
}
}
// File contracts/IFFixedSaleV12.sol
pragma solidity 0.8.9;
/**
@title Sale contract with user's allocation stored in merkle root
@notice Regular purchase from IFSale is disabled here
@notice Apart from merkle proof, users or the frontend has to supply allocation amount for verification
@notice That means functions having `merkleProof` as param will also need `allocation`
*/
contract IFFixedSaleV12 is IFSale {
// --- CONSTRUCTOR
constructor(
uint256 _salePrice,
address _funder,
ERC20 _paymentToken,
ERC20 _saleToken,
uint256 _startTime,
uint256 _endTime,
uint256 _maxTotalPayment
)
IFSale(
_funder,
_salePrice,
_paymentToken,
_saleToken,
_startTime,
_endTime,
_maxTotalPayment
)
{}
bool public isVestedGiveaway = false;
// allocation when the user is not whitelisted
// note that any user can get publicAllocation regardless of their whitelisted allocation
uint256 public publicAllocation = 0;
// --- SETTER FUNCTIONS
function setVestedGiveaway(bool _isVestedGiveaway) public onlyOwner onlyBeforeSale {
isVestedGiveaway = _isVestedGiveaway;
}
function setPublicAllocation(uint256 _publicAllocation) public onlyWhitelistSetterOrOwner onlyBeforeSale {
publicAllocation = _publicAllocation;
}
function setMaxTotalPurchasable(uint256 _maxTotalPurchasable) override public onlyWhitelistSetterOrOwner {
maxTotalPurchasable = _maxTotalPurchasable * salePrice;
require(maxTotalPurchasable >= saleTokenPurchased, 'Max purchasable should not be lower than the amount of token purchased');
emit SetMaxTotalPurchasable(_maxTotalPurchasable);
}
// --- DISABLED FUNCTIONS
function purchase(uint256) virtual override public {
revert("Use whitelistedPurchase(uint256 paymentAmount, bytes32[] calldata merkleProof, uint256 allocation)");
}
function whitelistedPurchase(uint256, bytes32[] calldata) override public pure {
revert("Use whitelistedPurchase(uint256 paymentAmount, bytes32[] calldata merkleProof, uint256 allocation)");
}
function withdrawGiveaway(bytes32[] calldata) override public pure {
revert("Use withdrawGiveaway(bytes32[] calldata merkleProof, uint256 allocation)");
}
// --- WHITELISTED ACTIONS
// purchase with code function when there is a whitelist
function whitelistedPurchaseWithCode(
uint256 paymentAmount,
bytes32[] calldata merkleProof,
uint256 _allocation,
string calldata code
) public onlyDuringSale {
uint256 allocation = publicAllocation;
if (merkleProof.length > 0) {
// require that user is whitelisted by checking proof
require(checkWhitelist(_msgSender(), merkleProof, _allocation), 'proof invalid');
if (_allocation > publicAllocation) {
allocation = _allocation;
}
}
uint256 remaining = getMaxPayment(_msgSender(), allocation);
_purchaseWithCode(paymentAmount, remaining, code);
}
// purchase function when there is a whitelist
function whitelistedPurchase(
uint256 paymentAmount,
bytes32[] calldata merkleProof,
uint256 _allocation
) public onlyDuringSale {
uint256 allocation = publicAllocation;
if (merkleProof.length > 0) {
// require that user is whitelisted by checking proof
require(checkWhitelist(_msgSender(), merkleProof, _allocation), 'proof invalid');
if (_allocation > publicAllocation) {
allocation = _allocation;
}
}
uint256 remaining = getMaxPayment(_msgSender(), allocation);
_purchase(paymentAmount, remaining);
}
// Function to withdraw (redeem) tokens from a zero cost "giveaway" sale
function withdrawGiveaway(bytes32[] calldata merkleProof, uint256 allocation)
external
nonReentrant
onlyAfterSale
{
address user = _msgSender();
// not vested giveaway
require(isVestedGiveaway == false, 'use withdrawGiveawayVested');
// must be a zero price sale
require(salePrice == 0, 'not a giveaway');
// can withdraw only once
require(hasWithdrawn[user] == false, 'already withdrawn');
// require that user is whitelisted by checking proof
require(checkWhitelist(user, merkleProof, allocation), 'proof invalid');
uint256 saleTokenOwed = 0;
// initialize claimable before the first time of withdrawal
if (!hasWithdrawn[user]) {
// each participant in the zero cost "giveaway" gets a flat amount of sale token
saleTokenOwed = allocation;
claimable[user] = allocation;
totalPurchased[user] = allocation;
}
// sale token owed must be greater than 0
require(saleTokenOwed != 0, 'withdraw giveaway amount 0');
// send token and update states
_withdraw(saleTokenOwed);
}
// Function to withdraw (redeem) tokens from a zero cost "giveaway" sale
function withdrawGiveawayVested(bytes32[] calldata merkleProof, uint256 allocation)
external
nonReentrant
onlyAfterSale
{
address user = _msgSender();
// not vested giveaway
require(isVestedGiveaway == true, 'use withdrawGiveaway');
// must be a zero price sale
require(salePrice == 0, 'not a giveaway');
// require that user is whitelisted by checking proof
require(checkWhitelist(user, merkleProof, allocation), 'proof invalid');
// initialize claimable before the first time of withdrawal
if (!hasWithdrawn[user]) {
claimable[user] = allocation;
totalPurchased[user] = allocation;
}
uint256 tokenOwed = getCurrentClaimableToken(user);
// sale token owed must be greater than 0
require(tokenOwed != 0, 'withdraw giveaway amount 0');
// send token and update states
_withdraw(tokenOwed);
}
// --- HELPER FUNCTIONS
// Returns true if user's allocation matches the one in merkle root, otherwise false
function checkWhitelist(address user, bytes32[] calldata merkleProof, uint256 allocation)
public
view
returns (bool)
{
// compute merkle leaf from input
bytes32 leaf = keccak256(abi.encodePacked(user, allocation));
// verify merkle proof
return MerkleProof.verify(merkleProof, whitelistRootHash, leaf);
}
// @dev get max payment from allocation
function getMaxPayment(address user, uint256 allocation) public view returns (uint256) {
// get the maximum total payment for a user
uint256 max = (salePrice * allocation) / SALE_PRICE_DECIMALS;
if (maxTotalPayment < max) {
max = maxTotalPayment;
}
// calculate and return remaining
return max - paymentReceived[user];
}
}
{
"compilationTarget": {
"IFFixedSaleV12.sol": "IFFixedSaleV12"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"uint256","name":"_salePrice","type":"uint256"},{"internalType":"address","name":"_funder","type":"address"},{"internalType":"contract ERC20","name":"_paymentToken","type":"address"},{"internalType":"contract ERC20","name":"_saleToken","type":"address"},{"internalType":"uint256","name":"_startTime","type":"uint256"},{"internalType":"uint256","name":"_endTime","type":"uint256"},{"internalType":"uint256","name":"_maxTotalPayment","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"paymentTokenBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"saleTokenBalance","type":"uint256"}],"name":"Cash","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EmergencyTokenRetrieve","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Fund","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"OptInBuyback","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"paymentAmount","type":"uint256"}],"name":"Purchase","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"paymentAmount","type":"uint256"},{"indexed":false,"internalType":"string","name":"code","type":"string"}],"name":"PurchaseWithCode","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"casher","type":"address"}],"name":"SetCasher","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256","name":"claimTime","type":"uint256"},{"internalType":"uint8","name":"pct","type":"uint8"}],"indexed":true,"internalType":"struct IFVestable.Cliff[]","name":"cliffPeriod","type":"tuple[]"}],"name":"SetCliffVestingPeriod","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"funder","type":"address"}],"name":"SetFunder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bool","name":"isPurchaseHalted","type":"bool"}],"name":"SetIsPurchaseHalted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"linearVestingEndTime","type":"uint256"}],"name":"SetLinearVestingEndTime","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_maxTotalPurchasable","type":"uint256"}],"name":"SetMaxTotalPurchasable","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"minTotalPayment","type":"uint256"}],"name":"SetMinTotalPayment","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"whitelistRootHash","type":"bytes32"}],"name":"SetWhitelist","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"whitelistSetter","type":"address"}],"name":"SetWhitelistSetter","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"withdrawDelay","type":"uint24"}],"name":"SetWithdrawDelay","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"amountPerCode","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"cashPaymentToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"casher","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"checkWhitelist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"},{"internalType":"uint256","name":"allocation","type":"uint256"}],"name":"checkWhitelist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"claimable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"cliffPeriod","outputs":[{"internalType":"uint256","name":"claimTime","type":"uint256"},{"internalType":"uint8","name":"pct","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"codes","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"emergencyTokenRetrieve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"endTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"fund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"funder","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCliffPeriod","outputs":[{"components":[{"internalType":"uint256","name":"claimTime","type":"uint256"},{"internalType":"uint8","name":"pct","type":"uint8"}],"internalType":"struct IFVestable.Cliff[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getCurrentClaimableToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"allocation","type":"uint256"}],"name":"getMaxPayment","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"totalPurchased","type":"uint256"},{"internalType":"uint256","name":"claimable","type":"uint256"},{"internalType":"address","name":"user","type":"address"}],"name":"getUnlockedToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hasCashed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"hasUsedCode","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"hasWithdrawn","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"paymentAmount","type":"uint256"}],"name":"isIntegerPayment","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isIntegerSale","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPurchaseHalted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isVestedGiveaway","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"latestClaimTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"linearVestingEndTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxPromoCodePerUser","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxTotalPayment","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxTotalPurchasable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minTotalPayment","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"paymentReceived","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"paymentReceivedWithCode","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"paymentReceivedWithEachCode","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paymentToken","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"promoCodesPerUser","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"publicAllocation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"purchase","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"purchaserCount","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"saleAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"salePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"saleTokenPurchased","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_casher","type":"address"}],"name":"setCasher","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"claimTimes","type":"uint256[]"},{"internalType":"uint8[]","name":"pct","type":"uint8[]"}],"name":"setCliffPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_funder","type":"address"}],"name":"setFunder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isIntegerSale","type":"bool"}],"name":"setIsIntegerSale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isPurchaseHalted","type":"bool"}],"name":"setIsPurchaseHalted","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_vestingEndTime","type":"uint256"}],"name":"setLinearVestingEndTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxTotalPurchasable","type":"uint256"}],"name":"setMaxTotalPurchasable","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minTotalPayment","type":"uint256"}],"name":"setMinTotalPayment","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_publicAllocation","type":"uint256"}],"name":"setPublicAllocation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isVestedGiveaway","type":"bool"}],"name":"setVestedGiveaway","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_vestingEditableOverride","type":"bool"}],"name":"setVestingEditable","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_whitelistRootHash","type":"bytes32"}],"name":"setWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_whitelistSetter","type":"address"}],"name":"setWhitelistSetter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint24","name":"_withdrawDelay","type":"uint24"}],"name":"setWithdrawDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalPaymentReceived","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"totalPurchased","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"uniqueUsePerCode","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingEditableOverride","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whitelistRootHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whitelistSetter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"name":"whitelistedPurchase","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"paymentAmount","type":"uint256"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"},{"internalType":"uint256","name":"_allocation","type":"uint256"}],"name":"whitelistedPurchase","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"paymentAmount","type":"uint256"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"},{"internalType":"uint256","name":"_allocation","type":"uint256"},{"internalType":"string","name":"code","type":"string"}],"name":"whitelistedPurchaseWithCode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawDelay","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"name":"withdrawGiveaway","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"},{"internalType":"uint256","name":"allocation","type":"uint256"}],"name":"withdrawGiveaway","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"},{"internalType":"uint256","name":"allocation","type":"uint256"}],"name":"withdrawGiveawayVested","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawerCount","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"}]