// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 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
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
} else if (error == RecoverError.InvalidSignatureV) {
revert("ECDSA: invalid signature 'v' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
// Check the signature length
// - case 65: r,s,v signature (standard)
// - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else if (signature.length == 64) {
bytes32 r;
bytes32 vs;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
assembly {
r := mload(add(signature, 0x20))
vs := mload(add(signature, 0x40))
}
return tryRecover(hash, r, vs);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address, RecoverError) {
bytes32 s;
uint8 v;
assembly {
s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
v := add(shr(255, vs), 27)
}
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
if (v != 27 && v != 28) {
return (address(0), RecoverError.InvalidSignatureV);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
//............................................................................................................................
//.BBBBBBBBBBBBBBBBBBBBBB...........BEEEEEEEEEEEEEEEEEEEEEEEEE.....EEEEEEEEEEEEEEEEEEEEEEEEEE....EPPPPPPPPPPPPPPPPPPPP........
//.BBBBBBBBBBBBBBBBBBBBBBBB.........BEEEEEEEEEEEEEEEEEEEEEEEEE.....EEEEEEEEEEEEEEEEEEEEEEEEEE....EPPPPPPPPPPPPPPPPPPPPPP......
//.BBBBBBBBBBBBBBBBBBBBBBBBB........BEEEEEEEEEEEEEEEEEEEEEEEEE.....EEEEEEEEEEEEEEEEEEEEEEEEEE....EPPPPPPPPPPPPPPPPPPPPPPP.....
//.BBBBBBBBBBBBBBBBBBBBBBBBBB.......BEEEEEEEEEEEEEEEEEEEEEEEEE.....EEEEEEEEEEEEEEEEEEEEEEEEEE....EPPPPPPPPPPPPPPPPPPPPPPPP....
//.BBBBBBBBBBBBBBBBBBBBBBBBBB.......BEEEEEEEEEEEEEEEEEEEEEEEEE.....EEEEEEEEEEEEEEEEEEEEEEEEEE....EPPPPPPPPPPPPPPPPPPPPPPPPP...
//.BBBBBBBB........BBBBBBBBBBB......BEEEEEEE.......................EEEEEEEE......................EPPPPPPP......PPPPPPPPPPPP...
//.BBBBBBBB..........BBBBBBBBB......BEEEEEEE.......................EEEEEEEE......................EPPPPPPP.........PPPPPPPPPP..
//.BBBBBBBB...........BBBBBBBB......BEEEEEEE.......................EEEEEEEE......................EPPPPPPP..........PPPPPPPPP..
//.BBBBBBBB...........BBBBBBBB......BEEEEEEE.......................EEEEEEEE......................EPPPPPPP..........PPPPPPPPP..
//.BBBBBBBB...........BBBBBBBB......BEEEEEEE.......................EEEEEEEE......................EPPPPPPP..........PPPPPPPPP..
//.BBBBBBBB...........BBBBBBBB......BEEEEEEE.......................EEEEEEEE......................EPPPPPPP...........PPPPPPPP..
//.BBBBBBBB..........BBBBBBBBB......BEEEEEEE.......................EEEEEEEE......................EPPPPPPP..........PPPPPPPPP..
//.BBBBBBBB.......BBBBBBBBBBB.......BEEEEEEE.......................EEEEEEEE......................EPPPPPPP..........PPPPPPPPP..
//.BBBBBBBBBBBBBBBBBBBBBBBBB........BEEEEEEEEEEEEEEEEEEEEEEEE......EEEEEEEEEEEEEEEEEEEEEEEE......EPPPPPPP..........PPPPPPPPP..
//.BBBBBBBBBBBBBBBBBBBBBBBB.........BEEEEEEEEEEEEEEEEEEEEEEEE......EEEEEEEEEEEEEEEEEEEEEEEE......EPPPPPPP.........PPPPPPPPP...
//.BBBBBBBBBBBBBBBBBBBBBBBB.........BEEEEEEEEEEEEEEEEEEEEEEEE......EEEEEEEEEEEEEEEEEEEEEEEE......EPPPPPPP.....PPPPPPPPPPPPP...
//.BBBBBBBBBBBBBBBBBBBBBBBBBB.......BEEEEEEEEEEEEEEEEEEEEEEEE......EEEEEEEEEEEEEEEEEEEEEEEE......EPPPPPPPPPPPPPPPPPPPPPPPPP...
//.BBBBBBBBBBBBBBBBBBBBBBBBBBB......BEEEEEEEEEEEEEEEEEEEEEEEE......EEEEEEEEEEEEEEEEEEEEEEEE......EPPPPPPPPPPPPPPPPPPPPPPPP....
//.BBBBBBBB.......BBBBBBBBBBBBB.....BEEEEEEE.......................EEEEEEEE......................EPPPPPPPPPPPPPPPPPPPPPPP.....
//.BBBBBBBB...........BBBBBBBBB.....BEEEEEEE.......................EEEEEEEE......................EPPPPPPPPPPPPPPPPPPPPPP......
//.BBBBBBBB............BBBBBBBB.....BEEEEEEE.......................EEEEEEEE......................EPPPPPPPPPPPPPPPPPPPP........
//.BBBBBBBB............BBBBBBBBB....BEEEEEEE.......................EEEEEEEE......................EPPPPPPP.....................
//.BBBBBBBB............BBBBBBBBB....BEEEEEEE.......................EEEEEEEE......................EPPPPPPP.....................
//.BBBBBBBB............BBBBBBBBB....BEEEEEEE.......................EEEEEEEE......................EPPPPPPP.....................
//.BBBBBBBB............BBBBBBBBB....BEEEEEEE.......................EEEEEEEE......................EPPPPPPP.....................
//.BBBBBBBB............BBBBBBBBB....BEEEEEEE.......................EEEEEEEE......................EPPPPPPP.....................
//.BBBBBBBB...........BBBBBBBBB.....BEEEEEEE.......................EEEEEEEE......................EPPPPPPP.....................
//.BBBBBBBB........BBBBBBBBBBBB.....BEEEEEEE.......................EEEEEEEE......................EPPPPPPP.....................
//.BBBBBBBBBBBBBBBBBBBBBBBBBBBB.....BEEEEEEEEEEEEEEEEEEEEEEEEE.....EEEEEEEEEEEEEEEEEEEEEEEEEE....EPPPPPPP.....................
//.BBBBBBBBBBBBBBBBBBBBBBBBBBB......BEEEEEEEEEEEEEEEEEEEEEEEEE.....EEEEEEEEEEEEEEEEEEEEEEEEEE....EPPPPPPP.....................
//.BBBBBBBBBBBBBBBBBBBBBBBBBB.......BEEEEEEEEEEEEEEEEEEEEEEEEE.....EEEEEEEEEEEEEEEEEEEEEEEEEE....EPPPPPPP.....................
//.BBBBBBBBBBBBBBBBBBBBBBBBB........BEEEEEEEEEEEEEEEEEEEEEEEEE.....EEEEEEEEEEEEEEEEEEEEEEEEEE....EPPPPPPP.....................
//.BBBBBBBBBBBBBBBBBBBBBBB..........BEEEEEEEEEEEEEEEEEEEEEEEEE.....EEEEEEEEEEEEEEEEEEEEEEEEEE....EPPPPPPP.....................
//............................................................................................................................
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
contract FairLaunch is ReentrancyGuard, Ownable {
using ECDSA for bytes32;
using SafeERC20 for IERC20;
/* ======== VARIABLES ======== */
address public token;
address public whitelistSigner;
address payable public treasury;
uint256 public rate;
uint256 public ethRaised;
uint256 public endICO;
uint256 public minPurchase;
uint256 public maxPurchase;
uint256 public availableTokensICO;
uint256 public boughtTokensICO;
bool public buyActive = true;
bool public withdrawActive = true;
bool public whitelistActive = true;
// bytes32 -> DomainSeparator
bytes32 public DOMAIN_SEPARATOR;
// bytes32 -> PRESALE_TYPEHASH
bytes32 public constant PRESALE_TYPEHASH = keccak256("FairLaunch(address buyer)");
/* ======== STRUCTS ======== */
struct Investor {
address wallet;
uint256 amountToReceive;
uint256 ethSpend;
}
/* ======== MAPPINGS ======== */
mapping(address => Investor) public investor;
/* ======== EVENTS ======== */
event TokensPurchased(address indexed _beneficiary, address indexed _treasury, uint256 _amount);
event SetICO(uint256 _block);
event TokenAddress(address token);
event WithdrawLeftovers(address _receipent, uint256 _amount);
event WithdrawRewards(address _receipent, uint256 _amount);
event MinPurchase(uint256 _amount);
event MaxPurchase(uint256 _amount);
event Rate(uint256 _amount);
event AvailableTokensICO(uint256 _amount);
event Sales(bool _buyActive, bool _withdrawActive);
event WhitelistActive(bool _whitelistActive);
event WhitelistSigner(address _whitelistSigner);
event Treasury(address payable _amount);
/* ======== MODIFIERS ======== */
modifier icoActive() {
require(endICO > 0 && block.number < endICO && availableTokensICO > 0, "ICO must be active");
_;
}
modifier icoNotActive() {
require(endICO < block.number, 'ICO is active');
_;
}
modifier withdrawIsActive() {
require(withdrawActive, 'Withdrawing has been paused');
_;
}
modifier buyIsActive() {
require(buyActive, 'Purchasing has been paused');
_;
}
/* ======== INITIALIZATION ======== */
constructor (
address payable _treasury,
uint256 _rate,
uint256 _availableTokensICO,
uint256 _endICO,
uint256 _minPurchase,
uint256 _maxPurchase
) public {
uint256 chainId;
assembly {
chainId := chainid()
}
DOMAIN_SEPARATOR = keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes("FairLaunch")),
keccak256(bytes("1")),
chainId,
address(this)
)
);
setRate(_rate);
setICO(_endICO);
setMaxPurchase(_maxPurchase);
setMinPurchase(_minPurchase);
setAvailableTokensICO(_availableTokensICO);
setWhitelistSigner(_msgSender());
setTreasury(_treasury);
}
/* ======== SETTERS ======== */
/**
* @notice Set the new end ICO, when setting this to 0 the ico will be done
* @param _whitelistActive Sets the whitelist active or not
*/
function setWhitelistActive(bool _whitelistActive) public onlyOwner {
whitelistActive = _whitelistActive;
emit WhitelistActive(_whitelistActive);
}
/**
* @notice Set the new signer of the whitelisters
* @param _whitelistSigner The new whitelist signer
*/
function setWhitelistSigner(address _whitelistSigner) public onlyOwner {
whitelistSigner = _whitelistSigner;
emit WhitelistSigner(_whitelistSigner);
}
/**
* @notice Set the new end ICO, when setting this to 0 the ico will be done
* @param _ICO The end ico block
*/
function setICO(uint256 _ICO) public onlyOwner {
endICO = _ICO;
emit SetICO(_ICO);
}
/**
* @notice Set Token Address
* @param _token The token address the presale is about
*/
function setToken(address _token) public onlyOwner {
require(_token != address(0x0), "FairLaunch: Token is the zero address");
token = _token;
emit TokenAddress(_token);
}
/**
* @notice Sets the new rate
* @param _rate The rate in (Gwei)
*/
function setRate(uint256 _rate) public onlyOwner {
require(_rate > 0, "FairLaunch: Cannot be 0");
rate = _rate;
emit Rate(rate);
}
/**
* @notice Sets the available tokens
* @param _availableTokensICO the available tokens in gwei
*/
function setAvailableTokensICO(uint256 _availableTokensICO) public onlyOwner {
availableTokensICO = _availableTokensICO;
emit AvailableTokensICO(_availableTokensICO);
}
/**
* @notice Sets the new receiver of the funds
* @param _treasury The address that will receive the presale funds
*/
function setTreasury(address payable _treasury) public onlyOwner {
require(_treasury != address(0), "FairLaunch: Invalid address");
treasury = _treasury;
emit Treasury(treasury);
}
/**
* @notice Sets the new min purchase
* @param _minPurchase The new min purchase in (Gwei)
*/
function setMinPurchase(uint256 _minPurchase) public onlyOwner {
minPurchase = _minPurchase;
emit MinPurchase(_minPurchase);
}
/**
* @notice Sets the new max purchase
* @param _maxPurchase The new max purchase in (Gwei)
*/
function setMaxPurchase(uint256 _maxPurchase) public onlyOwner {
maxPurchase = _maxPurchase;
emit MaxPurchase(_maxPurchase);
}
/**
* @notice Sets the activity of buy / withdraw
* @param _buyActive Enable or disable the buy activity
* @param _withdrawActive Enable or disable the withdraw activty
*/
function setSales(bool _buyActive, bool _withdrawActive) public onlyOwner {
buyActive = _buyActive;
withdrawActive = _withdrawActive;
emit Sales(_buyActive, _withdrawActive);
}
/* ======== GETTERS ======== */
/**
* @notice Returns the token amount based on the rewardTokenCount and the rate
* @param _weiAmount The amount of tokens in wei
*/
function getTokenAmount(uint256 _weiAmount) public view returns (uint256) {
return (_weiAmount * rate) / 1E18;
}
/**
* @notice Returns the amount of tokens in the contract
*/
function getTokensInContract() public view returns (uint256) {
return IERC20(token).balanceOf(address(this));
}
/**
* @notice Returns the amount the user can withdraw after the ICO has ended
* @param _beneficiary The wallet address for distributed amount
*/
function withdrawalAmount(address _beneficiary) public view returns (uint256 amount) {
return investor[_beneficiary].amountToReceive;
}
/* ======== CALLABLE FUNCTIONS ======== */
/**
* @notice deposit the tokens which will be claimed
*/
function depositTokens(uint256 amount) external onlyOwner {
IERC20(token).transferFrom(_msgSender(), address(this), amount);
}
/**
* @notice If the ICO is not active anymore, the owner can withdraw the leftovers
*/
function withdrawLeftoversToken() external icoNotActive onlyOwner {
require(IERC20(token).balanceOf(address(this)) > 0, 'FairLaunch: There are no tokens to withdraw');
IERC20(token).safeTransfer(_msgSender(), IERC20(token).balanceOf(address(this)));
emit WithdrawLeftovers(_msgSender(), IERC20(token).balanceOf(address(this)));
}
/**
* @notice Users can withdraw only when the ICO is ended and the amount is not equal to 0
*/
function withdrawTokens() external nonReentrant icoNotActive withdrawIsActive {
require(token != address(0), "FairLaunch: Token is the zero address");
require(withdrawalAmount(_msgSender()) != 0, "FairLaunch: Haven't bought any tokens");
require(withdrawalAmount(_msgSender()) <= getTokensInContract(), "FairLaunch: Not enough tokens in contract to withdraw from");
uint256 amountToWithdraw = withdrawalAmount(_msgSender());
investor[_msgSender()].amountToReceive = 0;
IERC20(token).safeTransfer(_msgSender(), amountToWithdraw);
emit WithdrawRewards(_msgSender(), amountToWithdraw);
}
/**
* @notice Buy tokens
*/
function buyTokens(bytes memory _signature) external nonReentrant icoActive buyIsActive payable {
require(availableTokensICO != 0, "FairLaunch: No available tokens left");
if(whitelistActive) {
bytes32 digest = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, keccak256(abi.encode(PRESALE_TYPEHASH, _msgSender()))));
address recoveredAddress = digest.recover(_signature);
require(recoveredAddress != address(0) && recoveredAddress == address(whitelistSigner), "Invalid signature");
}
uint256 ethPurchaseInWei = msg.value;
uint256 tokensPurchase = getTokenAmount(ethPurchaseInWei);
require(ethPurchaseInWei >= minPurchase, 'FairLaunch: Have to send at least minPurchase');
require(tokensPurchase != 0, "FairLaunch: Value is 0");
require(tokensPurchase <= availableTokensICO, "FairLaunch: No tokens left for purchase");
require((investor[_msgSender()].ethSpend + ethPurchaseInWei) <= maxPurchase, 'FairLaunch: Max purchase has been reached');
// Amount of ETH that has been raised
ethRaised += ethPurchaseInWei;
// Add person to distributed map and tokens bought
investor[_msgSender()].wallet = _msgSender();
investor[_msgSender()].amountToReceive += tokensPurchase;
investor[_msgSender()].ethSpend += ethPurchaseInWei;
availableTokensICO = availableTokensICO - tokensPurchase;
boughtTokensICO += tokensPurchase;
treasury.transfer(ethPurchaseInWei);
emit TokensPurchased(_msgSender(), treasury, tokensPurchase);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @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 `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, 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 `sender` to `recipient` 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 sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* 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() {
_setOwner(_msgSender());
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
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 {
_setOwner(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");
_setOwner(newOwner);
}
function _setOwner(address newOwner) private {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
// SPDX-License-Identifier: MIT
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 make 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;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
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));
}
}
/**
* @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");
}
}
}
{
"compilationTarget": {
"contracts/FairLaunch.sol": "FairLaunch"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address payable","name":"_treasury","type":"address"},{"internalType":"uint256","name":"_rate","type":"uint256"},{"internalType":"uint256","name":"_availableTokensICO","type":"uint256"},{"internalType":"uint256","name":"_endICO","type":"uint256"},{"internalType":"uint256","name":"_minPurchase","type":"uint256"},{"internalType":"uint256","name":"_maxPurchase","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"AvailableTokensICO","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"MaxPurchase","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"MinPurchase","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"Rate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"_buyActive","type":"bool"},{"indexed":false,"internalType":"bool","name":"_withdrawActive","type":"bool"}],"name":"Sales","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_block","type":"uint256"}],"name":"SetICO","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"TokenAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_beneficiary","type":"address"},{"indexed":true,"internalType":"address","name":"_treasury","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"TokensPurchased","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address payable","name":"_amount","type":"address"}],"name":"Treasury","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"_whitelistActive","type":"bool"}],"name":"WhitelistActive","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_whitelistSigner","type":"address"}],"name":"WhitelistSigner","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_receipent","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"WithdrawLeftovers","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_receipent","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"WithdrawRewards","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRESALE_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"availableTokensICO","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"boughtTokensICO","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"buyActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"buyTokens","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"endICO","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ethRaised","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_weiAmount","type":"uint256"}],"name":"getTokenAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTokensInContract","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"investor","outputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"uint256","name":"amountToReceive","type":"uint256"},{"internalType":"uint256","name":"ethSpend","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxPurchase","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minPurchase","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_availableTokensICO","type":"uint256"}],"name":"setAvailableTokensICO","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_ICO","type":"uint256"}],"name":"setICO","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxPurchase","type":"uint256"}],"name":"setMaxPurchase","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minPurchase","type":"uint256"}],"name":"setMinPurchase","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_rate","type":"uint256"}],"name":"setRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_buyActive","type":"bool"},{"internalType":"bool","name":"_withdrawActive","type":"bool"}],"name":"setSales","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"setToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"_treasury","type":"address"}],"name":"setTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_whitelistActive","type":"bool"}],"name":"setWhitelistActive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_whitelistSigner","type":"address"}],"name":"setWhitelistSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"treasury","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whitelistActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whitelistSigner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawLeftoversToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_beneficiary","type":"address"}],"name":"withdrawalAmount","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"}]