// Sources flattened with hardhat v2.2.1 https://hardhat.org
// File contracts/interfaces/IERC20.sol
pragma solidity >=0.6.0 <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);
}
// File contracts/interfaces/IPoolFactory.sol
pragma solidity >=0.7.1;
interface IPoolFactory {
function getTier() external view returns (address);
}
// File contracts/libraries/Ownable.sol
pragma solidity ^0.7.0;
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param _newOwner The address to transfer ownership to.
*/
function transferOwnership(address _newOwner) public onlyOwner {
_transferOwnership(_newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param _newOwner The address to transfer ownership to.
*/
function _transferOwnership(address _newOwner) internal {
require(_newOwner != address(0));
emit OwnershipTransferred(owner, _newOwner);
owner = _newOwner;
}
}
// File contracts/libraries/ReentrancyGuard.sol
pragma solidity >=0.6.0 <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;
}
}
// File contracts/libraries/SafeMath.sol
pragma solidity ^0.7.0;
// From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/Math.sol
// Subject to the MIT license.
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the addition of two unsigned integers, reverting with custom message on overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, errorMessage);
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on underflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot underflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction underflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on underflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot underflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, errorMessage);
return c;
}
/**
* @dev Returns the integer division of two unsigned integers.
* Reverts on division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers.
* Reverts with custom message on division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
// File contracts/libraries/Pausable.sol
pragma solidity >=0.7.1;
/**
* @title Pausable
* @dev Base contract which allows children to implement an emergency stop mechanism.
*/
contract Pausable is Ownable {
event Pause();
event Unpause();
bool public paused;
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*/
modifier whenNotPaused() {
require(!paused, "CONTRACT_PAUSED");
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*/
modifier whenPaused() {
require(paused, "CONTRACT_NOT_PAUSED");
_;
}
/**
* @dev called by the owner to pause, triggers stopped state
*/
function pause() onlyOwner whenNotPaused public {
paused = true;
emit Pause();
}
/**
* @dev called by the owner to unpause, returns to normal state
*/
function unpause() onlyOwner whenPaused public {
paused = false;
emit Unpause();
}
}
// File openzeppelin-solidity/contracts/cryptography/ECDSA.sol@v3.4.1
pragma solidity >=0.6.0 <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 {
/**
* @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) {
// Check the signature length
if (signature.length != 65) {
revert("ECDSA: invalid signature length");
}
// Divide the signature in r, s and v variables
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
// solhint-disable-next-line no-inline-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return recover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover-bytes32-bytes-} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
// 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 (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): 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.
require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value");
require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
require(signer != address(0), "ECDSA: invalid signature");
return signer;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* replicates the behavior of the
* https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
* JSON-RPC method.
*
* 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));
}
}
// File contracts/extensions/RedKiteWhitelist.sol
pragma solidity ^0.7.0;
// Signature Verification
/// @title RedKite Whitelists - Implement off-chain whitelist and on-chain verification
/// @author Thang Nguyen Quy <thang.nguyen5@sotatek.com>
contract RedKiteWhitelist {
// Using Openzeppelin ECDSA cryptography library
function getMessageHash(
address _candidate,
uint256 _maxAmount,
uint256 _minAmount
) public pure returns (bytes32) {
return keccak256(abi.encodePacked(_candidate, _maxAmount, _minAmount));
}
function getClaimMessageHash(
address _candidate,
uint256 _amount
) public pure returns (bytes32) {
return keccak256(abi.encodePacked(_candidate, _amount));
}
// Verify signature function
function verify(
address _signer,
address _candidate,
uint256 _maxAmount,
uint256 _minAmount,
bytes memory signature
) public pure returns (bool) {
bytes32 messageHash = getMessageHash(_candidate, _maxAmount, _minAmount);
bytes32 ethSignedMessageHash = getEthSignedMessageHash(messageHash);
return getSignerAddress(ethSignedMessageHash, signature) == _signer;
}
// Verify signature function
function verifyClaimToken(
address _signer,
address _candidate,
uint256 _amount,
bytes memory signature
) public pure returns (bool) {
bytes32 messageHash = getClaimMessageHash(_candidate, _amount);
bytes32 ethSignedMessageHash = getEthSignedMessageHash(messageHash);
return getSignerAddress(ethSignedMessageHash, signature) == _signer;
}
function getSignerAddress(bytes32 _messageHash, bytes memory _signature) public pure returns(address signer) {
return ECDSA.recover(_messageHash, _signature);
}
// Split signature to r, s, v
function splitSignature(bytes memory _signature)
public
pure
returns (
bytes32 r,
bytes32 s,
uint8 v
)
{
require(_signature.length == 65, "invalid signature length");
assembly {
r := mload(add(_signature, 32))
s := mload(add(_signature, 64))
v := byte(0, mload(add(_signature, 96)))
}
}
function getEthSignedMessageHash(bytes32 _messageHash)
public
pure
returns (bytes32)
{
return ECDSA.toEthSignedMessageHash(_messageHash);
}
}
// File contracts/presale-pool/PreSalePool.sol
pragma solidity ^0.7.1;
contract PreSalePool is Ownable, ReentrancyGuard, Pausable, RedKiteWhitelist {
using SafeMath for uint256;
struct OfferedCurrency {
uint256 decimals;
uint256 rate;
}
// The token being sold
IERC20 public token;
// The address of factory contract
address public factory;
// The address of signer account
address public signer;
// Address where funds are collected
address public fundingWallet;
// Timestamps when token started to sell
uint256 public openTime = block.timestamp;
// Timestamps when token stopped to sell
uint256 public closeTime;
// Amount of wei raised
uint256 public weiRaised = 0;
// Amount of token sold
uint256 public tokenSold = 0;
// Amount of token sold
uint256 public totalUnclaimed = 0;
// Number of token user purchased
mapping(address => uint256) public userPurchased;
// Number of token user claimed
mapping(address => uint256) public userClaimed;
// Number of token user purchased
mapping(address => mapping (address => uint)) public investedAmountOf;
// Get offered currencies
mapping(address => OfferedCurrency) public offeredCurrencies;
// Pool extensions
bool public useWhitelist;
// -----------------------------------------
// Lauchpad Starter's event
// -----------------------------------------
event PresalePoolCreated(
address token,
uint256 openTime,
uint256 closeTime,
address offeredCurrency,
uint256 offeredCurrencyDecimals,
uint256 offeredCurrencyRate,
address wallet,
address owner
);
event TokenPurchaseByEther(
address indexed purchaser,
address indexed beneficiary,
uint256 value,
uint256 amount
);
event TokenPurchaseByToken(
address indexed purchaser,
address indexed beneficiary,
address token,
uint256 value,
uint256 amount
);
event TokenClaimed(address user, uint256 amount);
event RefundedIcoToken(address wallet, uint256 amount);
event PoolStatsChanged();
// -----------------------------------------
// Constructor
// -----------------------------------------
constructor() {
factory = msg.sender;
}
// -----------------------------------------
// Red Kite external interface
// -----------------------------------------
/**
* @dev fallback function
*/
fallback() external {
revert();
}
/**
* @dev fallback function
*/
receive() external payable {
revert();
}
/**
* @param _token Address of the token being sold
* @param _duration Duration of ICO Pool
* @param _openTime When ICO Started
* @param _offeredCurrency Address of offered token
* @param _offeredCurrencyDecimals Decimals of offered token
* @param _offeredRate Number of currency token units a buyer gets
* @param _wallet Address where collected funds will be forwarded to
* @param _signer Address where collected funds will be forwarded to
*/
function initialize(
address _token,
uint256 _duration,
uint256 _openTime,
address _offeredCurrency,
uint256 _offeredRate,
uint256 _offeredCurrencyDecimals,
address _wallet,
address _signer
) external {
require(msg.sender == factory, "POOL::UNAUTHORIZED");
token = IERC20(_token);
openTime = _openTime;
closeTime = _openTime.add(_duration);
fundingWallet = _wallet;
owner = tx.origin;
paused = false;
signer = _signer;
offeredCurrencies[_offeredCurrency] = OfferedCurrency({
rate: _offeredRate,
decimals: _offeredCurrencyDecimals
});
emit PresalePoolCreated(
_token,
_openTime,
closeTime,
_offeredCurrency,
_offeredCurrencyDecimals,
_offeredRate,
_wallet,
owner
);
}
/**
* @notice Returns the conversion rate when user buy by offered token
* @return Returns only a fixed number of rate.
*/
function getOfferedCurrencyRate(address _token) public view returns (uint256) {
return offeredCurrencies[_token].rate;
}
/**
* @notice Returns the conversion rate decimals when user buy by offered token
* @return Returns only a fixed number of decimals.
*/
function getOfferedCurrencyDecimals(address _token) public view returns (uint256) {
return offeredCurrencies[_token].decimals;
}
/**
* @notice Return the available tokens for purchase
* @return availableTokens Number of total available
*/
function getAvailableTokensForSale() public view returns (uint256 availableTokens) {
return token.balanceOf(address(this)).add(totalUnclaimed).sub(tokenSold);
}
/**
* @notice Owner can set the offered token conversion rate. Receiver tokens = tradeTokens * tokenRate / 10 ** etherConversionRateDecimals
* @param _rate Fixed number of ether rate
* @param _decimals Fixed number of ether rate decimals
*/
function setOfferedCurrencyRateAndDecimals(address _token, uint256 _rate, uint256 _decimals)
external
onlyOwner
{
offeredCurrencies[_token].rate = _rate;
offeredCurrencies[_token].decimals = _decimals;
emit PoolStatsChanged();
}
/**
* @notice Owner can set the offered token conversion rate. Receiver tokens = tradeTokens * tokenRate / 10 ** etherConversionRateDecimals
* @param _rate Fixed number of rate
*/
function setOfferedCurrencyRate(address _token, uint256 _rate) external onlyOwner {
require(offeredCurrencies[_token].rate != _rate, "POOL::RATE_INVALID");
offeredCurrencies[_token].rate = _rate;
emit PoolStatsChanged();
}
/**
* @notice Owner can set the offered token conversion rate. Receiver tokens = tradeTokens * tokenRate / 10 ** etherConversionRateDecimals
* @param _newSigner Address of new signer
*/
function setNewSigner(address _newSigner) external onlyOwner {
require(signer != _newSigner, "POOL::SIGNER_INVALID");
signer = _newSigner;
}
/**
* @notice Owner can set the offered token conversion rate. Receiver tokens = tradeTokens * tokenRate / 10 ** etherConversionRateDecimals
* @param _decimals Fixed number of decimals
*/
function setOfferedCurrencyDecimals(address _token, uint256 _decimals) external onlyOwner {
require(offeredCurrencies[_token].decimals != _decimals, "POOL::RATE_INVALID");
offeredCurrencies[_token].decimals = _decimals;
emit PoolStatsChanged();
}
/**
* @notice Owner can set the close time (time in seconds). User can buy before close time.
* @param _closeTime Value in uint256 determine when we stop user to by tokens
*/
function setCloseTime(uint256 _closeTime) external onlyOwner() {
require(_closeTime >= block.timestamp, "POOL::INVALID_TIME");
closeTime = _closeTime;
emit PoolStatsChanged();
}
/**
* @notice Owner can set the open time (time in seconds). User can buy after open time.
* @param _openTime Value in uint256 determine when we allow user to by tokens
*/
function setOpenTime(uint256 _openTime) external onlyOwner() {
openTime = _openTime;
emit PoolStatsChanged();
}
/**
* @notice Owner can set extentions.
* @param _whitelist Value in bool. True if using whitelist
*/
function setPoolExtentions(bool _whitelist) external onlyOwner() {
useWhitelist = _whitelist;
emit PoolStatsChanged();
}
function buyTokenByEtherWithPermission(
address _beneficiary,
address _candidate,
uint256 _maxAmount,
uint256 _minAmount,
bytes memory _signature
) public payable whenNotPaused nonReentrant {
uint256 weiAmount = msg.value;
require(offeredCurrencies[address(0)].rate != 0, "POOL::PURCHASE_METHOD_NOT_ALLOWED");
_preValidatePurchase(_beneficiary, weiAmount);
require(_validPurchase(), "POOL::ENDED");
require(_verifyWhitelist(_candidate, _maxAmount, _minAmount, _signature));
// calculate token amount to be created
uint256 tokens = _getOfferedCurrencyToTokenAmount(address(0), weiAmount);
require(getAvailableTokensForSale() >= tokens, "POOL::NOT_ENOUGHT_TOKENS_FOR_SALE");
require(tokens >= _minAmount || userPurchased[_candidate].add(tokens) >= _minAmount, "POOL::MIN_AMOUNT_UNREACHED");
require(userPurchased[_candidate].add(tokens) <= _maxAmount, "POOL::PURCHASE_AMOUNT_EXCEED_ALLOWANCE");
_forwardFunds(weiAmount);
_updatePurchasingState(weiAmount, tokens);
investedAmountOf[address(0)][_candidate] = investedAmountOf[address(0)][_candidate].add(weiAmount);
emit TokenPurchaseByEther(msg.sender, _beneficiary, weiAmount, tokens);
}
function buyTokenByTokenWithPermission(
address _beneficiary,
address _token,
uint256 _amount,
address _candidate,
uint256 _maxAmount,
uint256 _minAmount,
bytes memory _signature
) public whenNotPaused nonReentrant {
require(offeredCurrencies[_token].rate != 0, "POOL::PURCHASE_METHOD_NOT_ALLOWED");
require(_validPurchase(), "POOL::ENDED");
require(_verifyWhitelist(_candidate, _maxAmount, _minAmount, _signature));
_verifyAllowance(msg.sender, _token, _amount);
_preValidatePurchase(_beneficiary, _amount);
uint256 tokens = _getOfferedCurrencyToTokenAmount(_token, _amount);
require(getAvailableTokensForSale() >= tokens, "POOL::NOT_ENOUGHT_TOKENS_FOR_SALE");
require(tokens >= _minAmount || userPurchased[_candidate].add(tokens) >= _minAmount, "POOL::MIN_AMOUNT_UNREACHED");
require(userPurchased[_candidate].add(tokens) <= _maxAmount, "POOL:PURCHASE_AMOUNT_EXCEED_ALLOWANCE");
_forwardTokenFunds(_token, _amount);
_updatePurchasingState(_amount, tokens);
investedAmountOf[_token][_candidate] = investedAmountOf[address(0)][_candidate].add(_amount);
emit TokenPurchaseByToken(
msg.sender,
_beneficiary,
_token,
_amount,
tokens
);
}
/**
* @notice Return true if pool has ended
* @dev User cannot purchase / trade tokens when isFinalized == true
* @return true if the ICO Ended.
*/
function isFinalized() public view returns (bool) {
return block.timestamp >= closeTime;
}
/**
* @notice Owner can receive their remaining tokens when ICO Ended
* @dev Can refund remainning token if the ico ended
* @param _wallet Address wallet who receive the remainning tokens when Ico end
*/
function refundRemainingTokens(address _wallet)
external
onlyOwner
{
require(isFinalized(), "POOL::ICO_NOT_ENDED");
require(token.balanceOf(address(this)) > 0, "POOL::EMPTY_BALANCE");
uint256 remainingTokens = getAvailableTokensForSale();
_deliverTokens(_wallet, remainingTokens);
emit RefundedIcoToken(_wallet, remainingTokens);
}
/**
* @notice User can receive their tokens when pool finished
*/
function claimTokens(address _candidate, uint256 _amount, bytes memory _signature) public {
require(_verifyClaimToken(_candidate, _amount, _signature), "POOL::NOT_ALLOW_TO_CLAIM");
require(isFinalized(), "POOL::NOT_FINALLIZED");
uint256 claimAmount = userPurchased[_candidate].sub(userClaimed[_candidate]);
if (claimAmount > _amount) {
claimAmount = _amount;
}
userClaimed[_candidate] = userClaimed[_candidate].add(claimAmount);
_deliverTokens(msg.sender, claimAmount);
totalUnclaimed = totalUnclaimed.sub(claimAmount);
emit TokenClaimed(msg.sender, claimAmount);
}
/**
* @dev Validation of an incoming purchase. Use require statements to revert state when conditions are not met. Use super to concatenate validations.
* @param _beneficiary Address performing the token purchase
* @param _weiAmount Value in wei involved in the purchase
*/
function _preValidatePurchase(address _beneficiary, uint256 _weiAmount)
internal
pure
{
require(_beneficiary != address(0), "POOL::INVALID_BENEFICIARY");
require(_weiAmount != 0, "POOL::INVALID_WEI_AMOUNT");
}
/**
* @dev Override to extend the way in which ether is converted to tokens.
* @param _amount Value in wei to be converted into tokens
* @return Number of tokens that can be purchased with the specified _weiAmount
*/
function _getOfferedCurrencyToTokenAmount(address _token, uint256 _amount)
internal
view
returns (uint256)
{
uint256 rate = getOfferedCurrencyRate(_token);
uint256 decimals = getOfferedCurrencyDecimals(_token);
return _amount.mul(rate).div(10**decimals);
}
/**
* @dev Source of tokens. Transfer / mint
* @param _beneficiary Address performing the token purchase
* @param _tokenAmount Number of tokens to be emitted
*/
function _deliverTokens(address _beneficiary, uint256 _tokenAmount)
internal
{
token.transfer(_beneficiary, _tokenAmount);
}
/**
* @dev Determines how ETH is stored/forwarded on purchases.
*/
function _forwardFunds(uint256 _value) internal {
address payable wallet = address(uint160(fundingWallet));
(bool success, ) = wallet.call{value: _value}("");
require(success, "POOL::WALLET_TRANSFER_FAILED");
}
/**
* @dev Determines how Token is stored/forwarded on purchases.
*/
function _forwardTokenFunds(address _token, uint256 _amount) internal {
IERC20(_token).transferFrom(msg.sender, fundingWallet, _amount);
}
/**
* @param _tokens Value of sold tokens
* @param _weiAmount Value in wei involved in the purchase
*/
function _updatePurchasingState(uint256 _weiAmount, uint256 _tokens)
internal
{
weiRaised = weiRaised.add(_weiAmount);
tokenSold = tokenSold.add(_tokens);
userPurchased[msg.sender] = userPurchased[msg.sender].add(_tokens);
totalUnclaimed = totalUnclaimed.add(_tokens);
}
// @return true if the transaction can buy tokens
function _validPurchase() internal view returns (bool) {
bool withinPeriod =
block.timestamp >= openTime && block.timestamp <= closeTime;
return withinPeriod;
}
/**
* @dev Transfer eth to an address
* @param _to Address receiving the eth
* @param _amount Amount of wei to transfer
*/
function _transfer(address _to, uint256 _amount) private {
address payable payableAddress = address(uint160(_to));
(bool success, ) = payableAddress.call{value: _amount}("");
require(success, "POOL::TRANSFER_FEE_FAILED");
}
/**
* @dev Verify allowance of purchase
* @param _user Address of buyer
* @param _token token address of purchasing token
* @param _amount Amount of token to buy pool token
*/
function _verifyAllowance(
address _user,
address _token,
uint256 _amount
) private view {
IERC20 tradeToken = IERC20(_token);
uint256 allowance = tradeToken.allowance(_user, address(this));
require(allowance >= _amount, "POOL::TOKEN_NOT_APPROVED");
}
/**
* @dev Verify permission of purchase
* @param _candidate Address of buyer
* @param _maxAmount max token can buy
* @param _minAmount min token can buy
* @param _signature Signature of signers
*/
function _verifyWhitelist(
address _candidate,
uint256 _maxAmount,
uint256 _minAmount,
bytes memory _signature
) private view returns (bool) {
require(msg.sender == _candidate, "POOL::WRONG_CANDIDATE");
if (useWhitelist) {
return (verify(signer, _candidate, _maxAmount, _minAmount, _signature));
}
return true;
}
/**
* @dev Verify permission of purchase
* @param _candidate Address of buyer
* @param _amount claimable amount
* @param _signature Signature of signers
*/
function _verifyClaimToken(
address _candidate,
uint256 _amount,
bytes memory _signature
) private view returns (bool) {
require(msg.sender == _candidate, "POOL::WRONG_CANDIDATE");
return (verifyClaimToken(signer, _candidate, _amount, _signature));
}
}
{
"compilationTarget": {
"PreSalePool.sol": "PreSalePool"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 1000
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"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":[],"name":"Pause","type":"event"},{"anonymous":false,"inputs":[],"name":"PoolStatsChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"openTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"closeTime","type":"uint256"},{"indexed":false,"internalType":"address","name":"offeredCurrency","type":"address"},{"indexed":false,"internalType":"uint256","name":"offeredCurrencyDecimals","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"offeredCurrencyRate","type":"uint256"},{"indexed":false,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"address","name":"owner","type":"address"}],"name":"PresalePoolCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RefundedIcoToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokenClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"purchaser","type":"address"},{"indexed":true,"internalType":"address","name":"beneficiary","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokenPurchaseByEther","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"purchaser","type":"address"},{"indexed":true,"internalType":"address","name":"beneficiary","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokenPurchaseByToken","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpause","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[{"internalType":"address","name":"_beneficiary","type":"address"},{"internalType":"address","name":"_candidate","type":"address"},{"internalType":"uint256","name":"_maxAmount","type":"uint256"},{"internalType":"uint256","name":"_minAmount","type":"uint256"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"buyTokenByEtherWithPermission","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_beneficiary","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_candidate","type":"address"},{"internalType":"uint256","name":"_maxAmount","type":"uint256"},{"internalType":"uint256","name":"_minAmount","type":"uint256"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"buyTokenByTokenWithPermission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_candidate","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"claimTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"closeTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fundingWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAvailableTokensForSale","outputs":[{"internalType":"uint256","name":"availableTokens","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_candidate","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"getClaimMessageHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_messageHash","type":"bytes32"}],"name":"getEthSignedMessageHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_candidate","type":"address"},{"internalType":"uint256","name":"_maxAmount","type":"uint256"},{"internalType":"uint256","name":"_minAmount","type":"uint256"}],"name":"getMessageHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getOfferedCurrencyDecimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getOfferedCurrencyRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_messageHash","type":"bytes32"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"getSignerAddress","outputs":[{"internalType":"address","name":"signer","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_duration","type":"uint256"},{"internalType":"uint256","name":"_openTime","type":"uint256"},{"internalType":"address","name":"_offeredCurrency","type":"address"},{"internalType":"uint256","name":"_offeredRate","type":"uint256"},{"internalType":"uint256","name":"_offeredCurrencyDecimals","type":"uint256"},{"internalType":"address","name":"_wallet","type":"address"},{"internalType":"address","name":"_signer","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"investedAmountOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isFinalized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"offeredCurrencies","outputs":[{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"uint256","name":"rate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"openTime","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":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"}],"name":"refundRemainingTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_closeTime","type":"uint256"}],"name":"setCloseTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newSigner","type":"address"}],"name":"setNewSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_decimals","type":"uint256"}],"name":"setOfferedCurrencyDecimals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_rate","type":"uint256"}],"name":"setOfferedCurrencyRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_rate","type":"uint256"},{"internalType":"uint256","name":"_decimals","type":"uint256"}],"name":"setOfferedCurrencyRateAndDecimals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_openTime","type":"uint256"}],"name":"setOpenTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_whitelist","type":"bool"}],"name":"setPoolExtentions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"signer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"splitSignature","outputs":[{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenSold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalUnclaimed","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":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"useWhitelist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userClaimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userPurchased","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"},{"internalType":"address","name":"_candidate","type":"address"},{"internalType":"uint256","name":"_maxAmount","type":"uint256"},{"internalType":"uint256","name":"_minAmount","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"verify","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"},{"internalType":"address","name":"_candidate","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"verifyClaimToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"weiRaised","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]