More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
0x5c878d6e12f2df10ba8ffab910ff574d81b6bb226f08659d31db61ef2cc90f4f | Buy | (pending) | 7 secs ago | IN | 0 ETH | (Pending) | |||
0x12d4799c26db217950f3c110d01e579fe1ecfba9b1ff89a388757e5b0cae5dc0 | Buy | (pending) | 4 days ago | IN | 0.00165 ETH | (Pending) | |||
Buy | 21382230 | 5 mins ago | IN | 0.44184046 ETH | 0.0028431 | ||||
Buy | 21382163 | 18 mins ago | IN | 0.008 ETH | 0.00338101 | ||||
Buy | 21382085 | 34 mins ago | IN | 0.00206776 ETH | 0.00294462 | ||||
Buy | 21382047 | 42 mins ago | IN | 0 ETH | 0.00342776 | ||||
Buy | 21382000 | 51 mins ago | IN | 0 ETH | 0.00357312 | ||||
Buy | 21381960 | 1 hrs ago | IN | 0.05 ETH | 0.00290331 | ||||
Buy | 21381946 | 1 hr ago | IN | 0.005 ETH | 0.00312141 | ||||
Buy | 21381860 | 1 hr ago | IN | 0 ETH | 0.00327282 | ||||
Buy | 21381836 | 1 hr ago | IN | 0.026 ETH | 0.00377896 | ||||
Buy | 21381739 | 1 hr ago | IN | 0.2 ETH | 0.00358849 | ||||
Buy | 21381716 | 1 hr ago | IN | 0.116862 ETH | 0.0031632 | ||||
Buy | 21381689 | 1 hr ago | IN | 0 ETH | 0.00446796 | ||||
Buy | 21381639 | 2 hrs ago | IN | 0.279 ETH | 0.00362547 | ||||
Buy | 21381585 | 2 hrs ago | IN | 0.01623 ETH | 0.00333098 | ||||
Buy | 21381506 | 2 hrs ago | IN | 0.1 ETH | 0.00394028 | ||||
Update Sale Stag... | 21381499 | 2 hrs ago | IN | 0 ETH | 0.00117809 | ||||
Buy | 21381304 | 3 hrs ago | IN | 0.06 ETH | 0.00485305 | ||||
Buy | 21381184 | 3 hrs ago | IN | 0 ETH | 0.00520061 | ||||
Buy | 21380800 | 4 hrs ago | IN | 0 ETH | 0.004157 | ||||
Buy | 21380693 | 5 hrs ago | IN | 0.22113804 ETH | 0.00329849 | ||||
Buy | 21380543 | 5 hrs ago | IN | 0.2743 ETH | 0.00319149 | ||||
Buy | 21380537 | 5 hrs ago | IN | 0.047 ETH | 0.00384265 | ||||
Buy | 21380440 | 6 hrs ago | IN | 0.11 ETH | 0.0037713 |
Advanced mode: Intended for advanced users or developers and will display all Internal Transactions including zero value transfers. Name tag integration is not available in advanced view.
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block | From | To | ||||
---|---|---|---|---|---|---|---|
21382230 | 5 mins ago | 0 ETH | |||||
21382163 | 18 mins ago | 0 ETH | |||||
21382085 | 34 mins ago | 0 ETH | |||||
21382047 | 42 mins ago | 0 ETH | |||||
21382047 | 42 mins ago | 0 ETH | |||||
21382047 | 42 mins ago | 0 ETH | |||||
21382047 | 42 mins ago | 0 ETH | |||||
21382000 | 51 mins ago | 0 ETH | |||||
21382000 | 51 mins ago | 0 ETH | |||||
21382000 | 51 mins ago | 0 ETH | |||||
21382000 | 51 mins ago | 0 ETH | |||||
21381960 | 1 hrs ago | 0 ETH | |||||
21381946 | 1 hr ago | 0 ETH | |||||
21381860 | 1 hr ago | 0 ETH | |||||
21381860 | 1 hr ago | 0 ETH | |||||
21381860 | 1 hr ago | 0 ETH | |||||
21381860 | 1 hr ago | 0 ETH | |||||
21381836 | 1 hr ago | 0 ETH | |||||
21381739 | 1 hr ago | 0 ETH | |||||
21381716 | 1 hr ago | 0 ETH | |||||
21381689 | 1 hr ago | 0 ETH | |||||
21381689 | 1 hr ago | 0 ETH | |||||
21381689 | 1 hr ago | 0 ETH | |||||
21381689 | 1 hr ago | 0 ETH | |||||
21381639 | 2 hrs ago | 0 ETH |
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x2C06a0B6...151476899 The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
TokenSale
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 200 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-or-later // Compatible with OpenZeppelin Contracts ^5.0.0 pragma solidity 0.8.20; import "@openzeppelin/contracts/access/Ownable2Step.sol"; import "./utils/TokenSalePurchase.sol"; import "./utils/RecoverableFunds.sol"; /** * @title TokenSale * @dev Main contract for managing token sales, including deposit, payment, withdraw, vesting, and fund recovery. * Inherits from Ownable2Step, RecoverableFunds and TokenSalePurchase. */ contract TokenSale is Ownable2Step, RecoverableFunds, TokenSalePurchase { /** * @dev Constructor to initialize the TokenSale contract with initial configurations. * @param sellableToken The address of the token that will be sold. * @param sellableTokenDecimals The number of decimals of the sellable token. * * Ownable(address initialOwner) * TeamWallet(address teamWallet) * RaisedFunds(bool autoWithdrawnRaisedFunds) * SellableToken(address sellableToken, uint8 sellableTokenDecimals) * PaymentToken(address[] memory tokens) * PaymentTokenDeposit(bool depositsEnabled) * Whitelist(bool whitelistingByDeposit, bytes32 merkleRoot) * Signature(address signer) * TokenSaleVesting(tokenVesting) * TokenSalePurchase(isReleaseAllowed, isBuyAllowed, isBuyWithProofAllowed, isBuyWithPriceAllowed) */ constructor( address sellableToken, uint8 sellableTokenDecimals ) Ownable(_msgSender()) TeamWallet(address(0)) RaisedFunds(false) SellableToken(sellableToken, sellableTokenDecimals) PaymentToken(new address[](0)) PaymentTokenDeposit(false) Whitelist(false, bytes32(0)) Signature(address(0)) TokenSaleVesting(address(0)) TokenSalePurchase(false, false, false, false) {} /** * @notice Returns the type of the token sale. * @return A string representing the type of the token sale. */ function tokenSaleType() external pure returns (string memory) { return "full"; } /** * @notice Returns the version of the token sale contract. * @return A string representing the version of the token sale contract. */ function tokenSaleVersion() external pure returns (string memory) { return "1"; } /** * @notice Pauses or unpauses the contract. * @dev Can only be called by the contract owner. * @param status A boolean indicating whether to pause (true) or unpause (false) the contract. */ function setPause(bool status) external onlyOwner { if (status) { _pause(); } else { _unpause(); } } /** * @notice Returns the recoverable funds for a specific token. * @dev Overrides the getRecoverableFunds function from the RecoverableFunds contract. * Calculates the recoverable balance by excluding deposits and unclaimed raised funds for payment tokens. * @param token The address of the token. * @return The amount of recoverable funds. */ function getRecoverableFunds( address token ) public view override returns (uint256) { uint256 accountedFunds = _getTotalTokenDeposit(token) + _getRaisedUnclaimed(token); if (accountedFunds > 0) { if (token == address(0)) { return address(this).balance - accountedFunds; } else { return IERC20(token).balanceOf(address(this)) - accountedFunds; } } else { return super.getRecoverableFunds(token); } } }
// SPDX-License-Identifier: GPL-3.0-or-later // Compatible with OpenZeppelin Contracts ^5.0.0 pragma solidity 0.8.20; import "@openzeppelin/contracts/access/Ownable2Step.sol"; import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; /** * @title Whitelist * @dev This abstract contract manages a whitelist for accessing token sales. * The owner can add or remove addresses to the whitelist and set a Merkle root for Merkle Tree based verification. * Inherits from Ownable2Step. */ abstract contract Whitelist is Ownable2Step { /// @dev Mapping to store whitelist status of addresses. mapping(address user => bool) private _whitelist; /// @dev Merkle root for verifying Merkle Proofs. bytes32 private _merkleRoot; /** * @dev Flag indicating if whitelisting by deposit is enabled. * If true, users can be whitelisted by making a deposit. */ bool private _whitelistingByDeposit; /** * @dev Emitted when the whitelisting by deposit status is updated. * @param status The new status of whitelisting by deposit. */ event WhitelistingByDepositUpdated(bool status); /** * @dev Emitted when the Merkle root is updated. * @param merkleRoot The new Merkle root. */ event MerkleRootUpdated(bytes32 indexed merkleRoot); /** * @dev Emitted when an address is added to the whitelist. * @param user The address that was added. */ event WhitelistGranted(address indexed user); /** * @dev Emitted when an address is removed from the whitelist. * @param user The address that was removed. */ event WhitelistRevoked(address indexed user); /// @dev Error thrown when the address is not whitelisted. error NotWhitelisted(); /** * @dev Constructor to initialize the contract with a Merkle root. * @param whitelistingByDeposit The new status of whitelisting by deposit. If true, users can be whitelisted by making a deposit. * @param merkleRoot The initial Merkle root for verifying proofs. */ constructor(bool whitelistingByDeposit, bytes32 merkleRoot) { _setWhitelistingByDeposit(whitelistingByDeposit); _setMerkleRoot(merkleRoot); } /** * @notice Returns the current Merkle root. * @return The current Merkle root. */ function getMerkleRoot() external view returns (bytes32) { return _merkleRoot; } /** * @notice Verifies if an address is whitelisted using a Merkle proof. * @param user The address to verify. * @param merkleProof The Merkle proof. * @return True if the address is verified, false otherwise. */ function verifyWhitelistProof( address user, bytes32[] calldata merkleProof ) external view returns (bool) { return _verifyWhitelistProof(user, merkleProof); } /** * @notice Returns the current status of whitelisting by deposit. * @return The current status of whitelisting by deposit. */ function isWhitelistingByDepositEnabled() external view returns (bool) { return _isWhitelistingByDepositEnabled(); } /** * @notice Checks if an address is whitelisted. * @param user The address to check. * @return True if the address is whitelisted, false otherwise. */ function isWhitelisted(address user) external view returns (bool) { return _isWhitelisted(user); } /** * @notice Adds addresses to the whitelist. * @dev Can only be called by the contract owner. * @param users The addresses to be added to the whitelist. */ function grantWhitelist(address[] calldata users) external onlyOwner { for (uint256 i = 0; i < users.length; i++) { _grantWhitelist(users[i]); } } /** * @notice Removes addresses from the whitelist. * @dev Can only be called by the contract owner. * @param users The addresses to be removed from the whitelist. */ function revokeWhitelist(address[] calldata users) external onlyOwner { for (uint256 i = 0; i < users.length; i++) { _revokeWhitelist(users[i]); } } /** * @notice Sets the Merkle root for verifying Merkle proofs. * @dev Can only be called by the contract owner. * @param merkleRoot The new Merkle root. */ function setMerkleRoot(bytes32 merkleRoot) external onlyOwner { _setMerkleRoot(merkleRoot); } /** * @notice Sets the status of whitelisting by deposit. * @dev Can only be called by the contract owner. * @param status The new status of whitelisting by deposit. If true, users can be whitelisted by making a deposit. */ function setWhitelistingByDeposit(bool status) external onlyOwner { _setWhitelistingByDeposit(status); } /** * @dev Internal function to check if an address is whitelisted. * @param user The address to check. * @return True if the address is whitelisted, false otherwise. */ function _isWhitelisted(address user) internal view returns (bool) { return _whitelist[user]; } /** * @dev Internal function to get the status of whitelisting by deposit. * @return The current status of whitelisting by deposit. */ function _isWhitelistingByDepositEnabled() internal view returns (bool) { return _whitelistingByDeposit; } /** * @dev Internal function to add an address to the whitelist. * Emits a WhitelistGranted event. * @param user The address to add. */ function _grantWhitelist(address user) internal { _whitelist[user] = true; emit WhitelistGranted(user); } /** * @dev Internal function to remove an address from the whitelist. * Emits a WhitelistRevoked event. * @param user The address to remove. */ function _revokeWhitelist(address user) internal { _whitelist[user] = false; emit WhitelistRevoked(user); } /** * @dev Internal function to check if an address has a valid Merkle proof. * Reverts with NotWhitelisted if neither is true. * @param user The address to check. * @param merkleProof The Merkle proof. */ function _checkWhitelistProof( address user, bytes32[] calldata merkleProof ) internal view { if (!_verifyWhitelistProof(user, merkleProof)) { revert NotWhitelisted(); } } /** * @dev private function to verify if an address is whitelisted using a Merkle proof. * @param user The address to verify. * @param merkleProof The Merkle proof. * @return True if the address is verified, false otherwise. */ function _verifyWhitelistProof( address user, bytes32[] calldata merkleProof ) private view returns (bool) { bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(user)))); return MerkleProof.verify(merkleProof, _merkleRoot, leaf); } /** * @dev Private function to set the Merkle root. * Emits a MerkleRootUpdated event. * @param merkleRoot The new Merkle root. */ function _setMerkleRoot(bytes32 merkleRoot) private { _merkleRoot = merkleRoot; emit MerkleRootUpdated(merkleRoot); } /** * @dev Private function to set the status of whitelisting by deposit. * Emits a WhitelistingByDepositUpdated event. * @param status The new status of whitelisting by deposit. */ function _setWhitelistingByDeposit(bool status) private { _whitelistingByDeposit = status; emit WhitelistingByDepositUpdated(status); } }
// SPDX-License-Identifier: GPL-3.0-or-later // Compatible with OpenZeppelin Contracts ^5.0.0 pragma solidity 0.8.20; import "@openzeppelin/contracts/access/Ownable2Step.sol"; /** * @title TokenSaleVesting * @dev Abstract contract to manage the TokenVesting contract address and its state. * Inherits from Ownable2Step to provide ownership control. */ abstract contract TokenSaleVesting is Ownable2Step { /// @dev Address of the TokenVesting contract. address private _tokenVesting; /// @dev Flag indicating if the TokenVesting address is frozen. bool private _tokenVestingFrozen; /** * @dev Emitted when the TokenVesting address is changed. * @param tokenVesting The new TokenVesting address. */ event TokenVestingUpdated(address tokenVesting); /// @dev Emitted when the TokenVesting is frozen. event TokenVestingFrozen(); /// @dev Error thrown when the TokenVesting address is zero. error TokenVestingIsZero(); /// @dev Error thrown when the TokenVesting is frozen and an update is attempted. error TokenVestingIsFrozen(); /** * @dev Constructor to initialize the TokenVesting. * @param tokenVesting The address of the initial TokenVesting. */ constructor(address tokenVesting) { _setTokenVesting(tokenVesting); } /** * @notice Returns the current TokenVesting address and its frozen status. * @return tokenVesting The current TokenVesting address. * @return tokenVestingFrozen The frozen status of the TokenVesting address. */ function getTokenVesting() external view returns (address tokenVesting, bool tokenVestingFrozen) { return (_tokenVesting, _tokenVestingFrozen); } /** * @notice Sets a new TokenVesting address. * @dev Can only be called by the contract owner. * Emits a TokenVestingUpdated event. * @param tokenVesting The address of the TokenVesting. */ function setTokenVesting(address tokenVesting) external onlyOwner { if (_tokenVestingFrozen) { revert TokenVestingIsFrozen(); } _setTokenVesting(tokenVesting); } /** * @notice Freezes the TokenVesting, preventing further changes. * @dev Can only be called by the contract owner. * Emits a TokenVestingFrozen event. */ function freezeTokenVesting() external onlyOwner { if (_tokenVesting == address(0)) { revert TokenVestingIsZero(); } if (_tokenVestingFrozen) { revert TokenVestingIsFrozen(); } _tokenVestingFrozen = true; emit TokenVestingFrozen(); } /** * @dev Internal function to get the current TokenVesting address. * @return The current TokenVesting address. */ function _getTokenVesting() internal view returns (address) { if (_tokenVesting == address(0)) { revert TokenVestingIsZero(); } return _tokenVesting; } /** * @dev Private function to set a new TokenVesting address and emit an event. * Emits a TokenVestingUpdated event. * @param tokenVesting The address of the new TokenVesting. */ function _setTokenVesting(address tokenVesting) private { _tokenVesting = tokenVesting; emit TokenVestingUpdated(tokenVesting); } }
// SPDX-License-Identifier: GPL-3.0-or-later // Compatible with OpenZeppelin Contracts ^5.0.0 pragma solidity 0.8.20; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "@openzeppelin/contracts/access/Ownable2Step.sol"; import "./SellableToken.sol"; import "./PaymentToken.sol"; import "./PaymentTokenDeposit.sol"; import "./Whitelist.sol"; /** * @title TokenSaleStages * @dev Abstract contract for managing stages of a token sale. * Inherits functionalities from Ownable2Step, SellableToken, PaymentToken, PaymentTokenDeposit, and Whitelist contracts. */ abstract contract TokenSaleStages is Ownable2Step, SellableToken, PaymentToken, PaymentTokenDeposit, Whitelist { /** * @dev Struct to provide a view of the sale stage data. * @param whitelist Indicates if whitelist is enabled for this sale stage. * @param startAt The start time of the token sale stage. If set to 0, it means the stage will start automatically after the previous stage ends. * @param duration The duration of the token sale stage in seconds. * @param cap The maximum number of tokens that can be sold in this stage. * @param sold The total number of tokens sold in this stage. * @param tokens An array of token addresses available for sale in this stage. * @param prices An array of prices corresponding to the token addresses available for sale in this stage. */ struct SaleStageDataView { bool whitelist; // If true, only addresses on the whitelist can participate in the token sale. uint256 startAt; // Token sale start time (0 means auto start after the previous stage). uint256 duration; // Duration of the token sale stage. uint256 cap; // Maximum amount of tokens to be sold in this stage. uint256 sold; // Total tokens sold in this stage. address[] tokens; // Array of token addresses available for sale. uint256[] prices; // Array of prices corresponding to the token addresses. } /** * @dev Struct to store the schedule of a sale stage. * @param stageId The ID of the sale stage. * @param whitelist Indicates if whitelist is enabled for this sale stage. * @param startAt The start time of the token sale stage. * @param duration The duration of the token sale stage in seconds. * @param cap The maximum number of tokens that can be sold in this stage. * @param tokens An array of token addresses available for sale in this stage. * @param prices An array of prices corresponding to the token addresses available for sale in this stage. */ struct SaleStageSchedule { uint256 stageId; // ID of the sale stage. bool whitelist; // If true, only addresses on the whitelist can participate in the token sale. uint256 startAt; // Token sale start time. uint256 duration; // Duration of the token sale stage in seconds. uint256 cap; // Maximum amount of tokens to be sold in this stage. address[] tokens; // Array of token addresses available for sale. uint256[] prices; // Array of prices corresponding to the token addresses. } /** * @dev Struct to store data for each token sale stage. * @param whitelist Indicates if whitelist is enabled for this sale stage. * @param startAt The start time of the token sale stage. If set to 0, it means the stage will start automatically after the previous stage ends. * @param duration The duration of the token sale stage in seconds. * @param cap The maximum number of tokens that can be sold in this stage. * @param sold The total number of tokens sold in this stage. */ struct SaleStageData { bool whitelist; // If true, only addresses on the whitelist can participate in the token sale. uint256 startAt; // Token sale start time (0 means auto start after the previous stage). uint256 duration; // Duration of the token sale stage. uint256 cap; // Maximum amount of tokens to be sold in this stage. uint256 sold; // Total tokens sold in this stage. } /** * @dev Mapping to store sale stage data for each stage ID. * param stageId The ID of the sale stage. * return The SaleStageData struct containing the details of the sale stage. */ mapping(uint256 stageId => SaleStageData) private _stageSale; /** * @dev Mapping to store token prices for each sale stage. * param stageId The ID of the sale stage. * param token The address of the token. * return The price of the token in the specified sale stage. */ mapping(uint256 stageId => mapping(address token => uint256)) private _stagePrice; /// @dev The total number of stages. uint256 private _totalStages; /// @dev The total number of tokens sold across all stages. uint256 private _totalSold; /** * @dev The ID of the current sale stage. * WARNING: may point to a non-existent stage ID if the stages have not been set up correctly. */ uint256 private _currentStageId; /** * @dev Emitted when a stage is updated. * @param stageId The ID of the updated stage. * @param whitelist Indicates if whitelist is enabled for this sale stage. * @param startAt The start time of the stage. * @param duration The duration of the stage. * @param cap The maximum amount of tokens to be sold in the stage. */ event SaleStageUpdated( uint256 stageId, bool whitelist, uint256 startAt, uint256 duration, uint256 cap ); /** * @dev Emitted when a stage is deleted. * @param stageId The ID of the deleted stage. */ event SaleStageDeleted(uint256 stageId); /** * @dev Emitted when the price of a token is updated for a stage. * @param stageId The ID of the stage. * @param token The address of the token. * @param price The new price of the token. */ event SaleStagePriceUpdated(uint256 stageId, address token, uint256 price); /** * @dev Emitted when the current stage ID is updated. * @param currentStageId The new current stage ID. */ event CurrentStageIdUpdated(uint256 currentStageId); /// @dev Error indicating that the specified stage does not exist. error StageNotExists(); /// @dev Error thrown when a stage is not active. error StageIsNotActive(); /// @dev Error indicating that the provided stage ID is incorrect. error WrongStageId(); /// @dev Error indicating that the stage has already been used. error StageAlreadyUsed(); /// @dev Error indicating that there is a mismatch between the provided parameters. error ParametersMismatch(); /// @dev Error indicating that the price of the payment token is zero. error PaymentTokenPriceIsZero(); /// @dev Error indicating that the cap of the stage cannot be less than the amount of tokens sold. error CapCannotBeLessThanSold(); /// @dev Error thrown when the start time of a stage is invalid. error WrongStartTime(); /// @dev Error indicating that the purchase amount is too small. error TooSmallPurchaseAmount(); /** * @dev Modifier to check if the stage exists. * Reverts with StageNotExists error if the stage ID does not exist. * @param stageId The ID of the stage to check. */ modifier stageExists(uint256 stageId) { if (_isStageNotExists(stageId)) { revert StageNotExists(); } _; } /** * @dev Modifier to check if a stage is currently active. * Reverts with `StageIsNotActive` if the stage ID does not match the current stage ID, * if the stage is sold out, if the stage has not started, or if the current time is not within the stage duration. * @param stageId The ID of the stage to check. */ modifier thenStageIsActive(uint256 stageId) { _fixExpiredStage(); if (stageId != _getCurrentStageIdRaw()) { revert StageIsNotActive(); } SaleStageData memory stage = _getSaleStage(stageId); if (stage.sold >= stage.cap) { revert StageIsNotActive(); } if (stage.startAt == 0) { revert StageIsNotActive(); } if ( block.timestamp < stage.startAt || block.timestamp >= (stage.startAt + stage.duration) ) { revert StageIsNotActive(); } _; } /// @dev Modifier to check if the user is whitelisted for the current stage. /// @param stageId The ID of the sale stage to check. modifier whenWhitelisted(uint256 stageId) { SaleStageData memory stage = _getSaleStage(stageId); if (stage.whitelist && !_isWhitelisted(_msgSender())) { revert NotWhitelisted(); } _; } /// @dev Modifier to check if the user is whitelisted for the current stage using Merkle proof. /// @param stageId The ID of the sale stage to check. /// @param merkleProof The Merkle proof to verify the user's whitelist status. modifier whenWhitelistedByProof( uint256 stageId, bytes32[] calldata merkleProof ) { address user = _msgSender(); SaleStageData memory stage = _getSaleStage(stageId); if (stage.whitelist) { _checkWhitelistProof(user, merkleProof); if (!_isWhitelisted(user)) { _grantWhitelist(user); } } _; } /** * @notice Returns the current state of the sale. * @return stages The total number of stages. * @return currentStageId The ID of the current stage. WARNING: may point to a non-existent stage ID if the stages have not been set up correctly. * @return sold The total number of tokens sold across all stages. */ function getSaleState() external view returns (uint256 stages, uint256 currentStageId, uint256 sold) { stages = _getTotalStages(); currentStageId = _getCurrentStageId(); sold = _totalSold; } /** * @notice Returns the details of a specific sale stage. * @dev This function returns the start time, duration, cap, sold amount, and token prices for the specified sale stage. * It defaults to the predefined payment tokens. * @param stageId The ID of the sale stage to retrieve. * @return whitelist Indicates if whitelist is enabled for this sale stage. * @return startAt The start time of the sale stage. * @return duration The duration of the sale stage. * @return cap The maximum amount of tokens to be sold in the sale stage. * @return sold The total amount of tokens sold in the sale stage. * @return tokens The list of token addresses used in the sale stage. * @return prices The list of prices for each token in the sale stage. */ function getSaleStage( uint256 stageId ) external view stageExists(stageId) returns ( bool whitelist, uint256 startAt, uint256 duration, uint256 cap, uint256 sold, address[] memory tokens, uint256[] memory prices ) { SaleStageData memory stage = _getSaleStage(stageId); whitelist = stage.whitelist; startAt = stage.startAt; duration = stage.duration; cap = stage.cap; sold = stage.sold; tokens = _getDefaultPaymentTokensIfEmpty(tokens); prices = new uint256[](tokens.length); for (uint256 i = 0; i < tokens.length; i++) { prices[i] = _getTokenPrice(stageId, tokens[i]); } } /** * @notice Returns the data for all sale stages. * @dev This function creates an array of SaleStageDataView structs representing all sale stages * and populates it with data from the _stageSale mapping. * @return stages An array of SaleStageDataView structs containing the data for all sale stages. */ function getSaleStages() external view returns (SaleStageDataView[] memory stages) { uint256 totalStages = _getTotalStages(); stages = new SaleStageDataView[](totalStages); address[] memory tokens = _getPaymentTokens(); for (uint256 stageId = 0; stageId < totalStages; stageId++) { SaleStageData memory stage = _getSaleStage(stageId); uint256[] memory prices = new uint256[](tokens.length); for (uint256 i = 0; i < tokens.length; i++) { prices[i] = _getTokenPrice(stageId, tokens[i]); } stages[stageId] = SaleStageDataView({ whitelist: stage.whitelist, startAt: stage.startAt, duration: stage.duration, cap: stage.cap, sold: stage.sold, tokens: tokens, prices: prices }); } } /** * @notice Calculates the number of tokens to be bought given a payment amount and token price. * @param price The price of the token in the specified payment token. * @param tokensToPay The amount of the payment token being used to buy tokens. * @return tokensToBuy The number of tokens to be bought. */ function calculateTokensToBuy( uint256 price, uint256 tokensToPay ) external view returns (uint256 tokensToBuy) { return _calculateTokensToBuyWithPrice(price, tokensToPay); } /** * @notice Calculates the amount of payment tokens needed to buy a specific number of tokens. * @param price The price of the token in the specified payment token. * @param tokensToBuy The number of tokens to buy. * @return tokensToPay The amount of payment tokens needed. */ function calculateTokensToPay( uint256 price, uint256 tokensToBuy ) external view returns (uint256 tokensToPay) { return _calculateTokensToPayWithPrice(price, tokensToBuy); } /** * @notice Calculates the number of tokens to be bought given a payment amount and token price for a specified stage. * @dev This function is external and can be called with a valid payment token. * It calls the internal function to perform the calculation. * @param stageId The ID of the sale stage. * @param token The address of the payment token. * @param tokensToPay The amount of the payment token being used to buy tokens. * @return tokensToBuy The number of tokens to be bought. * @return price The price of the token in the specified payment token. */ function calculateTokensToBuyAtStage( uint256 stageId, address token, uint256 tokensToPay ) external view onlyPaymentToken(token) returns (uint256 tokensToBuy, uint256 price) { return _calculateTokensToBuyAtStage(stageId, token, tokensToPay); } /** * @notice Calculates the amount of payment tokens needed to buy a specific number of tokens for a specified stage. * @dev This function is external and can be called with a valid payment token. * It calls the internal function to perform the calculation. * @param stageId The ID of the sale stage. * @param token The address of the payment token. * @param tokensToBuy The number of tokens to buy. * @return tokensToPay The amount of payment tokens needed. * @return price The price of the token in the specified payment token. */ function calculateTokensToPayAtStage( uint256 stageId, address token, uint256 tokensToBuy ) external view onlyPaymentToken(token) returns (uint256 tokensToPay, uint256 price) { return _calculateTokensToPayAtStage(stageId, token, tokensToBuy); } /** * @notice Creates a new stage with the specified parameters and sets its prices. * @dev Can only be called by the contract owner. * The stage ID must be equal to the current total stages. * @param stageId The ID of the new stage. * @param whitelist Indicates if whitelist is enabled for this sale stage. * @param startAt The start timestamp of the stage. * @param duration The duration of the stage. * @param cap The token cap for the stage. * @param tokens An array of token addresses for which prices are being set. * @param prices An array of prices corresponding to the tokens. */ function createNextSaleStage( uint256 stageId, bool whitelist, uint256 startAt, uint256 duration, uint256 cap, address[] calldata tokens, uint256[] calldata prices ) external onlyOwner { if (stageId != _getTotalStages()) { revert WrongStageId(); } _totalStages++; _updateSaleStage(stageId, whitelist, startAt, duration, cap); _updateSaleStagePrices(stageId, tokens, prices); } /** * @notice Creates new sale stages with the specified parameters. * @dev Can only be called by the contract owner. * The stage ID must be equal to the current total stages. * @param schedules An array of SaleStageSchedule structs containing the parameters for each sale stage. */ function createNextSaleStagesBatch( SaleStageSchedule[] calldata schedules ) external onlyOwner { uint256 total = schedules.length; for (uint256 i = 0; i < total; total++) { uint256 stageId = schedules[i].stageId; if (stageId != _getTotalStages()) { revert WrongStageId(); } _totalStages++; _updateSaleStage( schedules[i].stageId, schedules[i].whitelist, schedules[i].startAt, schedules[i].duration, schedules[i].cap ); _updateSaleStagePrices( schedules[i].stageId, schedules[i].tokens, schedules[i].prices ); } } /** * @notice Updates the parameters and prices of an existing stage. * @dev Can only be called by the contract owner. * The stage must exist. * @param stageId The ID of the stage to update. * @param whitelist Indicates if whitelist is enabled for this sale stage. * @param startAt The new start timestamp of the stage. * @param duration The new duration of the stage. * @param cap The new token cap for the stage. * @param tokens An array of token addresses for which prices are being set. * @param prices An array of prices corresponding to the tokens. */ function updateSaleStage( uint256 stageId, bool whitelist, uint256 startAt, uint256 duration, uint256 cap, address[] memory tokens, uint256[] memory prices ) external stageExists(stageId) onlyOwner { _updateSaleStage(stageId, whitelist, startAt, duration, cap); _updateSaleStagePrices(stageId, tokens, prices); } /** * @notice Updates the prices for the specified stage and payment tokens. * @dev Can only be called by the contract owner and for an existing stage. * @param stageId The ID of the stage to update prices for. * @param tokens An array of token addresses for which prices are being set. * @param prices An array of prices corresponding to the tokens. */ function updateSaleStagePrices( uint256 stageId, address[] memory tokens, uint256[] memory prices ) external stageExists(stageId) onlyOwner { _updateSaleStagePrices(stageId, tokens, prices); } /** * @notice Deletes the prices for the specified stage and payment tokens. * @dev Can only be called by the contract owner and for an existing stage. * If the input array of tokens is empty, it defaults to the predefined payment tokens. * @param stageId The ID of the stage to delete prices for. * @param tokens An array of token addresses for which prices are being deleted (use zero address for native coin). */ function deleteSaleStagePrices( uint256 stageId, address[] memory tokens ) external stageExists(stageId) onlyOwner { _deleteSaleStagePrices(stageId, tokens); } /** * @notice Deletes the last stage. * @dev Can only be called by the contract owner. * It defaults to the predefined payment tokens. * WARNING: The stage must exist and must not have any tokens sold in it. */ function deleteLastSaleStage() external onlyOwner { uint256 totalStages = _getTotalStages(); if (totalStages == 0) { revert StageNotExists(); } uint256 stageId = totalStages - 1; SaleStageData memory stage = _getSaleStage(stageId); if (stage.sold > 0 || stageId < _getCurrentStageIdRaw()) { revert StageAlreadyUsed(); } _totalStages--; delete _stageSale[stageId]; emit SaleStageDeleted(stageId); _deleteSaleStagePrices(stageId, new address[](0)); } /** * @notice Sets the current stage ID. * @dev Can only be called by the contract owner. * Reverts if the new stage ID is not greater than the current stage ID. * Emits a CurrentStageIdUpdated event. * @param newCurrentStageId The new stage ID to set. */ function setCurrentStageId(uint256 newCurrentStageId) external onlyOwner { if ( newCurrentStageId < _getCurrentStageId() || newCurrentStageId > _getTotalStages() ) { revert WrongStageId(); } _setCurrentStageId(newCurrentStageId); } /** * @dev Private function to handle deposits of tokens or native coin. * Emits a UserFundsDeposited event upon successful deposit. * @param user The address of the user making the deposit. * @param token The address of the token being deposited. * @param amount The amount of the token being deposited. */ function _deposit( address user, address token, uint256 amount ) internal override { super._deposit(user, token, amount); // If before sale starts, grant whitelist access. if ( !_isSaleStarted() && _isWhitelistingByDepositEnabled() && !_isWhitelisted(user) ) { _grantWhitelist(user); } } /** * @dev Private function to handle the cancellation of deposits. * Emits a UserFundsWithdrawn event upon successful withdrawal. * @param user The address of the user whose deposits are being canceled. * @param tokens The list of token addresses to withdraw. */ function _cancelDeposit( address user, address[] memory tokens ) internal override { super._cancelDeposit(user, tokens); // If before sale starts, revoke whitelist access. if ( !_isSaleStarted() && _isWhitelistingByDepositEnabled() && _isWhitelisted(user) ) { _revokeWhitelist(user); } } /** * @dev Internal function to get the total number of token sale stages. * @return The total number of token sale stages. */ function _getTotalStages() internal view virtual returns (uint256) { return _totalStages; } /** * @dev Private function to get the current sale stage ID. * WARNING: The returned stage ID may point to a non-existent stage if the stages have not been set up correctly. * @return The current sale stage ID. */ function _getCurrentStageIdRaw() private view returns (uint256) { return _currentStageId; } /** * @dev Private function to get the current active sale stage ID. * Calculates the current stage based on the total number of stages, the raw current stage ID, * and the start time and duration of the current stage. * @return The ID of the current active sale stage. */ function _getCurrentStageId() private view returns (uint256) { uint256 currentStageIdRaw = _getCurrentStageIdRaw(); uint256 totalStages = _getTotalStages(); if (totalStages == 0) { return 0; } if (currentStageIdRaw >= totalStages) { return totalStages; } SaleStageData memory stage = _getSaleStage(currentStageIdRaw); if (stage.startAt == 0) { return currentStageIdRaw; } if (block.timestamp >= (stage.startAt + stage.duration)) { return currentStageIdRaw + 1; } if (stage.sold >= stage.cap) { return currentStageIdRaw + 1; } return currentStageIdRaw; } /** * @dev Private function to fix the expired stage and update the current stage ID if necessary. * If the current stage ID is different from the raw current stage ID, it updates the current stage ID * and sets the start time for the new current stage. */ function _fixExpiredStage() private { uint256 curIdRaw = _getCurrentStageIdRaw(); uint256 curId = _getCurrentStageId(); if (curId > curIdRaw) { _setCurrentStageId(curId); SaleStageData storage curStage = _getSaleStageStorage(curId); if (curStage.startAt == 0) { SaleStageData memory prevStage = _getSaleStage(curId - 1); if (prevStage.sold >= prevStage.cap) { curStage.startAt = block.timestamp; _emitSaleStageUpdated(curId, curStage); } else if (prevStage.startAt != 0) { curStage.startAt = prevStage.startAt + prevStage.duration; _emitSaleStageUpdated(curId, curStage); } } } } /** * @dev Private function to set the current stage ID. * Updates the current stage ID. * Emits a CurrentStageIdUpdated event. * @param newCurrentStageId The new current stage ID. */ function _setCurrentStageId(uint256 newCurrentStageId) private { _currentStageId = newCurrentStageId; emit CurrentStageIdUpdated(newCurrentStageId); } /** * @dev Private function to check if the token sale has started. * This function checks if there are any stages defined and if the start time of the first stage has been reached. * @return True if the sale has started, false otherwise. */ function _isSaleStarted() private view returns (bool) { if (_getTotalStages() > 0) { SaleStageData memory stage = _getSaleStage(0); if (stage.startAt > 0 && block.timestamp >= stage.startAt) { return true; } } return false; } /** * @dev Internal function to get the remaining token reserve for a specific stage. * This function calculates the difference between the stage's cap and the number of tokens sold. * @param stageId The ID of the stage to query. * @return The number of tokens remaining in the reserve for the specified stage. */ function _getStageReserve(uint256 stageId) internal view returns (uint256) { SaleStageData memory stage = _getSaleStage(stageId); return stage.cap - stage.sold; } /** * @dev Internal function to adjust the number of tokens to be bought based on the remaining tokens available for sale in the specified stage. * If the requested number of tokens exceeds the remaining tokens, it reduces the amount to the available tokens. * @param stageId The ID of the sale stage. * @param tokensToBuy The number of tokens the user wants to buy. * @return The adjusted number of tokens to buy. */ function _fixTokensToBuy( uint256 stageId, uint256 tokensToBuy ) internal view returns (uint256) { uint256 restTokensForSale = _getStageReserve(stageId); if (tokensToBuy > restTokensForSale) { // Fix for the case when there are fewer tokens left than they want to buy. tokensToBuy = restTokensForSale; } return tokensToBuy; } /** * @dev Internal function to increase the number of tokens sold in a specific sale stage. * Updates the total tokens sold and the tokens sold in the stage. * Emits a CurrentStageIdUpdated event if the stage is sold out. * Switches to the next stage if the current stage is completed, and updates its start time if not set. * @param stageId The ID of the stage. * @param tokensToBuy The number of tokens to be added to the sold amount. */ function _increaseStageSold(uint256 stageId, uint256 tokensToBuy) internal { SaleStageData storage stage = _getSaleStageStorage(stageId); _totalSold += tokensToBuy; stage.sold += tokensToBuy; if (stage.sold >= stage.cap) { // Switch to the next stage. uint256 nextStageId = _currentStageId + 1; _setCurrentStageId(nextStageId); SaleStageData storage nextStage = _getSaleStageStorage(nextStageId); if (nextStage.startAt == 0) { nextStage.startAt = block.timestamp; _emitSaleStageUpdated(nextStageId, nextStage); } } } /** * @dev Internal function to calculate the number of tokens to be bought given a payment amount and token price for a specified stage. * @param stageId The ID of the sale stage. * @param token The address of the payment token. * @param tokensToPay The amount of the payment token being used to buy tokens. * @return tokensToBuy The number of tokens to be bought. * @return price The price of the token in the specified payment token. */ function _calculateTokensToBuyAtStage( uint256 stageId, address token, uint256 tokensToPay ) internal view returns (uint256 tokensToBuy, uint256 price) { price = _getTokenPrice(stageId, token); tokensToBuy = _calculateTokensToBuyWithPrice(price, tokensToPay); } /** * @dev Internal function to calculate the amount of payment tokens needed to buy a specific number of tokens for a specified stage. * @param stageId The ID of the sale stage. * @param token The address of the payment token. * @param tokensToBuy The number of tokens to buy. * @return tokensToPay The amount of payment tokens needed. * @return price The price of the token in the specified payment token. */ function _calculateTokensToPayAtStage( uint256 stageId, address token, uint256 tokensToBuy ) internal view returns (uint256 tokensToPay, uint256 price) { price = _getTokenPrice(stageId, token); tokensToPay = _calculateTokensToPayWithPrice(price, tokensToBuy); } /** * @dev Internal function to calculate the number of tokens to be bought given a payment amount and token price. * @param price The price of the token in the specified payment token. * @param tokensToPay The amount of the payment token being used to buy tokens. * @return tokensToBuy The number of tokens to be bought. */ function _calculateTokensToBuyWithPrice( uint256 price, uint256 tokensToPay ) internal view returns (uint256 tokensToBuy) { _checkTokenPrice(price); tokensToBuy = _calculateTokensToBuyPure( tokensToPay, price, _getSellableTokenDecimals() ); _checkPurchaseAmount(tokensToBuy); } /** * @dev Internal function to calculate the amount of payment tokens needed to buy a specific number of tokens. * @param price The price of the token in the specified payment token. * @param tokensToBuy The number of tokens to buy. * @return tokensToPay The amount of payment tokens needed. */ function _calculateTokensToPayWithPrice( uint256 price, uint256 tokensToBuy ) internal view returns (uint256 tokensToPay) { _checkTokenPrice(price); tokensToPay = _calculateTokensToPayPure( tokensToBuy, price, _getSellableTokenDecimals() ); _checkPurchaseAmount(tokensToPay); } /** * @dev Calculates the number of tokens to buy based on the amount of payment tokens provided, the price, and the number of decimals. * @param tokensToPay The amount of payment tokens provided. * @param price The price of the payment token. * @param decimals The number of decimals of the token being sold. * @return tokensToBuy The number of tokens to buy. */ function _calculateTokensToBuyPure( uint256 tokensToPay, uint256 price, uint8 decimals ) private pure returns (uint256 tokensToBuy) { tokensToBuy = (tokensToPay * 10 ** decimals) / price; } /** * @dev Calculates the amount of payment tokens needed to buy a specific number of tokens, based on the price and number of decimals. * @param tokensToBuy The number of tokens to buy. * @param price The price of the payment token. * @param decimals The number of decimals of the token being sold. * @return tokensToPay The amount of payment tokens needed. */ function _calculateTokensToPayPure( uint256 tokensToBuy, uint256 price, uint8 decimals ) private pure returns (uint256 tokensToPay) { tokensToPay = (tokensToBuy * price) / 10 ** decimals; } /** * @dev Private function to check if the token price is zero and revert if true. * Reverts with `PaymentTokenPriceIsZero` if the price is zero. * @param price The price of the payment token. */ function _checkTokenPrice(uint256 price) private pure { if (price == 0) { revert PaymentTokenPriceIsZero(); } } /** * @dev Internal function to check if the calculated tokens to pay or buy is zero and revert if true. * This function ensures that the purchase amount is not zero to prevent invalid transactions. * @param tokensToPayOrToBuy The amount of payment tokens or tokens to buy. * Reverts with a `TooSmallPurchaseAmount` error if the amount is zero. */ function _checkPurchaseAmount(uint256 tokensToPayOrToBuy) internal pure { if (tokensToPayOrToBuy == 0) { revert TooSmallPurchaseAmount(); } } /** * @dev Private function to get the price of a token for a specific stage. * @param stageId The ID of the stage. * @param token The address of the token. * @return price The price of the token for the specified stage. */ function _getTokenPrice( uint256 stageId, address token ) private view returns (uint256 price) { price = _stagePrice[stageId][token]; } /** * @dev Private function to get the data of a specific sale stage. * This function returns a copy of the stage data and does not allow modifications. * @param stageId The ID of the stage to retrieve. * @return stage The data of the specified sale stage as a copy. */ function _getSaleStage( uint256 stageId ) private view returns (SaleStageData memory stage) { stage = _stageSale[stageId]; } /** * @dev Private function to get the data of a specific sale stage. * This function returns a storage reference to the stage data, allowing modifications. * @param stageId The ID of the stage to retrieve. * @return stage The storage reference to the data of the specified sale stage. */ function _getSaleStageStorage( uint256 stageId ) private view returns (SaleStageData storage stage) { stage = _stageSale[stageId]; } /** * @dev Private function to check if a stage does not exist. * Returns true if the stage ID does not exist, false otherwise. * @param stageId The ID of the stage to check. * @return bool True if the stage ID does not exist, false otherwise. */ function _isStageNotExists(uint256 stageId) private view returns (bool) { return stageId >= _getTotalStages(); } /** * @dev Private function to update the parameters of a stage. * Emits a SaleStageUpdated event. * @param stageId The ID of the stage to update. * @param whitelist Indicates if whitelist is enabled for this sale stage. * @param startAt The new start timestamp of the stage. * @param duration The new duration of the stage. * @param cap The new token cap for the stage. */ function _updateSaleStage( uint256 stageId, bool whitelist, uint256 startAt, uint256 duration, uint256 cap ) private { SaleStageData storage stage = _getSaleStageStorage(stageId); stage.whitelist = whitelist; if ( stageId == _getCurrentStageIdRaw() && stage.startAt > 0 && block.timestamp >= stage.startAt ) { // If the stage has already started, then the startAt parameter cannot be decreased. revert WrongStartTime(); } if (stageId > 0 && startAt > 0) { SaleStageData memory previousStage = _getSaleStage(stageId - 1); if (startAt < (previousStage.startAt + previousStage.duration)) { revert WrongStartTime(); } } stage.startAt = startAt; stage.duration = duration; if (stage.sold > cap) { revert CapCannotBeLessThanSold(); } stage.cap = cap; _emitSaleStageUpdated(stageId, stage); } /** * @dev Private function to emit the SaleStageUpdated event. * This function is used to notify about the update of a sale stage. * @param stageId The ID of the stage. * @param stage The data of the sale stage. */ function _emitSaleStageUpdated( uint256 stageId, SaleStageData memory stage ) private { emit SaleStageUpdated( stageId, stage.whitelist, stage.startAt, stage.duration, stage.cap ); } /** * @notice Private function to update the prices for the specified stage and payment tokens. * WARNING: If a price was previously set for a token that is not included in the current `tokens` array, * its price will remain in memory. To remove its price, pass the token's address and set its price to 0. * @param stageId The ID of the stage to update prices for. * Emits a SaleStagePriceUpdated event. * @param tokens An array of token addresses for which prices are being set. * @param prices An array of prices corresponding to the tokens. */ function _updateSaleStagePrices( uint256 stageId, address[] memory tokens, uint256[] memory prices ) private { if (tokens.length != prices.length) { revert ParametersMismatch(); } for (uint256 i = 0; i < tokens.length; i++) { _checkPaymentToken(tokens[i]); _stagePrice[stageId][tokens[i]] = prices[i]; emit SaleStagePriceUpdated(stageId, tokens[i], prices[i]); } } /** * @dev Private function to delete the prices for the specified stage and payment tokens. * If the input array of tokens is empty, it defaults to the predefined payment tokens. * Emits a SaleStagePriceUpdated event. * @param stageId The ID of the stage to delete prices for. * @param tokens An array of token addresses for which prices are being deleted (use zero address for native coin). */ function _deleteSaleStagePrices( uint256 stageId, address[] memory tokens ) private { tokens = _getDefaultPaymentTokensIfEmpty(tokens); for (uint256 i = 0; i < tokens.length; i++) { delete _stagePrice[stageId][tokens[i]]; emit SaleStagePriceUpdated(stageId, tokens[i], 0); } } }
// SPDX-License-Identifier: GPL-3.0-or-later // Compatible with OpenZeppelin Contracts ^5.0.0 pragma solidity 0.8.20; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; import "../utils/SafePermit.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/access/Ownable2Step.sol"; import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import "@openzeppelin/contracts/utils/Pausable.sol"; import "./Signature.sol"; import "./PaymentToken.sol"; import "./TokenSaleStages.sol"; import "./RaisedFunds.sol"; import "./TokenSaleVesting.sol"; import "../interface/ITokenVesting.sol"; /** * @title TokenSalePurchase * @dev This abstract contract manages purchase settings. * It allows buying tokens directly, with permits, or with signatures. * The contract owner can update purchase settings and control token sales. * Inherits from Ownable2Step, ReentrancyGuard, Pausable, PaymentToken, TokenSaleStages, RaisedFunds, TokenSaleVesting, and Signature. */ abstract contract TokenSalePurchase is Ownable2Step, ReentrancyGuard, Pausable, PaymentToken, TokenSaleStages, RaisedFunds, Signature, TokenSaleVesting { using SafeERC20 for IERC20; using SafePermit for IERC20Permit; using Address for address payable; /** * @dev Struct to store purchase settings. * @param isReleaseAllowed Indicates if the release tokens with signature method is allowed. * @param isBuyAllowed Indicates if the direct buy method is allowed. * @param isBuyWithProofAllowed Indicates if the buy with Merkle Proof method is allowed. * @param isBuyWithPriceAllowed Indicates if the buy with signature and dinamic price method is allowed. */ struct PurchaseSettings { bool isReleaseAllowed; bool isBuyAllowed; bool isBuyWithProofAllowed; bool isBuyWithPriceAllowed; } /// @dev Stores the current purchase settings. PurchaseSettings private _purchaseSettings; /** * @dev Emitted when the purchase settings are updated. * @param isReleaseAllowed The new status of the release tokens with signature method. * @param isBuyAllowed The new status of the direct buy method. * @param isBuyWithProofAllowed Indicates if the buy with Merkle Proof is allowed. * @param isBuyWithPriceAllowed The new status of the buy with signature and dinamic price method. */ event PurchaseSettingsUpdated( bool isReleaseAllowed, bool isBuyAllowed, bool isBuyWithProofAllowed, bool isBuyWithPriceAllowed ); /** * @dev Emitted when tokens are released. * For cases where payment has been received off-chain, * @param user The address of the user who bought the tokens. * @param stageId The ID of the sale stage. * @param amount The amount of tokens bought. */ event TokensReleased( address indexed user, uint256 indexed stageId, uint256 amount ); /** * @dev Emitted when tokens are bought directly. * @param user The address of the user who bought the tokens. * @param stageId The ID of the sale stage. * @param token The address of the payment token used. * @param price The price of the tokens. * @param amount The amount of tokens bought. */ event TokensBought( address indexed user, uint256 indexed stageId, address indexed token, uint256 price, uint256 amount ); /// @dev Error thrown when a disabled purchase method is attempted. error ThisPurchaseMethodIsDisabled(); /// @dev Error thrown when there are insufficient funds for the purchase. error InsufficientFunds(); /** * @dev Constructor to initialize the purchase settings. * @param isReleaseAllowed Indicates if the release tokens with signature method is allowed. * @param isBuyAllowed Indicates if the direct buy method is allowed. * @param isBuyWithProofAllowed Indicates if the buy with Merkle Proof is allowed. * @param isBuyWithPriceAllowed Indicates if the buy with signature and dinamic price method is allowed. */ constructor( bool isReleaseAllowed, bool isBuyAllowed, bool isBuyWithProofAllowed, bool isBuyWithPriceAllowed ) { _setPurchaseSettings( isReleaseAllowed, isBuyAllowed, isBuyWithProofAllowed, isBuyWithPriceAllowed ); } /** * @notice Returns the current purchase settings. * @dev This function returns the status of various purchase methods. * @return isReleaseAllowed Indicates if the release tokens with signature method is allowed. * @return isBuyAllowed Indicates if the direct buy method is allowed. * @return isBuyWithProofAllowed Indicates if the buy with Merkle Proof is allowed. * @return isBuyWithPriceAllowed Indicates if the buy with signature and dinamic price method is allowed. */ function getPurchaseSettings() external view returns ( bool isReleaseAllowed, bool isBuyAllowed, bool isBuyWithProofAllowed, bool isBuyWithPriceAllowed ) { return ( _purchaseSettings.isReleaseAllowed, _purchaseSettings.isBuyAllowed, _purchaseSettings.isBuyWithProofAllowed, _purchaseSettings.isBuyWithPriceAllowed ); } /** * @notice Allows users to release tokens using a signature issued by the server. * @dev This function is intended for cases where payment has been received off-chain, * and the function only release tokens the corresponding amount of tokens. * @param stageId The ID of the sale stage. * @param tokensToBuy The amount of tokens to be released. * @param signature The signature issued by the server to authorize the release tokens. */ function release( uint256 stageId, uint256 tokensToBuy, bytes memory signature ) external thenStageIsActive(stageId) whenNotPaused { address user = _msgSender(); _checkSignatureRelease(user, stageId, tokensToBuy, signature); tokensToBuy = _fixTokensToBuy(stageId, tokensToBuy); _increaseSoldAndVested(user, stageId, tokensToBuy); emit TokensReleased(user, stageId, tokensToBuy); } /** * @notice Allows users to buy tokens with dinamic price using a signature issued by the server. * @param stageId The ID of the sale stage. * @param token The address of the payment token used for the purchase. * @param price The price of the tokens. * @param tokensToPay The amount of payment tokens to pay. * @param signature The signature issued by the server to authorize the purchase tokens. */ function buyWithPrice( uint256 stageId, address token, uint256 price, uint256 tokensToPay, bytes memory signature ) external payable nonReentrant { address user = _msgSender(); if (!_purchaseSettings.isBuyWithPriceAllowed) { revert ThisPurchaseMethodIsDisabled(); } _checkSignatureBuyWithPrice( user, stageId, token, price, tokensToPay, signature ); _processingWithPrice(user, stageId, token, price, tokensToPay); } /** * @notice Allows users to buy tokens with dinamic price using a signature issued by the server and a permit (EIP-2612). * @param stageId The ID of the sale stage. * @param token The address of the payment token used for the purchase. * @param price The price of the tokens. * @param tokensToPay The amount of payment tokens to pay. * @param deadline The permit deadline. * @param v The recovery id. * @param r The r value of the signature. * @param s The s value of the signature. * @param signature The signature issued by the server to authorize the purchase tokens. */ function buyWithPricePermit( uint256 stageId, address token, uint256 price, uint256 tokensToPay, uint256 deadline, uint8 v, bytes32 r, bytes32 s, bytes memory signature ) external { address user = _msgSender(); if (!_purchaseSettings.isBuyWithPriceAllowed) { revert ThisPurchaseMethodIsDisabled(); } _checkSignatureBuyWithPrice( user, stageId, token, price, tokensToPay, signature ); _processingWithPricePermit( user, stageId, token, price, tokensToPay, deadline, v, r, s ); } /** * @notice Allows users to buy tokens directly with a specified payment token. * @param stageId The ID of the sale stage. * @param token The address of the payment token used for the purchase. * @param tokensToPay The amount of payment tokens to pay. */ function buy( uint256 stageId, address token, uint256 tokensToPay ) external payable whenWhitelisted(stageId) nonReentrant { if (!_purchaseSettings.isBuyAllowed) { revert ThisPurchaseMethodIsDisabled(); } _buyProcessing(_msgSender(), stageId, token, tokensToPay); } /** * @notice Allows users to buy tokens with a permit (EIP-2612). * @param stageId The ID of the sale stage. * @param token The address of the payment token used for the purchase. * @param tokensToPay The amount of payment tokens to pay. * @param deadline The permit deadline. * @param v The recovery id. * @param r The r value of the signature. * @param s The s value of the signature. */ function buyWithPermit( uint256 stageId, address token, uint256 tokensToPay, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external whenWhitelisted(stageId) { if (!_purchaseSettings.isBuyAllowed) { revert ThisPurchaseMethodIsDisabled(); } _buyWithPermitProcessing( _msgSender(), stageId, token, tokensToPay, deadline, v, r, s ); } /** * @notice Allows users to buy tokens directly with a specified payment token and Merkle proof. * @param stageId The ID of the sale stage. * @param token The address of the payment token used for the purchase. * @param tokensToPay The amount of payment tokens to pay. * @param merkleProof The Merkle proof for whitelisting. */ function buyWithProof( uint256 stageId, address token, uint256 tokensToPay, bytes32[] calldata merkleProof ) external payable whenWhitelistedByProof(stageId, merkleProof) nonReentrant { if (!_purchaseSettings.isBuyWithProofAllowed) { revert ThisPurchaseMethodIsDisabled(); } _buyProcessing(_msgSender(), stageId, token, tokensToPay); } /** * @notice Allows users to buy tokens with a permit (EIP-2612) and Merkle proof. * @param stageId The ID of the sale stage. * @param token The address of the payment token used for the purchase. * @param tokensToPay The amount of payment tokens to pay. * @param deadline The permit deadline. * @param v The recovery id. * @param r The r value of the signature. * @param s The s value of the signature. * @param merkleProof The Merkle proof for whitelisting. */ function buyWithProofPermit( uint256 stageId, address token, uint256 tokensToPay, uint256 deadline, uint8 v, bytes32 r, bytes32 s, bytes32[] calldata merkleProof ) external whenWhitelistedByProof(stageId, merkleProof) { if (!_purchaseSettings.isBuyWithProofAllowed) { revert ThisPurchaseMethodIsDisabled(); } _buyWithPermitProcessing( _msgSender(), stageId, token, tokensToPay, deadline, v, r, s ); } /** * @notice Updates the purchase settings. * @dev Can only be called by the contract owner. * Emits a PurchaseSettingsUpdated event. * @param isReleaseAllowed Indicates if the release tokens with signature method is allowed. * @param isBuyAllowed Indicates if the direct buy method is allowed. * @param isBuyWithProofAllowed Indicates if the buy with Merkle Proof is allowed. * @param isBuyWithPriceAllowed Indicates if the buy with signature and dinamic price method is allowed. */ function setPurchaseSettings( bool isReleaseAllowed, bool isBuyAllowed, bool isBuyWithProofAllowed, bool isBuyWithPriceAllowed ) external onlyOwner { _setPurchaseSettings( isReleaseAllowed, isBuyAllowed, isBuyWithProofAllowed, isBuyWithPriceAllowed ); } /** * @dev Private function to set the purchase settings. * Emits a PurchaseSettingsUpdated event. * @param isReleaseAllowed Indicates if the release tokens with signature method is allowed. * @param isBuyAllowed Indicates if the direct buy method is allowed. * @param isBuyWithProofAllowed Indicates if the buy with Merkle Proof is allowed. * @param isBuyWithPriceAllowed Indicates if the buy with signature and dinamic price method is allowed. */ function _setPurchaseSettings( bool isReleaseAllowed, bool isBuyAllowed, bool isBuyWithProofAllowed, bool isBuyWithPriceAllowed ) private { _purchaseSettings.isReleaseAllowed = isReleaseAllowed; _purchaseSettings.isBuyAllowed = isBuyAllowed; _purchaseSettings.isBuyWithProofAllowed = isBuyWithProofAllowed; _purchaseSettings.isBuyWithPriceAllowed = isBuyWithPriceAllowed; emit PurchaseSettingsUpdated( isReleaseAllowed, isBuyAllowed, isBuyWithProofAllowed, isBuyWithPriceAllowed ); } /** * @dev Private function to process a purchase with a specified price using permit for token approval. * @param user The address of the user making the purchase. * @param stageId The ID of the sale stage. * @param token The address of the payment token used for the purchase. * @param price The price of the tokens. * @param tokensToPay The amount of payment tokens to pay. * @param deadline The deadline for the permit signature. * @param v The v component of the permit signature. * @param r The r component of the permit signature. * @param s The s component of the permit signature. */ function _processingWithPricePermit( address user, uint256 stageId, address token, uint256 price, uint256 tokensToPay, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) private { IERC20Permit(token).safePermit( user, address(this), tokensToPay, deadline, v, r, s ); _processingWithPrice(user, stageId, token, price, tokensToPay); } /** * @dev Private function to process a purchase with a specified price. * @param user The address of the user making the purchase. * @param stageId The ID of the sale stage. * @param token The address of the payment token used for the purchase. * @param price The price of the tokens. * @param tokensToPay The amount of payment tokens to pay. */ function _processingWithPrice( address user, uint256 stageId, address token, uint256 price, uint256 tokensToPay ) private onlyPaymentToken(token) thenStageIsActive(stageId) whenNotPaused { (uint256 tokensToBuy, uint256 tokensToPay_) = _calculateWithPrice( stageId, price, tokensToPay ); _raiseFunds(user, token, tokensToPay_); _increaseRaisedAndSoldAndVested( user, stageId, token, tokensToBuy, tokensToPay_, price ); } /** * @dev Private function to process the purchase with direct buy method. * @param user The address of the user making the purchase. * @param stageId The ID of the sale stage. * @param token The address of the payment token used for the purchase. * @param tokensToPay The amount of payment tokens to pay. */ function _buyProcessing( address user, uint256 stageId, address token, uint256 tokensToPay ) private { _processing(user, stageId, token, tokensToPay); } /** * @dev Private function to process the purchase with permit method. * @param user The address of the user making the purchase. * @param stageId The ID of the sale stage. * @param token The address of the payment token used for the purchase. * @param tokensToPay The amount of payment tokens to pay. * @param deadline The permit deadline. * @param v The recovery id. * @param r The r value of the signature. * @param s The s value of the signature. */ function _buyWithPermitProcessing( address user, uint256 stageId, address token, uint256 tokensToPay, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) private { IERC20Permit(token).safePermit( user, address(this), tokensToPay, deadline, v, r, s ); _processing(user, stageId, token, tokensToPay); } /** * @dev Private function to handle the process of buying tokens. * @param user The address of the user. * @param stageId The ID of the sale stage. * @param token The address of the payment token used. * @param tokensToPay The amount of payment tokens to pay. */ function _processing( address user, uint256 stageId, address token, uint256 tokensToPay ) private onlyPaymentToken(token) thenStageIsActive(stageId) whenNotPaused { (uint256 tokensToBuy, uint256 tokensToPay_, uint256 price) = _calculate( stageId, token, tokensToPay ); _raiseFunds(user, token, tokensToPay_); _increaseRaisedAndSoldAndVested( user, stageId, token, tokensToBuy, tokensToPay_, price ); } /** * @dev Private function to calculate the number of tokens to buy and the amount to pay. * @param stageId The ID of the sale stage. * @param price The price of the tokens. * @param tokensToPay_ The amount of payment tokens to pay. * @return tokensToBuy The number of tokens to buy. * @return tokensToPay The adjusted amount of payment tokens to pay. */ function _calculateWithPrice( uint256 stageId, uint256 price, uint256 tokensToPay_ ) private view returns (uint256 tokensToBuy, uint256 tokensToPay) { tokensToPay = tokensToPay_; tokensToBuy = _calculateTokensToBuyWithPrice(price, tokensToPay); uint256 reserve = _getStageReserve(stageId); if (tokensToBuy > reserve) { tokensToBuy = reserve; tokensToPay = _calculateTokensToPayWithPrice(price, tokensToBuy); } } /** * @dev Private function to calculate the number of tokens to buy and the amount to pay. * @param stageId The ID of the sale stage. * @param token The address of the payment token used. * @param tokensToPay_ The amount of payment tokens to pay. * @return tokensToBuy The number of tokens to buy. * @return tokensToPay The adjusted amount of payment tokens to pay. * @return price The price of the token. */ function _calculate( uint256 stageId, address token, uint256 tokensToPay_ ) private view returns (uint256 tokensToBuy, uint256 tokensToPay, uint256 price) { tokensToPay = tokensToPay_; (tokensToBuy, price) = _calculateTokensToBuyAtStage( stageId, token, tokensToPay ); uint256 reserve = _getStageReserve(stageId); if (tokensToBuy > reserve) { tokensToBuy = reserve; (tokensToPay, ) = _calculateTokensToPayAtStage( stageId, token, tokensToBuy ); } } /** * @dev Private function to handle the raising of funds. * @param user The address of the user. * @param token The address of the payment token used. * @param tokensToPay The amount of payment tokens to pay. */ function _raiseFunds( address user, address token, uint256 tokensToPay ) private { uint256 restToPay = tokensToPay; uint256 deposit = _getUserTokenDeposit(user, token); if (deposit > 0) { uint256 toDepositReduce; if (deposit >= tokensToPay) { restToPay = 0; toDepositReduce = tokensToPay; } else { restToPay = tokensToPay - deposit; toDepositReduce = deposit; } _reduceDeposit(user, token, toDepositReduce); } if (restToPay > 0) { if (token == address(0)) { // Take the restToPay of ETH from msg.value, return the rest to the user. if (msg.value < restToPay) { revert InsufficientFunds(); } else if (msg.value > restToPay) { payable(user).sendValue(msg.value - restToPay); } } else { // Take the restToPay of ERC20 tokens from the user’s balance. IERC20 _token = IERC20(token); uint256 before = _token.balanceOf(address(this)); _token.safeTransferFrom(user, address(this), restToPay); uint256 delta = _token.balanceOf(address(this)) - before; // Check and prohibition of tax tokens. if (delta != restToPay) { revert InvalidTokenAmount(); } } } } /** * @dev Private function to increase raised funds, sold tokens, and vesting for a user. * @param user The address of the user. * @param stageId The ID of the sale stage. * @param token The address of the payment token used. * @param tokensToBuy The amount of tokens to buy. * @param tokensToPay The amount of payment tokens to pay. * @param price The price of the token. */ function _increaseRaisedAndSoldAndVested( address user, uint256 stageId, address token, uint256 tokensToBuy, uint256 tokensToPay, uint256 price ) private { _increaseRaisedFunds(token, tokensToPay); _increaseSoldAndVested(user, stageId, tokensToBuy); emit TokensBought(user, stageId, token, price, tokensToBuy); } /** * @dev Private function to increase the sold tokens and vesting for a user. * @param user The address of the user. * @param stageId The ID of the sale stage. * @param tokensToBuy The amount of tokens to buy. */ function _increaseSoldAndVested( address user, uint256 stageId, uint256 tokensToBuy ) private { _increaseStageSold(stageId, tokensToBuy); ITokenVesting(_getTokenVesting()).onTokensPurchase( user, stageId, tokensToBuy ); } }
// SPDX-License-Identifier: GPL-3.0-or-later // Compatible with OpenZeppelin Contracts ^5.0.0 pragma solidity 0.8.20; import "@openzeppelin/contracts/access/Ownable2Step.sol"; /** * @title TeamWallet * @dev Abstract contract for managing the team wallet address. * The team wallet can be set and changed by the contract owner. * Inherits from Ownable2Step. */ abstract contract TeamWallet is Ownable2Step { /// @dev Address of the team wallet. address private _teamWallet; /// @dev Flag indicating if the team wallet is frozen. bool private _teamWalletFrozen; /** * @dev Emitted when the team wallet address is changed. * @param teamWallet The new team wallet address. */ event TeamWalletUpdated(address teamWallet); /// @dev Emitted when the team wallet is frozen. event TeamWalletFrozen(); /// @dev Error thrown when the team wallet address is zero. error TeamWalletIsZero(); /// @dev Error thrown when the team wallet is frozen and an update is attempted. error TeamWalletIsFrozen(); /** * @dev Constructor to initialize the team wallet. * @param teamWallet The address of the initial team wallet. */ constructor(address teamWallet) { _setTeamWallet(teamWallet); } /// @dev Modifier to check that the team wallet address is not zero. modifier whenTeamWalletIsNotZero() { if (_isTeamWalletZero()) { revert TeamWalletIsZero(); } _; } /** * @notice Returns the current team wallet address and its frozen status. * @return isFrozen Indicates if the team wallet is frozen. * @return teamWallet The address of the team wallet. */ function getTeamWallet() external view returns (bool isFrozen, address teamWallet) { isFrozen = _teamWalletFrozen; teamWallet = _getTeamWallet(); } /** * @notice Sets a new team wallet address. * @dev Can only be called by the contract owner. * Emits a TeamWalletUpdated event. * @param teamWallet The address of the new team wallet. */ function setTeamWallet(address teamWallet) external onlyOwner { if (_teamWalletFrozen) { revert TeamWalletIsFrozen(); } _setTeamWallet(teamWallet); } /** * @notice Freezes the team wallet, preventing further changes. * @dev Can only be called by the contract owner. * Emits a TeamWalletFrozen event. */ function freezeTeamWallet() external whenTeamWalletIsNotZero onlyOwner { if (_teamWalletFrozen) { revert TeamWalletIsFrozen(); } _teamWalletFrozen = true; emit TeamWalletFrozen(); } /** * @dev Internal function to get the team wallet address. * @return The address of the team wallet. */ function _getTeamWallet() internal view returns (address) { return _teamWallet; } /** * @dev Internal function to check if the team wallet address is zero. * @return True if the team wallet address is zero, false otherwise. */ function _isTeamWalletZero() internal view returns (bool) { return _getTeamWallet() == address(0); } /** * @dev Private function to set a new team wallet address and emit an event. * Emits a TeamWalletUpdated event. * @param teamWallet The address of the new team wallet. */ function _setTeamWallet(address teamWallet) private { _teamWallet = teamWallet; emit TeamWalletUpdated(teamWallet); } }
// SPDX-License-Identifier: GPL-3.0-or-later // Compatible with OpenZeppelin Contracts ^5.0.0 pragma solidity 0.8.20; import "@openzeppelin/contracts/access/Ownable2Step.sol"; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; /** * @title Signature * @dev Abstract contract to authorize token purchases using signatures provided by a server. * The contract checks the validity of signatures to ensure authorized transactions. * Inherits from Ownable2Step. */ abstract contract Signature is Ownable2Step { /// @dev The chain ID of the network. uint256 private immutable _chainId; /// @dev The address of the authorized signer. address private _signer; /// @dev Nonce to track signatures per user. mapping(address user => uint256) private _userNonce; /// @dev Event emitted when the signer address is updated. event SignerUpdated(address signer); /// @dev Event emitted when the user used nonce. event UserSignatureNonceUsed(address indexed user, uint256 nonce); /// @dev Error thrown when the signer address is zero. error SignerIsZeroAddress(); /// @dev Error thrown when an unauthorized action is attempted. error NotAuthorized(); /** * @dev Initializes the contract with the provided signer address and sets the chain ID. * @param signer The address of the initial signer. */ constructor(address signer) { _chainId = block.chainid; _setSigner(signer); } /** * @notice Returns the current nonce for the given user address. * @param user The address of the user. * @return The current nonce for the user. */ function getUserSignatureNonce( address user ) external view returns (uint256) { return _userNonce[user]; } /** * @notice Returns the chain ID of the network. * @return The chain ID. */ function getChainId() external view returns (uint256) { return _chainId; } /** * @notice Returns the address of the current signer. * @return The address of the signer. */ function getSigner() external view returns (address) { return _signer; } /** * @notice Checks the validity of a provided signature for the given parameters without incrementing the nonce. * @dev This function can be called externally to verify the signature without affecting the state. * @param user The address of the user. * @param stageId The ID of the purchase stage. * @param token The address of the payment token used for the purchase. * @param price The price of the tokens. * @param tokensToPay The amount of payment tokens to pay. * @param signature The signature provided by the server. * @return bool Returns true if the signature is valid, otherwise false. */ function checkSignatureBuyWithPrice( address user, uint256 stageId, address token, uint256 price, uint256 tokensToPay, bytes memory signature ) external view returns (bool) { if ( _signatureBuyWithPriceWallet( _chainId, user, _userNonce[user], stageId, token, price, tokensToPay, signature ) != _signer ) { return false; } return true; } /** * @notice Checks the validity of a provided signature for the given parameters without incrementing the nonce. * @dev This function can be called externally to verify the signature without affecting the state. * @param user The address of the user. * @param stageId The ID of the purchase stage. * @param tokensToBuy The amount of tokens to purchase. * @param signature The signature provided by the server. * @return bool Returns true if the signature is valid, otherwise false. */ function checkSignatureRelease( address user, uint256 stageId, uint256 tokensToBuy, bytes memory signature ) external view returns (bool) { if ( _signatureReleaseWallet( _chainId, user, _userNonce[user], stageId, tokensToBuy, signature ) != _signer ) { return false; } return true; } /** * @notice Sets a new signer address. Can only be called by the owner. * @dev Can only be called by the contract owner. * @param newSigner The address of the new signer. */ function setSigner(address newSigner) external onlyOwner { _setSigner(newSigner); } /** * @dev Checks the validity of the provided signature for the given parameters. * Increments the user's nonce if the signature is valid. * Reverts with NotAuthorized error if the signature is invalid. * @param user The address of the user. * @param stageId The ID of the purchase stage. * @param token The address of the payment token used for the purchase. * @param price The price of the tokens. * @param tokensToPay The amount of payment tokens to pay. * @param signature The signature provided by the server. */ function _checkSignatureBuyWithPrice( address user, uint256 stageId, address token, uint256 price, uint256 tokensToPay, bytes memory signature ) internal { _ensureSignerIsNotZeroAddress(); uint256 userNonce = _userNonce[user]; if ( _signatureBuyWithPriceWallet( _chainId, user, userNonce, stageId, token, price, tokensToPay, signature ) != _signer ) { revert NotAuthorized(); } _userNonce[user] += 1; emit UserSignatureNonceUsed(user, userNonce); } /** * @dev Checks the validity of the provided signature for the given parameters. * Increments the user's nonce if the signature is valid. * Reverts with NotAuthorized error if the signature is invalid. * @param user The address of the user. * @param stageId The ID of the purchase stage. * @param tokensToBuy The amount of tokens to purchase. * @param signature The signature provided by the server. */ function _checkSignatureRelease( address user, uint256 stageId, uint256 tokensToBuy, bytes memory signature ) internal { _ensureSignerIsNotZeroAddress(); uint256 userNonce = _userNonce[user]; if ( _signatureReleaseWallet( _chainId, user, userNonce, stageId, tokensToBuy, signature ) != _signer ) { revert NotAuthorized(); } _userNonce[user] += 1; emit UserSignatureNonceUsed(user, userNonce); } /** * @dev Recovers the address that signed the message given the provided parameters. * @param chainId The chain ID. * @param user The address of the user. * @param userNonce The user's current nonce. * @param stageId The ID of the purchase stage. * @param token The address of the payment token used for the purchase. * @param price The price of the tokens. * @param tokensToPay The amount of payment tokens to pay. * @param signature The signature to recover the address from. * @return The address that signed the message. */ function _signatureBuyWithPriceWallet( uint256 chainId, address user, uint256 userNonce, uint256 stageId, address token, uint256 price, uint256 tokensToPay, bytes memory signature ) private pure returns (address) { return ECDSA.recover( keccak256( abi.encode( chainId, user, userNonce, stageId, token, price, tokensToPay ) ), signature ); } /** * @dev Recovers the address that signed the message given the provided parameters. * @param chainId The chain ID. * @param user The address of the user. * @param userNonce The user's current nonce. * @param stageId The ID of the purchase stage. * @param tokensToBuy The amount of tokens to purchase. * @param signature The signature to recover the address from. * @return The address that signed the message. */ function _signatureReleaseWallet( uint256 chainId, address user, uint256 userNonce, uint256 stageId, uint256 tokensToBuy, bytes memory signature ) private pure returns (address) { return ECDSA.recover( keccak256( abi.encode(chainId, user, userNonce, stageId, tokensToBuy) ), signature ); } /** * @dev Private function to ensure the signer address is not zero. * @dev Reverts with `SignerIsZeroAddress` if the signer address is zero. */ function _ensureSignerIsNotZeroAddress() private view { if (_signer == address(0)) { revert SignerIsZeroAddress(); } } /** * @dev Private function to set the signer address. * Emits a SignerUpdated event. * @param newSigner The address of the new signer. */ function _setSigner(address newSigner) private { _signer = newSigner; emit SignerUpdated(newSigner); } }
// SPDX-License-Identifier: GPL-3.0-or-later // Compatible with OpenZeppelin Contracts ^5.0.0 pragma solidity 0.8.20; import "@openzeppelin/contracts/access/Ownable2Step.sol"; /** * @title SellableToken * @dev Abstract contract to manage a sellable token address and its decimals. Allows setting and freezing the sellable token. * Inherits from Ownable2Step. */ abstract contract SellableToken is Ownable2Step { /// @dev Indicates whether the sellable token is frozen. bool private _sellableTokenFrozen; /// @dev Address of the sellable token. address private _sellableToken; /// @dev Number of decimals of the sellable token. uint8 private _sellableTokenDecimals; /// @dev Event emitted when the sellable token is updated. event SellableTokenUpdated(address token, uint8 decimals); /// @dev Event emitted when the sellable token is frozen. event SellableTokenFrozen(); /// @dev Error thrown when the sellable token address is zero. error SellableTokenIsZero(); /// @dev Error thrown when the sellable token decimals is zero. error SellableTokenDecimalsIsZero(); /// @dev Error thrown when the sellable token is frozen. error SellableTokenIsFrozen(); /** * @dev Sets the initial values for the sellable token and its decimals. * @param token The address of the sellable token. * @param decimals The number of decimals of the sellable token. Must be greater than 0. */ constructor(address token, uint8 decimals) { _setSellableToken(token, decimals); } /// @dev Modifier to check if the sellable token address is not zero. modifier whenSellableTokenIsNotZero() { if (_getSellableToken() == address(0)) { revert SellableTokenIsZero(); } _; } /** * @notice Gets the sellable token details. * @return isFrozen Boolean indicating if the sellable token is frozen. * @return token The address of the sellable token. * @return decimals The number of decimals of the sellable token. */ function getSellableToken() external view returns (bool isFrozen, address token, uint8 decimals) { isFrozen = _isSellableTokenFrozen(); token = _getSellableToken(); decimals = _getSellableTokenDecimals(); } /** * @notice Sets the sellable token and its decimals. * @dev Can only be called by the contract owner and if the sellable token is not frozen. * @param token The address of the sellable token. * @param decimals The number of decimals of the sellable token. Must be greater than 0. */ function setSellableToken( address token, uint8 decimals ) external onlyOwner { if (_isSellableTokenFrozen()) { revert SellableTokenIsFrozen(); } _setSellableToken(token, decimals); } /** * @notice Freezes the sellable token, preventing further changes to its address or decimals. * @dev Can only be called by the contract owner and only if the sellable token is not already frozen. * This action is irreversible. Once the sellable token is frozen, it cannot be unfrozen. * Emits a SellableTokenFrozen event upon successful freezing of the sellable token. */ function freezeSellableToken() external whenSellableTokenIsNotZero onlyOwner { if (_sellableTokenFrozen) { revert SellableTokenIsFrozen(); } _sellableTokenFrozen = true; emit SellableTokenFrozen(); } /** * @dev Internal function to get the address of the sellable token. * @return The address of the sellable token. */ function _getSellableToken() internal view returns (address) { return _sellableToken; } /** * @dev Internal function to get the number of decimals of the sellable token. * @return The number of decimals of the sellable token. */ function _getSellableTokenDecimals() internal view returns (uint8) { return _sellableTokenDecimals; } /** * @dev Private function to check if the sellable token is frozen. * @return Boolean indicating if the sellable token is frozen. */ function _isSellableTokenFrozen() private view returns (bool) { return _sellableTokenFrozen; } /** * @dev Private function to set the sellable token and its decimals. * Emits a SellableTokenUpdated event. * @param token The address of the sellable token. * @param decimals The number of decimals of the sellable token. Must be greater than 0. */ function _setSellableToken(address token, uint8 decimals) private { if (decimals == 0) { revert SellableTokenDecimalsIsZero(); } _sellableToken = token; _sellableTokenDecimals = decimals; emit SellableTokenUpdated(token, decimals); } }
// SPDX-License-Identifier: MIT // Compatible with OpenZeppelin Contracts ^5.0.0 pragma solidity 0.8.20; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; /** * @title SafePermit * @dev Library to safely handle ERC-2612 permits for ERC-20 tokens. * Provides a function to securely grant approvals using off-chain signatures. */ library SafePermit { /** * @dev Error indicating that the permit operation did not succeed. * @param token The address of the token for which the permit operation failed. */ error SafeERC20PermitDidNotSucceed(address token); /** * @dev Uses an ERC-2612 signature to set the `owner` approval toward `spender` on `token`. * Reverts on invalid signature or unsuccessful permit operation. * @param token The token contract supporting the ERC-2612 interface. * @param owner The address of the token owner. * @param spender The address to receive the approval. * @param value The amount of tokens to be approved. * @param deadline The deadline timestamp by which the permit must be mined. * @param v The recovery byte of the signature. * @param r Half of the ECDSA signature pair. * @param s Half of the ECDSA signature pair. */ function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); if (nonceAfter != nonceBefore + 1) { revert SafeERC20PermitDidNotSucceed(address(token)); } } }
// SPDX-License-Identifier: GPL-3.0-or-later // Compatible with OpenZeppelin Contracts ^5.0.0 pragma solidity 0.8.20; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/access/Ownable2Step.sol"; import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; /** * @title RecoverableFunds * @dev Abstract contract that allows the owner to recover accidentally sent ERC20 tokens * and native coins (ETH) that are not part of the project's tracked funds. * Ensures the amount to be recovered does not exceed the recoverable balance. * Inherits from Ownable2Step and ReentrancyGuard. */ abstract contract RecoverableFunds is Ownable2Step, ReentrancyGuard { using SafeERC20 for IERC20; using Address for address payable; /** * @dev Emitted when funds are recovered. * @param user The address that received the recovered funds. * @param token The address of the recovered ERC20 token or address(0) for ETH. * @param amount The amount of ERC20 tokens or ETH recovered. */ event FundsRecovered( address indexed user, address indexed token, uint256 amount ); /// @dev Error to indicate that the amount to be recovered exceeds the recoverable balance. error AmountExceedsRecoverableFunds(); /// @dev Error to indicate that the recipient address is zero. error RecipientIsZeroAddress(); /** * @notice Returns the recoverable amount of a specific token or ETH. * @dev If the `token` is the zero address, it returns the balance of the contract in ETH. * Otherwise, it returns the balance of the specified ERC20 token held by the contract. * This function is designed to be overridden in derived contracts if needed. * @param token The address of the ERC20 token or the zero address for ETH. * @return The recoverable amount of the specified token or ETH. */ function getRecoverableFunds( address token ) public view virtual returns (uint256) { if (token == address(0)) return address(this).balance; else return IERC20(token).balanceOf(address(this)); } /** * @notice Allows the owner to recover ERC20 tokens and native coins (ETH) accidentally sent to the contract. * @dev Can only be called by the contract owner. * Ensures the amount to be recovered does not exceed the recoverable balance. * Emits a FundsRecovered event. * @param user The address to receive recovered funds from the contract. * @param token The address of the ERC20 token to recover or address(0) to recover ETH. * @param amount The amount of ERC20 tokens or ETH to recover. * @return Returns true if the recovery was successful. */ function recoverFunds( address user, address token, uint256 amount ) external onlyOwner nonReentrant returns (bool) { if (user == address(0)) { revert RecipientIsZeroAddress(); } uint256 recoverableAmount = getRecoverableFunds(token); if (amount > recoverableAmount) { revert AmountExceedsRecoverableFunds(); } _transferRecoverableFundsTo(user, token, amount); emit FundsRecovered(user, token, amount); return true; } /** * @dev Private function to handle the transfer of recovered funds. * @param user The address to receive the recovered funds. * @param token The address of the ERC20 token to recover or address(0) to recover ETH. * @param amount The amount of ERC20 tokens or ETH to recover. */ function _transferRecoverableFundsTo( address user, address token, uint256 amount ) private { if (token == address(0)) { payable(user).sendValue(amount); } else { IERC20(token).safeTransfer(user, amount); } } }
// SPDX-License-Identifier: GPL-3.0-or-later // Compatible with OpenZeppelin Contracts ^5.0.0 pragma solidity 0.8.20; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/access/Ownable2Step.sol"; import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import "./TeamWallet.sol"; import "./PaymentToken.sol"; /** * @title RaisedFunds * @dev Abstract contract to manage raised funds in different tokens, with auto-withdrawal functionality to the team wallet. * Inherits from Ownable2Step, ReentrancyGuard, TeamWallet and PaymentToken. */ abstract contract RaisedFunds is Ownable2Step, ReentrancyGuard, TeamWallet, PaymentToken { using SafeERC20 for IERC20; using Address for address payable; /// @dev Mapping to track unclaimed raised funds for each token. mapping(address token => uint256) private _raisedUnclaimed; /// @dev Flag to enable auto-withdrawal of funds to the team wallet. bool private _autoWithdrawnRaisedFunds; /// @dev Event emitted when the auto-withdrawal funds status is updated. event AutoWithdrawnRaisedFundsUpdated(bool status); /// @dev Event emitted when funds are deposited. event RaisedFundsDeposited(address indexed token, uint256 amount); /// @dev Event emitted when funds are withdrawn. event RaisedFundsWithdrawn( address indexed user, address indexed token, uint256 amount ); /** * @dev Constructor to initialize the contract with the auto-withdraw funds status. * @param autoWithdrawnRaisedFunds Initial status of auto-withdraw funds. */ constructor(bool autoWithdrawnRaisedFunds) { _setAutoWithdrawRaisedFunds(autoWithdrawnRaisedFunds); } /** * @notice Returns the current status of auto-withdraw funds. * @return The status of auto-withdraw funds. */ function getAutoWithdrawRaisedFunds() external view returns (bool) { return _autoWithdrawnRaisedFunds; } /** * @notice Returns the amount of unclaimed raised funds for the specified tokens. * @dev If the input array of tokens is empty, it defaults to the predefined payment tokens. * @param tokens_ An array of token addresses to query for unclaimed raised funds. * @return tokens An array of token addresses to query for unclaimed raised funds. * @return funds An array containing the amount of unclaimed raised funds for each specified token. */ function getRaisedUnclaimedFunds( address[] memory tokens_ ) external view returns (address[] memory tokens, uint256[] memory funds) { tokens = _getDefaultPaymentTokensIfEmpty(tokens_); funds = new uint256[](tokens.length); for (uint256 i = 0; i < tokens.length; i++) { funds[i] = _getRaisedUnclaimed(tokens[i]); } } /** * @notice Allows the owner to withdraw raised funds for the specified tokens. * @dev Can only be called by the contract owner. * If the input array of tokens is empty, it defaults to the predefined payment tokens. * @param tokens The list of token addresses to withdraw. */ function withdrawRaisedFunds( address[] memory tokens ) external whenTeamWalletIsNotZero onlyOwner nonReentrant { tokens = _getDefaultPaymentTokensIfEmpty(tokens); for (uint256 i = 0; i < tokens.length; i++) { _withdrawRaisedFunds(tokens[i]); } } /** * @notice Allows the owner to set the auto-withdraw funds status. * @dev Can only be called by the contract owner. * @param status The new status of auto-withdraw funds. */ function setAutoWithdrawRaisedFunds(bool status) external onlyOwner { _setAutoWithdrawRaisedFunds(status); } /** * @dev Internal function to get the unclaimed raised funds for a specific token. * @param token The address of the token. * @return The unclaimed raised funds amount. */ function _getRaisedUnclaimed( address token ) internal view returns (uint256) { return _raisedUnclaimed[token]; } /** * @dev Internal function to increase the raised funds for a specific token. * If auto-withdrawal is enabled and the team wallet is set, transfers the funds to the team wallet. * Emits a RaisedFundsDeposited event upon increasing the funds. * @param token The address of the token. * @param amount The amount to increase. */ function _increaseRaisedFunds(address token, uint256 amount) internal { emit RaisedFundsDeposited(token, amount); if (_autoWithdrawnRaisedFunds && !_isTeamWalletZero()) { _transferRaisedFundsTo(_getTeamWallet(), token, amount); } else { _raisedUnclaimed[token] += amount; } } /** * @dev Private function to decrease the raised funds for a specific token. * @param token The address of the token. * @param amount The amount to decrease. */ function _decreaseRaisedFunds(address token, uint256 amount) private { _raisedUnclaimed[token] -= amount; } /** * @dev Private function to withdraw raised funds for a specific token. * @param token The address of the token to withdraw. */ function _withdrawRaisedFunds(address token) private { uint256 amount = _getRaisedUnclaimed(token); if (amount > 0) { _decreaseRaisedFunds(token, amount); _transferRaisedFundsTo(_getTeamWallet(), token, amount); } } /** * @dev Private function to transfer tokens or ETH to a specified address. * Emits a RaisedFundsWithdrawn event upon successful transfer. * @param user The address to transfer the funds to. * @param token The address of the token to transfer, or address(0) for ETH. * @param amount The amount to transfer. */ function _transferRaisedFundsTo( address user, address token, uint256 amount ) private { if (token == address(0)) { payable(user).sendValue(amount); } else { IERC20(token).safeTransfer(user, amount); } emit RaisedFundsWithdrawn(user, token, amount); } /** * @dev Private function to set the auto-withdraw funds status. * Emits an AutoWithdrawnRaisedFundsUpdated event upon updating the status. * @param status The new status of auto-withdraw funds. */ function _setAutoWithdrawRaisedFunds(bool status) private { _autoWithdrawnRaisedFunds = status; emit AutoWithdrawnRaisedFundsUpdated(status); } }
// SPDX-License-Identifier: GPL-3.0-or-later // Compatible with OpenZeppelin Contracts ^5.0.0 pragma solidity 0.8.20; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; import "../utils/SafePermit.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/access/Ownable2Step.sol"; import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import "@openzeppelin/contracts/utils/Pausable.sol"; import "./PaymentToken.sol"; /** * @title PaymentTokenDeposit * @dev Abstract contract to manage the deposit and withdrawal of payment tokens and native coin. * Allows users to deposit and withdraw tokens, supports permit for deposit, * and allows the owner to force the return of tokens to users. * Inherits from Ownable2Step, ReentrancyGuard, Pausable and PaymentToken. */ abstract contract PaymentTokenDeposit is Ownable2Step, ReentrancyGuard, Pausable, PaymentToken { using SafeERC20 for IERC20; using SafePermit for IERC20Permit; using Address for address payable; /// @dev Mapping to store total deposits per token. mapping(address token => uint256) private _totalDeposits; /// @dev Mapping to store user deposits per token. mapping(address user => mapping(address token => uint256)) private _userDeposits; /// @dev Flag to enable or disable deposits. bool private _depositsEnabled; /** * @dev Emitted when a user deposits tokens or native coin. * @param user The address of the user who made the deposit. * @param token The address of the token that was deposited (zero address for native coin). * @param amount The amount of tokens or native coin that were deposited. */ event UserFundsDeposited( address indexed user, address indexed token, uint256 amount ); /** * @dev Emitted when a user withdraws tokens or native coin. * @param user The address of the user who made the withdrawal. * @param token The address of the token that was withdrawn (zero address for native coin). * @param amount The amount of tokens or native coin that were withdrawn. */ event UserFundsWithdrawn( address indexed user, address indexed token, uint256 amount ); /** * @dev Emitted when the deposit status is updated. * @param status The new status of deposits. */ event UpdatedDepositsEnabled(bool status); /// @dev Error indicating an invalid token amount. error InvalidTokenAmount(); /// @dev Error indicating that the deposit balance is insufficient. error InsufficientDepositBalance(); /// @dev Error indicating that deposits are currently disabled. error DepositsDisabled(); /** * @dev Constructor to initialize the deposit status. * @param depositsEnabled Initial status of deposits. */ constructor(bool depositsEnabled) { _setDepositsEnabled(depositsEnabled); } /** * @notice Returns the current status of deposits. * @return A boolean indicating whether deposits are enabled. */ function isDepositsEnabled() external view returns (bool) { return _depositsEnabled; } /** * @notice Returns the total deposits for the specified tokens. * @dev If the input array of tokens is empty, it defaults to the predefined payment tokens. * @param tokens_ The list of token addresses. * @return tokens The list of token addresses. * @return deposits The list of corresponding deposit amounts. */ function getTotalDeposits( address[] memory tokens_ ) external view returns (address[] memory tokens, uint256[] memory deposits) { tokens = _getDefaultPaymentTokensIfEmpty(tokens_); deposits = new uint256[](tokens.length); for (uint256 i = 0; i < tokens.length; i++) { deposits[i] = _getTotalTokenDeposit(tokens[i]); } } /** * @notice Returns the user's deposits for the specified tokens. * @dev If the input array of tokens is empty, it defaults to the predefined payment tokens. * @param user The address of the user. * @param tokens_ The list of token addresses. * @return tokens The list of token addresses. * @return deposits The list of corresponding deposit amounts. */ function getUserDeposits( address user, address[] memory tokens_ ) external view returns (address[] memory tokens, uint256[] memory deposits) { tokens = _getDefaultPaymentTokensIfEmpty(tokens_); deposits = new uint256[](tokens.length); for (uint256 i = 0; i < tokens.length; i++) { deposits[i] = _getUserTokenDeposit(user, tokens[i]); } } /** * @notice Allows users to deposit allowed payment tokens or native coin. * @param token The address of the token to deposit (use zero address for native coin). * @param amount The amount of the token or native coin to deposit. */ function deposit(address token, uint256 amount) external payable { _deposit(_msgSender(), token, amount); } /** * @notice Allows users to deposit payment tokens using a permit. * @param token The address of the payment token to deposit. * @param amount The amount of the token to deposit. * @param deadline The deadline for the permit. * @param v The v component of the permit signature. * @param r The r component of the permit signature. * @param s The s component of the permit signature. */ function depositWithPermit( address token, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external { address user = _msgSender(); IERC20Permit(token).safePermit( user, address(this), amount, deadline, v, r, s ); _deposit(user, token, amount); } /** * @notice Allows users to withdraw their deposited tokens or native coin without restrictions. * @dev If the input array of tokens is empty, it defaults to the predefined payment tokens. * @param tokens The list of token addresses to withdraw (use zero address for native coin). */ function cancelDeposit(address[] memory tokens) external nonReentrant { tokens = _getDefaultPaymentTokensIfEmpty(tokens); _cancelDeposit(_msgSender(), tokens); } /** * @notice Allows the owner to force the return of tokens to users. * @dev Can only be called by the contract owner. * If the input array of tokens is empty, it defaults to the predefined payment tokens. * @param users The list of user addresses to return tokens or native coin to. * @param tokens The list of token addresses to return (use zero address for native coin). */ function cancelDepositForced( address[] memory users, address[] memory tokens ) external onlyOwner nonReentrant { tokens = _getDefaultPaymentTokensIfEmpty(tokens); for (uint256 i = 0; i < users.length; i++) { _cancelDeposit(users[i], tokens); } } /** * @notice Allows the owner to enable/disable deposits. * @dev Can only be called by the contract owner. * @param status The new status of deposits. */ function setDepositsEnabled(bool status) external onlyOwner { _setDepositsEnabled(status); } /** * @dev Internal function to get the total deposit for a specific token. * @param token The address of the token. * @return The total deposit amount for the specified token. */ function _getTotalTokenDeposit( address token ) internal view returns (uint256) { return _totalDeposits[token]; } /** * @dev Internal function to get the user's deposit for a specific token. * @param user The address of the user. * @param token The address of the token. * @return The user's deposit amount for the specified token. */ function _getUserTokenDeposit( address user, address token ) internal view returns (uint256) { return _userDeposits[user][token]; } /** * @dev Internal function to reduce the user's deposit balance. * @param user The address of the user. * @param token The address of the token. * @param amount The amount to reduce. */ function _reduceDeposit( address user, address token, uint256 amount ) internal { if (amount > _getUserTokenDeposit(user, token)) { revert InsufficientDepositBalance(); } _totalDeposits[token] -= amount; _userDeposits[user][token] -= amount; } /** * @dev Internal function to handle deposits of tokens or native coin. * Emits a UserFundsDeposited event upon successful deposit. * @param user The address of the user making the deposit. * @param token The address of the token being deposited. * @param amount The amount of the token being deposited. */ function _deposit( address user, address token, uint256 amount ) internal virtual onlyPaymentToken(token) whenNotPaused { if (!_depositsEnabled) { revert DepositsDisabled(); } if (amount == 0) { revert InvalidTokenAmount(); } if (token == address(0)) { // Handle native coin deposit. if (msg.value != amount) { // If msg.value is not equal to amount. revert InvalidTokenAmount(); } } else if (msg.value != 0) { // Reject non-zero msg.value for ERC20 token deposit. revert InvalidTokenAmount(); } else { // Handle ERC20 token deposit. IERC20 _token = IERC20(token); uint256 before = _token.balanceOf(address(this)); _token.safeTransferFrom(user, address(this), amount); uint256 delta = _token.balanceOf(address(this)) - before; // Check and prohibition of tax tokens. if (delta != amount) { revert InvalidTokenAmount(); } } _totalDeposits[token] += amount; _userDeposits[user][token] += amount; emit UserFundsDeposited(user, token, amount); } /** * @dev Internal function to handle the cancellation of deposits. * Emits a UserFundsWithdrawn event upon successful withdrawal. * @param user The address of the user whose deposits are being canceled. * @param tokens The list of token addresses to withdraw. */ function _cancelDeposit( address user, address[] memory tokens ) internal virtual { for (uint256 i = 0; i < tokens.length; i++) { address token = tokens[i]; uint256 deposited = _getUserTokenDeposit(user, token); if (deposited > 0) { _reduceDeposit(user, token, deposited); _transferDepositTo(user, token, deposited); emit UserFundsWithdrawn(user, token, deposited); } } } /** * @dev Private function to transfer tokens or native coin to a user. * @param user The address of the user. * @param token The address of the token to transfer (use zero address for native coin). * @param amount The amount of tokens or native coin to transfer. */ function _transferDepositTo( address user, address token, uint256 amount ) private { if (token == address(0)) { payable(user).sendValue(amount); } else { IERC20(token).safeTransfer(user, amount); } } /** * @dev Private function to set the deposit status. * Emits an {UpdatedDepositsEnabled} event upon changing the deposit status. * @param status The new status of deposits (true to enable, false to disable). */ function _setDepositsEnabled(bool status) private { _depositsEnabled = status; emit UpdatedDepositsEnabled(status); } }
// SPDX-License-Identifier: GPL-3.0-or-later // Compatible with OpenZeppelin Contracts ^5.0.0 pragma solidity 0.8.20; import "@openzeppelin/contracts/access/Ownable2Step.sol"; /** * @title PaymentToken * @dev Abstract contract to manage a list of payment token addresses with add and remove functionalities. * Inherits from Ownable2Step. */ abstract contract PaymentToken is Ownable2Step { /// @dev List of allowed payment tokens. address[] private _tokens; /// @dev Mapping to check if a token exists in the allowed payment tokens list. mapping(address token => bool) private _tokenExist; /// @dev Event emitted when a new payment token is added. event PaymentTokenAdded(address token); /// @dev Event emitted when a payment token is removed. event PaymentTokenRemoved(address token); /// @dev Error thrown when a payment token is not found. error PaymentTokenNotFound(address token); /** * @dev Constructor to initialize the list of allowed payment tokens. * @param tokens The addresses of the tokens to add. */ constructor(address[] memory tokens) { _addPaymentTokens(tokens); } /** * @dev Modifier to check if the token is allowed for payment. * Reverts with PaymentTokenNotFound error if the token is not allowed. * @param token The address of the token to check. */ modifier onlyPaymentToken(address token) { _checkPaymentToken(token); _; } /** * @notice Verifies if a token is allowed for payment. * @param token The address of the token to verify. * @return bool True if the token is allowed, false otherwise. */ function isPaymentToken(address token) external view returns (bool) { return _isPaymentToken(token); } /** * @notice Retrieves the list of all allowed payment tokens. * @return address[] List of allowed payment token addresses. */ function getPaymentTokens() external view returns (address[] memory) { return _getPaymentTokens(); } /** * @notice Adds new tokens to the list of allowed payment tokens. * @dev Can only be called by the contract owner. * Emits a PaymentTokenAdded event on successful addition. * @param tokens The addresses of the tokens to add. */ function addPaymentTokens(address[] memory tokens) external onlyOwner { _addPaymentTokens(tokens); } /** * @notice Removes tokens from the list of allowed payment tokens. * @dev Can only be called by the contract owner. * Emits a PaymentTokenRemoved event on successful removal. * @param tokens The addresses of the tokens to remove. */ function removePaymentTokens(address[] memory tokens) external onlyOwner { for (uint256 i = 0; i < tokens.length; i++) { address token = tokens[i]; if (_isPaymentToken(token)) { _removeToken(token); emit PaymentTokenRemoved(token); } } } /** * @dev Internal function to check if a token is allowed for payment. * @param token The address of the token to check. * @return bool True if the token is allowed, false otherwise. */ function _isPaymentToken(address token) internal view returns (bool) { return _tokenExist[token]; } /** * @dev Internal function to check if a token is a valid payment token. * Reverts with PaymentTokenNotFound error if the token is not valid. * @param token The address of the token to check. */ function _checkPaymentToken(address token) internal view { if (!_isPaymentToken(token)) { revert PaymentTokenNotFound(token); } } /** * @dev Internal function to get the list of all allowed payment tokens. * @return address[] List of allowed payment token addresses. */ function _getPaymentTokens() internal view returns (address[] memory) { return _tokens; } /** * @dev Checks if the token list is empty and sets it to the default payment tokens list if true. * @param tokens The list of tokens to check. * @return The validated list of tokens. */ function _getDefaultPaymentTokensIfEmpty( address[] memory tokens ) internal view returns (address[] memory) { if (tokens.length == 0) { return _getPaymentTokens(); } return tokens; } /** * @dev Private function to add new payment tokens. * Adds new tokens to the list of accepted payment tokens if they are not already included. * Emits a PaymentTokenAdded event for each token added. * @param tokens The addresses of the tokens to add. */ function _addPaymentTokens(address[] memory tokens) private { for (uint256 i = 0; i < tokens.length; i++) { address token = tokens[i]; if (!_isPaymentToken(token)) { _tokens.push(token); _tokenExist[token] = true; emit PaymentTokenAdded(token); } } } /** * @dev Private function to remove a token from the list. * @param token The address of the token to remove. */ function _removeToken(address token) private { uint256 index = _findTokenIndex(token, _getPaymentTokens()); uint256 lastIndex = _tokens.length - 1; address lastToken = _tokens[lastIndex]; _tokens[index] = lastToken; // Move the last token to the index being removed. _tokens.pop(); // Remove the last element. delete _tokenExist[token]; } /** * @dev Private function to find the index of a token in a given list. * Throws a PaymentTokenNotFound error if the token is not found in the list. * @param token The address of the token to find. * @param tokens The list of token addresses to search through. * @return uint256 The index of the token in the list. */ function _findTokenIndex( address token, address[] memory tokens ) private pure returns (uint256) { for (uint256 i = 0; i < tokens.length; i++) { if (tokens[i] == token) { return i; } } revert PaymentTokenNotFound(token); } }
// SPDX-License-Identifier: GPL-3.0-or-later // Compatible with OpenZeppelin Contracts ^5.0.0 pragma solidity 0.8.20; interface ITokenVesting { /** * @notice Handles the purchase of tokens for a specific user during a token sale stage. * @dev This function is called by the TokenSale contract when tokens are purchased. * It verifies the allocation exists and increases the vested amount for the user. * @param user The address of the user purchasing tokens. * @param stageId The ID of the sale stage. * @param tokensToBuy The amount of tokens being purchased. * @return bool Returns true if the purchase is successfully processed. */ function onTokensPurchase( address user, uint256 stageId, uint256 tokensToBuy ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol) pragma solidity ^0.8.20; /** * @dev These functions deal with verification of Merkle Tree proofs. * * The tree and the proofs can be generated using our * https://github.com/OpenZeppelin/merkle-tree[JavaScript library]. * You will find a quickstart guide in the readme. * * WARNING: You should avoid using leaf values that are 64 bytes long prior to * hashing, or use a hash function other than keccak256 for hashing leaves. * This is because the concatenation of a sorted pair of internal nodes in * the Merkle tree could be reinterpreted as a leaf value. * OpenZeppelin's JavaScript library generates Merkle trees that are safe * against this attack out of the box. */ library MerkleProof { /** *@dev The multiproof provided is not valid. */ error MerkleProofInvalidMultiproof(); /** * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree * defined by `root`. For this, a `proof` must be provided, containing * sibling hashes on the branch from the leaf to the root of the tree. Each * pair of leaves and each pair of pre-images are assumed to be sorted. */ function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) { return processProof(proof, leaf) == root; } /** * @dev Calldata version of {verify} */ function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) { return processProofCalldata(proof, leaf) == root; } /** * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. When processing the proof, the pairs * of leafs & pre-images are assumed to be sorted. */ function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { computedHash = _hashPair(computedHash, proof[i]); } return computedHash; } /** * @dev Calldata version of {processProof} */ function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { computedHash = _hashPair(computedHash, proof[i]); } return computedHash; } /** * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}. * * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details. */ function multiProofVerify( bytes32[] memory proof, bool[] memory proofFlags, bytes32 root, bytes32[] memory leaves ) internal pure returns (bool) { return processMultiProof(proof, proofFlags, leaves) == root; } /** * @dev Calldata version of {multiProofVerify} * * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details. */ function multiProofVerifyCalldata( bytes32[] calldata proof, bool[] calldata proofFlags, bytes32 root, bytes32[] memory leaves ) internal pure returns (bool) { return processMultiProofCalldata(proof, proofFlags, leaves) == root; } /** * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false * respectively. * * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer). */ function processMultiProof( bytes32[] memory proof, bool[] memory proofFlags, bytes32[] memory leaves ) internal pure returns (bytes32 merkleRoot) { // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the Merkle tree. uint256 leavesLen = leaves.length; uint256 proofLen = proof.length; uint256 totalHashes = proofFlags.length; // Check proof validity. if (leavesLen + proofLen != totalHashes + 1) { revert MerkleProofInvalidMultiproof(); } // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". bytes32[] memory hashes = new bytes32[](totalHashes); uint256 leafPos = 0; uint256 hashPos = 0; uint256 proofPos = 0; // At each step, we compute the next hash using two values: // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we // get the next hash. // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the // `proof` array. for (uint256 i = 0; i < totalHashes; i++) { bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; bytes32 b = proofFlags[i] ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]) : proof[proofPos++]; hashes[i] = _hashPair(a, b); } if (totalHashes > 0) { if (proofPos != proofLen) { revert MerkleProofInvalidMultiproof(); } unchecked { return hashes[totalHashes - 1]; } } else if (leavesLen > 0) { return leaves[0]; } else { return proof[0]; } } /** * @dev Calldata version of {processMultiProof}. * * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details. */ function processMultiProofCalldata( bytes32[] calldata proof, bool[] calldata proofFlags, bytes32[] memory leaves ) internal pure returns (bytes32 merkleRoot) { // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the Merkle tree. uint256 leavesLen = leaves.length; uint256 proofLen = proof.length; uint256 totalHashes = proofFlags.length; // Check proof validity. if (leavesLen + proofLen != totalHashes + 1) { revert MerkleProofInvalidMultiproof(); } // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". bytes32[] memory hashes = new bytes32[](totalHashes); uint256 leafPos = 0; uint256 hashPos = 0; uint256 proofPos = 0; // At each step, we compute the next hash using two values: // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we // get the next hash. // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the // `proof` array. for (uint256 i = 0; i < totalHashes; i++) { bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; bytes32 b = proofFlags[i] ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]) : proof[proofPos++]; hashes[i] = _hashPair(a, b); } if (totalHashes > 0) { if (proofPos != proofLen) { revert MerkleProofInvalidMultiproof(); } unchecked { return hashes[totalHashes - 1]; } } else if (leavesLen > 0) { return leaves[0]; } else { return proof[0]; } } /** * @dev Sorts the pair (a, b) and hashes the result. */ function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) { return a < b ? _efficientHash(a, b) : _efficientHash(b, a); } /** * @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory. */ function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) { /// @solidity memory-safe-assembly assembly { mstore(0x00, a) mstore(0x20, b) value := keccak256(0x00, 0x40) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol) pragma solidity ^0.8.20; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS } /** * @dev The signature derives the `address(0)`. */ error ECDSAInvalidSignature(); /** * @dev The signature has an invalid length. */ error ECDSAInvalidSignatureLength(uint256 length); /** * @dev The signature has an S value that is in the upper half order. */ error ECDSAInvalidSignatureS(bytes32 s); /** * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not * return address(0) without also returning an error description. Errors are documented using an enum (error type) * and a bytes32 providing additional information about the error. * * If no error is returned, then the address can be used for verification purposes. * * The `ecrecover` EVM precompile 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 {MessageHashUtils-toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) { if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. /// @solidity memory-safe-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else { return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length)); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM precompile 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 {MessageHashUtils-toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature); _throwError(error, errorArg); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] */ function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) { unchecked { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); // We do not check for an overflow here since the shift operation results in 0 or 1. uint8 v = uint8((uint256(vs) >> 255) + 27); return tryRecover(hash, v, r, s); } } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. */ function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) { (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs); _throwError(error, errorArg); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. */ function tryRecover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address, RecoverError, bytes32) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS, s); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature, bytes32(0)); } return (signer, RecoverError.NoError, bytes32(0)); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s); _throwError(error, errorArg); return recovered; } /** * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided. */ function _throwError(RecoverError error, bytes32 errorArg) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert ECDSAInvalidSignature(); } else if (error == RecoverError.InvalidSignatureLength) { revert ECDSAInvalidSignatureLength(uint256(errorArg)); } else if (error == RecoverError.InvalidSignatureS) { revert ECDSAInvalidSignatureS(errorArg); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol) 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; /** * @dev Unauthorized reentrant call. */ error ReentrancyGuardReentrantCall(); constructor() { _status = NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be NOT_ENTERED if (_status == ENTERED) { revert ReentrancyGuardReentrantCall(); } // Any calls to nonReentrant after this point will fail _status = ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { return _status == ENTERED; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol) pragma solidity ^0.8.20; import {Context} from "../utils/Context.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract Pausable is Context { bool private _paused; /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); /** * @dev The operation failed because the contract is paused. */ error EnforcedPause(); /** * @dev The operation failed because the contract is not paused. */ error ExpectedPause(); /** * @dev Initializes the contract in unpaused state. */ constructor() { _paused = false; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { _requireNotPaused(); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { _requirePaused(); _; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Throws if the contract is paused. */ function _requireNotPaused() internal view virtual { if (paused()) { revert EnforcedPause(); } } /** * @dev Throws if the contract is not paused. */ function _requirePaused() internal view virtual { if (!paused()) { revert ExpectedPause(); } } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } }
// 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; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol) pragma solidity ^0.8.20; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error AddressInsufficientBalance(address account); /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedInnerCall(); /** * @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://consensys.net/diligence/blog/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.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert AddressInsufficientBalance(address(this)); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert FailedInnerCall(); } } /** * @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 or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {FailedInnerCall} error. * * 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. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @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`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert AddressInsufficientBalance(address(this)); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an * unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {FailedInnerCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}. */ function _revert(bytes memory returndata) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert FailedInnerCall(); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC20Permit} from "../extensions/IERC20Permit.sol"; import {Address} from "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev An operation with an ERC20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @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); if (returndata.length != 0 && !abi.decode(returndata, (bool))) { revert SafeERC20FailedOperation(address(token)); } } /** * @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). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // 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 cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. * * CAUTION: See Security Considerations above. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// 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); }
// 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); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol) pragma solidity ^0.8.20; import {Ownable} from "./Ownable.sol"; /** * @dev Contract module which provides access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * The initial owner is specified at deployment time in the constructor for `Ownable`. This * can later be changed with {transferOwnership} and {acceptOwnership}. * * This module is used through inheritance. It will make available all functions * from parent (Ownable). */ abstract contract Ownable2Step is Ownable { address private _pendingOwner; event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner); /** * @dev Returns the address of the pending owner. */ function pendingOwner() public view virtual returns (address) { return _pendingOwner; } /** * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual override onlyOwner { _pendingOwner = newOwner; emit OwnershipTransferStarted(owner(), newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner. * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual override { delete _pendingOwner; super._transferOwnership(newOwner); } /** * @dev The new owner accepts the ownership transfer. */ function acceptOwnership() public virtual { address sender = _msgSender(); if (pendingOwner() != sender) { revert OwnableUnauthorizedAccount(sender); } _transferOwnership(sender); } }
// 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); } }
{ "remappings": [], "optimizer": { "enabled": true, "runs": 200 }, "evmVersion": "shanghai", "libraries": {}, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"sellableToken","type":"address"},{"internalType":"uint8","name":"sellableTokenDecimals","type":"uint8"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"AmountExceedsRecoverableFunds","type":"error"},{"inputs":[],"name":"CapCannotBeLessThanSold","type":"error"},{"inputs":[],"name":"DepositsDisabled","type":"error"},{"inputs":[],"name":"ECDSAInvalidSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"ECDSAInvalidSignatureLength","type":"error"},{"inputs":[{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"ECDSAInvalidSignatureS","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InsufficientDepositBalance","type":"error"},{"inputs":[],"name":"InsufficientFunds","type":"error"},{"inputs":[],"name":"InvalidTokenAmount","type":"error"},{"inputs":[],"name":"NotAuthorized","type":"error"},{"inputs":[],"name":"NotWhitelisted","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ParametersMismatch","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"PaymentTokenNotFound","type":"error"},{"inputs":[],"name":"PaymentTokenPriceIsZero","type":"error"},{"inputs":[],"name":"RecipientIsZeroAddress","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20PermitDidNotSucceed","type":"error"},{"inputs":[],"name":"SellableTokenDecimalsIsZero","type":"error"},{"inputs":[],"name":"SellableTokenIsFrozen","type":"error"},{"inputs":[],"name":"SellableTokenIsZero","type":"error"},{"inputs":[],"name":"SignerIsZeroAddress","type":"error"},{"inputs":[],"name":"StageAlreadyUsed","type":"error"},{"inputs":[],"name":"StageIsNotActive","type":"error"},{"inputs":[],"name":"StageNotExists","type":"error"},{"inputs":[],"name":"TeamWalletIsFrozen","type":"error"},{"inputs":[],"name":"TeamWalletIsZero","type":"error"},{"inputs":[],"name":"ThisPurchaseMethodIsDisabled","type":"error"},{"inputs":[],"name":"TokenVestingIsFrozen","type":"error"},{"inputs":[],"name":"TokenVestingIsZero","type":"error"},{"inputs":[],"name":"TooSmallPurchaseAmount","type":"error"},{"inputs":[],"name":"WrongStageId","type":"error"},{"inputs":[],"name":"WrongStartTime","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"AutoWithdrawnRaisedFundsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"currentStageId","type":"uint256"}],"name":"CurrentStageIdUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FundsRecovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"merkleRoot","type":"bytes32"}],"name":"MerkleRootUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"PaymentTokenAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"PaymentTokenRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isReleaseAllowed","type":"bool"},{"indexed":false,"internalType":"bool","name":"isBuyAllowed","type":"bool"},{"indexed":false,"internalType":"bool","name":"isBuyWithProofAllowed","type":"bool"},{"indexed":false,"internalType":"bool","name":"isBuyWithPriceAllowed","type":"bool"}],"name":"PurchaseSettingsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RaisedFundsDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RaisedFundsWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"stageId","type":"uint256"}],"name":"SaleStageDeleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"stageId","type":"uint256"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"}],"name":"SaleStagePriceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"stageId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"whitelist","type":"bool"},{"indexed":false,"internalType":"uint256","name":"startAt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"duration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"cap","type":"uint256"}],"name":"SaleStageUpdated","type":"event"},{"anonymous":false,"inputs":[],"name":"SellableTokenFrozen","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"SellableTokenUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"signer","type":"address"}],"name":"SignerUpdated","type":"event"},{"anonymous":false,"inputs":[],"name":"TeamWalletFrozen","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"teamWallet","type":"address"}],"name":"TeamWalletUpdated","type":"event"},{"anonymous":false,"inputs":[],"name":"TokenVestingFrozen","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"tokenVesting","type":"address"}],"name":"TokenVestingUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"stageId","type":"uint256"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensBought","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"stageId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensReleased","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"UpdatedDepositsEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"UserFundsDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"UserFundsWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"UserSignatureNonceUsed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"WhitelistGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"WhitelistRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"WhitelistingByDepositUpdated","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"addPaymentTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"stageId","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokensToPay","type":"uint256"}],"name":"buy","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"stageId","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokensToPay","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"buyWithPermit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"stageId","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"tokensToPay","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"buyWithPrice","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"stageId","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"tokensToPay","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"buyWithPricePermit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"stageId","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokensToPay","type":"uint256"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"buyWithProof","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"stageId","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokensToPay","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"buyWithProofPermit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"tokensToPay","type":"uint256"}],"name":"calculateTokensToBuy","outputs":[{"internalType":"uint256","name":"tokensToBuy","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"stageId","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokensToPay","type":"uint256"}],"name":"calculateTokensToBuyAtStage","outputs":[{"internalType":"uint256","name":"tokensToBuy","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"tokensToBuy","type":"uint256"}],"name":"calculateTokensToPay","outputs":[{"internalType":"uint256","name":"tokensToPay","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"stageId","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokensToBuy","type":"uint256"}],"name":"calculateTokensToPayAtStage","outputs":[{"internalType":"uint256","name":"tokensToPay","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"cancelDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"users","type":"address[]"},{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"cancelDepositForced","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"stageId","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"tokensToPay","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"checkSignatureBuyWithPrice","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"stageId","type":"uint256"},{"internalType":"uint256","name":"tokensToBuy","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"checkSignatureRelease","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"stageId","type":"uint256"},{"internalType":"bool","name":"whitelist","type":"bool"},{"internalType":"uint256","name":"startAt","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"cap","type":"uint256"},{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"prices","type":"uint256[]"}],"name":"createNextSaleStage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"stageId","type":"uint256"},{"internalType":"bool","name":"whitelist","type":"bool"},{"internalType":"uint256","name":"startAt","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"cap","type":"uint256"},{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"prices","type":"uint256[]"}],"internalType":"struct TokenSaleStages.SaleStageSchedule[]","name":"schedules","type":"tuple[]"}],"name":"createNextSaleStagesBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"deleteLastSaleStage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"stageId","type":"uint256"},{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"deleteSaleStagePrices","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"depositWithPermit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"freezeSellableToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"freezeTeamWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"freezeTokenVesting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAutoWithdrawRaisedFunds","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getChainId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMerkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPaymentTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPurchaseSettings","outputs":[{"internalType":"bool","name":"isReleaseAllowed","type":"bool"},{"internalType":"bool","name":"isBuyAllowed","type":"bool"},{"internalType":"bool","name":"isBuyWithProofAllowed","type":"bool"},{"internalType":"bool","name":"isBuyWithPriceAllowed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens_","type":"address[]"}],"name":"getRaisedUnclaimedFunds","outputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"funds","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getRecoverableFunds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"stageId","type":"uint256"}],"name":"getSaleStage","outputs":[{"internalType":"bool","name":"whitelist","type":"bool"},{"internalType":"uint256","name":"startAt","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"cap","type":"uint256"},{"internalType":"uint256","name":"sold","type":"uint256"},{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"prices","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSaleStages","outputs":[{"components":[{"internalType":"bool","name":"whitelist","type":"bool"},{"internalType":"uint256","name":"startAt","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"cap","type":"uint256"},{"internalType":"uint256","name":"sold","type":"uint256"},{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"prices","type":"uint256[]"}],"internalType":"struct TokenSaleStages.SaleStageDataView[]","name":"stages","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSaleState","outputs":[{"internalType":"uint256","name":"stages","type":"uint256"},{"internalType":"uint256","name":"currentStageId","type":"uint256"},{"internalType":"uint256","name":"sold","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSellableToken","outputs":[{"internalType":"bool","name":"isFrozen","type":"bool"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint8","name":"decimals","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSigner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTeamWallet","outputs":[{"internalType":"bool","name":"isFrozen","type":"bool"},{"internalType":"address","name":"teamWallet","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTokenVesting","outputs":[{"internalType":"address","name":"tokenVesting","type":"address"},{"internalType":"bool","name":"tokenVestingFrozen","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens_","type":"address[]"}],"name":"getTotalDeposits","outputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"deposits","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address[]","name":"tokens_","type":"address[]"}],"name":"getUserDeposits","outputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"deposits","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserSignatureNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"users","type":"address[]"}],"name":"grantWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isDepositsEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"isPaymentToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"isWhitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isWhitelistingByDepositEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"recoverFunds","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"stageId","type":"uint256"},{"internalType":"uint256","name":"tokensToBuy","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"release","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"removePaymentTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"users","type":"address[]"}],"name":"revokeWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"status","type":"bool"}],"name":"setAutoWithdrawRaisedFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newCurrentStageId","type":"uint256"}],"name":"setCurrentStageId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"status","type":"bool"}],"name":"setDepositsEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"}],"name":"setMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"status","type":"bool"}],"name":"setPause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"isReleaseAllowed","type":"bool"},{"internalType":"bool","name":"isBuyAllowed","type":"bool"},{"internalType":"bool","name":"isBuyWithProofAllowed","type":"bool"},{"internalType":"bool","name":"isBuyWithPriceAllowed","type":"bool"}],"name":"setPurchaseSettings","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"setSellableToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newSigner","type":"address"}],"name":"setSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"teamWallet","type":"address"}],"name":"setTeamWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenVesting","type":"address"}],"name":"setTokenVesting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"status","type":"bool"}],"name":"setWhitelistingByDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tokenSaleType","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"tokenSaleVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"stageId","type":"uint256"},{"internalType":"bool","name":"whitelist","type":"bool"},{"internalType":"uint256","name":"startAt","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"cap","type":"uint256"},{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"prices","type":"uint256[]"}],"name":"updateSaleStage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"stageId","type":"uint256"},{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"prices","type":"uint256[]"}],"name":"updateSaleStagePrices","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"verifyWhitelistProof","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"withdrawRaisedFunds","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Deployed Bytecode
0x608060405260043610610416575f3560e01c806370c1354511610220578063ad9ebcfd11610129578063d64b650c116100b3578063ec3601d011610078578063ec3601d014610ccc578063f0c4361314610ceb578063f2fde38b14610d0a578063f311758014610d29578063fdf1087b14610d7c575f80fd5b8063d64b650c14610c16578063d9b51fce14610c35578063e30c397814610c71578063e548922714610c8e578063ea3fd12114610cad575f80fd5b8063bedb86fb116100f9578063bedb86fb14610b77578063c3c027b114610b96578063ca5e553e14610bb5578063cbf3b2a714610bd6578063d04899e914610bf7575f80fd5b8063ad9ebcfd14610afc578063b223e35814610b1b578063b3c1c00d14610b44578063b57b179f14610b58575f80fd5b806387ae3a22116101aa578063960697571161017a5780639606975714610a615780639f04586c14610a80578063a1ac390714610a9f578063a991411214610abe578063aa1078ee14610add575f80fd5b806387ae3a22146109e85780638da5cb5b14610a07578063930eaddc14610a2357806393aa4bd714610a42575f80fd5b80637ac3c02f116101f05780637ac3c02f1461093e5780637bbfbcfa146109745780637cb647591461098b5780637d7cd772146109aa5780638136f590146109c9575f80fd5b806370c13545146108cb578063715018a6146108df57806372172364146108f357806379ba50971461092a575f80fd5b806347e7ef24116103225780635ce60c6a116102ac57806364e60ef41161027c57806364e60ef41461082757806369e1f1c3146108465780636a92df6a146108595780636af9c8fb146108785780636c19e783146108ac575f80fd5b80635ce60c6a146107b75780635d3590d5146107d65780635d5c49a6146107f557806363711ff114610814575f80fd5b80634b389c00116102f25780634b389c001461072f578063573aa7d51461074357806357742d27146107755780635777bf50146107895780635c975abb146107a0575f80fd5b806347e7ef24146106ca578063480d5703146106dd57806349590657146106fc5780634af5e79114610710575f80fd5b80632afaca20116103a357806334383465116103735780633438346514610619578063387e45121461064e5780633af32abf1461066d5780634234fb941461068c57806343e34696146106ab575f80fd5b80632afaca20146105975780632cc6ca69146105aa57806331da3f25146105c95780633408e470146105dd575f80fd5b806312a62cff116103e957806312a62cff146104ea5780631525ff7d1461050b5780631631bdcb1461052a57806325bdb2a814610549578063267bbcc614610578575f80fd5b8063019dcf2c1461041a5780630474761e1461044e5780630759a3541461047b5780630e3e2ea4146104b6575b5f80fd5b348015610425575f80fd5b50610439610434366004614eec565b610d9b565b60405190151581526020015b60405180910390f35b348015610459575f80fd5b5061046d61046836600461500f565b610db1565b6040516104459291906150c9565b348015610486575f80fd5b5060045460408051600160a81b830460ff16151581526101009092046001600160a01b0316602083015201610445565b3480156104c1575f80fd5b506104d56104d03660046150ed565b610e69565b60408051928352602083019190915201610445565b3480156104f5575f80fd5b5061050961050436600461512f565b610e8e565b005b348015610516575f80fd5b50610509610525366004615183565b610ebc565b348015610535575f80fd5b5061050961054436600461519c565b610efb565b348015610554575f80fd5b5061055d610f86565b60408051938452602084019290925290820152606001610445565b348015610583575f80fd5b50610439610592366004615238565b610fa8565b6105096105a53660046150ed565b611024565b3480156105b5575f80fd5b506105096105c436600461519c565b6110b1565b3480156105d4575f80fd5b506104396110d8565b3480156105e8575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000015b604051908152602001610445565b348015610624575f80fd5b50604080518082019091526004815263199d5b1b60e21b60208201525b60405161044591906152ce565b348015610659575f80fd5b5061060b610668366004615183565b6110ea565b348015610678575f80fd5b50610439610687366004615183565b6111b3565b348015610697575f80fd5b506105096106a6366004615365565b6111c3565b3480156106b6575f80fd5b506105096106c5366004615183565b611215565b6105096106d83660046153fa565b611251565b3480156106e8575f80fd5b506105096106f7366004615422565b611260565b348015610707575f80fd5b50600b5461060b565b34801561071b575f80fd5b5061050961072a366004615480565b6112e3565b34801561073a575f80fd5b50610509611349565b34801561074e575f80fd5b5061076261075d3660046154d5565b6113e2565b60405161044597969594939291906154ec565b348015610780575f80fd5b506105096114fb565b348015610794575f80fd5b5060095460ff16610439565b3480156107ab575f80fd5b5060045460ff16610439565b3480156107c2575f80fd5b506105096107d136600461553e565b611616565b3480156107e1575f80fd5b506104396107f036600461556f565b611653565b348015610800575f80fd5b5061050961080f366004615598565b61171f565b6105096108223660046155d6565b61197e565b348015610832575f80fd5b5061050961084136600461519c565b6119d8565b61050961085436600461563b565b611a87565b348015610864575f80fd5b5061060b61087336600461569d565b611b13565b348015610883575f80fd5b5061060b610892366004615183565b6001600160a01b03165f9081526014602052604090205490565b3480156108b7575f80fd5b506105096108c6366004615183565b611b1e565b3480156108d6575f80fd5b50610509611b2f565b3480156108ea575f80fd5b50610509611bc5565b3480156108fe575f80fd5b50601554604080516001600160a01b0383168152600160a01b90920460ff161515602083015201610445565b348015610935575f80fd5b50610509611bd8565b348015610949575f80fd5b5060135461010090046001600160a01b03165b6040516001600160a01b039091168152602001610445565b34801561097f575f80fd5b5060135460ff16610439565b348015610996575f80fd5b506105096109a53660046154d5565b611c1e565b3480156109b5575f80fd5b506105096109c4366004615598565b611c2f565b3480156109d4575f80fd5b506105096109e33660046154d5565b611c82565b3480156109f3575f80fd5b50610509610a023660046156bd565b611cc7565b348015610a12575f80fd5b505f546001600160a01b031661095c565b348015610a2e575f80fd5b50610439610a3d366004615183565b611d01565b348015610a4d575f80fd5b5061046d610a5c36600461519c565b611d21565b348015610a6c575f80fd5b5061060b610a7b36600461569d565b611deb565b348015610a8b575f80fd5b50610509610a9a3660046156ea565b611df6565b348015610aaa575f80fd5b50610509610ab9366004615598565b611e07565b348015610ac9575f80fd5b50610509610ad83660046156ea565b611e5a565b348015610ae8575f80fd5b50610509610af73660046156ea565b611e6b565b348015610b07575f80fd5b506104d5610b163660046150ed565b611e7c565b348015610b26575f80fd5b506040805180820190915260018152603160f81b6020820152610641565b348015610b4f575f80fd5b50610509611e93565b348015610b63575f80fd5b50610439610b72366004615705565b611f3d565b348015610b82575f80fd5b50610509610b913660046156ea565b611fb5565b348015610ba1575f80fd5b50610509610bb0366004615761565b611fd3565b348015610bc0575f80fd5b50610bc9612111565b60405161044591906157ac565b348015610be1575f80fd5b50610bea61211b565b60405161044591906157be565b348015610c02575f80fd5b50610509610c113660046158a8565b6122ea565b348015610c21575f80fd5b50610509610c30366004615905565b612325565b348015610c40575f80fd5b50610c496123dc565b6040805193151584526001600160a01b03909216602084015260ff1690820152606001610445565b348015610c7c575f80fd5b506001546001600160a01b031661095c565b348015610c99575f80fd5b50610509610ca83660046159a7565b61241e565b348015610cb8575f80fd5b50610509610cc736600461519c565b612469565b348015610cd7575f80fd5b50610509610ce6366004615a38565b61247a565b348015610cf6575f80fd5b5061046d610d0536600461519c565b6124fc565b348015610d15575f80fd5b50610509610d24366004615183565b6125c0565b348015610d34575f80fd5b506016546040805160ff808416151582526101008404811615156020830152620100008404811615159282019290925263010000009092041615156060820152608001610445565b348015610d87575f80fd5b50610509610d96366004615ab1565b612630565b5f610da7848484612644565b90505b9392505050565b606080610dbd836126da565b915081516001600160401b03811115610dd857610dd8614f3a565b604051908082528060200260200182016040528015610e01578160200160208202803683370190505b5090505f5b8251811015610e6157610e3285848381518110610e2557610e25615b0a565b60200260200101516126f0565b828281518110610e4457610e44615b0a565b602090810291909101015280610e5981615b32565b915050610e06565b509250929050565b5f8083610e758161271a565b610e8086868661275d565b925092505b50935093915050565b33610ea86001600160a01b0388168230898989898961277f565b610eb3818888612910565b50505050505050565b610ec4612952565b600454600160a81b900460ff1615610eef576040516330cc939360e11b815260040160405180910390fd5b610ef88161297e565b50565b610f036129db565b15610f2157604051635b0999e760e01b815260040160405180910390fd5b610f29612952565b610f31612a04565b610f3a816126da565b90505f5b8151811015610f7b57610f69828281518110610f5c57610f5c615b0a565b6020026020010151612a2e565b80610f7381615b32565b915050610f3e565b50610ef86001600355565b5f805f610f92600f5490565b9250610f9c612a73565b91506010549050909192565b6013546001600160a01b038781165f908152601460205260408120549092610100900490911690611001907f0000000000000000000000000000000000000000000000000000000000000001908a908a8a8a8a8a612b16565b6001600160a01b03161461101657505f61101a565b5060015b9695505050505050565b825f61102f82612b8b565b80519091508015611046575061104433612c0d565b155b1561106457604051630b094f2760e31b815260040160405180910390fd5b61106c612a04565b601654610100900460ff166110945760405163fe9538cf60e01b815260040160405180910390fd5b6110a033868686612c2a565b6110aa6001600355565b5050505050565b6110b9612a04565b6110c2816126da565b90506110ce3382612c36565b610ef86001600355565b5f6110e5600c5460ff1690565b905090565b6001600160a01b0381165f908152601260209081526040808320546007909252822054829161111891615b4a565b905080156111aa576001600160a01b03831661113857610daa8147615b5d565b6040516370a0823160e01b815230600482015281906001600160a01b038516906370a0823190602401602060405180830381865afa15801561117c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111a09190615b70565b610daa9190615b5d565b610daa83612c75565b5f6111bd82612c0d565b92915050565b866111cd81612cf6565b156111eb576040516327d190b160e11b815260040160405180910390fd5b6111f3612952565b6112008888888888612d09565b61120b888484612e4a565b5050505050505050565b61121d612952565b601554600160a01b900460ff161561124857604051630203cf6160e51b815260040160405180910390fd5b610ef881612f9b565b61125c338383612910565b5050565b865f61126b82612b8b565b80519091508015611282575061128033612c0d565b155b156112a057604051630b094f2760e31b815260040160405180910390fd5b601654610100900460ff166112c85760405163fe9538cf60e01b815260040160405180910390fd5b6112d8338a8a8a8a8a8a8a612fe9565b505050505050505050565b6112eb612952565b6112f3612a04565b6112fc816126da565b90505f5b825181101561133e5761132c83828151811061131e5761131e615b0a565b602002602001015183612c36565b8061133681615b32565b915050611300565b5061125c6001600355565b611351612952565b6015546001600160a01b031661137a5760405163309a778560e11b815260040160405180910390fd5b601554600160a01b900460ff16156113a557604051630203cf6160e51b815260040160405180910390fd5b6015805460ff60a01b1916600160a01b1790556040517fa072ef0a510e82127c47aabc8141857e02b88891822ce45dd4c32a2af00563cb905f90a1565b5f805f805f606080876113f481612cf6565b15611412576040516327d190b160e11b815260040160405180910390fd5b5f61141c8a612b8b565b9050805f0151985080602001519750806040015196508060600151955080608001519450611449846126da565b935083516001600160401b0381111561146457611464614f3a565b60405190808252806020026020018201604052801561148d578160200160208202803683370190505b5092505f5b84518110156114ed576114be8b8683815181106114b1576114b1615b0a565b602002602001015161300e565b8482815181106114d0576114d0615b0a565b6020908102919091010152806114e581615b32565b915050611492565b505050919395979092949650565b611503612952565b5f61150d600f5490565b9050805f0361152f576040516327d190b160e11b815260040160405180910390fd5b5f61153b600183615b5d565b90505f61154782612b8b565b90505f8160800151118061155c575060115482105b1561157a57604051633440594960e21b815260040160405180910390fd5b600f8054905f61158983615b87565b90915550505f828152600d6020526040808220805460ff1916815560018101839055600281018390556003810183905560040191909155517fc575d70441d376437764ff913c8a93268fd5b38b4442c068c2e7f8bdeb81bc0b906115f09084815260200190565b60405180910390a1604080515f815260208101909152611611908390613034565b505050565b61161e612952565b600154600160a01b900460ff1615611649576040516303ed52c560e41b815260040160405180910390fd5b61125c8282613118565b5f61165c612952565b611664612a04565b6001600160a01b03841661168b576040516311d000e160e31b815260040160405180910390fd5b5f611695846110ea565b9050808311156116b8576040516305d632b960e41b815260040160405180910390fd5b6116c38585856131a9565b836001600160a01b0316856001600160a01b03167f13e06184555481b6d2cb327155e8d2e1d0b1f0252a7fe6621e32cf99881488358560405161170891815260200190565b60405180910390a36001915050610daa6001600355565b611727612952565b805f5b81811015611978575f84848381811061174557611745615b0a565b90506020028101906117579190615b9c565b359050611763600f5490565b81146117825760405163b929687760e01b815260040160405180910390fd5b600f8054905f61179183615b32565b91905055506118708585848181106117ab576117ab615b0a565b90506020028101906117bd9190615b9c565b358686858181106117d0576117d0615b0a565b90506020028101906117e29190615b9c565b6117f39060408101906020016156ea565b87878681811061180557611805615b0a565b90506020028101906118179190615b9c565b6040013588888781811061182d5761182d615b0a565b905060200281019061183f9190615b9c565b6060013589898881811061185557611855615b0a565b90506020028101906118679190615b9c565b60800135612d09565b61196585858481811061188557611885615b0a565b90506020028101906118979190615b9c565b358686858181106118aa576118aa615b0a565b90506020028101906118bc9190615b9c565b6118ca9060a0810190615bba565b808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152508a925089915087905081811061190f5761190f615b0a565b90506020028101906119219190615b9c565b61192f9060c0810190615bba565b808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250612e4a92505050565b508161197081615b32565b92505061172a565b50505050565b611986612a04565b60165433906301000000900460ff166119b25760405163fe9538cf60e01b815260040160405180910390fd5b6119c08187878787876131de565b6119cd81878787876132d6565b506110aa6001600355565b6119e0612952565b5f5b815181101561125c575f8282815181106119fe576119fe615b0a565b60200260200101519050611a29816001600160a01b03165f9081526006602052604090205460ff1690565b15611a7457611a37816133d8565b6040516001600160a01b03821681527f85a3e72f8dd6db3794f93109c3c5f5b79d6112f6979431c45f98b26134b42af29060200160405180910390a15b5080611a7f81615b32565b9150506119e2565b848282335f611a9585612b8b565b805190915015611ac057611aaa8285856134c7565b611ab382612c0d565b611ac057611ac0826134ef565b611ac8612a04565b60165462010000900460ff16611af15760405163fe9538cf60e01b815260040160405180910390fd5b611afd338b8b8b612c2a565b611b076001600355565b50505050505050505050565b5f610daa838361353a565b611b26612952565b610ef88161356d565b611b376129db565b15611b5557604051635b0999e760e01b815260040160405180910390fd5b611b5d612952565b600454600160a81b900460ff1615611b88576040516330cc939360e11b815260040160405180910390fd5b6004805460ff60a81b1916600160a81b1790556040517fb3211088ecd5ab1725233a5886071950e736a017c35ec09f1539974e3b81d299905f90a1565b611bcd612952565b611bd65f6135c3565b565b60015433906001600160a01b03168114611c155760405163118cdaa760e01b81526001600160a01b03821660048201526024015b60405180910390fd5b610ef8816135c3565b611c26612952565b610ef8816135dc565b611c37612952565b5f5b8181101561161157611c70838383818110611c5657611c56615b0a565b9050602002016020810190611c6b9190615183565b6134ef565b80611c7a81615b32565b915050611c39565b611c8a612952565b611c92612a73565b811080611ca05750600f5481115b15611cbe5760405163b929687760e01b815260040160405180910390fd5b610ef88161360e565b81611cd181612cf6565b15611cef576040516327d190b160e11b815260040160405180910390fd5b611cf7612952565b6116118383613034565b6001600160a01b0381165f9081526006602052604081205460ff166111bd565b606080611d2d836126da565b915081516001600160401b03811115611d4857611d48614f3a565b604051908082528060200260200182016040528015611d71578160200160208202803683370190505b5090505f5b8251811015611de557611db6838281518110611d9457611d94615b0a565b60200260200101516001600160a01b03165f9081526012602052604090205490565b828281518110611dc857611dc8615b0a565b602090810291909101015280611ddd81615b32565b915050611d76565b50915091565b5f610daa8383613643565b611dfe612952565b610ef88161366b565b611e0f612952565b5f5b8181101561161157611e48838383818110611e2e57611e2e615b0a565b9050602002016020810190611e439190615183565b6136ac565b80611e5281615b32565b915050611e11565b611e62612952565b610ef8816136f4565b611e73612952565b610ef881613735565b5f8083611e888161271a565b610e80868686613776565b5f611ea66002546001600160a01b031690565b6001600160a01b031603611ecd5760405163488afa1b60e01b815260040160405180910390fd5b611ed5612952565b600154600160a01b900460ff1615611f00576040516303ed52c560e41b815260040160405180910390fd5b6001805460ff60a01b1916600160a01b1790556040517f9efd8729cc972ca7884de34bbf48c2823c0d69fda2946916c65311c81c4c52c3905f90a1565b6013546001600160a01b038581165f908152601460205260408120549092610100900490911690611f94907f000000000000000000000000000000000000000000000000000000000000000190889088888861378e565b6001600160a01b031614611fa957505f611fad565b5060015b949350505050565b611fbd612952565b8015611fcb57610ef86137d8565b610ef8613832565b82611fdc61386b565b6011548114611ffe57604051633b47fcf960e11b815260040160405180910390fd5b5f61200882612b8b565b9050806060015181608001511061203257604051633b47fcf960e11b815260040160405180910390fd5b80602001515f0361205657604051633b47fcf960e11b815260040160405180910390fd5b806020015142108061207b5750806040015181602001516120779190615b4a565b4210155b1561209957604051633b47fcf960e11b815260040160405180910390fd5b6120a1613984565b336120ae818787876139a8565b6120b88686613a9c565b94506120c5818787613ab8565b85816001600160a01b03167fc5c52c2a9175470464d5ea4429889e7df2ea88630a3d32f4d0d3d2d4486562108760405161210191815260200190565b60405180910390a3505050505050565b60606110e5613b43565b60605f612127600f5490565b9050806001600160401b0381111561214157612141614f3a565b6040519080825280602002602001820160405280156121af57816020015b61219c6040518060e001604052805f151581526020015f81526020015f81526020015f81526020015f815260200160608152602001606081525090565b81526020019060019003908161215f5790505b5091505f6121bb613b43565b90505f5b828110156122e4575f6121d182612b8b565b90505f83516001600160401b038111156121ed576121ed614f3a565b604051908082528060200260200182016040528015612216578160200160208202803683370190505b5090505f5b84518110156122695761223a848683815181106114b1576114b1615b0a565b82828151811061224c5761224c615b0a565b60209081029190910101528061226181615b32565b91505061221b565b506040518060e00160405280835f01511515815260200183602001518152602001836040015181526020018360600151815260200183608001518152602001858152602001828152508684815181106122c4576122c4615b0a565b6020026020010181905250505080806122dc90615b32565b9150506121bf565b50505090565b826122f481612cf6565b15612312576040516327d190b160e11b815260040160405180910390fd5b61231a612952565b611978848484612e4a565b61232d612952565b600f54891461234f5760405163b929687760e01b815260040160405180910390fd5b600f8054905f61235e83615b32565b91905055506123708989898989612d09565b6112d8898585808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250506040805160208089028281018201909352888252909350889250879182918501908490808284375f92019190915250612e4a92505050565b5f805f6123f360015460ff600160a01b9091041690565b92506124076002546001600160a01b031690565b600254909250600160a01b900460ff169050909192565b60165433906301000000900460ff1661244a5760405163fe9538cf60e01b815260040160405180910390fd5b612458818b8b8b8b876131de565b611b07818b8b8b8b8b8b8b8b613ba3565b612471612952565b610ef881613bc9565b888282335f61248885612b8b565b8051909150156124b35761249d8285856134c7565b6124a682612c0d565b6124b3576124b3826134ef565b60165462010000900460ff166124dc5760405163fe9538cf60e01b815260040160405180910390fd5b6124ec338f8f8f8f8f8f8f612fe9565b5050505050505050505050505050565b606080612508836126da565b915081516001600160401b0381111561252357612523614f3a565b60405190808252806020026020018201604052801561254c578160200160208202803683370190505b5090505f5b8251811015611de55761259183828151811061256f5761256f615b0a565b60200260200101516001600160a01b03165f9081526007602052604090205490565b8282815181106125a3576125a3615b0a565b6020908102919091010152806125b881615b32565b915050612551565b6125c8612952565b600180546001600160a01b0383166001600160a01b031990911681179091556125f85f546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b612638612952565b61197884848484613cc0565b604080516001600160a01b03851660208201525f9182910160408051601f19818403018152828252805160209182012090830152016040516020818303038152906040528051906020012090506126d18484808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050600b549150849050613d5f565b95945050505050565b606081515f036126ec576111bd613b43565b5090565b6001600160a01b039182165f90815260086020908152604080832093909416825291909152205490565b6001600160a01b0381165f9081526006602052604090205460ff16610ef857604051631e50c79f60e01b81526001600160a01b0382166004820152602401611c0c565b5f80612769858561300e565b90506127758184613643565b9150935093915050565b604051623f675f60e91b81526001600160a01b0388811660048301525f91908a1690637ecebe0090602401602060405180830381865afa1580156127c5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127e99190615b70565b60405163d505accf60e01b81526001600160a01b038a811660048301528981166024830152604482018990526064820188905260ff8716608483015260a4820186905260c48201859052919250908a169063d505accf9060e4015f604051808303815f87803b15801561285a575f80fd5b505af115801561286c573d5f803e3d5ffd5b5050604051623f675f60e91b81526001600160a01b038b811660048301525f93508c169150637ecebe0090602401602060405180830381865afa1580156128b5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128d99190615b70565b90506128e6826001615b4a565b8114611b075760405163ceeaf5db60e01b81526001600160a01b038b166004820152602401611c0c565b61291b838383613d74565b612923613ff2565b1580156129325750600c5460ff165b8015612944575061294283612c0d565b155b1561161157611611836134ef565b5f546001600160a01b03163314611bd65760405163118cdaa760e01b8152336004820152602401611c0c565b60048054610100600160a81b0319166101006001600160a01b038416908102919091179091556040519081527ff6215f245bfd24e51265c56ef650fdd856aa4ece6221ee1ef395bbe0a5558010906020015b60405180910390a150565b5f806129f56004546001600160a01b036101009091041690565b6001600160a01b031614905090565b600260035403612a2757604051633ee5aeb560e01b815260040160405180910390fd5b6002600355565b6001600160a01b0381165f90815260126020526040902054801561125c57612a56828261403a565b60045461125c9061010090046001600160a01b03165b838361406a565b5f80612a7e60115490565b90505f612a8a600f5490565b9050805f03612a9b575f9250505090565b808210612aa85792915050565b5f612ab283612b8b565b905080602001515f03612ac757509092915050565b80604001518160200151612adb9190615b4a565b4210612af457612aec836001615b4a565b935050505090565b8060600151816080015110612b0e57612aec836001615b4a565b509092915050565b60408051602081018a90526001600160a01b03808a1692820192909252606081018890526080810187905290851660a082015260c0810184905260e081018390525f90612b7e90610100015b60405160208183030381529060405280519060200120836140f6565b9998505050505050505050565b612bba6040518060a001604052805f151581526020015f81526020015f81526020015f81526020015f81525090565b505f908152600d6020908152604091829020825160a081018452815460ff161515815260018201549281019290925260028101549282019290925260038201546060820152600490910154608082015290565b6001600160a01b03165f908152600a602052604090205460ff1690565b6119788484848461411e565b612c408282614223565b612c48613ff2565b158015612c575750600c5460ff165b8015612c675750612c6782612c0d565b1561125c5761125c826136ac565b5f6001600160a01b038216612c8b575047919050565b6040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015612ccd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111bd9190615b70565b919050565b5f612d00600f5490565b90911015919050565b5f858152600d60205260409020805460ff191685151517815560115486148015612d3657505f8160010154115b8015612d46575080600101544210155b15612d645760405163795792b760e01b815260040160405180910390fd5b5f86118015612d7257505f84115b15612dc3575f612d8b612d86600189615b5d565b612b8b565b905080604001518160200151612da19190615b4a565b851015612dc15760405163795792b760e01b815260040160405180910390fd5b505b60018101849055600281018390556004810154821015612df657604051632f56c88b60e11b815260040160405180910390fd5b600381018290556040805160a081018252825460ff1615158152600183015460208201526002830154918101919091526060810183905260048201546080820152612e429087906142d7565b505050505050565b8051825114612e6c576040516328e7033560e01b815260040160405180910390fd5b5f5b825181101561197857612e99838281518110612e8c57612e8c615b0a565b602002602001015161271a565b818181518110612eab57612eab615b0a565b6020026020010151600e5f8681526020019081526020015f205f858481518110612ed757612ed7615b0a565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f20819055507f28af1bd3648c38edc812346775bcc36a5f5fc9870d47c8c69a0e2ec0445afaae84848381518110612f3657612f36615b0a565b6020026020010151848481518110612f5057612f50615b0a565b6020026020010151604051612f81939291909283526001600160a01b03919091166020830152604082015260600190565b60405180910390a180612f9381615b32565b915050612e6e565b601580546001600160a01b0319166001600160a01b0383169081179091556040519081527ff4e654cefbf8c2fe608d6868b30dfaf564ff18be7ac7404b5c06b2667f8a4608906020016129d0565b6130026001600160a01b0387168930888888888861277f565b61120b8888888861411e565b5f918252600e602090815260408084206001600160a01b03909316845291905290205490565b61303d816126da565b90505f5b815181101561161157600e5f8481526020019081526020015f205f83838151811061306e5761306e615b0a565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f205f90557f28af1bd3648c38edc812346775bcc36a5f5fc9870d47c8c69a0e2ec0445afaae838383815181106130cc576130cc615b0a565b60200260200101515f6040516130fe939291909283526001600160a01b03919091166020830152604082015260600190565b60405180910390a18061311081615b32565b915050613041565b8060ff165f0361313b57604051638bd9be8160e01b815260040160405180910390fd5b600280546001600160a01b0384166001600160a81b03199091168117600160a01b60ff8516908102919091179092556040805191825260208201929092527fc9cbf5af9e78df9093a676978e83f2a04e258ad3b0d8fa81cb44b357c657606a91015b60405180910390a15050565b6001600160a01b0382166131ca576116116001600160a01b03841682614333565b6116116001600160a01b03831684836143c6565b6131e6614425565b6001600160a01b038087165f9081526014602052604090205460135490916101009091041661323b7f000000000000000000000000000000000000000000000000000000000000000189848a8a8a8a8a612b16565b6001600160a01b0316146132625760405163ea8e4eb560e01b815260040160405180910390fd5b6001600160a01b0387165f90815260146020526040812080546001929061328a908490615b4a565b90915550506040518181526001600160a01b038816907f3e266ebc4c4f97d954ad3c456dd72416a5e3a203f706c4d0682085901b1ad0ed9060200160405180910390a250505050505050565b826132e08161271a565b846132e961386b565b601154811461330b57604051633b47fcf960e11b815260040160405180910390fd5b5f61331582612b8b565b9050806060015181608001511061333f57604051633b47fcf960e11b815260040160405180910390fd5b80602001515f0361336357604051633b47fcf960e11b815260040160405180910390fd5b80602001514210806133885750806040015181602001516133849190615b4a565b4210155b156133a657604051633b47fcf960e11b815260040160405180910390fd5b6133ae613984565b5f806133bb898888614453565b915091506133ca8a898361448d565b611b078a8a8a85858c614646565b5f6133ea826133e5613b43565b6146ba565b6005549091505f906133fe90600190615b5d565b90505f6005828154811061341457613414615b0a565b5f91825260209091200154600580546001600160a01b03909216925082918590811061344257613442615b0a565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b03160217905550600580548061347e5761347e615bff565b5f828152602080822083015f1990810180546001600160a01b03191690559092019092556001600160a01b03959095168152600690945250506040909120805460ff1916905550565b6134d2838383612644565b61161157604051630b094f2760e31b815260040160405180910390fd5b6001600160a01b0381165f818152600a6020526040808220805460ff19166001179055517fd5a6b3454d1aa211b5b7e99c94012bed9883c7695b5c58fd97e20f425ec0b5bf9190a250565b5f61354483614736565b613562828461355d60025460ff600160a01b9091041690565b614756565b90506111bd81614776565b60138054610100600160a81b0319166101006001600160a01b038416908102919091179091556040519081527f5553331329228fbd4123164423717a4a7539f6dfa1c3279a923b98fd681a6c73906020016129d0565b600180546001600160a01b0319169055610ef881614796565b600b81905560405181907f90004c04698bc3322499a575ed3752dd4abf33e0a7294c06a787a0fe01bea941905f90a250565b60118190556040518181527fc7941f8838c3de40641130e81ef820f45856796923e29f642fb8680ea13c1a3b906020016129d0565b5f61364d83614736565b613562828461366660025460ff600160a01b9091041690565b6147e5565b6009805460ff19168215159081179091556040519081527f080b99e58282ae4610da5730d7f94b53b3a2e6c83e0a471b89874fdd2e33515f906020016129d0565b6001600160a01b0381165f818152600a6020526040808220805460ff19169055517f79bc96e8fb893a32b4429ae9fd9084cdb98be910b9c7cdbb4307cc96732c7ec79190a250565b600c805460ff19168215159081179091556040519081527f2e66025596ef0553cb2e878a0f141264297c58f317ab613bf6dbe35a8be44cf3906020016129d0565b6013805460ff19168215159081179091556040519081527fcbb1b2ab6aab49b57a6ea192e0596dffffe7c371b29eb63ef80bd5840daa9bb5906020016129d0565b5f80613782858561300e565b9050612775818461353a565b60408051602081018890526001600160a01b03871691810191909152606081018590526080810184905260a081018390525f906137cd9060c001612b62565b979650505050505050565b6137e0613984565b6004805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586138153390565b6040516001600160a01b03909116815260200160405180910390a1565b61383a6147fc565b6004805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33613815565b5f61387560115490565b90505f613880612a73565b90508181111561125c576138938161360e565b5f818152600d602052604081206001810154909103611611575f6138bb612d86600185615b5d565b9050806060015181608001511061391c5742600183018190556040805160a081018252845460ff1615158152602081019290925260028401549082015260038301546060820152600483015460808201526139179084906142d7565b611978565b602081015115611978578060400151816020015161393a9190615b4a565b600183018190556040805160a081018252845460ff1615158152602081019290925260028401549082015260038301546060820152600483015460808201526119789084906142d7565b60045460ff1615611bd65760405163d93c066560e01b815260040160405180910390fd5b6139b0614425565b6001600160a01b038085165f90815260146020526040902054601354909161010090910416613a037f0000000000000000000000000000000000000000000000000000000000000001878488888861378e565b6001600160a01b031614613a2a5760405163ea8e4eb560e01b815260040160405180910390fd5b6001600160a01b0385165f908152601460205260408120805460019290613a52908490615b4a565b90915550506040518181526001600160a01b038616907f3e266ebc4c4f97d954ad3c456dd72416a5e3a203f706c4d0682085901b1ad0ed9060200160405180910390a25050505050565b5f80613aa78461481f565b905080831115612b0e579392505050565b613ac28282614840565b613aca61490d565b60405163d279792d60e01b81526001600160a01b0385811660048301526024820185905260448201849052919091169063d279792d906064016020604051808303815f875af1158015613b1f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119789190615c13565b60606005805480602002602001604051908101604052809291908181526020018280548015613b9957602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311613b7b575b5050505050905090565b613bbc6001600160a01b0388168a30888888888861277f565b6112d889898989896132d6565b5f5b815181101561125c575f828281518110613be757613be7615b0a565b60200260200101519050613c12816001600160a01b03165f9081526006602052604090205460ff1690565b613cad576005805460018082019092557f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00180546001600160a01b0319166001600160a01b0384169081179091555f81815260066020908152604091829020805460ff1916909417909355519081527fa317c10673baf4f03b3c1041bd5ddbb537d0333a86fec3607c75f9dbb630f48f910160405180910390a15b5080613cb881615b32565b915050613bcb565b6016805461ffff191685151561ff00198116919091176101008615159081029190911763ffff000019166201000086151590810263ff0000001916919091176301000000861515908102919091179094556040805193845260208401929092529082015260608101919091527fae3d63e035fb1d35cb2faa50509104c27a5f07f4fa920f7a05bd31ed4f5e0b0d9060800160405180910390a150505050565b5f82613d6b8584614948565b14949350505050565b81613d7e8161271a565b613d86613984565b60095460ff16613da957604051630e2f42c960e31b815260040160405180910390fd5b815f03613dc957604051632160733960e01b815260040160405180910390fd5b6001600160a01b038316613dfc57813414613df757604051632160733960e01b815260040160405180910390fd5b613f36565b3415613e1b57604051632160733960e01b815260040160405180910390fd5b6040516370a0823160e01b815230600482015283905f906001600160a01b038316906370a0823190602401602060405180830381865afa158015613e61573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613e859190615b70565b9050613e9c6001600160a01b038316873087614994565b6040516370a0823160e01b81523060048201525f9082906001600160a01b038516906370a0823190602401602060405180830381865afa158015613ee2573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613f069190615b70565b613f109190615b5d565b9050848114613f3257604051632160733960e01b815260040160405180910390fd5b5050505b6001600160a01b0383165f9081526007602052604081208054849290613f5d908490615b4a565b90915550506001600160a01b038085165f90815260086020908152604080832093871683529290529081208054849290613f98908490615b4a565b92505081905550826001600160a01b0316846001600160a01b03167fc67994043444b22a702bf75a242bcc1720705569fb433fe91272cbf8d0d99c1084604051613fe491815260200190565b60405180910390a350505050565b5f80613ffd600f5490565b1115614035575f61400d5f612b8b565b90505f8160200151118015614026575080602001514210155b1561403357600191505090565b505b505f90565b6001600160a01b0382165f9081526012602052604081208054839290614061908490615b5d565b90915550505050565b6001600160a01b0382166140905761408b6001600160a01b03841682614333565b6140a4565b6140a46001600160a01b03831684836143c6565b816001600160a01b0316836001600160a01b03167fa77f6af926d34e43c5b52329799fa34e6d594dea2777723745b977d1d6e271c3836040516140e991815260200190565b60405180910390a3505050565b5f805f8061410486866149cd565b9250925092506141148282614a16565b5090949350505050565b816141288161271a565b8361413161386b565b601154811461415357604051633b47fcf960e11b815260040160405180910390fd5b5f61415d82612b8b565b9050806060015181608001511061418757604051633b47fcf960e11b815260040160405180910390fd5b80602001515f036141ab57604051633b47fcf960e11b815260040160405180910390fd5b80602001514210806141d05750806040015181602001516141cc9190615b4a565b4210155b156141ee57604051633b47fcf960e11b815260040160405180910390fd5b6141f6613984565b5f805f614204898989614ace565b9250925092506142158a898461448d565b611b078a8a8a868686614646565b5f5b8151811015611611575f82828151811061424157614241615b0a565b602002602001015190505f61425685836126f0565b905080156142c257614269858383614b11565b6142748583836131a9565b816001600160a01b0316856001600160a01b03167f933380dab6ffd07b472a6e643e8349744d3263e3b449d12dcd616b69c7312f35836040516142b991815260200190565b60405180910390a35b505080806142cf90615b32565b915050614225565b80516020808301516040808501516060808701518351898152961515958701959095529185019290925283015260808201527f600c4518738a76b007c7da3f323d8a50d668dde43b2b5f2fd3cd4689c1e261339060a00161319d565b804710156143565760405163cd78605960e01b8152306004820152602401611c0c565b5f826001600160a01b0316826040515f6040518083038185875af1925050503d805f811461439f576040519150601f19603f3d011682016040523d82523d5f602084013e6143a4565b606091505b505090508061161157604051630a12f52160e11b815260040160405180910390fd5b6040516001600160a01b0383811660248301526044820183905261161191859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050614ba7565b60135461010090046001600160a01b0316611bd65760405163327effa960e11b815260040160405180910390fd5b5f8161445f8482613643565b91505f61446b8661481f565b905080831115610e8557809250614482858461353a565b915050935093915050565b805f61449985856126f0565b905080156144d0575f8382106144b357505f9150826144c3565b6144bd8285615b5d565b92508190505b6144ce868683614b11565b505b81156110aa576001600160a01b03841661452f57813410156145055760405163356680b760e01b815260040160405180910390fd5b8134111561452a5761452a61451a8334615b5d565b6001600160a01b03871690614333565b6110aa565b6040516370a0823160e01b815230600482015284905f906001600160a01b038316906370a0823190602401602060405180830381865afa158015614575573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906145999190615b70565b90506145b06001600160a01b038316883087614994565b6040516370a0823160e01b81523060048201525f9082906001600160a01b038516906370a0823190602401602060405180830381865afa1580156145f6573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061461a9190615b70565b6146249190615b5d565b905084811461120b57604051632160733960e01b815260040160405180910390fd5b6146508483614c08565b61465b868685613ab8565b836001600160a01b031685876001600160a01b03167f62e796e00a8e66154d78da76daae129635b4795a6e1b889f2caa6c5cea22ac6884876040516146aa929190918252602082015260400190565b60405180910390a4505050505050565b5f805b825181101561471157836001600160a01b03168382815181106146e2576146e2615b0a565b60200260200101516001600160a01b0316036146ff5790506111bd565b8061470981615b32565b9150506146bd565b50604051631e50c79f60e01b81526001600160a01b0384166004820152602401611c0c565b805f03610ef85760405163e9589bbd60e01b815260040160405180910390fd5b5f61476282600a615d06565b61476c8486615d14565b610da79190615d2b565b805f03610ef8576040516334cf99ff60e11b815260040160405180910390fd5b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f826147f283600a615d06565b61476c9086615d14565b60045460ff16611bd657604051638dfc202b60e01b815260040160405180910390fd5b5f8061482a83612b8b565b905080608001518160600151610daa9190615b5d565b5f828152600d6020526040812090508160105f8282546148609190615b4a565b9250508190555081816004015f82825461487a9190615b4a565b90915550506003810154600482015410611611575f601154600161489e9190615b4a565b90506148a98161360e565b5f818152600d6020526040812060018101549091036110aa5742600182018190556040805160a081018252835460ff1615158152602081019290925260028301549082015260038201546060820152600482015460808201526110aa9083906142d7565b6015545f906001600160a01b03166149385760405163309a778560e11b815260040160405180910390fd5b506015546001600160a01b031690565b5f81815b845181101561498c576149788286838151811061496b5761496b615b0a565b6020026020010151614ca8565b91508061498481615b32565b91505061494c565b509392505050565b6040516001600160a01b0384811660248301528381166044830152606482018390526119789186918216906323b872dd906084016143f3565b5f805f8351604103614a04576020840151604085015160608601515f1a6149f688828585614cd4565b955095509550505050614a0f565b505081515f91506002905b9250925092565b5f826003811115614a2957614a29615d4a565b03614a32575050565b6001826003811115614a4657614a46615d4a565b03614a645760405163f645eedf60e01b815260040160405180910390fd5b6002826003811115614a7857614a78615d4a565b03614a995760405163fce698f760e01b815260048101829052602401611c0c565b6003826003811115614aad57614aad615d4a565b0361125c576040516335e2f38360e21b815260048101829052602401611c0c565b5f8181614adc86868461275d565b90935090505f614aeb8761481f565b905080841115614b0757809350614b03878786613776565b5092505b5093509350939050565b614b1b83836126f0565b811115614b3b57604051631650c97f60e11b815260040160405180910390fd5b6001600160a01b0382165f9081526007602052604081208054839290614b62908490615b5d565b90915550506001600160a01b038084165f90815260086020908152604080832093861683529290529081208054839290614b9d908490615b5d565b9091555050505050565b5f614bbb6001600160a01b03841683614d9c565b905080515f14158015614bdf575080806020019051810190614bdd9190615c13565b155b1561161157604051635274afe760e01b81526001600160a01b0384166004820152602401611c0c565b816001600160a01b03167fd98279195c58b6b93d53ab20cbd8ad7d353cfdcbcc0f514dc4a0f114f1e3f0e682604051614c4391815260200190565b60405180910390a260135460ff168015614c625750614c606129db565b155b15614c815760045461125c9061010090046001600160a01b0316612a6c565b6001600160a01b0382165f9081526012602052604081208054839290614061908490615b4a565b5f818310614cc2575f828152602084905260409020610daa565b5f838152602083905260409020610daa565b5f80807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115614d0d57505f91506003905082614d92565b604080515f808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015614d5e573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b038116614d8957505f925060019150829050614d92565b92505f91508190505b9450945094915050565b6060610daa83835f845f80856001600160a01b03168486604051614dc09190615d5e565b5f6040518083038185875af1925050503d805f8114614dfa576040519150601f19603f3d011682016040523d82523d5f602084013e614dff565b606091505b509150915061101a868383606082614e1f57614e1a82614e66565b610daa565b8151158015614e3657506001600160a01b0384163b155b15614e5f57604051639996b31560e01b81526001600160a01b0385166004820152602401611c0c565b5080610daa565b805115614e765780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b80356001600160a01b0381168114612cf1575f80fd5b5f8083601f840112614eb5575f80fd5b5081356001600160401b03811115614ecb575f80fd5b6020830191508360208260051b8501011115614ee5575f80fd5b9250929050565b5f805f60408486031215614efe575f80fd5b614f0784614e8f565b925060208401356001600160401b03811115614f21575f80fd5b614f2d86828701614ea5565b9497909650939450505050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b0381118282101715614f7657614f76614f3a565b604052919050565b5f6001600160401b03821115614f9657614f96614f3a565b5060051b60200190565b5f82601f830112614faf575f80fd5b81356020614fc4614fbf83614f7e565b614f4e565b82815260059290921b84018101918181019086841115614fe2575f80fd5b8286015b8481101561500457614ff781614e8f565b8352918301918301614fe6565b509695505050505050565b5f8060408385031215615020575f80fd5b61502983614e8f565b915060208301356001600160401b03811115615043575f80fd5b61504f85828601614fa0565b9150509250929050565b5f8151808452602080850194508084015f5b838110156150905781516001600160a01b03168752958201959082019060010161506b565b509495945050505050565b5f8151808452602080850194508084015f5b83811015615090578151875295820195908201906001016150ad565b604081525f6150db6040830185615059565b82810360208401526126d1818561509b565b5f805f606084860312156150ff575f80fd5b8335925061510f60208501614e8f565b9150604084013590509250925092565b803560ff81168114612cf1575f80fd5b5f805f805f8060c08789031215615144575f80fd5b61514d87614e8f565b955060208701359450604087013593506151696060880161511f565b92506080870135915060a087013590509295509295509295565b5f60208284031215615193575f80fd5b610daa82614e8f565b5f602082840312156151ac575f80fd5b81356001600160401b038111156151c1575f80fd5b611fad84828501614fa0565b5f82601f8301126151dc575f80fd5b81356001600160401b038111156151f5576151f5614f3a565b615208601f8201601f1916602001614f4e565b81815284602083860101111561521c575f80fd5b816020850160208301375f918101602001919091529392505050565b5f805f805f8060c0878903121561524d575f80fd5b61525687614e8f565b95506020870135945061526b60408801614e8f565b9350606087013592506080870135915060a08701356001600160401b03811115615293575f80fd5b61529f89828a016151cd565b9150509295509295509295565b5f5b838110156152c65781810151838201526020016152ae565b50505f910152565b602081525f82518060208401526152ec8160408501602087016152ac565b601f01601f19169190910160400192915050565b8015158114610ef8575f80fd5b5f82601f83011261531c575f80fd5b8135602061532c614fbf83614f7e565b82815260059290921b8401810191818101908684111561534a575f80fd5b8286015b84811015615004578035835291830191830161534e565b5f805f805f805f60e0888a03121561537b575f80fd5b87359650602088013561538d81615300565b955060408801359450606088013593506080880135925060a08801356001600160401b03808211156153bd575f80fd5b6153c98b838c01614fa0565b935060c08a01359150808211156153de575f80fd5b506153eb8a828b0161530d565b91505092959891949750929550565b5f806040838503121561540b575f80fd5b61541483614e8f565b946020939093013593505050565b5f805f805f805f60e0888a031215615438575f80fd5b8735965061544860208901614e8f565b955060408801359450606088013593506154646080890161511f565b925060a0880135915060c0880135905092959891949750929550565b5f8060408385031215615491575f80fd5b82356001600160401b03808211156154a7575f80fd5b6154b386838701614fa0565b935060208501359150808211156154c8575f80fd5b5061504f85828601614fa0565b5f602082840312156154e5575f80fd5b5035919050565b871515815286602082015285604082015284606082015283608082015260e060a08201525f61551e60e0830185615059565b82810360c0840152615530818561509b565b9a9950505050505050505050565b5f806040838503121561554f575f80fd5b61555883614e8f565b91506155666020840161511f565b90509250929050565b5f805f60608486031215615581575f80fd5b61558a84614e8f565b925061510f60208501614e8f565b5f80602083850312156155a9575f80fd5b82356001600160401b038111156155be575f80fd5b6155ca85828601614ea5565b90969095509350505050565b5f805f805f60a086880312156155ea575f80fd5b853594506155fa60208701614e8f565b9350604086013592506060860135915060808601356001600160401b03811115615622575f80fd5b61562e888289016151cd565b9150509295509295909350565b5f805f805f6080868803121561564f575f80fd5b8535945061565f60208701614e8f565b93506040860135925060608601356001600160401b03811115615680575f80fd5b61568c88828901614ea5565b969995985093965092949392505050565b5f80604083850312156156ae575f80fd5b50508035926020909101359150565b5f80604083850312156156ce575f80fd5b8235915060208301356001600160401b03811115615043575f80fd5b5f602082840312156156fa575f80fd5b8135610daa81615300565b5f805f8060808587031215615718575f80fd5b61572185614e8f565b9350602085013592506040850135915060608501356001600160401b03811115615749575f80fd5b615755878288016151cd565b91505092959194509250565b5f805f60608486031215615773575f80fd5b833592506020840135915060408401356001600160401b03811115615796575f80fd5b6157a2868287016151cd565b9150509250925092565b602081525f610daa6020830184615059565b5f6020808301818452808551808352604092508286019150828160051b8701018488015f805b8481101561589957898403603f19018652825180511515855288810151898601528781015188860152606080820151908601526080808201519086015260a08082015160e09187018290528051918701829052610100870191908b0190855b818110156158685782516001600160a01b03168452928c0192918c0191600101615843565b50505060c08083015192508682038188015250615885818361509b565b978a019795505050918701916001016157e4565b50919998505050505050505050565b5f805f606084860312156158ba575f80fd5b8335925060208401356001600160401b03808211156158d7575f80fd5b6158e387838801614fa0565b935060408601359150808211156158f8575f80fd5b506157a28682870161530d565b5f805f805f805f805f60e08a8c03121561591d575f80fd5b8935985060208a013561592f81615300565b975060408a0135965060608a0135955060808a0135945060a08a01356001600160401b038082111561595f575f80fd5b61596b8d838e01614ea5565b909650945060c08c0135915080821115615983575f80fd5b506159908c828d01614ea5565b915080935050809150509295985092959850929598565b5f805f805f805f805f6101208a8c0312156159c0575f80fd5b893598506159d060208b01614e8f565b975060408a0135965060608a0135955060808a013594506159f360a08b0161511f565b935060c08a0135925060e08a013591506101008a01356001600160401b03811115615a1c575f80fd5b615a288c828d016151cd565b9150509295985092959850929598565b5f805f805f805f805f6101008a8c031215615a51575f80fd5b89359850615a6160208b01614e8f565b975060408a0135965060608a01359550615a7d60808b0161511f565b945060a08a0135935060c08a0135925060e08a01356001600160401b03811115615aa5575f80fd5b6159908c828d01614ea5565b5f805f8060808587031215615ac4575f80fd5b8435615acf81615300565b93506020850135615adf81615300565b92506040850135615aef81615300565b91506060850135615aff81615300565b939692955090935050565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b5f60018201615b4357615b43615b1e565b5060010190565b808201808211156111bd576111bd615b1e565b818103818111156111bd576111bd615b1e565b5f60208284031215615b80575f80fd5b5051919050565b5f81615b9557615b95615b1e565b505f190190565b5f823560de19833603018112615bb0575f80fd5b9190910192915050565b5f808335601e19843603018112615bcf575f80fd5b8301803591506001600160401b03821115615be8575f80fd5b6020019150600581901b3603821315614ee5575f80fd5b634e487b7160e01b5f52603160045260245ffd5b5f60208284031215615c23575f80fd5b8151610daa81615300565b600181815b80851115610e6157815f1904821115615c4e57615c4e615b1e565b80851615615c5b57918102915b93841c9390800290615c33565b5f82615c76575060016111bd565b81615c8257505f6111bd565b8160018114615c985760028114615ca257615cbe565b60019150506111bd565b60ff841115615cb357615cb3615b1e565b50506001821b6111bd565b5060208310610133831016604e8410600b8410161715615ce1575081810a6111bd565b615ceb8383615c2e565b805f1904821115615cfe57615cfe615b1e565b029392505050565b5f610daa60ff841683615c68565b80820281158282048414176111bd576111bd615b1e565b5f82615d4557634e487b7160e01b5f52601260045260245ffd5b500490565b634e487b7160e01b5f52602160045260245ffd5b5f8251615bb08184602087016152ac56fea2646970667358221220c52dc068b18ad600a3ef08184aef45b97e64aa1b94a361345300f95f628ef71564736f6c63430008140033
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ 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.