More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 7,740 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Withdraw Multipl... | 59860353 | 15 days ago | IN | 0 AVAX | 0.00034308 | ||||
Withdraw Multipl... | 56590584 | 82 days ago | IN | 0 AVAX | 0.00034308 | ||||
Withdraw Multipl... | 56513120 | 84 days ago | IN | 0 AVAX | 0.00034308 | ||||
Withdraw Multipl... | 56203253 | 90 days ago | IN | 0 AVAX | 0.00034308 | ||||
Withdraw Multipl... | 56073195 | 93 days ago | IN | 0 AVAX | 0.00034308 | ||||
Withdraw Multipl... | 56022762 | 94 days ago | IN | 0 AVAX | 0.00034308 | ||||
Withdraw Multipl... | 55641800 | 102 days ago | IN | 0 AVAX | 0.00009802 | ||||
Withdraw Multipl... | 54482939 | 126 days ago | IN | 0 AVAX | 0.00034308 | ||||
Withdraw Multipl... | 54302661 | 130 days ago | IN | 0 AVAX | 0.00269566 | ||||
Withdraw Multipl... | 54286418 | 131 days ago | IN | 0 AVAX | 0.00269566 | ||||
Withdraw Multipl... | 54279928 | 131 days ago | IN | 0 AVAX | 0.00269566 | ||||
Withdraw Multipl... | 54248158 | 132 days ago | IN | 0 AVAX | 0.00269566 | ||||
Withdraw Multipl... | 54247840 | 132 days ago | IN | 0 AVAX | 0.00269566 | ||||
Withdraw Multipl... | 53993046 | 138 days ago | IN | 0 AVAX | 0.00222541 | ||||
Withdraw Multipl... | 53928788 | 139 days ago | IN | 0 AVAX | 0.00269566 | ||||
Withdraw Multipl... | 53921415 | 140 days ago | IN | 0 AVAX | 0.00269566 | ||||
Withdraw Multipl... | 53760136 | 143 days ago | IN | 0 AVAX | 0.00269566 | ||||
Withdraw Multipl... | 53712299 | 144 days ago | IN | 0 AVAX | 0.00269566 | ||||
Withdraw Multipl... | 53612935 | 147 days ago | IN | 0 AVAX | 0.00269566 | ||||
Withdraw Multipl... | 53457545 | 150 days ago | IN | 0 AVAX | 0.00269566 | ||||
Withdraw Multipl... | 53362571 | 153 days ago | IN | 0 AVAX | 0.00269566 | ||||
Withdraw Multipl... | 53072269 | 159 days ago | IN | 0 AVAX | 0.00269566 | ||||
Withdraw Multipl... | 53032382 | 160 days ago | IN | 0 AVAX | 0.00269566 | ||||
Withdraw Multipl... | 52842312 | 165 days ago | IN | 0 AVAX | 0.00269566 | ||||
Withdraw Multipl... | 51154031 | 205 days ago | IN | 0 AVAX | 0.00269566 |
Latest 25 internal transactions (View All)
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
29249111 | 728 days ago | 295 AVAX | ||||
29249096 | 728 days ago | 17,552.87049269 AVAX | ||||
29239247 | 728 days ago | 0.60632056 AVAX | ||||
29239245 | 728 days ago | 0.60508956 AVAX | ||||
29239242 | 728 days ago | 0.58702881 AVAX | ||||
29239239 | 728 days ago | 0.58518429 AVAX | ||||
29239235 | 728 days ago | 0.5787005 AVAX | ||||
29239231 | 728 days ago | 0.5765735 AVAX | ||||
29239227 | 728 days ago | 0.57341645 AVAX | ||||
29239224 | 728 days ago | 0.546586 AVAX | ||||
29239213 | 728 days ago | 0.40341821 AVAX | ||||
29239205 | 728 days ago | 0.01560269 AVAX | ||||
29239196 | 728 days ago | 0.4004433 AVAX | ||||
29239188 | 728 days ago | 0.34852206 AVAX | ||||
29239128 | 728 days ago | 0.57169606 AVAX | ||||
29239114 | 728 days ago | 0.55442534 AVAX | ||||
29239111 | 728 days ago | 0.55338463 AVAX | ||||
29239108 | 728 days ago | 0.55107816 AVAX | ||||
29239104 | 728 days ago | 0.53830926 AVAX | ||||
29239093 | 728 days ago | 0.53681854 AVAX | ||||
29239087 | 728 days ago | 0.53587923 AVAX | ||||
29239083 | 728 days ago | 0.52942724 AVAX | ||||
29239079 | 728 days ago | 0.52617934 AVAX | ||||
29239075 | 728 days ago | 0.51411527 AVAX | ||||
29239072 | 728 days ago | 0.51287822 AVAX |
Loading...
Loading
Minimal Proxy Contract for 0x4e05033e96ecb624507835ffccb451ecea1e7156
Contract Name:
AvalaunchSaleV2
Compiler Version
v0.6.12+commit.27d51765
Contract Source Code (Solidity)
/** *Submitted for verification at snowscan.xyz on 2023-02-06 */ // Sources flattened with hardhat v2.5.0 https://hardhat.org // File contracts/interfaces/IAdmin.sol pragma solidity 0.6.12; interface IAdmin { function isAdmin(address user) external view returns (bool); } // File contracts/interfaces/IAllocationStaking.sol pragma solidity 0.6.12; interface IAllocationStaking { function redistributeXava(uint256 _pid, address _user, uint256 _amountToBurn) external; function deposited(uint256 _pid, address _user) external view returns (uint256); function setTokensUnlockTime(uint256 _pid, address _user, uint256 _tokensUnlockTime) external; } // File contracts/interfaces/IERC20Metadata.sol pragma solidity ^0.6.12; /** * @dev Interface for the optional metadata functions from the ERC20 standard. */ interface IERC20Metadata { /** * @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 contracts/interfaces/IDexalotPortfolio.sol pragma solidity ^0.6.12; /** * IDexalotPortfolio contract. * Date created: 28.1.22. */ interface IDexalotPortfolio { function depositTokenFromContract(address _from, bytes32 _symbol, uint _quantity) external; } // File contracts/interfaces/ICollateral.sol pragma solidity ^0.6.12; interface ICollateral { function depositCollateral() external payable; function withdrawCollateral() external payable; function totalBalance() external view returns (uint256); } // File contracts/interfaces/IAvalaunchMarketplace.sol pragma solidity ^0.6.12; interface IAvalaunchMarketplace { function listPortions(address owner, uint256[] calldata portions) external; function removePortions(address owner, uint256[] calldata portions) external; function approveSale(address sale) external; } // File @openzeppelin/contracts/cryptography/[email protected] 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 @openzeppelin/contracts/token/ERC20/[email protected] 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 @openzeppelin/contracts/math/[email protected] pragma solidity >=0.6.0 <0.8.0; /** * @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, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b > a) return (false, 0); return (true, a - b); } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, 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 (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a / b); } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a % b); } /** * @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 subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction overflow"); return a - b; } /** * @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) { if (a == 0) return 0; uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers, reverting 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) { require(b > 0, "SafeMath: division by zero"); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting 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) { require(b > 0, "SafeMath: modulo by zero"); return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); return a - b; } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryDiv}. * * 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) { require(b > 0, errorMessage); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * 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 @openzeppelin/contracts/utils/[email protected] pragma solidity >=0.6.2 <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; // solhint-disable-next-line no-inline-assembly 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"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (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"); // solhint-disable-next-line avoid-low-level-calls (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"); // solhint-disable-next-line avoid-low-level-calls (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"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.delegatecall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private 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 // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } } // File @openzeppelin/contracts/token/ERC20/[email protected] pragma solidity >=0.6.0 <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 SafeMath for uint256; 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' // solhint-disable-next-line max-line-length 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).add(value); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); _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 // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } // File @openzeppelin/contracts/proxy/[email protected] // solhint-disable-next-line compiler-version pragma solidity >=0.4.24 <0.8.0; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. */ bool private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Modifier to protect an initializer function from being invoked twice. */ modifier initializer() { require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized"); bool isTopLevelCall = !_initializing; if (isTopLevelCall) { _initializing = true; _initialized = true; } _; if (isTopLevelCall) { _initializing = false; } } /// @dev Returns true if and only if the function is running in the constructor function _isConstructor() private view returns (bool) { return !Address.isContract(address(this)); } } // File contracts/sales/AvalaunchSaleV2.sol pragma solidity 0.6.12; contract AvalaunchSaleV2 is Initializable { using SafeERC20 for IERC20; using ECDSA for bytes32; using SafeMath for uint256; // Pointer allocation staking contract IAllocationStaking public allocationStaking; // Pointer to sales factory contract address public factory; // Pointer to admin contract IAdmin public admin; // Pointer to collateral contract ICollateral public collateral; // Pointer to marketplace contract IAvalaunchMarketplace public marketplace; // Pointer to dexalot portfolio contract IDexalotPortfolio public dexalotPortfolio; // Official sale mod address public moderator; // Sale Phases enum Phases { Idle, Registration, Validator, Staking, Booster } // Portion States enum PortionStates { Available, Withdrawn, WithdrawnToDexalot, OnMarket, Sold } struct Sale { IERC20 token; // Official sale token Phases phase; // Current phase of sale bool isCreated; // Sale creation marker bool earningsWithdrawn; // Earnings withdrawal marker bool leftoverWithdrawn; // Leftover withdrawal marker bool tokensDeposited; // Token deposit marker uint256 tokenPriceInAVAX; // Sale token's price in AVAX uint256 amountOfTokensToSell; // Amount of tokens to sell uint256 totalTokensSold; // Amount of sold tokens uint256 totalAVAXRaised; // Total AVAX amount raised uint256 saleEnd; // Sale end timestamp } struct Participation { uint256 amountBought; // Amount of tokens bought uint256 amountAVAXPaid; // Amount of $AVAX paid for tokens uint256 timeParticipated; // Timestamp of participation time uint256 phaseId; // Phase user is registered for uint256[] portionAmounts; // Amount of tokens for each portion PortionStates[] portionStates; // State of each portion uint256 boostedAmountAVAXPaid; // Amount of $AVAX paid for boost uint256 boostedAmountBought; // Amount of tokens bought with boost uint256 amountBoughtOnMarketplace; // Amount of tokens purchased on marketplace uint256 amountSoldOnMarketplace; // Amount of tokens sold on marketplace } // Sale state structure Sale public sale; // Mapping user to his participation mapping(address => Participation) public userToParticipation; // User to phase for which he registered mapping(address => uint256) public addressToPhaseRegisteredFor; // Mapping if user is participated or not mapping(address => bool) public isParticipated; // Number of sale registrants uint256 public numberOfRegistrants; // Times when portions are getting unlocked uint256[] public vestingPortionsUnlockTime; // Percent of the participation user can withdraw uint256[] public vestingPercentPerPortion; // Number of users participated in the sale uint256 public numberOfParticipants; // Number of vested token portions uint256 public numberOfVestedPortions; // Precision for percent for portion vesting uint256 public portionVestingPrecision; // Registration deposit AVAX, deposited during the registration, returned after the participation. uint256 public registrationDepositAVAX; // Accounting total AVAX collected, after sale end admin can withdraw this uint256 public registrationFees; // Timestamp of sale.tokenPriceInAvax latest update uint256 public lastPriceUpdateTimestamp; // First vested portion's Dexalot unlock timestamp uint256 public dexalotUnlockTime; // Sale setter lock flag bool public isLockOn; // Multiplier used on token amount calculations uint256 public oneTokenInWei; // Events event SaleCreated(uint256 tokenPriceInAVAX, uint256 amountOfTokensToSell, uint256 saleEnd); event TokensSold(address user, uint256 amount); event UserRegistered(address user, uint256 phaseId); event NewTokenPriceSet(uint256 newPrice); event RegistrationAVAXRefunded(address user, uint256 amountRefunded); event TokensWithdrawn(address user, uint256 amount); event TokensWithdrawnToDexalot(address user, uint256 amount); event LockActivated(uint256 time); event ParticipationBoosted(address user, uint256 amountAVAX, uint256 amountTokens); event PhaseChanged(Phases phase); // Restricting calls only to moderator modifier onlyModerator() { _onlyModerator(); _; } // Internal function containing onlyModerator modifier logic function _onlyModerator() private view { require(msg.sender == moderator, "Only moderator."); } // Restricting calls only to sale admin modifier onlyAdmin() { _onlyAdmin(); _; } // Internal function containing onlyAdmin modifier logic function _onlyAdmin() private view { require(admin.isAdmin(msg.sender), "Only admin."); } // Restricting calls only to collateral contract modifier onlyCollateral() { _onlyCollateral(); _; } // Internal function containing onlyCollateral modifier logic function _onlyCollateral() private view { require(msg.sender == address(collateral), "Only collateral."); } // Restricting setter calls after gate closing modifier ifUnlocked() { _ifUnlocked(); _; } // Internal function containing ifUnlocked modifier logic function _ifUnlocked() private view { require(!isLockOn, "Lock active."); } constructor() public initializer {} function initialize( IAdmin _admin, IAllocationStaking _allocationStaking, ICollateral _collateral, IAvalaunchMarketplace _marketplace, address _moderator ) external initializer { require(address(_admin) != address(0)); require(address(_allocationStaking) != address(0)); require(address(_collateral) != address(0)); require(address(_marketplace) != address(0)); require(_moderator != address(0)); factory = msg.sender; admin = _admin; allocationStaking = _allocationStaking; collateral = _collateral; marketplace = _marketplace; moderator = _moderator; } /** * @notice Function to set vesting params * @param _unlockingTimes is array of unlock times for each portion * @param _percents are percents of purchased tokens that are distributed among portions */ function setVestingParams( uint256[] calldata _unlockingTimes, uint256[] calldata _percents ) external onlyAdmin { require(_unlockingTimes.length == _percents.length); require(vestingPercentPerPortion.length == 0 && vestingPortionsUnlockTime.length == 0, "Already set."); require(portionVestingPrecision != 0, "Sale params not set."); // Set number of vested portions numberOfVestedPortions = _unlockingTimes.length; // Gas optimization uint256 _numberOfVestedPortions = numberOfVestedPortions; // Require that locking times are later than sale end require(_unlockingTimes[0] > sale.saleEnd, "Invalid first unlock time."); // Use precision to make sure percents of portions align uint256 precision = portionVestingPrecision; // Set vesting portions percents and unlock times for (uint256 i = 0; i < _numberOfVestedPortions; i++) { if (i > 0) { // Each portion unlock time must be later than previous require(_unlockingTimes[i] > _unlockingTimes[i-1], "Invalid unlock time."); } vestingPortionsUnlockTime.push(_unlockingTimes[i]); vestingPercentPerPortion.push(_percents[i]); precision = precision.sub(_percents[i]); } require(precision == 0, "Invalid percentage calculation."); } /** * @notice Function to shift vested portion unlock times by admin * @param timeToShift is amount of time to add to all portion unlock times * @dev will shift all portions which's unlock time hasn't passed yet */ function shiftVestingUnlockTimes(uint256 timeToShift) external onlyAdmin { require(timeToShift > 0, "Invalid shift time."); bool movable; // Gas optimization uint256 _numberOfVestedPortions = numberOfVestedPortions; // Shift the unlock time for each portion for (uint256 i = 0; i < _numberOfVestedPortions; i++) { // Shift only portions that time didn't reach yet if (!movable && block.timestamp < vestingPortionsUnlockTime[i]) movable = true; // Each portion is after the previous so once movable flag is active all later portions may be shifted if (movable) vestingPortionsUnlockTime[i] = vestingPortionsUnlockTime[i].add(timeToShift); } } /** * @notice Function to set fundamental sale parameters * @param _token is official sale token, may be set asynchronously too * @param _tokenPriceInAVAX is token price in $AVAX, dynamically set by admin every 'n' minutes * @param _amountOfTokensToSell is amount of tokens that will be deposited to sale contract and available to buy * @param _saleEnd is timestamp of sale end * @param _portionVestingPrecision is precision rate for vested portion percents */ function setSaleParams( IERC20 _token, uint256 _tokenPriceInAVAX, uint256 _amountOfTokensToSell, uint256 _saleEnd, uint256 _portionVestingPrecision, uint256 _registrationDepositAVAX ) external onlyAdmin { require(!sale.isCreated, "Sale already created."); require(_portionVestingPrecision >= 100, "Invalid vesting precision."); require( _tokenPriceInAVAX != 0 && _amountOfTokensToSell != 0 && _saleEnd > block.timestamp, "Invalid input." ); // Set sale params sale.isCreated = true; sale.token = _token; sale.tokenPriceInAVAX = _tokenPriceInAVAX; sale.amountOfTokensToSell = _amountOfTokensToSell; sale.saleEnd = _saleEnd; // Set portion vesting precision portionVestingPrecision = _portionVestingPrecision; registrationDepositAVAX = _registrationDepositAVAX; if(address(_token) != address(0x0)) { oneTokenInWei = uint(10) ** IERC20Metadata(address(_token)).decimals(); } // Emit event emit SaleCreated( _tokenPriceInAVAX, _amountOfTokensToSell, _saleEnd ); } /** * @notice Function to shift sale end timestamp * @dev sale end timestamp needs to be before any unlock times */ function shiftSaleEnd(uint256 timeToShift) external onlyAdmin { require(timeToShift > 0, "Invalid time to shift."); require(block.timestamp < sale.saleEnd, "Sale already ended."); sale.saleEnd = sale.saleEnd.add(timeToShift); if (vestingPortionsUnlockTime.length > 0) { require(sale.saleEnd < vestingPortionsUnlockTime[0], "Sale end crossing vesting unlock times."); } if (dexalotUnlockTime > 0) require(sale.saleEnd < dexalotUnlockTime, "Sale end crossing dexalot unlock time."); } /** * @notice Function to set Dexalot parameters * @param _dexalotPortfolio is official Dexalot Portfolio contract address * @param _dexalotUnlockTime is unlock time for first portion withdrawal to Dexalot Portfolio * @dev Optional feature to enable user portion withdrawals directly to Dexalot Portfolio */ function setDexalotParameters( IDexalotPortfolio _dexalotPortfolio, uint256 _dexalotUnlockTime ) external onlyAdmin ifUnlocked { // Require that dexalot unlock time is inbetween sale end and first vesting unlock.. // ..and that vesting params are already set require( address(_dexalotPortfolio) != address(0) && _dexalotUnlockTime > sale.saleEnd && _dexalotUnlockTime <= vestingPortionsUnlockTime[0] && vestingPortionsUnlockTime[0] > 0 && sale.saleEnd > 0, "Invalid parameter(s)." ); dexalotPortfolio = _dexalotPortfolio; dexalotUnlockTime = _dexalotUnlockTime; } /** * @notice Function to shift dexalot unlocking time */ function shiftDexalotUnlockTime(uint256 timeToShift) external onlyAdmin { uint256 shiftedDexalotUnlockTime = dexalotUnlockTime.add(timeToShift); require(block.timestamp < dexalotUnlockTime && shiftedDexalotUnlockTime <= vestingPortionsUnlockTime[0]); dexalotUnlockTime = shiftedDexalotUnlockTime; } /** * @notice Function to retroactively set sale token address * @param saleToken is official token of the project * @dev Retroactive calls are option for teams which do not have token at the moment of sale launch */ function setSaleToken( IERC20 saleToken ) external onlyAdmin ifUnlocked { require(address(saleToken) != address(0)); require(!sale.tokensDeposited, "Tokens already deposited."); oneTokenInWei = uint(10) ** IERC20Metadata(address(saleToken)).decimals(); sale.token = saleToken; } /** * @notice Function to register for the upcoming sale * @param signature is pass for sale registration provided by admins * @param sigExpTime is timestamp after which signature is no longer valid * @param phaseId is id of phase user is registering for */ function registerForSale( bytes calldata signature, uint256 sigExpTime, uint256 phaseId ) external payable { // Check if sale is created require(sale.isCreated, "Sale is not created yet."); // Sale registration validity checks require(msg.value == registrationDepositAVAX, "Invalid deposit amount."); // Register only for validator or staking phase require(phaseId > uint8(Phases.Registration) && phaseId < uint8(Phases.Booster), "Invalid phase id."); require(sale.phase == Phases.Registration, "Must be called during registration phase."); require(block.timestamp <= sigExpTime, "Signature expired."); require(addressToPhaseRegisteredFor[msg.sender] == 0, "Already registered."); // Make sure signature is signed by admin, with proper parameters verifySignature( keccak256(abi.encodePacked(sigExpTime, msg.sender, phaseId, address(this), "registerForSale")), signature ); // Set user's registration phase addressToPhaseRegisteredFor[msg.sender] = phaseId; // Increment number of registered users numberOfRegistrants++; // Increase earnings from registration fees registrationFees += msg.value; // Locking tokens for participants of staking phase until the sale ends if (phaseId == uint8(Phases.Staking)) { allocationStaking.setTokensUnlockTime( 0, msg.sender, sale.saleEnd ); } // Emit event emit UserRegistered(msg.sender, phaseId); } /** * @notice Function to update token price in $AVAX to match real time value of token * @param price is token price in $AVAX to be set * @dev To help us reduce reliance on $AVAX volatility, oracle will update price during sale every 'n' minutes (n>=5) */ function updateTokenPriceInAVAX(uint256 price) external onlyAdmin { // Compute 30% of the current token price uint256 thirtyPercent = sale.tokenPriceInAVAX.mul(30).div(100); // Require that new price is under 30% difference compared to current require( sale.tokenPriceInAVAX.add(thirtyPercent) > price && sale.tokenPriceInAVAX - thirtyPercent < price, "Price out of range." ); require(lastPriceUpdateTimestamp + 5 minutes < block.timestamp); // Set new token price via internal call _setNewTokenPrice(price); } /** * @notice Function to set new token price by admin * @dev Works only until setter lock becomes active */ function overrideTokenPrice(uint256 price) external onlyAdmin ifUnlocked { // Set new token price via internal call _setNewTokenPrice(price); } /** * @notice Function for internal set of token price in $AVAX */ function _setNewTokenPrice(uint256 price) internal { // Update parameters sale.tokenPriceInAVAX = price; lastPriceUpdateTimestamp = block.timestamp; // Emit event emit NewTokenPriceSet(price); } /** * @notice Function to deposit sale tokens * @dev Only sale moderator may deposit */ function depositTokens() external onlyModerator { // Require that setSaleParams was called require( sale.isCreated && address(sale.token) != address(0) && !sale.tokensDeposited ); // Mark that tokens are deposited sale.tokensDeposited = true; // Perform safe transfer sale.token.safeTransferFrom( msg.sender, address(this), sale.amountOfTokensToSell ); } /** * @notice Function to auto-participate for user via collateral */ function autoParticipate( address user, uint256 amount, uint256 amountXavaToBurn, uint256 phaseId ) external payable onlyCollateral { _participate(user, amount, amountXavaToBurn, phaseId); } /** * @notice Function to boost user's participation via collateral */ function boostParticipation( address user, uint256 amountXavaToBurn ) external payable onlyCollateral { _participate(user, 0, amountXavaToBurn, uint256(Phases.Booster)); } /** * @notice Function to participate in sale manually */ function participate( uint256 amount, uint256 amountXavaToBurn, uint256 phaseId, bytes calldata signature ) external payable { require(msg.sender == tx.origin, "Only direct calls."); // Make sure admin signature is valid verifySignature( keccak256(abi.encodePacked(msg.sender, amount, amountXavaToBurn, phaseId, address(this), "participate")), signature ); _participate(msg.sender, amount, amountXavaToBurn, phaseId); } /** * @notice Function to participate in sale with multiple variants * @param user is user who participates in a sale * @param amount is maximal amount of tokens allowed for user to buy * @param amountXavaToBurn is amount of xava to be burned from user's stake * @param phaseId is round phase id user registered for (Validator, Staking or Booster) * @dev Regular participation by direct call is considered usual flow and it is applicable on 2 rounds - Validator and Staking * * Main diff is that on Staking round participation user's $XAVA is getting burned in small amount * * These rounds can be participated automatically too if user signs up for it and deposits $AVAX to Collateral contract * * Collateral contract will be performing automatic participations for users who signed up * * Booster round is 3rd one, available only for users who participated in one of first 2 rounds * * In booster round, it is possible to participate only through collateral, on user's demand * * Function flow changes based on round type */ function _participate( address user, uint256 amount, uint256 amountXavaToBurn, uint256 phaseId ) internal { // Make sure selected phase is ongoing and is round phase (Validator, Staking, Booster) require(phaseId > uint8(Phases.Registration) && phaseId == uint8(sale.phase), "Invalid phase."); bool isBooster = phaseId == uint8(Phases.Booster); // Load participation storage pointer Participation storage p = userToParticipation[user]; if (!isBooster) { // Normal flow // User must have registered for the phase in advance require(addressToPhaseRegisteredFor[user] == phaseId, "Not registered for this phase."); // Check user haven't participated before require(!isParticipated[user], "Already participated."); } else { // Booster flow // Check user has participated before require(isParticipated[user], "Only participated users."); // Check if user participated in staking round require(addressToPhaseRegisteredFor[user] == uint8(Phases.Staking), "User is not staker."); // Cannot boost participation more than once require(p.boostedAmountBought == 0, "Already boosted."); } // Compute the amount of tokens user is buying uint256 amountOfTokensBuying = (msg.value).mul(oneTokenInWei).div(sale.tokenPriceInAVAX); // Cannot buy zero tokens require(amountOfTokensBuying > 0, "Can't buy 0 tokens."); if (!isBooster) { // Check in terms of user allo require(amountOfTokensBuying <= amount, "Exceeding allowance."); } // Require that amountOfTokensBuying is less than sale token leftover cap require(amountOfTokensBuying <= sale.amountOfTokensToSell.sub(sale.totalTokensSold), "Out of tokens."); // Increase amount of sold tokens sale.totalTokensSold = sale.totalTokensSold.add(amountOfTokensBuying); // Increase amount of AVAX raised sale.totalAVAXRaised = sale.totalAVAXRaised.add(msg.value); // Gas optimization uint256 _numberOfVestedPortions = numberOfVestedPortions; if (!isBooster) { // Normal flow // Initialize user's participation _initParticipationForUser(user, amountOfTokensBuying, msg.value, block.timestamp, phaseId, _numberOfVestedPortions); } uint256 lastPercent; uint256 lastAmount; // Gas optimization uint256 _portionVestingPrecision = portionVestingPrecision; // Compute portion amounts for (uint256 i = 0; i < _numberOfVestedPortions; i++) { if (lastPercent != vestingPercentPerPortion[i]) { lastPercent = vestingPercentPerPortion[i]; lastAmount = amountOfTokensBuying.mul(lastPercent).div(_portionVestingPrecision); } p.portionAmounts[i] += lastAmount; } if (phaseId == uint8(Phases.Staking) || isBooster) { // Burn XAVA from user allocationStaking.redistributeXava( 0, user, amountXavaToBurn ); } if (!isBooster) { // Normal flow // Mark user is participated isParticipated[user] = true; // Increment number of participants in the Sale numberOfParticipants++; // Decrease of available registration fees registrationFees = registrationFees.sub(registrationDepositAVAX); // Transfer registration deposit amount in AVAX back to the users. safeTransferAVAX(user, registrationDepositAVAX); // Trigger events emit RegistrationAVAXRefunded(user, registrationDepositAVAX); emit TokensSold(user, amountOfTokensBuying); } else { // Booster flow // Add msg.value to boosted avax paid p.boostedAmountAVAXPaid = msg.value; // Add amountOfTokensBuying as boostedAmount p.boostedAmountBought = amountOfTokensBuying; // Emit participation boosted event emit ParticipationBoosted(user, msg.value, amountOfTokensBuying); } } /** * @notice Function to withdraw unlocked portions to wallet or Dexalot portfolio * @dev This function will deal with specific flow differences on withdrawals to wallet or dexalot * * First portion has different unlocking time for regular and dexalot withdraw */ function withdrawMultiplePortions(uint256[] calldata portionIds, bool toDexalot) external { if (toDexalot) { require(address(dexalotPortfolio) != address(0) && dexalotUnlockTime != 0, "Dexalot withdraw not supported."); // Means first portion is unlocked for dexalot require(block.timestamp >= dexalotUnlockTime, "Dexalot withdraw is locked."); } uint256 totalToWithdraw; // Retrieve participation from storage Participation storage p = userToParticipation[msg.sender]; for (uint256 i = 0; i < portionIds.length; i++) { uint256 portionId = portionIds[i]; require(portionId < numberOfVestedPortions, "Invalid portion id."); if ( p.portionStates[portionId] == PortionStates.Available && p.portionAmounts[portionId] > 0 && ( vestingPortionsUnlockTime[portionId] <= block.timestamp || (portionId == 0 && toDexalot) ) ) { // Mark portion as withdrawn to dexalot if (!toDexalot) p.portionStates[portionId] = PortionStates.Withdrawn; else p.portionStates[portionId] = PortionStates.WithdrawnToDexalot; // Withdraw percent which is unlocked at that portion totalToWithdraw = totalToWithdraw.add(p.portionAmounts[portionId]); } } if (totalToWithdraw > 0) { // Transfer tokens to user sale.token.safeTransfer(msg.sender, totalToWithdraw); // Trigger an event emit TokensWithdrawn(msg.sender, totalToWithdraw); // For Dexalot withdraw approval must be made through fe if (toDexalot) { // Deposit tokens to dexalot contract - Withdraw from sale contract dexalotPortfolio.depositTokenFromContract( msg.sender, getTokenSymbolBytes32(), totalToWithdraw ); // Trigger an event emit TokensWithdrawnToDexalot(msg.sender, totalToWithdraw); } } } /** * @notice Function to add available portions to market * @param portions are an array of portion ids * @param signature is admin signed message which acts as an approval for this action * @param sigExpTime is signature expiration timestamp * @dev prices for portions are being set through be - afterwards be will provide user with signature */ function addPortionsToMarket( uint256[] calldata portions, bytes calldata signature, uint256 sigExpTime ) external { require(block.timestamp > sale.saleEnd && sale.phase == Phases.Idle, "Sale still in progress."); verifySignature( keccak256(abi.encodePacked(msg.sender, address(this), portions, sigExpTime, "addPortionsToMarket")), signature ); require(block.timestamp <= sigExpTime, "Signature expired."); for(uint256 i = 0; i < portions.length; i++) { Participation storage p = userToParticipation[msg.sender]; uint256 portionId = portions[i]; require( p.portionStates[portionId] == PortionStates.Available && p.portionAmounts[portionId] > 0, "Portion unavailable." ); p.portionStates[portionId] = PortionStates.OnMarket; } marketplace.listPortions(msg.sender, portions); } /** * @notice Function to remove portions from market * @param portions is array of sale portions user wants to remove from market * @dev be must confirm action by giving user necessary signature */ function removePortionsFromMarket( uint256[] calldata portions, bytes calldata signature, uint256 sigExpTime ) external { verifySignature( keccak256(abi.encodePacked(msg.sender, address(this), portions, sigExpTime, "removePortionsFromMarket")), signature ); require(block.timestamp <= sigExpTime, "Signature expired."); for(uint256 i = 0; i < portions.length; i++) { Participation storage p = userToParticipation[msg.sender]; require(p.portionStates[portions[i]] == PortionStates.OnMarket, "Portion not on market."); p.portionStates[portions[i]] = PortionStates.Available; } marketplace.removePortions(msg.sender, portions); } /** * @notice Function to transfer portions from seller to buyer * @dev Called by marketplace only */ function transferPortions(address seller, address buyer, uint256[] calldata portions) external { require(msg.sender == address(marketplace), "Marketplace only."); Participation storage pSeller = userToParticipation[seller]; Participation storage pBuyer = userToParticipation[buyer]; // Initialize portions for user if hasn't participated the sale if(!isParticipated[buyer]) { _initParticipationForUser(buyer, 0, 0, block.timestamp, uint(sale.phase) /*==Phases.Idle*/, numberOfVestedPortions); isParticipated[buyer] = true; } uint256 totalAmountExchanged; for(uint256 i = 0; i < portions.length; i++) { uint256 portionId = portions[i]; require(pSeller.portionStates[portionId] == PortionStates.OnMarket, "Portion unavailable."); pSeller.portionStates[portionId] = PortionStates.Sold; PortionStates portionState = pBuyer.portionStates[portionId]; /* case 1: portion with same id is on market case 2: portion is available case 3: portion is unavailable (withdrawn or sold) */ uint256 amountToSell = pSeller.portionAmounts[portionId]; require(portionState != PortionStates.OnMarket, "Can't buy portion with same id you listed on market."); if (portionState == PortionStates.Available) { pBuyer.portionAmounts[portionId] += amountToSell; } else { pBuyer.portionAmounts[portionId] = amountToSell; pBuyer.portionStates[portionId] = PortionStates.Available; } totalAmountExchanged += amountToSell; } // Update personal stats for transfer participants pSeller.amountSoldOnMarketplace = pSeller.amountSoldOnMarketplace.add(totalAmountExchanged); pBuyer.amountBoughtOnMarketplace = pBuyer.amountBoughtOnMarketplace.add(totalAmountExchanged); } /** * @notice External function to withdraw earnings and/or leftover */ function withdrawEarningsAndLeftover(bool earnings, bool leftover) external onlyModerator { // Make sure sale ended require(block.timestamp > sale.saleEnd); // Perform withdrawals if (earnings) withdrawEarningsInternal(); if (leftover) withdrawLeftoverInternal(); } /** * @notice Internal function to withdraw earnings */ function withdrawEarningsInternal() internal { // Make sure moderator can't withdraw twice require(!sale.earningsWithdrawn); sale.earningsWithdrawn = true; // Earnings amount of the moderator in AVAX uint256 totalProfit = sale.totalAVAXRaised; // Perform AVAX safe transfer safeTransferAVAX(msg.sender, totalProfit); } /** * @notice Internal function to withdraw leftover */ function withdrawLeftoverInternal() internal { // Make sure moderator can't withdraw twice require(!sale.leftoverWithdrawn); sale.leftoverWithdrawn = true; // Amount of tokens which are not sold uint256 leftover = sale.amountOfTokensToSell.sub(sale.totalTokensSold); if (leftover > 0) { sale.token.safeTransfer(msg.sender, leftover); } } /** * @notice Function to withdraw registration fees by admin * @dev only after sale has ended and there is fund leftover */ function withdrawRegistrationFees() external onlyAdmin { // Gas optimization uint256 _registrationFees = registrationFees; // Require that sale is over require(block.timestamp > sale.saleEnd, "Sale isn't over."); // Check if there are accumulated fees require(_registrationFees > 0, "No fees accumulated."); // Set registration fees to zero registrationFees = 0; // Transfer AVAX to the admin wallet safeTransferAVAX(msg.sender, _registrationFees); } /** * @notice Function to withdraw all unused funds by admin */ function withdrawUnusedFunds() external onlyAdmin { uint256 balanceAVAX = address(this).balance; uint256 totalReservedForRaise = sale.earningsWithdrawn ? 0 : sale.totalAVAXRaised; // Transfer funds to admin wallet safeTransferAVAX( msg.sender, balanceAVAX.sub(totalReservedForRaise.add(registrationFees)) ); } /** * @notice Function to get participation for passed user address */ function getParticipationAmountsAndStates(address user) external view returns (uint256[] memory, PortionStates[] memory) { Participation memory p = userToParticipation[user]; return ( p.portionAmounts, p.portionStates ); } /** * @notice Function to get vesting info */ function getVestingInfo() external view returns (uint256[] memory, uint256[] memory) { return (vestingPortionsUnlockTime, vestingPercentPerPortion); } /** * @notice Function to remove stuck tokens from contract */ function removeStuckTokens(IERC20 token, address beneficiary, uint256 amount) external onlyAdmin { // Require that token address does not match with sale token require(address(token) != address(sale.token)); // Safe transfer token from sale contract to beneficiary token.safeTransfer(beneficiary, amount); } /** * @notice Function to switch between sale phases by admin */ function changePhase(Phases _phase) external onlyAdmin { // Require that sale is not over for changing phase state to other than idle if(_phase != Phases.Idle) require(block.timestamp < sale.saleEnd); // switch the currently active phase sale.phase = _phase; // Emit relevant event emit PhaseChanged(_phase); } /** * @notice Function which locks setters after initial configuration * @dev Contract lock can be activated only once and never unlocked */ function activateLock() external onlyAdmin ifUnlocked { // Lock the setters isLockOn = true; // Emit relevant event emit LockActivated(block.timestamp); } /** * @notice function to initialize participation structure for user */ function _initParticipationForUser( address user, uint256 amountBought, uint256 amountAVAXPaid, uint256 timeParticipated, uint256 phaseId, uint256 numberOfPortions ) internal { userToParticipation[user] = Participation({ amountBought: amountBought, amountAVAXPaid: amountAVAXPaid, timeParticipated: timeParticipated, phaseId: phaseId, portionAmounts: getEmptyUint256Array(numberOfPortions), portionStates: getEmptyPortionStatesArray(numberOfPortions), boostedAmountAVAXPaid: 0, boostedAmountBought: 0, amountBoughtOnMarketplace: 0, amountSoldOnMarketplace: 0 }); } /** * @notice function to retrieve an empty uint256 array */ function getEmptyUint256Array(uint256 size) internal pure returns (uint256[] memory) { return new uint256[](size); } /** * @notice function to retrieve an empty PortionStates array */ function getEmptyPortionStatesArray(uint256 size) internal pure returns (PortionStates[] memory) { return new PortionStates[](size); } /** * @notice Function to verify admin signed signatures */ function verifySignature(bytes32 hash, bytes calldata signature) internal view { require( admin.isAdmin(hash.toEthSignedMessageHash().recover(signature)), "Invalid signature." ); } /** * @notice Function to perform AVAX safe transfer */ function safeTransferAVAX(address to, uint256 value) internal { (bool success, ) = to.call{value: value}(new bytes(0)); require(success); } /** * @notice Function to parse token symbol as bytes32 */ function getTokenSymbolBytes32() internal view returns (bytes32 _symbol) { // Get token symbol string memory symbol = IERC20Metadata(address(sale.token)).symbol(); // Check if token has a valid symbol require(bytes(symbol).length > 0, "Token does not contain a valid symbol."); // Parse token symbol to bytes32 assembly { _symbol := mload(add(symbol, 32)) } } /** * @notice Function to handle receiving AVAX */ receive() external payable {} }
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"time","type":"uint256"}],"name":"LockActivated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newPrice","type":"uint256"}],"name":"NewTokenPriceSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountAVAX","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountTokens","type":"uint256"}],"name":"ParticipationBoosted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum AvalaunchSaleV2.Phases","name":"phase","type":"uint8"}],"name":"PhaseChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountRefunded","type":"uint256"}],"name":"RegistrationAVAXRefunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenPriceInAVAX","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOfTokensToSell","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"saleEnd","type":"uint256"}],"name":"SaleCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensSold","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensWithdrawnToDexalot","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"phaseId","type":"uint256"}],"name":"UserRegistered","type":"event"},{"inputs":[],"name":"activateLock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"portions","type":"uint256[]"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"uint256","name":"sigExpTime","type":"uint256"}],"name":"addPortionsToMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"addressToPhaseRegisteredFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"contract IAdmin","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allocationStaking","outputs":[{"internalType":"contract IAllocationStaking","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"amountXavaToBurn","type":"uint256"},{"internalType":"uint256","name":"phaseId","type":"uint256"}],"name":"autoParticipate","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"amountXavaToBurn","type":"uint256"}],"name":"boostParticipation","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"enum AvalaunchSaleV2.Phases","name":"_phase","type":"uint8"}],"name":"changePhase","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collateral","outputs":[{"internalType":"contract ICollateral","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"depositTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dexalotPortfolio","outputs":[{"internalType":"contract IDexalotPortfolio","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dexalotUnlockTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getParticipationAmountsAndStates","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"enum AvalaunchSaleV2.PortionStates[]","name":"","type":"uint8[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVestingInfo","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IAdmin","name":"_admin","type":"address"},{"internalType":"contract IAllocationStaking","name":"_allocationStaking","type":"address"},{"internalType":"contract ICollateral","name":"_collateral","type":"address"},{"internalType":"contract IAvalaunchMarketplace","name":"_marketplace","type":"address"},{"internalType":"address","name":"_moderator","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isLockOn","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isParticipated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastPriceUpdateTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"marketplace","outputs":[{"internalType":"contract IAvalaunchMarketplace","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"moderator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numberOfParticipants","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numberOfRegistrants","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numberOfVestedPortions","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oneTokenInWei","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"name":"overrideTokenPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"amountXavaToBurn","type":"uint256"},{"internalType":"uint256","name":"phaseId","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"participate","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"portionVestingPrecision","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"uint256","name":"sigExpTime","type":"uint256"},{"internalType":"uint256","name":"phaseId","type":"uint256"}],"name":"registerForSale","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"registrationDepositAVAX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"registrationFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"portions","type":"uint256[]"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"uint256","name":"sigExpTime","type":"uint256"}],"name":"removePortionsFromMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"address","name":"beneficiary","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"removeStuckTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sale","outputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"enum AvalaunchSaleV2.Phases","name":"phase","type":"uint8"},{"internalType":"bool","name":"isCreated","type":"bool"},{"internalType":"bool","name":"earningsWithdrawn","type":"bool"},{"internalType":"bool","name":"leftoverWithdrawn","type":"bool"},{"internalType":"bool","name":"tokensDeposited","type":"bool"},{"internalType":"uint256","name":"tokenPriceInAVAX","type":"uint256"},{"internalType":"uint256","name":"amountOfTokensToSell","type":"uint256"},{"internalType":"uint256","name":"totalTokensSold","type":"uint256"},{"internalType":"uint256","name":"totalAVAXRaised","type":"uint256"},{"internalType":"uint256","name":"saleEnd","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IDexalotPortfolio","name":"_dexalotPortfolio","type":"address"},{"internalType":"uint256","name":"_dexalotUnlockTime","type":"uint256"}],"name":"setDexalotParameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"uint256","name":"_tokenPriceInAVAX","type":"uint256"},{"internalType":"uint256","name":"_amountOfTokensToSell","type":"uint256"},{"internalType":"uint256","name":"_saleEnd","type":"uint256"},{"internalType":"uint256","name":"_portionVestingPrecision","type":"uint256"},{"internalType":"uint256","name":"_registrationDepositAVAX","type":"uint256"}],"name":"setSaleParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"saleToken","type":"address"}],"name":"setSaleToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_unlockingTimes","type":"uint256[]"},{"internalType":"uint256[]","name":"_percents","type":"uint256[]"}],"name":"setVestingParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"timeToShift","type":"uint256"}],"name":"shiftDexalotUnlockTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"timeToShift","type":"uint256"}],"name":"shiftSaleEnd","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"timeToShift","type":"uint256"}],"name":"shiftVestingUnlockTimes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"seller","type":"address"},{"internalType":"address","name":"buyer","type":"address"},{"internalType":"uint256[]","name":"portions","type":"uint256[]"}],"name":"transferPortions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"name":"updateTokenPriceInAVAX","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userToParticipation","outputs":[{"internalType":"uint256","name":"amountBought","type":"uint256"},{"internalType":"uint256","name":"amountAVAXPaid","type":"uint256"},{"internalType":"uint256","name":"timeParticipated","type":"uint256"},{"internalType":"uint256","name":"phaseId","type":"uint256"},{"internalType":"uint256","name":"boostedAmountAVAXPaid","type":"uint256"},{"internalType":"uint256","name":"boostedAmountBought","type":"uint256"},{"internalType":"uint256","name":"amountBoughtOnMarketplace","type":"uint256"},{"internalType":"uint256","name":"amountSoldOnMarketplace","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"vestingPercentPerPortion","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"vestingPortionsUnlockTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"earnings","type":"bool"},{"internalType":"bool","name":"leftover","type":"bool"}],"name":"withdrawEarningsAndLeftover","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"portionIds","type":"uint256[]"},{"internalType":"bool","name":"toDexalot","type":"bool"}],"name":"withdrawMultiplePortions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawRegistrationFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawUnusedFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.