Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"@openzeppelin/contracts/access/Ownable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
"
},
"@openzeppelin/contracts/proxy/utils/Initializable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @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 proxied contracts do not make use of 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.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* 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 {ERC1967Proxy-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.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}
"
},
"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
"
},
"@openzeppelin/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) 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 a `value` amount of tokens 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 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
},
"@openzeppelin/contracts/utils/Context.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
"
},
"contracts/ICO.sol": {
"content": "//SPDX-License-Identifier: MIT
pragma solidity =0.8.20;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import "./interfaces/IICO.sol";
import "./utils/ReentrancyGuard.sol";
import "./libraries/TransferHelper.sol";
import "./interfaces/OracleWrapper.sol";
contract ICO is Ownable, Initializable, ReentrancyGuard, IICO {
uint256 public tokensToBeSold;
uint256 public totalTokenSold;
uint256 public totalUSDRaised;
uint256 public tokenDecimal;
uint8 public defaultPhase;
uint8 public totalPhases;
address public receiverAddress;
address public investor;
address public founderA;
address public founderB;
address public founderC;
/* ================ STRUCT SECTION ================ */
/* Stores phases */
struct Phases {
uint256 tokenSold;
uint256 tokenLimit;
uint32 startTime;
uint32 expirationTimestamp;
uint32 price /* 10 ** 8 */;
uint32 percentage;
bool isComplete;
}
/**
* User Data Structure for users info like:-
* Users total amount for claim.
* Users claimed amount that is till claimed.
* Users claim for how many times user claims the amount.
* The Categories are:-
* Seed sale = 1
* Pre sale = 2
* Public sale = 3
* Investor = 4
* FounderA = 5
* FounderB = 5
* FounderC = 5
*/
struct UserData {
uint256 totalAmount;
uint256 claimedAmount;
uint8 claims;
bool isClaimed;
}
struct VestingInfo {
uint32 startTime;
uint32 claimGap;
uint8 claims;
}
mapping(uint256 => Phases) public phaseInfo;
mapping(address => mapping(uint8 => UserData)) public userMapping;
mapping(uint8 => VestingInfo) public vestingInfoMapping;
mapping(uint8 => uint256[]) public percentageArray;
IERC20Metadata public tokenInstance; /* Lumen token instance */
IERC20Metadata public usdtInstance; /* USDT token instance */
IERC20Metadata public usdcInstance; /* USDC token instance */
OracleWrapper public USDTOracle;
OracleWrapper public USDCOracle;
OracleWrapper public ETHOracle;
/* ================ CONSTRUCTOR SECTION ================ */
constructor() Ownable(msg.sender) {}
function initialize(
address _tokenAddress,
address _usdtToken,
address _usdcToken,
address _owner
) external initializer {
receiverAddress = 0xAe6Bb9bBe47865A0834649A2600DDFab93d6277b;
investor = 0x75e4fF970a3B225d7770027bE8f4D5f041460974;
founderA = 0xC4b2aCFF87A2121461a13F141854e67D86274B75;
founderB = 0x7f59DA2eb8eb9deb73229E36Fecec35C6502A604;
founderC = 0xa8396FF84b94D4aed3Ec8EaCc063c2cf40B897D2;
USDTOracle = OracleWrapper(0x3E7d1eAB13ad0104d2750B8863b489D65364e32D);
USDCOracle = OracleWrapper(0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6);
ETHOracle = OracleWrapper(0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419);
tokenInstance = IERC20Metadata(_tokenAddress);
usdtInstance = IERC20Metadata(_usdtToken);
usdcInstance = IERC20Metadata(_usdcToken);
totalPhases = 3;
defaultPhase = 1;
tokenDecimal = uint256(10 ** tokenInstance.decimals());
tokensToBeSold = 220_000_000 * tokenDecimal;
percentageArray[1] = [3_333_333_333]; // 33.33%
percentageArray[2] = [3_333_333_333]; // 33.33%
percentageArray[3] = [5_000_000_000]; // 50.00%
percentageArray[4] = [
1_000_000_000,
1_500_000_000,
1_500_000_000,
1_500_000_000,
1_500_000_000,
1_500_000_000,
1_500_000_000
];
percentageArray[5] = [
800_000_000,
1_533_333_333,
1_533_333_333,
1_533_333_333,
1_533_333_333,
1_533_333_333,
1_533_333_333
];
// phases timming is not defined yet so we just use dummy date for phase = 6 days
// but other vesting suration is proper.
setVestingCategory(1764547199, 30 days, 1, 3); // ico end time
setVestingCategory(1764547199, 30 days, 2, 3); // ico end time
setVestingCategory(1764547199, 30 days, 3, 2); // ico end time
setVestingCategory(1764547199 + 30 days, 30 days, 4, 7); // TGE + 30 days (TGE = Token Generation event.)
setVestingCategory(1764547199 + 90 days, 30 days, 5, 7); // TGE + 90 days
/* setup the vesting amount */
_registerUser(
(250_000_000 * (10 ** tokenInstance.decimals())),
4,
investor
);
_registerUser(
(60_000_000 * (10 ** tokenInstance.decimals())),
5,
founderA
);
_registerUser(
(60_000_000 * (10 ** tokenInstance.decimals())),
5,
founderB
);
_registerUser(
(60_000_000 * (10 ** tokenInstance.decimals())),
5,
founderC
);
phaseInfo[1] = Phases({
tokenLimit: 50_000_000 * tokenDecimal,
tokenSold: 0,
startTime: 1762387200, // 2025-11-06 00:00:00
expirationTimestamp: 1762819199, // 2025-11-10 23:59:59
price: 60000 /* 0.0006 */,
percentage: 2000,
isComplete: false
});
phaseInfo[2] = Phases({
tokenLimit: 70_000_000 * tokenDecimal,
tokenSold: 0,
startTime: 1762819200, // 2025-11-11 00:00:00
expirationTimestamp: 1763683199, // 2025-11-20 23:59:59
price: 80000 /* 0.0008 */,
percentage: 3000,
isComplete: false
});
phaseInfo[3] = Phases({
tokenLimit: 100_000_000 * tokenDecimal,
tokenSold: 0,
startTime: 1763683200, // 2025-11-21 00:00:00
expirationTimestamp: 1764547199, // 2025-11-30 23:59:59
price: 100000 /* 0.001 */,
percentage: 5000,
isComplete: false
});
_transferOwnership(_owner);
}
/* ================ BUYING TOKENS SECTION ================ */
/* Receive Function */
receive() external payable {
/* Sending deposited currency to the receiver address */
TransferHelper.safeTransferETH(receiverAddress, msg.value);
}
/** Function lets user buy ALTR tokens || Type 1 = BNB or ETH,
* Type = 2 for USDT,
* Type = 3 for USDC
*/
function buyTokens(
uint8 _type,
uint256 _usdtAmount
) external payable override nonReentrant {
uint8 _currentPhase = getCurrentPhase();
require(
block.timestamp >= phaseInfo[_currentPhase].startTime,
"ICO has not started yet."
);
require(
block.timestamp < phaseInfo[(totalPhases)].expirationTimestamp,
"Buying phases are over."
);
uint256 _buyAmount;
IERC20Metadata _instance;
/* If type == 1 */
if (_type == 1) {
_buyAmount = msg.value;
}
/* If type == 2 */
else {
require(msg.value == 0, "ETH amount should be zero.");
_buyAmount = _usdtAmount;
_instance = _type == 2 ? usdtInstance : usdcInstance;
/* Balance Check */
require(
_instance.balanceOf(_msgSender()) >= _buyAmount,
"User doesn't have enough balance."
);
/* Allowance Check */
require(
_instance.allowance(_msgSender(), address(this)) >= _buyAmount,
"Allowance provided is low."
);
}
require(_buyAmount > 0, "Please enter value more than 0.");
/* Token calculation */
(
uint256 _tokenAmount,
uint256 _amountToUSD,
uint8 _phaseNo
) = calculateTokens(_buyAmount, 0, defaultPhase, _type);
require(_phaseNo != 0, "All phases are over.");
/* Setup for vesting in vesting contract */
require(_tokenAmount > 0, "Token amount should be more then zero.");
/* Phase info setting */
setPhaseInfo(_tokenAmount, defaultPhase);
/* Update Phase number and add token amount */
defaultPhase = _phaseNo;
totalTokenSold += _tokenAmount;
totalUSDRaised += _amountToUSD;
uint256 _amountToTransfer = ((_tokenAmount *
phaseInfo[_phaseNo].percentage) / 10000);
_registerUser(_tokenAmount - _amountToTransfer, _phaseNo, _msgSender());
if (_type == 1) {
/* Sending deposited currency to the receiver address */
TransferHelper.safeTransferETH(receiverAddress, _buyAmount);
} else {
/* Sending deposited currency to the receiver address */
TransferHelper.safeTransferFrom(
address(_instance),
_msgSender(),
receiverAddress,
_buyAmount
);
}
TransferHelper.safeTransfer(
address(tokenInstance),
_msgSender(),
_amountToTransfer
);
/* Emits event */
emit BuyTokenDetail(
_buyAmount,
_tokenAmount,
_amountToTransfer,
_tokenAmount - _amountToTransfer,
_amountToUSD,
uint32(block.timestamp),
_type,
_phaseNo,
_msgSender()
);
}
function getCurrentPhase() public view returns (uint8) {
uint8 phase;
for (uint8 i = defaultPhase; i <= totalPhases; i++) {
if (
block.timestamp >= phaseInfo[i].startTime &&
block.timestamp < phaseInfo[i].expirationTimestamp
) {
phase = i;
break;
} else if (block.timestamp < phaseInfo[i].startTime) {
phase = i;
break;
}
}
return phase;
}
/* Internal function to calculate tokens */
// _types 0 == USD, 1 == ETH, 2 == USDT, 3 == USDC
function calculateTokens(
uint256 _amount,
uint256 _previousTokens,
uint8 _phaseNo,
uint8 _buyType
) public view returns (uint256, uint256, uint8) {
(uint256 _usdAmount, uint256 _typeDecimal) = cryptoValues(_buyType);
_amount =
(_amount * _usdAmount * (10 ** 8)) /
(_typeDecimal * (10 ** 8));
(uint256 _tokenAmount, uint8 _newPhaseNo) = _calculateTokens(
_amount,
_previousTokens,
0,
_phaseNo
);
return (_tokenAmount, _amount, _newPhaseNo);
}
function _calculateTokens(
uint256 _amount,
uint256 _previousTokens,
uint256 _previousPhaseTokens,
uint8 _phaseNo
) internal view returns (uint256, uint8) {
/* Phases cannot exceed totalPhases */
require(_phaseNo <= totalPhases, "Not enough tokens in the ICO.");
Phases memory pInfo = phaseInfo[_phaseNo];
if (_phaseNo == totalPhases) {
require(
pInfo.expirationTimestamp >= uint32(block.timestamp),
"ICO is Ended."
);
}
if (pInfo.expirationTimestamp > uint32(block.timestamp)) {
uint256 _tokensAmount = tokensUserWillGet(_amount, pInfo.price);
uint256 _tokensLeftToSell = (pInfo.tokenLimit +
_previousPhaseTokens) - pInfo.tokenSold;
if (_tokensLeftToSell >= _tokensAmount) {
return (_tokensAmount + _previousTokens, _phaseNo);
}
/* In case the phase is expired. New will begin after sending the left tokens to the next phase */
else {
uint256 _remainingTokens = _tokensAmount - _tokensLeftToSell;
_previousTokens += _tokensLeftToSell;
uint256 _remainingUsdAmount = getUsdAmount(
_remainingTokens,
pInfo.price
);
return
_calculateTokens(
_remainingUsdAmount,
_previousTokens,
0,
_phaseNo + 1
);
}
} else {
_previousPhaseTokens += (pInfo.tokenLimit - pInfo.tokenSold);
return
_calculateTokens(
_amount,
_previousTokens,
_previousPhaseTokens,
_phaseNo + 1
);
}
}
function getUsdAmount(
uint256 _remainingTokens,
uint32 _phasePrice
) internal view returns (uint256 _remainingUsdAmount) {
return ((_remainingTokens * _phasePrice * (10 ** 18) * (10 ** 8)) /
(tokenDecimal * (10 ** 18) * (10 ** 8)));
}
/* Tokens user will get according to the price */
function tokensUserWillGet(
uint256 _amount,
uint32 _price
) internal view returns (uint256) {
return ((_amount * tokenDecimal * (10 ** 8)) /
((10 ** 8) * uint256(_price)));
}
/* Returns the crypto values used */
function cryptoValues(
uint8 _type
) internal view returns (uint256, uint256) {
uint256 _amountToUSD;
uint256 _typeDecimal;
if (_type == 1) {
_amountToUSD = ETHOracle.latestAnswer();
_typeDecimal = 10 ** 18;
} else if (_type == 2) {
_amountToUSD = USDTOracle.latestAnswer();
_typeDecimal = uint256(10 ** usdtInstance.decimals());
} else {
_amountToUSD = USDCOracle.latestAnswer();
_typeDecimal = uint256(10 ** usdcInstance.decimals());
}
return (_amountToUSD, _typeDecimal);
}
/* Sets phase info according to the tokens bought */
function setPhaseInfo(uint256 _tokensUserWillGet, uint8 _phaseNo) internal {
require(_phaseNo <= totalPhases, "All tokens have been exhausted.");
Phases storage pInfo = phaseInfo[_phaseNo];
if (block.timestamp < pInfo.expirationTimestamp) {
/* when phase has more tokens than reuired */
if ((pInfo.tokenLimit - pInfo.tokenSold) > _tokensUserWillGet) {
pInfo.tokenSold += _tokensUserWillGet;
}
/* when phase has equal tokens as reuired */
else if (
(pInfo.tokenLimit - pInfo.tokenSold) == _tokensUserWillGet
) {
pInfo.tokenSold = pInfo.tokenLimit;
pInfo.expirationTimestamp = uint32(block.timestamp);
pInfo.isComplete = true;
phaseInfo[_phaseNo + 1].startTime = uint32(block.timestamp) + 1;
}
/* when tokens required are more than left tokens in phase */
else {
_tokensUserWillGet -= (pInfo.tokenLimit - pInfo.tokenSold);
pInfo.tokenSold = pInfo.tokenLimit;
pInfo.expirationTimestamp = uint32(block.timestamp);
pInfo.isComplete = true;
phaseInfo[_phaseNo + 1].startTime = uint32(block.timestamp) + 1;
setPhaseInfo(_tokensUserWillGet, _phaseNo + 1);
}
}
/* if tokens left in phase afterb completion of expiration time */
else {
uint256 remainingTokens = pInfo.tokenLimit - pInfo.tokenSold;
pInfo.tokenLimit = pInfo.tokenSold;
pInfo.isComplete = true;
phaseInfo[_phaseNo + 1].tokenLimit += remainingTokens;
setPhaseInfo(_tokensUserWillGet, _phaseNo + 1);
}
}
/* =============== Set Vesting Time and Claims ===============*/
function setVestingCategory(
uint32 _time,
uint32 _claimGap,
uint8 _choice,
uint8 _claims
) internal {
VestingInfo storage phase = vestingInfoMapping[_choice];
phase.startTime = _time;
phase.claimGap = _claimGap;
phase.claims = _claims;
}
/* =============== Register The Address For Claiming ===============*/
/**
* Register User for Vesting
* _amount for Total Claimable Amount
* _choice for Vesting Category
* _to for User's Address
*/
function _registerUser(
uint256 _amount,
uint8 _choice,
address _to
) internal {
UserData storage user = userMapping[_to][_choice];
user.totalAmount += _amount;
emit RegisterUser(_amount, _to, _choice);
}
/* =============== Token Claiming Functions =============== */
/**
* User can claim the tokens with claimTokens function.
* after start the vesting for that particular vesting category.
*/
function claimTokens(uint8 _choice) external nonReentrant {
require(
userMapping[_msgSender()][_choice].totalAmount > 0,
"User is not registered with this vesting."
);
(uint256 _amount, uint8 _claimCount) = tokensToBeClaimed(
_msgSender(),
_choice
);
require(_amount > 0, "Nothing to claim right now.");
UserData storage user = userMapping[_msgSender()][_choice];
user.claimedAmount += _amount;
user.claims = _claimCount;
TransferHelper.safeTransfer(
address(tokenInstance),
_msgSender(),
_amount
);
uint8 claims = uint8(vestingInfoMapping[_choice].claims);
if (claims == _claimCount) {
user.isClaimed = true;
}
emit ClaimedToken(
_msgSender(),
_amount,
uint32(block.timestamp),
_claimCount,
_choice
);
}
/* =============== Tokens to be claimed =============== */
/**
* tokensToBeClaimed function can be used for checking the claimable amount of the user.
*/
function tokensToBeClaimed(
address _to,
uint8 _choice
) public view returns (uint256 _toBeTransfer, uint8 _claimCount) {
UserData memory user = userMapping[_to][_choice];
VestingInfo memory _vestingInfo = vestingInfoMapping[_choice];
if (
(block.timestamp < (_vestingInfo.startTime)) ||
(user.totalAmount == 0)
) {
return (0, 0);
}
if (user.totalAmount == user.claimedAmount) {
return (0, 0);
}
uint32 _time = uint32(block.timestamp - (_vestingInfo.startTime));
/* Claim in Ever Month 30 days for main net and 1 minutes for the test */
uint256 _claimCountInternal = uint256(
(_time / _vestingInfo.claimGap) + 1
);
uint8 claims = uint8(_vestingInfo.claims);
if (_claimCountInternal > claims) {
_claimCountInternal = claims;
}
_claimCount = uint8(_claimCountInternal);
if (_claimCount <= user.claims) {
return (0, _claimCount);
}
if (_claimCount == claims) {
_toBeTransfer = user.totalAmount - user.claimedAmount;
} else {
_toBeTransfer = vestingCalulations(
user.totalAmount,
_claimCount,
user.claims,
_choice
);
}
return (_toBeTransfer, _claimCount);
}
function vestingSchedule(
address _to,
uint8 _choice
)
public
view
returns (uint256[] memory _tokensToBeTransferred, uint8 _userClaims)
{
UserData memory user = userMapping[_to][_choice];
VestingInfo memory _vestingInfo = vestingInfoMapping[_choice];
uint256[] memory _tokensToBeTransferredArray = new uint256[](
_vestingInfo.claims
);
uint256 _lastClaimAmount;
for (uint8 i = 0; i < _vestingInfo.claims; i++) {
uint8 _claimCount = uint8(i + 1);
uint256 _toBeTransfer;
if (_claimCount == _vestingInfo.claims) {
_toBeTransfer = user.totalAmount;
} else {
_toBeTransfer = vestingCalulations(
user.totalAmount,
_claimCount,
0,
_choice
);
}
_tokensToBeTransferredArray[i] = _toBeTransfer - _lastClaimAmount;
_lastClaimAmount = _toBeTransfer;
}
_tokensToBeTransferred = _tokensToBeTransferredArray;
return (_tokensToBeTransferred, user.claims);
}
/* =============== Vesting Calculations =============== */
/**
* vestingCalulations function is used for calculating the amount of token for claim
*/
function vestingCalulations(
uint256 _userTotalAmount,
uint8 _claimCount,
uint8 _userClaimCount,
uint8 _choice
) internal view returns (uint256) {
uint256 amount;
for (uint8 i = _userClaimCount; i < _claimCount; ++i) {
if ((percentageArray[_choice]).length == 1) {
amount +=
(_userTotalAmount * (percentageArray[_choice][0])) /
10_000_000_000;
} else {
amount +=
(_userTotalAmount * (percentageArray[_choice][i])) /
10_000_000_000;
}
}
return amount;
}
function sendLeftoverTokens() external onlyOwner {
require(
phaseInfo[totalPhases].expirationTimestamp < block.timestamp,
"ICO is not over yet."
);
uint256 _balance = (tokensToBeSold - totalTokenSold);
require(_balance > 0, "No tokens left to send.");
TransferHelper.safeTransfer(
address(tokenInstance),
receiverAddress,
_balance
);
}
/* ================ OTHER FUNCTIONS SECTION ================ */
/* Updates Receiver Address */
function updateReceiverAddress(
address _receiverAddress
) external onlyOwner {
require(_receiverAddress != address(0), "Zero address passed.");
receiverAddress = _receiverAddress;
emit UpdateReceiverAddress(_receiverAddress);
}
}
"
},
"contracts/interfaces/IICO.sol": {
"content": "//SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IICO {
event RegisterUser(uint256 totalTokens, address userAddress, uint8 choice);
event UpdateReceiverAddress(address receiverAddress);
event ClaimedToken(
address userAddress,
uint256 claimedAmount,
uint32 timestamp,
uint8 claimCount,
uint8 choice
);
event TokenBought(
uint256 tokenAmount,
uint32 timestamp,
address userAddress
);
event BuyTokenDetail(
uint256 buyAmount,
uint256 tokenAmount,
uint256 amountToTransfer,
uint256 amountToVesting,
uint256 amountToUSD,
uint32 timestamp,
uint8 buyType,
uint8 phaseNo,
address userAddress
);
function buyTokens(uint8 _type, uint256 _usdtAmount) external payable;
}
"
},
"contracts/interfaces/OracleWrapper.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity =0.8.20;
interface OracleWrapper {
function latestAnswer() external view returns (uint256);
}
"
},
"contracts/libraries/TransferHelper.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity =0.8.20;
// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library TransferHelper {
function safeApprove(
address token,
address to,
uint256 value
) internal {
// bytes4(keccak256(bytes('approve(address,uint256)')));
(bool success, bytes memory data) = token.call(
abi.encodeWithSelector(0x095ea7b3, to, value)
);
require(
success && (data.length == 0 || abi.decode(data, (bool))),
"TransferHelper::safeApprove: approve failed"
);
}
function safeTransfer(
address token,
address to,
uint256 value
) internal {
// bytes4(keccak256(bytes('transfer(address,uint256)')));
(bool success, bytes memory data) = token.call(
abi.encodeWithSelector(0xa9059cbb, to, value)
);
require(
success && (data.length == 0 || abi.decode(data, (bool))),
"TransferHelper::safeTransfer: transfer failed"
);
}
function safeTransferFrom(
address token,
address from,
address to,
uint256 value
) internal {
// bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
(bool success, bytes memory data) = token.call(
abi.encodeWithSelector(0x23b872dd, from, to, value)
);
require(
success && (data.length == 0 || abi.decode(data, (bool))),
"TransferHelper::transferFrom: transferFrom failed"
);
}
function safeTransferETH(address to, uint256 value) internal {
(bool success, ) = to.call{value: value}(new bytes(0));
require(
success,
"TransferHelper::safeTransferETH: ETH transfer failed"
);
}
}"
},
"contracts/utils/ReentrancyGuard.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity =0.8.20;
/**
* @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;
}
}
"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}
}}
Submitted on: 2025-11-06 12:42:22
Comments
Log in to comment.
No comments yet.