Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Loading...
Loading
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"}]
Contract Creation Code
60a060405234801562000010575f80fd5b5060405162006440380380620064408339810160408190526200003391620005e2565b5f8080808080808080808060405190808252806020026020018201604052801562000068578160200160208202803683370190505b505f8d8d33806200009257604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b6200009d816200013a565b50620000aa828262000158565b505060016003556004805460ff19169055620000c681620001e9565b50620000d28162000246565b50620000de8162000339565b50620000ea826200037b565b620000f581620003bd565b5062000103905081620003ef565b5046608052620001138162000431565b506200011f8162000488565b506200012e84848484620004d7565b50505050505062000666565b600180546001600160a01b0319169055620001558162000576565b50565b8060ff165f036200017c57604051638bd9be8160e01b815260040160405180910390fd5b600280546001600160a01b0384166001600160a81b03199091168117600160a01b60ff8516908102919091179092556040805191825260208201929092527fc9cbf5af9e78df9093a676978e83f2a04e258ad3b0d8fa81cb44b357c657606a910160405180910390a15050565b60048054610100600160a81b0319166101006001600160a01b038416908102919091179091556040519081527ff6215f245bfd24e51265c56ef650fdd856aa4ece6221ee1ef395bbe0a5558010906020015b60405180910390a150565b5f5b815181101562000335575f8282815181106200026857620002686200062d565b602002602001015190506200028381620005c560201b60201c565b6200031f576005805460018082019092557f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00180546001600160a01b0319166001600160a01b0384169081179091555f81815260066020908152604091829020805460ff1916909417909355519081527fa317c10673baf4f03b3c1041bd5ddbb537d0333a86fec3607c75f9dbb630f48f910160405180910390a15b50806200032c8162000641565b91505062000248565b5050565b6009805460ff19168215159081179091556040519081527f080b99e58282ae4610da5730d7f94b53b3a2e6c83e0a471b89874fdd2e33515f906020016200023b565b600c805460ff19168215159081179091556040519081527f2e66025596ef0553cb2e878a0f141264297c58f317ab613bf6dbe35a8be44cf3906020016200023b565b600b81905560405181907f90004c04698bc3322499a575ed3752dd4abf33e0a7294c06a787a0fe01bea941905f90a250565b6013805460ff19168215159081179091556040519081527fcbb1b2ab6aab49b57a6ea192e0596dffffe7c371b29eb63ef80bd5840daa9bb5906020016200023b565b60138054610100600160a81b0319166101006001600160a01b038416908102919091179091556040519081527f5553331329228fbd4123164423717a4a7539f6dfa1c3279a923b98fd681a6c73906020016200023b565b601580546001600160a01b0319166001600160a01b0383169081179091556040519081527ff4e654cefbf8c2fe608d6868b30dfaf564ff18be7ac7404b5c06b2667f8a4608906020016200023b565b6016805461ffff191685151561ff00198116919091176101008615159081029190911763ffff000019166201000086151590810263ff0000001916919091176301000000861515908102919091179094556040805193845260208401929092529082015260608101919091527fae3d63e035fb1d35cb2faa50509104c27a5f07f4fa920f7a05bd31ed4f5e0b0d9060800160405180910390a150505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f8060408385031215620005f4575f80fd5b82516001600160a01b03811681146200060b575f80fd5b602084015190925060ff8116811462000622575f80fd5b809150509250929050565b634e487b7160e01b5f52603260045260245ffd5b5f600182016200065f57634e487b7160e01b5f52601160045260245ffd5b5060010190565b608051615da56200069b5f395f81816105eb01528181610fd501528181611f6a0152818161321001526139da0152615da55ff3fe608060405260043610610416575f3560e01c806370c1354511610220578063ad9ebcfd11610129578063d64b650c116100b3578063ec3601d011610078578063ec3601d014610ccc578063f0c4361314610ceb578063f2fde38b14610d0a578063f311758014610d29578063fdf1087b14610d7c575f80fd5b8063d64b650c14610c16578063d9b51fce14610c35578063e30c397814610c71578063e548922714610c8e578063ea3fd12114610cad575f80fd5b8063bedb86fb116100f9578063bedb86fb14610b77578063c3c027b114610b96578063ca5e553e14610bb5578063cbf3b2a714610bd6578063d04899e914610bf7575f80fd5b8063ad9ebcfd14610afc578063b223e35814610b1b578063b3c1c00d14610b44578063b57b179f14610b58575f80fd5b806387ae3a22116101aa578063960697571161017a5780639606975714610a615780639f04586c14610a80578063a1ac390714610a9f578063a991411214610abe578063aa1078ee14610add575f80fd5b806387ae3a22146109e85780638da5cb5b14610a07578063930eaddc14610a2357806393aa4bd714610a42575f80fd5b80637ac3c02f116101f05780637ac3c02f1461093e5780637bbfbcfa146109745780637cb647591461098b5780637d7cd772146109aa5780638136f590146109c9575f80fd5b806370c13545146108cb578063715018a6146108df57806372172364146108f357806379ba50971461092a575f80fd5b806347e7ef24116103225780635ce60c6a116102ac57806364e60ef41161027c57806364e60ef41461082757806369e1f1c3146108465780636a92df6a146108595780636af9c8fb146108785780636c19e783146108ac575f80fd5b80635ce60c6a146107b75780635d3590d5146107d65780635d5c49a6146107f557806363711ff114610814575f80fd5b80634b389c00116102f25780634b389c001461072f578063573aa7d51461074357806357742d27146107755780635777bf50146107895780635c975abb146107a0575f80fd5b806347e7ef24146106ca578063480d5703146106dd57806349590657146106fc5780634af5e79114610710575f80fd5b80632afaca20116103a357806334383465116103735780633438346514610619578063387e45121461064e5780633af32abf1461066d5780634234fb941461068c57806343e34696146106ab575f80fd5b80632afaca20146105975780632cc6ca69146105aa57806331da3f25146105c95780633408e470146105dd575f80fd5b806312a62cff116103e957806312a62cff146104ea5780631525ff7d1461050b5780631631bdcb1461052a57806325bdb2a814610549578063267bbcc614610578575f80fd5b8063019dcf2c1461041a5780630474761e1461044e5780630759a3541461047b5780630e3e2ea4146104b6575b5f80fd5b348015610425575f80fd5b50610439610434366004614eec565b610d9b565b60405190151581526020015b60405180910390f35b348015610459575f80fd5b5061046d61046836600461500f565b610db1565b6040516104459291906150c9565b348015610486575f80fd5b5060045460408051600160a81b830460ff16151581526101009092046001600160a01b0316602083015201610445565b3480156104c1575f80fd5b506104d56104d03660046150ed565b610e69565b60408051928352602083019190915201610445565b3480156104f5575f80fd5b5061050961050436600461512f565b610e8e565b005b348015610516575f80fd5b50610509610525366004615183565b610ebc565b348015610535575f80fd5b5061050961054436600461519c565b610efb565b348015610554575f80fd5b5061055d610f86565b60408051938452602084019290925290820152606001610445565b348015610583575f80fd5b50610439610592366004615238565b610fa8565b6105096105a53660046150ed565b611024565b3480156105b5575f80fd5b506105096105c436600461519c565b6110b1565b3480156105d4575f80fd5b506104396110d8565b3480156105e8575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000005b604051908152602001610445565b348015610624575f80fd5b50604080518082019091526004815263199d5b1b60e21b60208201525b60405161044591906152ce565b348015610659575f80fd5b5061060b610668366004615183565b6110ea565b348015610678575f80fd5b50610439610687366004615183565b6111b3565b348015610697575f80fd5b506105096106a6366004615365565b6111c3565b3480156106b6575f80fd5b506105096106c5366004615183565b611215565b6105096106d83660046153fa565b611251565b3480156106e8575f80fd5b506105096106f7366004615422565b611260565b348015610707575f80fd5b50600b5461060b565b34801561071b575f80fd5b5061050961072a366004615480565b6112e3565b34801561073a575f80fd5b50610509611349565b34801561074e575f80fd5b5061076261075d3660046154d5565b6113e2565b60405161044597969594939291906154ec565b348015610780575f80fd5b506105096114fb565b348015610794575f80fd5b5060095460ff16610439565b3480156107ab575f80fd5b5060045460ff16610439565b3480156107c2575f80fd5b506105096107d136600461553e565b611616565b3480156107e1575f80fd5b506104396107f036600461556f565b611653565b348015610800575f80fd5b5061050961080f366004615598565b61171f565b6105096108223660046155d6565b61197e565b348015610832575f80fd5b5061050961084136600461519c565b6119d8565b61050961085436600461563b565b611a87565b348015610864575f80fd5b5061060b61087336600461569d565b611b13565b348015610883575f80fd5b5061060b610892366004615183565b6001600160a01b03165f9081526014602052604090205490565b3480156108b7575f80fd5b506105096108c6366004615183565b611b1e565b3480156108d6575f80fd5b50610509611b2f565b3480156108ea575f80fd5b50610509611bc5565b3480156108fe575f80fd5b50601554604080516001600160a01b0383168152600160a01b90920460ff161515602083015201610445565b348015610935575f80fd5b50610509611bd8565b348015610949575f80fd5b5060135461010090046001600160a01b03165b6040516001600160a01b039091168152602001610445565b34801561097f575f80fd5b5060135460ff16610439565b348015610996575f80fd5b506105096109a53660046154d5565b611c1e565b3480156109b5575f80fd5b506105096109c4366004615598565b611c2f565b3480156109d4575f80fd5b506105096109e33660046154d5565b611c82565b3480156109f3575f80fd5b50610509610a023660046156bd565b611cc7565b348015610a12575f80fd5b505f546001600160a01b031661095c565b348015610a2e575f80fd5b50610439610a3d366004615183565b611d01565b348015610a4d575f80fd5b5061046d610a5c36600461519c565b611d21565b348015610a6c575f80fd5b5061060b610a7b36600461569d565b611deb565b348015610a8b575f80fd5b50610509610a9a3660046156ea565b611df6565b348015610aaa575f80fd5b50610509610ab9366004615598565b611e07565b348015610ac9575f80fd5b50610509610ad83660046156ea565b611e5a565b348015610ae8575f80fd5b50610509610af73660046156ea565b611e6b565b348015610b07575f80fd5b506104d5610b163660046150ed565b611e7c565b348015610b26575f80fd5b506040805180820190915260018152603160f81b6020820152610641565b348015610b4f575f80fd5b50610509611e93565b348015610b63575f80fd5b50610439610b72366004615705565b611f3d565b348015610b82575f80fd5b50610509610b913660046156ea565b611fb5565b348015610ba1575f80fd5b50610509610bb0366004615761565b611fd3565b348015610bc0575f80fd5b50610bc9612111565b60405161044591906157ac565b348015610be1575f80fd5b50610bea61211b565b60405161044591906157be565b348015610c02575f80fd5b50610509610c113660046158a8565b6122ea565b348015610c21575f80fd5b50610509610c30366004615905565b612325565b348015610c40575f80fd5b50610c496123dc565b6040805193151584526001600160a01b03909216602084015260ff1690820152606001610445565b348015610c7c575f80fd5b506001546001600160a01b031661095c565b348015610c99575f80fd5b50610509610ca83660046159a7565b61241e565b348015610cb8575f80fd5b50610509610cc736600461519c565b612469565b348015610cd7575f80fd5b50610509610ce6366004615a38565b61247a565b348015610cf6575f80fd5b5061046d610d0536600461519c565b6124fc565b348015610d15575f80fd5b50610509610d24366004615183565b6125c0565b348015610d34575f80fd5b506016546040805160ff808416151582526101008404811615156020830152620100008404811615159282019290925263010000009092041615156060820152608001610445565b348015610d87575f80fd5b50610509610d96366004615ab1565b612630565b5f610da7848484612644565b90505b9392505050565b606080610dbd836126da565b915081516001600160401b03811115610dd857610dd8614f3a565b604051908082528060200260200182016040528015610e01578160200160208202803683370190505b5090505f5b8251811015610e6157610e3285848381518110610e2557610e25615b0a565b60200260200101516126f0565b828281518110610e4457610e44615b0a565b602090810291909101015280610e5981615b32565b915050610e06565b509250929050565b5f8083610e758161271a565b610e8086868661275d565b925092505b50935093915050565b33610ea86001600160a01b0388168230898989898961277f565b610eb3818888612910565b50505050505050565b610ec4612952565b600454600160a81b900460ff1615610eef576040516330cc939360e11b815260040160405180910390fd5b610ef88161297e565b50565b610f036129db565b15610f2157604051635b0999e760e01b815260040160405180910390fd5b610f29612952565b610f31612a04565b610f3a816126da565b90505f5b8151811015610f7b57610f69828281518110610f5c57610f5c615b0a565b6020026020010151612a2e565b80610f7381615b32565b915050610f3e565b50610ef86001600355565b5f805f610f92600f5490565b9250610f9c612a73565b91506010549050909192565b6013546001600160a01b038781165f908152601460205260408120549092610100900490911690611001907f0000000000000000000000000000000000000000000000000000000000000000908a908a8a8a8a8a612b16565b6001600160a01b03161461101657505f61101a565b5060015b9695505050505050565b825f61102f82612b8b565b80519091508015611046575061104433612c0d565b155b1561106457604051630b094f2760e31b815260040160405180910390fd5b61106c612a04565b601654610100900460ff166110945760405163fe9538cf60e01b815260040160405180910390fd5b6110a033868686612c2a565b6110aa6001600355565b5050505050565b6110b9612a04565b6110c2816126da565b90506110ce3382612c36565b610ef86001600355565b5f6110e5600c5460ff1690565b905090565b6001600160a01b0381165f908152601260209081526040808320546007909252822054829161111891615b4a565b905080156111aa576001600160a01b03831661113857610daa8147615b5d565b6040516370a0823160e01b815230600482015281906001600160a01b038516906370a0823190602401602060405180830381865afa15801561117c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111a09190615b70565b610daa9190615b5d565b610daa83612c75565b5f6111bd82612c0d565b92915050565b866111cd81612cf6565b156111eb576040516327d190b160e11b815260040160405180910390fd5b6111f3612952565b6112008888888888612d09565b61120b888484612e4a565b5050505050505050565b61121d612952565b601554600160a01b900460ff161561124857604051630203cf6160e51b815260040160405180910390fd5b610ef881612f9b565b61125c338383612910565b5050565b865f61126b82612b8b565b80519091508015611282575061128033612c0d565b155b156112a057604051630b094f2760e31b815260040160405180910390fd5b601654610100900460ff166112c85760405163fe9538cf60e01b815260040160405180910390fd5b6112d8338a8a8a8a8a8a8a612fe9565b505050505050505050565b6112eb612952565b6112f3612a04565b6112fc816126da565b90505f5b825181101561133e5761132c83828151811061131e5761131e615b0a565b602002602001015183612c36565b8061133681615b32565b915050611300565b5061125c6001600355565b611351612952565b6015546001600160a01b031661137a5760405163309a778560e11b815260040160405180910390fd5b601554600160a01b900460ff16156113a557604051630203cf6160e51b815260040160405180910390fd5b6015805460ff60a01b1916600160a01b1790556040517fa072ef0a510e82127c47aabc8141857e02b88891822ce45dd4c32a2af00563cb905f90a1565b5f805f805f606080876113f481612cf6565b15611412576040516327d190b160e11b815260040160405180910390fd5b5f61141c8a612b8b565b9050805f0151985080602001519750806040015196508060600151955080608001519450611449846126da565b935083516001600160401b0381111561146457611464614f3a565b60405190808252806020026020018201604052801561148d578160200160208202803683370190505b5092505f5b84518110156114ed576114be8b8683815181106114b1576114b1615b0a565b602002602001015161300e565b8482815181106114d0576114d0615b0a565b6020908102919091010152806114e581615b32565b915050611492565b505050919395979092949650565b611503612952565b5f61150d600f5490565b9050805f0361152f576040516327d190b160e11b815260040160405180910390fd5b5f61153b600183615b5d565b90505f61154782612b8b565b90505f8160800151118061155c575060115482105b1561157a57604051633440594960e21b815260040160405180910390fd5b600f8054905f61158983615b87565b90915550505f828152600d6020526040808220805460ff1916815560018101839055600281018390556003810183905560040191909155517fc575d70441d376437764ff913c8a93268fd5b38b4442c068c2e7f8bdeb81bc0b906115f09084815260200190565b60405180910390a1604080515f815260208101909152611611908390613034565b505050565b61161e612952565b600154600160a01b900460ff1615611649576040516303ed52c560e41b815260040160405180910390fd5b61125c8282613118565b5f61165c612952565b611664612a04565b6001600160a01b03841661168b576040516311d000e160e31b815260040160405180910390fd5b5f611695846110ea565b9050808311156116b8576040516305d632b960e41b815260040160405180910390fd5b6116c38585856131a9565b836001600160a01b0316856001600160a01b03167f13e06184555481b6d2cb327155e8d2e1d0b1f0252a7fe6621e32cf99881488358560405161170891815260200190565b60405180910390a36001915050610daa6001600355565b611727612952565b805f5b81811015611978575f84848381811061174557611745615b0a565b90506020028101906117579190615b9c565b359050611763600f5490565b81146117825760405163b929687760e01b815260040160405180910390fd5b600f8054905f61179183615b32565b91905055506118708585848181106117ab576117ab615b0a565b90506020028101906117bd9190615b9c565b358686858181106117d0576117d0615b0a565b90506020028101906117e29190615b9c565b6117f39060408101906020016156ea565b87878681811061180557611805615b0a565b90506020028101906118179190615b9c565b6040013588888781811061182d5761182d615b0a565b905060200281019061183f9190615b9c565b6060013589898881811061185557611855615b0a565b90506020028101906118679190615b9c565b60800135612d09565b61196585858481811061188557611885615b0a565b90506020028101906118979190615b9c565b358686858181106118aa576118aa615b0a565b90506020028101906118bc9190615b9c565b6118ca9060a0810190615bba565b808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152508a925089915087905081811061190f5761190f615b0a565b90506020028101906119219190615b9c565b61192f9060c0810190615bba565b808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250612e4a92505050565b508161197081615b32565b92505061172a565b50505050565b611986612a04565b60165433906301000000900460ff166119b25760405163fe9538cf60e01b815260040160405180910390fd5b6119c08187878787876131de565b6119cd81878787876132d6565b506110aa6001600355565b6119e0612952565b5f5b815181101561125c575f8282815181106119fe576119fe615b0a565b60200260200101519050611a29816001600160a01b03165f9081526006602052604090205460ff1690565b15611a7457611a37816133d8565b6040516001600160a01b03821681527f85a3e72f8dd6db3794f93109c3c5f5b79d6112f6979431c45f98b26134b42af29060200160405180910390a15b5080611a7f81615b32565b9150506119e2565b848282335f611a9585612b8b565b805190915015611ac057611aaa8285856134c7565b611ab382612c0d565b611ac057611ac0826134ef565b611ac8612a04565b60165462010000900460ff16611af15760405163fe9538cf60e01b815260040160405180910390fd5b611afd338b8b8b612c2a565b611b076001600355565b50505050505050505050565b5f610daa838361353a565b611b26612952565b610ef88161356d565b611b376129db565b15611b5557604051635b0999e760e01b815260040160405180910390fd5b611b5d612952565b600454600160a81b900460ff1615611b88576040516330cc939360e11b815260040160405180910390fd5b6004805460ff60a81b1916600160a81b1790556040517fb3211088ecd5ab1725233a5886071950e736a017c35ec09f1539974e3b81d299905f90a1565b611bcd612952565b611bd65f6135c3565b565b60015433906001600160a01b03168114611c155760405163118cdaa760e01b81526001600160a01b03821660048201526024015b60405180910390fd5b610ef8816135c3565b611c26612952565b610ef8816135dc565b611c37612952565b5f5b8181101561161157611c70838383818110611c5657611c56615b0a565b9050602002016020810190611c6b9190615183565b6134ef565b80611c7a81615b32565b915050611c39565b611c8a612952565b611c92612a73565b811080611ca05750600f5481115b15611cbe5760405163b929687760e01b815260040160405180910390fd5b610ef88161360e565b81611cd181612cf6565b15611cef576040516327d190b160e11b815260040160405180910390fd5b611cf7612952565b6116118383613034565b6001600160a01b0381165f9081526006602052604081205460ff166111bd565b606080611d2d836126da565b915081516001600160401b03811115611d4857611d48614f3a565b604051908082528060200260200182016040528015611d71578160200160208202803683370190505b5090505f5b8251811015611de557611db6838281518110611d9457611d94615b0a565b60200260200101516001600160a01b03165f9081526012602052604090205490565b828281518110611dc857611dc8615b0a565b602090810291909101015280611ddd81615b32565b915050611d76565b50915091565b5f610daa8383613643565b611dfe612952565b610ef88161366b565b611e0f612952565b5f5b8181101561161157611e48838383818110611e2e57611e2e615b0a565b9050602002016020810190611e439190615183565b6136ac565b80611e5281615b32565b915050611e11565b611e62612952565b610ef8816136f4565b611e73612952565b610ef881613735565b5f8083611e888161271a565b610e80868686613776565b5f611ea66002546001600160a01b031690565b6001600160a01b031603611ecd5760405163488afa1b60e01b815260040160405180910390fd5b611ed5612952565b600154600160a01b900460ff1615611f00576040516303ed52c560e41b815260040160405180910390fd5b6001805460ff60a01b1916600160a01b1790556040517f9efd8729cc972ca7884de34bbf48c2823c0d69fda2946916c65311c81c4c52c3905f90a1565b6013546001600160a01b038581165f908152601460205260408120549092610100900490911690611f94907f000000000000000000000000000000000000000000000000000000000000000090889088888861378e565b6001600160a01b031614611fa957505f611fad565b5060015b949350505050565b611fbd612952565b8015611fcb57610ef86137d8565b610ef8613832565b82611fdc61386b565b6011548114611ffe57604051633b47fcf960e11b815260040160405180910390fd5b5f61200882612b8b565b9050806060015181608001511061203257604051633b47fcf960e11b815260040160405180910390fd5b80602001515f0361205657604051633b47fcf960e11b815260040160405180910390fd5b806020015142108061207b5750806040015181602001516120779190615b4a565b4210155b1561209957604051633b47fcf960e11b815260040160405180910390fd5b6120a1613984565b336120ae818787876139a8565b6120b88686613a9c565b94506120c5818787613ab8565b85816001600160a01b03167fc5c52c2a9175470464d5ea4429889e7df2ea88630a3d32f4d0d3d2d4486562108760405161210191815260200190565b60405180910390a3505050505050565b60606110e5613b43565b60605f612127600f5490565b9050806001600160401b0381111561214157612141614f3a565b6040519080825280602002602001820160405280156121af57816020015b61219c6040518060e001604052805f151581526020015f81526020015f81526020015f81526020015f815260200160608152602001606081525090565b81526020019060019003908161215f5790505b5091505f6121bb613b43565b90505f5b828110156122e4575f6121d182612b8b565b90505f83516001600160401b038111156121ed576121ed614f3a565b604051908082528060200260200182016040528015612216578160200160208202803683370190505b5090505f5b84518110156122695761223a848683815181106114b1576114b1615b0a565b82828151811061224c5761224c615b0a565b60209081029190910101528061226181615b32565b91505061221b565b506040518060e00160405280835f01511515815260200183602001518152602001836040015181526020018360600151815260200183608001518152602001858152602001828152508684815181106122c4576122c4615b0a565b6020026020010181905250505080806122dc90615b32565b9150506121bf565b50505090565b826122f481612cf6565b15612312576040516327d190b160e11b815260040160405180910390fd5b61231a612952565b611978848484612e4a565b61232d612952565b600f54891461234f5760405163b929687760e01b815260040160405180910390fd5b600f8054905f61235e83615b32565b91905055506123708989898989612d09565b6112d8898585808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250506040805160208089028281018201909352888252909350889250879182918501908490808284375f92019190915250612e4a92505050565b5f805f6123f360015460ff600160a01b9091041690565b92506124076002546001600160a01b031690565b600254909250600160a01b900460ff169050909192565b60165433906301000000900460ff1661244a5760405163fe9538cf60e01b815260040160405180910390fd5b612458818b8b8b8b876131de565b611b07818b8b8b8b8b8b8b8b613ba3565b612471612952565b610ef881613bc9565b888282335f61248885612b8b565b8051909150156124b35761249d8285856134c7565b6124a682612c0d565b6124b3576124b3826134ef565b60165462010000900460ff166124dc5760405163fe9538cf60e01b815260040160405180910390fd5b6124ec338f8f8f8f8f8f8f612fe9565b5050505050505050505050505050565b606080612508836126da565b915081516001600160401b0381111561252357612523614f3a565b60405190808252806020026020018201604052801561254c578160200160208202803683370190505b5090505f5b8251811015611de55761259183828151811061256f5761256f615b0a565b60200260200101516001600160a01b03165f9081526007602052604090205490565b8282815181106125a3576125a3615b0a565b6020908102919091010152806125b881615b32565b915050612551565b6125c8612952565b600180546001600160a01b0383166001600160a01b031990911681179091556125f85f546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b612638612952565b61197884848484613cc0565b604080516001600160a01b03851660208201525f9182910160408051601f19818403018152828252805160209182012090830152016040516020818303038152906040528051906020012090506126d18484808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050600b549150849050613d5f565b95945050505050565b606081515f036126ec576111bd613b43565b5090565b6001600160a01b039182165f90815260086020908152604080832093909416825291909152205490565b6001600160a01b0381165f9081526006602052604090205460ff16610ef857604051631e50c79f60e01b81526001600160a01b0382166004820152602401611c0c565b5f80612769858561300e565b90506127758184613643565b9150935093915050565b604051623f675f60e91b81526001600160a01b0388811660048301525f91908a1690637ecebe0090602401602060405180830381865afa1580156127c5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127e99190615b70565b60405163d505accf60e01b81526001600160a01b038a811660048301528981166024830152604482018990526064820188905260ff8716608483015260a4820186905260c48201859052919250908a169063d505accf9060e4015f604051808303815f87803b15801561285a575f80fd5b505af115801561286c573d5f803e3d5ffd5b5050604051623f675f60e91b81526001600160a01b038b811660048301525f93508c169150637ecebe0090602401602060405180830381865afa1580156128b5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128d99190615b70565b90506128e6826001615b4a565b8114611b075760405163ceeaf5db60e01b81526001600160a01b038b166004820152602401611c0c565b61291b838383613d74565b612923613ff2565b1580156129325750600c5460ff165b8015612944575061294283612c0d565b155b1561161157611611836134ef565b5f546001600160a01b03163314611bd65760405163118cdaa760e01b8152336004820152602401611c0c565b60048054610100600160a81b0319166101006001600160a01b038416908102919091179091556040519081527ff6215f245bfd24e51265c56ef650fdd856aa4ece6221ee1ef395bbe0a5558010906020015b60405180910390a150565b5f806129f56004546001600160a01b036101009091041690565b6001600160a01b031614905090565b600260035403612a2757604051633ee5aeb560e01b815260040160405180910390fd5b6002600355565b6001600160a01b0381165f90815260126020526040902054801561125c57612a56828261403a565b60045461125c9061010090046001600160a01b03165b838361406a565b5f80612a7e60115490565b90505f612a8a600f5490565b9050805f03612a9b575f9250505090565b808210612aa85792915050565b5f612ab283612b8b565b905080602001515f03612ac757509092915050565b80604001518160200151612adb9190615b4a565b4210612af457612aec836001615b4a565b935050505090565b8060600151816080015110612b0e57612aec836001615b4a565b509092915050565b60408051602081018a90526001600160a01b03808a1692820192909252606081018890526080810187905290851660a082015260c0810184905260e081018390525f90612b7e90610100015b60405160208183030381529060405280519060200120836140f6565b9998505050505050505050565b612bba6040518060a001604052805f151581526020015f81526020015f81526020015f81526020015f81525090565b505f908152600d6020908152604091829020825160a081018452815460ff161515815260018201549281019290925260028101549282019290925260038201546060820152600490910154608082015290565b6001600160a01b03165f908152600a602052604090205460ff1690565b6119788484848461411e565b612c408282614223565b612c48613ff2565b158015612c575750600c5460ff165b8015612c675750612c6782612c0d565b1561125c5761125c826136ac565b5f6001600160a01b038216612c8b575047919050565b6040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015612ccd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111bd9190615b70565b919050565b5f612d00600f5490565b90911015919050565b5f858152600d60205260409020805460ff191685151517815560115486148015612d3657505f8160010154115b8015612d46575080600101544210155b15612d645760405163795792b760e01b815260040160405180910390fd5b5f86118015612d7257505f84115b15612dc3575f612d8b612d86600189615b5d565b612b8b565b905080604001518160200151612da19190615b4a565b851015612dc15760405163795792b760e01b815260040160405180910390fd5b505b60018101849055600281018390556004810154821015612df657604051632f56c88b60e11b815260040160405180910390fd5b600381018290556040805160a081018252825460ff1615158152600183015460208201526002830154918101919091526060810183905260048201546080820152612e429087906142d7565b505050505050565b8051825114612e6c576040516328e7033560e01b815260040160405180910390fd5b5f5b825181101561197857612e99838281518110612e8c57612e8c615b0a565b602002602001015161271a565b818181518110612eab57612eab615b0a565b6020026020010151600e5f8681526020019081526020015f205f858481518110612ed757612ed7615b0a565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f20819055507f28af1bd3648c38edc812346775bcc36a5f5fc9870d47c8c69a0e2ec0445afaae84848381518110612f3657612f36615b0a565b6020026020010151848481518110612f5057612f50615b0a565b6020026020010151604051612f81939291909283526001600160a01b03919091166020830152604082015260600190565b60405180910390a180612f9381615b32565b915050612e6e565b601580546001600160a01b0319166001600160a01b0383169081179091556040519081527ff4e654cefbf8c2fe608d6868b30dfaf564ff18be7ac7404b5c06b2667f8a4608906020016129d0565b6130026001600160a01b0387168930888888888861277f565b61120b8888888861411e565b5f918252600e602090815260408084206001600160a01b03909316845291905290205490565b61303d816126da565b90505f5b815181101561161157600e5f8481526020019081526020015f205f83838151811061306e5761306e615b0a565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f205f90557f28af1bd3648c38edc812346775bcc36a5f5fc9870d47c8c69a0e2ec0445afaae838383815181106130cc576130cc615b0a565b60200260200101515f6040516130fe939291909283526001600160a01b03919091166020830152604082015260600190565b60405180910390a18061311081615b32565b915050613041565b8060ff165f0361313b57604051638bd9be8160e01b815260040160405180910390fd5b600280546001600160a01b0384166001600160a81b03199091168117600160a01b60ff8516908102919091179092556040805191825260208201929092527fc9cbf5af9e78df9093a676978e83f2a04e258ad3b0d8fa81cb44b357c657606a91015b60405180910390a15050565b6001600160a01b0382166131ca576116116001600160a01b03841682614333565b6116116001600160a01b03831684836143c6565b6131e6614425565b6001600160a01b038087165f9081526014602052604090205460135490916101009091041661323b7f000000000000000000000000000000000000000000000000000000000000000089848a8a8a8a8a612b16565b6001600160a01b0316146132625760405163ea8e4eb560e01b815260040160405180910390fd5b6001600160a01b0387165f90815260146020526040812080546001929061328a908490615b4a565b90915550506040518181526001600160a01b038816907f3e266ebc4c4f97d954ad3c456dd72416a5e3a203f706c4d0682085901b1ad0ed9060200160405180910390a250505050505050565b826132e08161271a565b846132e961386b565b601154811461330b57604051633b47fcf960e11b815260040160405180910390fd5b5f61331582612b8b565b9050806060015181608001511061333f57604051633b47fcf960e11b815260040160405180910390fd5b80602001515f0361336357604051633b47fcf960e11b815260040160405180910390fd5b80602001514210806133885750806040015181602001516133849190615b4a565b4210155b156133a657604051633b47fcf960e11b815260040160405180910390fd5b6133ae613984565b5f806133bb898888614453565b915091506133ca8a898361448d565b611b078a8a8a85858c614646565b5f6133ea826133e5613b43565b6146ba565b6005549091505f906133fe90600190615b5d565b90505f6005828154811061341457613414615b0a565b5f91825260209091200154600580546001600160a01b03909216925082918590811061344257613442615b0a565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b03160217905550600580548061347e5761347e615bff565b5f828152602080822083015f1990810180546001600160a01b03191690559092019092556001600160a01b03959095168152600690945250506040909120805460ff1916905550565b6134d2838383612644565b61161157604051630b094f2760e31b815260040160405180910390fd5b6001600160a01b0381165f818152600a6020526040808220805460ff19166001179055517fd5a6b3454d1aa211b5b7e99c94012bed9883c7695b5c58fd97e20f425ec0b5bf9190a250565b5f61354483614736565b613562828461355d60025460ff600160a01b9091041690565b614756565b90506111bd81614776565b60138054610100600160a81b0319166101006001600160a01b038416908102919091179091556040519081527f5553331329228fbd4123164423717a4a7539f6dfa1c3279a923b98fd681a6c73906020016129d0565b600180546001600160a01b0319169055610ef881614796565b600b81905560405181907f90004c04698bc3322499a575ed3752dd4abf33e0a7294c06a787a0fe01bea941905f90a250565b60118190556040518181527fc7941f8838c3de40641130e81ef820f45856796923e29f642fb8680ea13c1a3b906020016129d0565b5f61364d83614736565b613562828461366660025460ff600160a01b9091041690565b6147e5565b6009805460ff19168215159081179091556040519081527f080b99e58282ae4610da5730d7f94b53b3a2e6c83e0a471b89874fdd2e33515f906020016129d0565b6001600160a01b0381165f818152600a6020526040808220805460ff19169055517f79bc96e8fb893a32b4429ae9fd9084cdb98be910b9c7cdbb4307cc96732c7ec79190a250565b600c805460ff19168215159081179091556040519081527f2e66025596ef0553cb2e878a0f141264297c58f317ab613bf6dbe35a8be44cf3906020016129d0565b6013805460ff19168215159081179091556040519081527fcbb1b2ab6aab49b57a6ea192e0596dffffe7c371b29eb63ef80bd5840daa9bb5906020016129d0565b5f80613782858561300e565b9050612775818461353a565b60408051602081018890526001600160a01b03871691810191909152606081018590526080810184905260a081018390525f906137cd9060c001612b62565b979650505050505050565b6137e0613984565b6004805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586138153390565b6040516001600160a01b03909116815260200160405180910390a1565b61383a6147fc565b6004805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33613815565b5f61387560115490565b90505f613880612a73565b90508181111561125c576138938161360e565b5f818152600d602052604081206001810154909103611611575f6138bb612d86600185615b5d565b9050806060015181608001511061391c5742600183018190556040805160a081018252845460ff1615158152602081019290925260028401549082015260038301546060820152600483015460808201526139179084906142d7565b611978565b602081015115611978578060400151816020015161393a9190615b4a565b600183018190556040805160a081018252845460ff1615158152602081019290925260028401549082015260038301546060820152600483015460808201526119789084906142d7565b60045460ff1615611bd65760405163d93c066560e01b815260040160405180910390fd5b6139b0614425565b6001600160a01b038085165f90815260146020526040902054601354909161010090910416613a037f0000000000000000000000000000000000000000000000000000000000000000878488888861378e565b6001600160a01b031614613a2a5760405163ea8e4eb560e01b815260040160405180910390fd5b6001600160a01b0385165f908152601460205260408120805460019290613a52908490615b4a565b90915550506040518181526001600160a01b038616907f3e266ebc4c4f97d954ad3c456dd72416a5e3a203f706c4d0682085901b1ad0ed9060200160405180910390a25050505050565b5f80613aa78461481f565b905080831115612b0e579392505050565b613ac28282614840565b613aca61490d565b60405163d279792d60e01b81526001600160a01b0385811660048301526024820185905260448201849052919091169063d279792d906064016020604051808303815f875af1158015613b1f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119789190615c13565b60606005805480602002602001604051908101604052809291908181526020018280548015613b9957602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311613b7b575b5050505050905090565b613bbc6001600160a01b0388168a30888888888861277f565b6112d889898989896132d6565b5f5b815181101561125c575f828281518110613be757613be7615b0a565b60200260200101519050613c12816001600160a01b03165f9081526006602052604090205460ff1690565b613cad576005805460018082019092557f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00180546001600160a01b0319166001600160a01b0384169081179091555f81815260066020908152604091829020805460ff1916909417909355519081527fa317c10673baf4f03b3c1041bd5ddbb537d0333a86fec3607c75f9dbb630f48f910160405180910390a15b5080613cb881615b32565b915050613bcb565b6016805461ffff191685151561ff00198116919091176101008615159081029190911763ffff000019166201000086151590810263ff0000001916919091176301000000861515908102919091179094556040805193845260208401929092529082015260608101919091527fae3d63e035fb1d35cb2faa50509104c27a5f07f4fa920f7a05bd31ed4f5e0b0d9060800160405180910390a150505050565b5f82613d6b8584614948565b14949350505050565b81613d7e8161271a565b613d86613984565b60095460ff16613da957604051630e2f42c960e31b815260040160405180910390fd5b815f03613dc957604051632160733960e01b815260040160405180910390fd5b6001600160a01b038316613dfc57813414613df757604051632160733960e01b815260040160405180910390fd5b613f36565b3415613e1b57604051632160733960e01b815260040160405180910390fd5b6040516370a0823160e01b815230600482015283905f906001600160a01b038316906370a0823190602401602060405180830381865afa158015613e61573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613e859190615b70565b9050613e9c6001600160a01b038316873087614994565b6040516370a0823160e01b81523060048201525f9082906001600160a01b038516906370a0823190602401602060405180830381865afa158015613ee2573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613f069190615b70565b613f109190615b5d565b9050848114613f3257604051632160733960e01b815260040160405180910390fd5b5050505b6001600160a01b0383165f9081526007602052604081208054849290613f5d908490615b4a565b90915550506001600160a01b038085165f90815260086020908152604080832093871683529290529081208054849290613f98908490615b4a565b92505081905550826001600160a01b0316846001600160a01b03167fc67994043444b22a702bf75a242bcc1720705569fb433fe91272cbf8d0d99c1084604051613fe491815260200190565b60405180910390a350505050565b5f80613ffd600f5490565b1115614035575f61400d5f612b8b565b90505f8160200151118015614026575080602001514210155b1561403357600191505090565b505b505f90565b6001600160a01b0382165f9081526012602052604081208054839290614061908490615b5d565b90915550505050565b6001600160a01b0382166140905761408b6001600160a01b03841682614333565b6140a4565b6140a46001600160a01b03831684836143c6565b816001600160a01b0316836001600160a01b03167fa77f6af926d34e43c5b52329799fa34e6d594dea2777723745b977d1d6e271c3836040516140e991815260200190565b60405180910390a3505050565b5f805f8061410486866149cd565b9250925092506141148282614a16565b5090949350505050565b816141288161271a565b8361413161386b565b601154811461415357604051633b47fcf960e11b815260040160405180910390fd5b5f61415d82612b8b565b9050806060015181608001511061418757604051633b47fcf960e11b815260040160405180910390fd5b80602001515f036141ab57604051633b47fcf960e11b815260040160405180910390fd5b80602001514210806141d05750806040015181602001516141cc9190615b4a565b4210155b156141ee57604051633b47fcf960e11b815260040160405180910390fd5b6141f6613984565b5f805f614204898989614ace565b9250925092506142158a898461448d565b611b078a8a8a868686614646565b5f5b8151811015611611575f82828151811061424157614241615b0a565b602002602001015190505f61425685836126f0565b905080156142c257614269858383614b11565b6142748583836131a9565b816001600160a01b0316856001600160a01b03167f933380dab6ffd07b472a6e643e8349744d3263e3b449d12dcd616b69c7312f35836040516142b991815260200190565b60405180910390a35b505080806142cf90615b32565b915050614225565b80516020808301516040808501516060808701518351898152961515958701959095529185019290925283015260808201527f600c4518738a76b007c7da3f323d8a50d668dde43b2b5f2fd3cd4689c1e261339060a00161319d565b804710156143565760405163cd78605960e01b8152306004820152602401611c0c565b5f826001600160a01b0316826040515f6040518083038185875af1925050503d805f811461439f576040519150601f19603f3d011682016040523d82523d5f602084013e6143a4565b606091505b505090508061161157604051630a12f52160e11b815260040160405180910390fd5b6040516001600160a01b0383811660248301526044820183905261161191859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050614ba7565b60135461010090046001600160a01b0316611bd65760405163327effa960e11b815260040160405180910390fd5b5f8161445f8482613643565b91505f61446b8661481f565b905080831115610e8557809250614482858461353a565b915050935093915050565b805f61449985856126f0565b905080156144d0575f8382106144b357505f9150826144c3565b6144bd8285615b5d565b92508190505b6144ce868683614b11565b505b81156110aa576001600160a01b03841661452f57813410156145055760405163356680b760e01b815260040160405180910390fd5b8134111561452a5761452a61451a8334615b5d565b6001600160a01b03871690614333565b6110aa565b6040516370a0823160e01b815230600482015284905f906001600160a01b038316906370a0823190602401602060405180830381865afa158015614575573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906145999190615b70565b90506145b06001600160a01b038316883087614994565b6040516370a0823160e01b81523060048201525f9082906001600160a01b038516906370a0823190602401602060405180830381865afa1580156145f6573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061461a9190615b70565b6146249190615b5d565b905084811461120b57604051632160733960e01b815260040160405180910390fd5b6146508483614c08565b61465b868685613ab8565b836001600160a01b031685876001600160a01b03167f62e796e00a8e66154d78da76daae129635b4795a6e1b889f2caa6c5cea22ac6884876040516146aa929190918252602082015260400190565b60405180910390a4505050505050565b5f805b825181101561471157836001600160a01b03168382815181106146e2576146e2615b0a565b60200260200101516001600160a01b0316036146ff5790506111bd565b8061470981615b32565b9150506146bd565b50604051631e50c79f60e01b81526001600160a01b0384166004820152602401611c0c565b805f03610ef85760405163e9589bbd60e01b815260040160405180910390fd5b5f61476282600a615d06565b61476c8486615d14565b610da79190615d2b565b805f03610ef8576040516334cf99ff60e11b815260040160405180910390fd5b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f826147f283600a615d06565b61476c9086615d14565b60045460ff16611bd657604051638dfc202b60e01b815260040160405180910390fd5b5f8061482a83612b8b565b905080608001518160600151610daa9190615b5d565b5f828152600d6020526040812090508160105f8282546148609190615b4a565b9250508190555081816004015f82825461487a9190615b4a565b90915550506003810154600482015410611611575f601154600161489e9190615b4a565b90506148a98161360e565b5f818152600d6020526040812060018101549091036110aa5742600182018190556040805160a081018252835460ff1615158152602081019290925260028301549082015260038201546060820152600482015460808201526110aa9083906142d7565b6015545f906001600160a01b03166149385760405163309a778560e11b815260040160405180910390fd5b506015546001600160a01b031690565b5f81815b845181101561498c576149788286838151811061496b5761496b615b0a565b6020026020010151614ca8565b91508061498481615b32565b91505061494c565b509392505050565b6040516001600160a01b0384811660248301528381166044830152606482018390526119789186918216906323b872dd906084016143f3565b5f805f8351604103614a04576020840151604085015160608601515f1a6149f688828585614cd4565b955095509550505050614a0f565b505081515f91506002905b9250925092565b5f826003811115614a2957614a29615d4a565b03614a32575050565b6001826003811115614a4657614a46615d4a565b03614a645760405163f645eedf60e01b815260040160405180910390fd5b6002826003811115614a7857614a78615d4a565b03614a995760405163fce698f760e01b815260048101829052602401611c0c565b6003826003811115614aad57614aad615d4a565b0361125c576040516335e2f38360e21b815260048101829052602401611c0c565b5f8181614adc86868461275d565b90935090505f614aeb8761481f565b905080841115614b0757809350614b03878786613776565b5092505b5093509350939050565b614b1b83836126f0565b811115614b3b57604051631650c97f60e11b815260040160405180910390fd5b6001600160a01b0382165f9081526007602052604081208054839290614b62908490615b5d565b90915550506001600160a01b038084165f90815260086020908152604080832093861683529290529081208054839290614b9d908490615b5d565b9091555050505050565b5f614bbb6001600160a01b03841683614d9c565b905080515f14158015614bdf575080806020019051810190614bdd9190615c13565b155b1561161157604051635274afe760e01b81526001600160a01b0384166004820152602401611c0c565b816001600160a01b03167fd98279195c58b6b93d53ab20cbd8ad7d353cfdcbcc0f514dc4a0f114f1e3f0e682604051614c4391815260200190565b60405180910390a260135460ff168015614c625750614c606129db565b155b15614c815760045461125c9061010090046001600160a01b0316612a6c565b6001600160a01b0382165f9081526012602052604081208054839290614061908490615b4a565b5f818310614cc2575f828152602084905260409020610daa565b5f838152602083905260409020610daa565b5f80807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115614d0d57505f91506003905082614d92565b604080515f808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015614d5e573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b038116614d8957505f925060019150829050614d92565b92505f91508190505b9450945094915050565b6060610daa83835f845f80856001600160a01b03168486604051614dc09190615d5e565b5f6040518083038185875af1925050503d805f8114614dfa576040519150601f19603f3d011682016040523d82523d5f602084013e614dff565b606091505b509150915061101a868383606082614e1f57614e1a82614e66565b610daa565b8151158015614e3657506001600160a01b0384163b155b15614e5f57604051639996b31560e01b81526001600160a01b0385166004820152602401611c0c565b5080610daa565b805115614e765780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b80356001600160a01b0381168114612cf1575f80fd5b5f8083601f840112614eb5575f80fd5b5081356001600160401b03811115614ecb575f80fd5b6020830191508360208260051b8501011115614ee5575f80fd5b9250929050565b5f805f60408486031215614efe575f80fd5b614f0784614e8f565b925060208401356001600160401b03811115614f21575f80fd5b614f2d86828701614ea5565b9497909650939450505050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b0381118282101715614f7657614f76614f3a565b604052919050565b5f6001600160401b03821115614f9657614f96614f3a565b5060051b60200190565b5f82601f830112614faf575f80fd5b81356020614fc4614fbf83614f7e565b614f4e565b82815260059290921b84018101918181019086841115614fe2575f80fd5b8286015b8481101561500457614ff781614e8f565b8352918301918301614fe6565b509695505050505050565b5f8060408385031215615020575f80fd5b61502983614e8f565b915060208301356001600160401b03811115615043575f80fd5b61504f85828601614fa0565b9150509250929050565b5f8151808452602080850194508084015f5b838110156150905781516001600160a01b03168752958201959082019060010161506b565b509495945050505050565b5f8151808452602080850194508084015f5b83811015615090578151875295820195908201906001016150ad565b604081525f6150db6040830185615059565b82810360208401526126d1818561509b565b5f805f606084860312156150ff575f80fd5b8335925061510f60208501614e8f565b9150604084013590509250925092565b803560ff81168114612cf1575f80fd5b5f805f805f8060c08789031215615144575f80fd5b61514d87614e8f565b955060208701359450604087013593506151696060880161511f565b92506080870135915060a087013590509295509295509295565b5f60208284031215615193575f80fd5b610daa82614e8f565b5f602082840312156151ac575f80fd5b81356001600160401b038111156151c1575f80fd5b611fad84828501614fa0565b5f82601f8301126151dc575f80fd5b81356001600160401b038111156151f5576151f5614f3a565b615208601f8201601f1916602001614f4e565b81815284602083860101111561521c575f80fd5b816020850160208301375f918101602001919091529392505050565b5f805f805f8060c0878903121561524d575f80fd5b61525687614e8f565b95506020870135945061526b60408801614e8f565b9350606087013592506080870135915060a08701356001600160401b03811115615293575f80fd5b61529f89828a016151cd565b9150509295509295509295565b5f5b838110156152c65781810151838201526020016152ae565b50505f910152565b602081525f82518060208401526152ec8160408501602087016152ac565b601f01601f19169190910160400192915050565b8015158114610ef8575f80fd5b5f82601f83011261531c575f80fd5b8135602061532c614fbf83614f7e565b82815260059290921b8401810191818101908684111561534a575f80fd5b8286015b84811015615004578035835291830191830161534e565b5f805f805f805f60e0888a03121561537b575f80fd5b87359650602088013561538d81615300565b955060408801359450606088013593506080880135925060a08801356001600160401b03808211156153bd575f80fd5b6153c98b838c01614fa0565b935060c08a01359150808211156153de575f80fd5b506153eb8a828b0161530d565b91505092959891949750929550565b5f806040838503121561540b575f80fd5b61541483614e8f565b946020939093013593505050565b5f805f805f805f60e0888a031215615438575f80fd5b8735965061544860208901614e8f565b955060408801359450606088013593506154646080890161511f565b925060a0880135915060c0880135905092959891949750929550565b5f8060408385031215615491575f80fd5b82356001600160401b03808211156154a7575f80fd5b6154b386838701614fa0565b935060208501359150808211156154c8575f80fd5b5061504f85828601614fa0565b5f602082840312156154e5575f80fd5b5035919050565b871515815286602082015285604082015284606082015283608082015260e060a08201525f61551e60e0830185615059565b82810360c0840152615530818561509b565b9a9950505050505050505050565b5f806040838503121561554f575f80fd5b61555883614e8f565b91506155666020840161511f565b90509250929050565b5f805f60608486031215615581575f80fd5b61558a84614e8f565b925061510f60208501614e8f565b5f80602083850312156155a9575f80fd5b82356001600160401b038111156155be575f80fd5b6155ca85828601614ea5565b90969095509350505050565b5f805f805f60a086880312156155ea575f80fd5b853594506155fa60208701614e8f565b9350604086013592506060860135915060808601356001600160401b03811115615622575f80fd5b61562e888289016151cd565b9150509295509295909350565b5f805f805f6080868803121561564f575f80fd5b8535945061565f60208701614e8f565b93506040860135925060608601356001600160401b03811115615680575f80fd5b61568c88828901614ea5565b969995985093965092949392505050565b5f80604083850312156156ae575f80fd5b50508035926020909101359150565b5f80604083850312156156ce575f80fd5b8235915060208301356001600160401b03811115615043575f80fd5b5f602082840312156156fa575f80fd5b8135610daa81615300565b5f805f8060808587031215615718575f80fd5b61572185614e8f565b9350602085013592506040850135915060608501356001600160401b03811115615749575f80fd5b615755878288016151cd565b91505092959194509250565b5f805f60608486031215615773575f80fd5b833592506020840135915060408401356001600160401b03811115615796575f80fd5b6157a2868287016151cd565b9150509250925092565b602081525f610daa6020830184615059565b5f6020808301818452808551808352604092508286019150828160051b8701018488015f805b8481101561589957898403603f19018652825180511515855288810151898601528781015188860152606080820151908601526080808201519086015260a08082015160e09187018290528051918701829052610100870191908b0190855b818110156158685782516001600160a01b03168452928c0192918c0191600101615843565b50505060c08083015192508682038188015250615885818361509b565b978a019795505050918701916001016157e4565b50919998505050505050505050565b5f805f606084860312156158ba575f80fd5b8335925060208401356001600160401b03808211156158d7575f80fd5b6158e387838801614fa0565b935060408601359150808211156158f8575f80fd5b506157a28682870161530d565b5f805f805f805f805f60e08a8c03121561591d575f80fd5b8935985060208a013561592f81615300565b975060408a0135965060608a0135955060808a0135945060a08a01356001600160401b038082111561595f575f80fd5b61596b8d838e01614ea5565b909650945060c08c0135915080821115615983575f80fd5b506159908c828d01614ea5565b915080935050809150509295985092959850929598565b5f805f805f805f805f6101208a8c0312156159c0575f80fd5b893598506159d060208b01614e8f565b975060408a0135965060608a0135955060808a013594506159f360a08b0161511f565b935060c08a0135925060e08a013591506101008a01356001600160401b03811115615a1c575f80fd5b615a288c828d016151cd565b9150509295985092959850929598565b5f805f805f805f805f6101008a8c031215615a51575f80fd5b89359850615a6160208b01614e8f565b975060408a0135965060608a01359550615a7d60808b0161511f565b945060a08a0135935060c08a0135925060e08a01356001600160401b03811115615aa5575f80fd5b6159908c828d01614ea5565b5f805f8060808587031215615ac4575f80fd5b8435615acf81615300565b93506020850135615adf81615300565b92506040850135615aef81615300565b91506060850135615aff81615300565b939692955090935050565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b5f60018201615b4357615b43615b1e565b5060010190565b808201808211156111bd576111bd615b1e565b818103818111156111bd576111bd615b1e565b5f60208284031215615b80575f80fd5b5051919050565b5f81615b9557615b95615b1e565b505f190190565b5f823560de19833603018112615bb0575f80fd5b9190910192915050565b5f808335601e19843603018112615bcf575f80fd5b8301803591506001600160401b03821115615be8575f80fd5b6020019150600581901b3603821315614ee5575f80fd5b634e487b7160e01b5f52603160045260245ffd5b5f60208284031215615c23575f80fd5b8151610daa81615300565b600181815b80851115610e6157815f1904821115615c4e57615c4e615b1e565b80851615615c5b57918102915b93841c9390800290615c33565b5f82615c76575060016111bd565b81615c8257505f6111bd565b8160018114615c985760028114615ca257615cbe565b60019150506111bd565b60ff841115615cb357615cb3615b1e565b50506001821b6111bd565b5060208310610133831016604e8410600b8410161715615ce1575081810a6111bd565b615ceb8383615c2e565b805f1904821115615cfe57615cfe615b1e565b029392505050565b5f610daa60ff841683615c68565b80820281158282048414176111bd576111bd615b1e565b5f82615d4557634e487b7160e01b5f52601260045260245ffd5b500490565b634e487b7160e01b5f52602160045260245ffd5b5f8251615bb08184602087016152ac56fea2646970667358221220c52dc068b18ad600a3ef08184aef45b97e64aa1b94a361345300f95f628ef71564736f6c63430008140033000000000000000000000000b25766a96a90c251c328b73965d6931ea9a469550000000000000000000000000000000000000000000000000000000000000012
Deployed Bytecode
0x608060405260043610610416575f3560e01c806370c1354511610220578063ad9ebcfd11610129578063d64b650c116100b3578063ec3601d011610078578063ec3601d014610ccc578063f0c4361314610ceb578063f2fde38b14610d0a578063f311758014610d29578063fdf1087b14610d7c575f80fd5b8063d64b650c14610c16578063d9b51fce14610c35578063e30c397814610c71578063e548922714610c8e578063ea3fd12114610cad575f80fd5b8063bedb86fb116100f9578063bedb86fb14610b77578063c3c027b114610b96578063ca5e553e14610bb5578063cbf3b2a714610bd6578063d04899e914610bf7575f80fd5b8063ad9ebcfd14610afc578063b223e35814610b1b578063b3c1c00d14610b44578063b57b179f14610b58575f80fd5b806387ae3a22116101aa578063960697571161017a5780639606975714610a615780639f04586c14610a80578063a1ac390714610a9f578063a991411214610abe578063aa1078ee14610add575f80fd5b806387ae3a22146109e85780638da5cb5b14610a07578063930eaddc14610a2357806393aa4bd714610a42575f80fd5b80637ac3c02f116101f05780637ac3c02f1461093e5780637bbfbcfa146109745780637cb647591461098b5780637d7cd772146109aa5780638136f590146109c9575f80fd5b806370c13545146108cb578063715018a6146108df57806372172364146108f357806379ba50971461092a575f80fd5b806347e7ef24116103225780635ce60c6a116102ac57806364e60ef41161027c57806364e60ef41461082757806369e1f1c3146108465780636a92df6a146108595780636af9c8fb146108785780636c19e783146108ac575f80fd5b80635ce60c6a146107b75780635d3590d5146107d65780635d5c49a6146107f557806363711ff114610814575f80fd5b80634b389c00116102f25780634b389c001461072f578063573aa7d51461074357806357742d27146107755780635777bf50146107895780635c975abb146107a0575f80fd5b806347e7ef24146106ca578063480d5703146106dd57806349590657146106fc5780634af5e79114610710575f80fd5b80632afaca20116103a357806334383465116103735780633438346514610619578063387e45121461064e5780633af32abf1461066d5780634234fb941461068c57806343e34696146106ab575f80fd5b80632afaca20146105975780632cc6ca69146105aa57806331da3f25146105c95780633408e470146105dd575f80fd5b806312a62cff116103e957806312a62cff146104ea5780631525ff7d1461050b5780631631bdcb1461052a57806325bdb2a814610549578063267bbcc614610578575f80fd5b8063019dcf2c1461041a5780630474761e1461044e5780630759a3541461047b5780630e3e2ea4146104b6575b5f80fd5b348015610425575f80fd5b50610439610434366004614eec565b610d9b565b60405190151581526020015b60405180910390f35b348015610459575f80fd5b5061046d61046836600461500f565b610db1565b6040516104459291906150c9565b348015610486575f80fd5b5060045460408051600160a81b830460ff16151581526101009092046001600160a01b0316602083015201610445565b3480156104c1575f80fd5b506104d56104d03660046150ed565b610e69565b60408051928352602083019190915201610445565b3480156104f5575f80fd5b5061050961050436600461512f565b610e8e565b005b348015610516575f80fd5b50610509610525366004615183565b610ebc565b348015610535575f80fd5b5061050961054436600461519c565b610efb565b348015610554575f80fd5b5061055d610f86565b60408051938452602084019290925290820152606001610445565b348015610583575f80fd5b50610439610592366004615238565b610fa8565b6105096105a53660046150ed565b611024565b3480156105b5575f80fd5b506105096105c436600461519c565b6110b1565b3480156105d4575f80fd5b506104396110d8565b3480156105e8575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000015b604051908152602001610445565b348015610624575f80fd5b50604080518082019091526004815263199d5b1b60e21b60208201525b60405161044591906152ce565b348015610659575f80fd5b5061060b610668366004615183565b6110ea565b348015610678575f80fd5b50610439610687366004615183565b6111b3565b348015610697575f80fd5b506105096106a6366004615365565b6111c3565b3480156106b6575f80fd5b506105096106c5366004615183565b611215565b6105096106d83660046153fa565b611251565b3480156106e8575f80fd5b506105096106f7366004615422565b611260565b348015610707575f80fd5b50600b5461060b565b34801561071b575f80fd5b5061050961072a366004615480565b6112e3565b34801561073a575f80fd5b50610509611349565b34801561074e575f80fd5b5061076261075d3660046154d5565b6113e2565b60405161044597969594939291906154ec565b348015610780575f80fd5b506105096114fb565b348015610794575f80fd5b5060095460ff16610439565b3480156107ab575f80fd5b5060045460ff16610439565b3480156107c2575f80fd5b506105096107d136600461553e565b611616565b3480156107e1575f80fd5b506104396107f036600461556f565b611653565b348015610800575f80fd5b5061050961080f366004615598565b61171f565b6105096108223660046155d6565b61197e565b348015610832575f80fd5b5061050961084136600461519c565b6119d8565b61050961085436600461563b565b611a87565b348015610864575f80fd5b5061060b61087336600461569d565b611b13565b348015610883575f80fd5b5061060b610892366004615183565b6001600160a01b03165f9081526014602052604090205490565b3480156108b7575f80fd5b506105096108c6366004615183565b611b1e565b3480156108d6575f80fd5b50610509611b2f565b3480156108ea575f80fd5b50610509611bc5565b3480156108fe575f80fd5b50601554604080516001600160a01b0383168152600160a01b90920460ff161515602083015201610445565b348015610935575f80fd5b50610509611bd8565b348015610949575f80fd5b5060135461010090046001600160a01b03165b6040516001600160a01b039091168152602001610445565b34801561097f575f80fd5b5060135460ff16610439565b348015610996575f80fd5b506105096109a53660046154d5565b611c1e565b3480156109b5575f80fd5b506105096109c4366004615598565b611c2f565b3480156109d4575f80fd5b506105096109e33660046154d5565b611c82565b3480156109f3575f80fd5b50610509610a023660046156bd565b611cc7565b348015610a12575f80fd5b505f546001600160a01b031661095c565b348015610a2e575f80fd5b50610439610a3d366004615183565b611d01565b348015610a4d575f80fd5b5061046d610a5c36600461519c565b611d21565b348015610a6c575f80fd5b5061060b610a7b36600461569d565b611deb565b348015610a8b575f80fd5b50610509610a9a3660046156ea565b611df6565b348015610aaa575f80fd5b50610509610ab9366004615598565b611e07565b348015610ac9575f80fd5b50610509610ad83660046156ea565b611e5a565b348015610ae8575f80fd5b50610509610af73660046156ea565b611e6b565b348015610b07575f80fd5b506104d5610b163660046150ed565b611e7c565b348015610b26575f80fd5b506040805180820190915260018152603160f81b6020820152610641565b348015610b4f575f80fd5b50610509611e93565b348015610b63575f80fd5b50610439610b72366004615705565b611f3d565b348015610b82575f80fd5b50610509610b913660046156ea565b611fb5565b348015610ba1575f80fd5b50610509610bb0366004615761565b611fd3565b348015610bc0575f80fd5b50610bc9612111565b60405161044591906157ac565b348015610be1575f80fd5b50610bea61211b565b60405161044591906157be565b348015610c02575f80fd5b50610509610c113660046158a8565b6122ea565b348015610c21575f80fd5b50610509610c30366004615905565b612325565b348015610c40575f80fd5b50610c496123dc565b6040805193151584526001600160a01b03909216602084015260ff1690820152606001610445565b348015610c7c575f80fd5b506001546001600160a01b031661095c565b348015610c99575f80fd5b50610509610ca83660046159a7565b61241e565b348015610cb8575f80fd5b50610509610cc736600461519c565b612469565b348015610cd7575f80fd5b50610509610ce6366004615a38565b61247a565b348015610cf6575f80fd5b5061046d610d0536600461519c565b6124fc565b348015610d15575f80fd5b50610509610d24366004615183565b6125c0565b348015610d34575f80fd5b506016546040805160ff808416151582526101008404811615156020830152620100008404811615159282019290925263010000009092041615156060820152608001610445565b348015610d87575f80fd5b50610509610d96366004615ab1565b612630565b5f610da7848484612644565b90505b9392505050565b606080610dbd836126da565b915081516001600160401b03811115610dd857610dd8614f3a565b604051908082528060200260200182016040528015610e01578160200160208202803683370190505b5090505f5b8251811015610e6157610e3285848381518110610e2557610e25615b0a565b60200260200101516126f0565b828281518110610e4457610e44615b0a565b602090810291909101015280610e5981615b32565b915050610e06565b509250929050565b5f8083610e758161271a565b610e8086868661275d565b925092505b50935093915050565b33610ea86001600160a01b0388168230898989898961277f565b610eb3818888612910565b50505050505050565b610ec4612952565b600454600160a81b900460ff1615610eef576040516330cc939360e11b815260040160405180910390fd5b610ef88161297e565b50565b610f036129db565b15610f2157604051635b0999e760e01b815260040160405180910390fd5b610f29612952565b610f31612a04565b610f3a816126da565b90505f5b8151811015610f7b57610f69828281518110610f5c57610f5c615b0a565b6020026020010151612a2e565b80610f7381615b32565b915050610f3e565b50610ef86001600355565b5f805f610f92600f5490565b9250610f9c612a73565b91506010549050909192565b6013546001600160a01b038781165f908152601460205260408120549092610100900490911690611001907f0000000000000000000000000000000000000000000000000000000000000001908a908a8a8a8a8a612b16565b6001600160a01b03161461101657505f61101a565b5060015b9695505050505050565b825f61102f82612b8b565b80519091508015611046575061104433612c0d565b155b1561106457604051630b094f2760e31b815260040160405180910390fd5b61106c612a04565b601654610100900460ff166110945760405163fe9538cf60e01b815260040160405180910390fd5b6110a033868686612c2a565b6110aa6001600355565b5050505050565b6110b9612a04565b6110c2816126da565b90506110ce3382612c36565b610ef86001600355565b5f6110e5600c5460ff1690565b905090565b6001600160a01b0381165f908152601260209081526040808320546007909252822054829161111891615b4a565b905080156111aa576001600160a01b03831661113857610daa8147615b5d565b6040516370a0823160e01b815230600482015281906001600160a01b038516906370a0823190602401602060405180830381865afa15801561117c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111a09190615b70565b610daa9190615b5d565b610daa83612c75565b5f6111bd82612c0d565b92915050565b866111cd81612cf6565b156111eb576040516327d190b160e11b815260040160405180910390fd5b6111f3612952565b6112008888888888612d09565b61120b888484612e4a565b5050505050505050565b61121d612952565b601554600160a01b900460ff161561124857604051630203cf6160e51b815260040160405180910390fd5b610ef881612f9b565b61125c338383612910565b5050565b865f61126b82612b8b565b80519091508015611282575061128033612c0d565b155b156112a057604051630b094f2760e31b815260040160405180910390fd5b601654610100900460ff166112c85760405163fe9538cf60e01b815260040160405180910390fd5b6112d8338a8a8a8a8a8a8a612fe9565b505050505050505050565b6112eb612952565b6112f3612a04565b6112fc816126da565b90505f5b825181101561133e5761132c83828151811061131e5761131e615b0a565b602002602001015183612c36565b8061133681615b32565b915050611300565b5061125c6001600355565b611351612952565b6015546001600160a01b031661137a5760405163309a778560e11b815260040160405180910390fd5b601554600160a01b900460ff16156113a557604051630203cf6160e51b815260040160405180910390fd5b6015805460ff60a01b1916600160a01b1790556040517fa072ef0a510e82127c47aabc8141857e02b88891822ce45dd4c32a2af00563cb905f90a1565b5f805f805f606080876113f481612cf6565b15611412576040516327d190b160e11b815260040160405180910390fd5b5f61141c8a612b8b565b9050805f0151985080602001519750806040015196508060600151955080608001519450611449846126da565b935083516001600160401b0381111561146457611464614f3a565b60405190808252806020026020018201604052801561148d578160200160208202803683370190505b5092505f5b84518110156114ed576114be8b8683815181106114b1576114b1615b0a565b602002602001015161300e565b8482815181106114d0576114d0615b0a565b6020908102919091010152806114e581615b32565b915050611492565b505050919395979092949650565b611503612952565b5f61150d600f5490565b9050805f0361152f576040516327d190b160e11b815260040160405180910390fd5b5f61153b600183615b5d565b90505f61154782612b8b565b90505f8160800151118061155c575060115482105b1561157a57604051633440594960e21b815260040160405180910390fd5b600f8054905f61158983615b87565b90915550505f828152600d6020526040808220805460ff1916815560018101839055600281018390556003810183905560040191909155517fc575d70441d376437764ff913c8a93268fd5b38b4442c068c2e7f8bdeb81bc0b906115f09084815260200190565b60405180910390a1604080515f815260208101909152611611908390613034565b505050565b61161e612952565b600154600160a01b900460ff1615611649576040516303ed52c560e41b815260040160405180910390fd5b61125c8282613118565b5f61165c612952565b611664612a04565b6001600160a01b03841661168b576040516311d000e160e31b815260040160405180910390fd5b5f611695846110ea565b9050808311156116b8576040516305d632b960e41b815260040160405180910390fd5b6116c38585856131a9565b836001600160a01b0316856001600160a01b03167f13e06184555481b6d2cb327155e8d2e1d0b1f0252a7fe6621e32cf99881488358560405161170891815260200190565b60405180910390a36001915050610daa6001600355565b611727612952565b805f5b81811015611978575f84848381811061174557611745615b0a565b90506020028101906117579190615b9c565b359050611763600f5490565b81146117825760405163b929687760e01b815260040160405180910390fd5b600f8054905f61179183615b32565b91905055506118708585848181106117ab576117ab615b0a565b90506020028101906117bd9190615b9c565b358686858181106117d0576117d0615b0a565b90506020028101906117e29190615b9c565b6117f39060408101906020016156ea565b87878681811061180557611805615b0a565b90506020028101906118179190615b9c565b6040013588888781811061182d5761182d615b0a565b905060200281019061183f9190615b9c565b6060013589898881811061185557611855615b0a565b90506020028101906118679190615b9c565b60800135612d09565b61196585858481811061188557611885615b0a565b90506020028101906118979190615b9c565b358686858181106118aa576118aa615b0a565b90506020028101906118bc9190615b9c565b6118ca9060a0810190615bba565b808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152508a925089915087905081811061190f5761190f615b0a565b90506020028101906119219190615b9c565b61192f9060c0810190615bba565b808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250612e4a92505050565b508161197081615b32565b92505061172a565b50505050565b611986612a04565b60165433906301000000900460ff166119b25760405163fe9538cf60e01b815260040160405180910390fd5b6119c08187878787876131de565b6119cd81878787876132d6565b506110aa6001600355565b6119e0612952565b5f5b815181101561125c575f8282815181106119fe576119fe615b0a565b60200260200101519050611a29816001600160a01b03165f9081526006602052604090205460ff1690565b15611a7457611a37816133d8565b6040516001600160a01b03821681527f85a3e72f8dd6db3794f93109c3c5f5b79d6112f6979431c45f98b26134b42af29060200160405180910390a15b5080611a7f81615b32565b9150506119e2565b848282335f611a9585612b8b565b805190915015611ac057611aaa8285856134c7565b611ab382612c0d565b611ac057611ac0826134ef565b611ac8612a04565b60165462010000900460ff16611af15760405163fe9538cf60e01b815260040160405180910390fd5b611afd338b8b8b612c2a565b611b076001600355565b50505050505050505050565b5f610daa838361353a565b611b26612952565b610ef88161356d565b611b376129db565b15611b5557604051635b0999e760e01b815260040160405180910390fd5b611b5d612952565b600454600160a81b900460ff1615611b88576040516330cc939360e11b815260040160405180910390fd5b6004805460ff60a81b1916600160a81b1790556040517fb3211088ecd5ab1725233a5886071950e736a017c35ec09f1539974e3b81d299905f90a1565b611bcd612952565b611bd65f6135c3565b565b60015433906001600160a01b03168114611c155760405163118cdaa760e01b81526001600160a01b03821660048201526024015b60405180910390fd5b610ef8816135c3565b611c26612952565b610ef8816135dc565b611c37612952565b5f5b8181101561161157611c70838383818110611c5657611c56615b0a565b9050602002016020810190611c6b9190615183565b6134ef565b80611c7a81615b32565b915050611c39565b611c8a612952565b611c92612a73565b811080611ca05750600f5481115b15611cbe5760405163b929687760e01b815260040160405180910390fd5b610ef88161360e565b81611cd181612cf6565b15611cef576040516327d190b160e11b815260040160405180910390fd5b611cf7612952565b6116118383613034565b6001600160a01b0381165f9081526006602052604081205460ff166111bd565b606080611d2d836126da565b915081516001600160401b03811115611d4857611d48614f3a565b604051908082528060200260200182016040528015611d71578160200160208202803683370190505b5090505f5b8251811015611de557611db6838281518110611d9457611d94615b0a565b60200260200101516001600160a01b03165f9081526012602052604090205490565b828281518110611dc857611dc8615b0a565b602090810291909101015280611ddd81615b32565b915050611d76565b50915091565b5f610daa8383613643565b611dfe612952565b610ef88161366b565b611e0f612952565b5f5b8181101561161157611e48838383818110611e2e57611e2e615b0a565b9050602002016020810190611e439190615183565b6136ac565b80611e5281615b32565b915050611e11565b611e62612952565b610ef8816136f4565b611e73612952565b610ef881613735565b5f8083611e888161271a565b610e80868686613776565b5f611ea66002546001600160a01b031690565b6001600160a01b031603611ecd5760405163488afa1b60e01b815260040160405180910390fd5b611ed5612952565b600154600160a01b900460ff1615611f00576040516303ed52c560e41b815260040160405180910390fd5b6001805460ff60a01b1916600160a01b1790556040517f9efd8729cc972ca7884de34bbf48c2823c0d69fda2946916c65311c81c4c52c3905f90a1565b6013546001600160a01b038581165f908152601460205260408120549092610100900490911690611f94907f000000000000000000000000000000000000000000000000000000000000000190889088888861378e565b6001600160a01b031614611fa957505f611fad565b5060015b949350505050565b611fbd612952565b8015611fcb57610ef86137d8565b610ef8613832565b82611fdc61386b565b6011548114611ffe57604051633b47fcf960e11b815260040160405180910390fd5b5f61200882612b8b565b9050806060015181608001511061203257604051633b47fcf960e11b815260040160405180910390fd5b80602001515f0361205657604051633b47fcf960e11b815260040160405180910390fd5b806020015142108061207b5750806040015181602001516120779190615b4a565b4210155b1561209957604051633b47fcf960e11b815260040160405180910390fd5b6120a1613984565b336120ae818787876139a8565b6120b88686613a9c565b94506120c5818787613ab8565b85816001600160a01b03167fc5c52c2a9175470464d5ea4429889e7df2ea88630a3d32f4d0d3d2d4486562108760405161210191815260200190565b60405180910390a3505050505050565b60606110e5613b43565b60605f612127600f5490565b9050806001600160401b0381111561214157612141614f3a565b6040519080825280602002602001820160405280156121af57816020015b61219c6040518060e001604052805f151581526020015f81526020015f81526020015f81526020015f815260200160608152602001606081525090565b81526020019060019003908161215f5790505b5091505f6121bb613b43565b90505f5b828110156122e4575f6121d182612b8b565b90505f83516001600160401b038111156121ed576121ed614f3a565b604051908082528060200260200182016040528015612216578160200160208202803683370190505b5090505f5b84518110156122695761223a848683815181106114b1576114b1615b0a565b82828151811061224c5761224c615b0a565b60209081029190910101528061226181615b32565b91505061221b565b506040518060e00160405280835f01511515815260200183602001518152602001836040015181526020018360600151815260200183608001518152602001858152602001828152508684815181106122c4576122c4615b0a565b6020026020010181905250505080806122dc90615b32565b9150506121bf565b50505090565b826122f481612cf6565b15612312576040516327d190b160e11b815260040160405180910390fd5b61231a612952565b611978848484612e4a565b61232d612952565b600f54891461234f5760405163b929687760e01b815260040160405180910390fd5b600f8054905f61235e83615b32565b91905055506123708989898989612d09565b6112d8898585808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250506040805160208089028281018201909352888252909350889250879182918501908490808284375f92019190915250612e4a92505050565b5f805f6123f360015460ff600160a01b9091041690565b92506124076002546001600160a01b031690565b600254909250600160a01b900460ff169050909192565b60165433906301000000900460ff1661244a5760405163fe9538cf60e01b815260040160405180910390fd5b612458818b8b8b8b876131de565b611b07818b8b8b8b8b8b8b8b613ba3565b612471612952565b610ef881613bc9565b888282335f61248885612b8b565b8051909150156124b35761249d8285856134c7565b6124a682612c0d565b6124b3576124b3826134ef565b60165462010000900460ff166124dc5760405163fe9538cf60e01b815260040160405180910390fd5b6124ec338f8f8f8f8f8f8f612fe9565b5050505050505050505050505050565b606080612508836126da565b915081516001600160401b0381111561252357612523614f3a565b60405190808252806020026020018201604052801561254c578160200160208202803683370190505b5090505f5b8251811015611de55761259183828151811061256f5761256f615b0a565b60200260200101516001600160a01b03165f9081526007602052604090205490565b8282815181106125a3576125a3615b0a565b6020908102919091010152806125b881615b32565b915050612551565b6125c8612952565b600180546001600160a01b0383166001600160a01b031990911681179091556125f85f546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b612638612952565b61197884848484613cc0565b604080516001600160a01b03851660208201525f9182910160408051601f19818403018152828252805160209182012090830152016040516020818303038152906040528051906020012090506126d18484808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050600b549150849050613d5f565b95945050505050565b606081515f036126ec576111bd613b43565b5090565b6001600160a01b039182165f90815260086020908152604080832093909416825291909152205490565b6001600160a01b0381165f9081526006602052604090205460ff16610ef857604051631e50c79f60e01b81526001600160a01b0382166004820152602401611c0c565b5f80612769858561300e565b90506127758184613643565b9150935093915050565b604051623f675f60e91b81526001600160a01b0388811660048301525f91908a1690637ecebe0090602401602060405180830381865afa1580156127c5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127e99190615b70565b60405163d505accf60e01b81526001600160a01b038a811660048301528981166024830152604482018990526064820188905260ff8716608483015260a4820186905260c48201859052919250908a169063d505accf9060e4015f604051808303815f87803b15801561285a575f80fd5b505af115801561286c573d5f803e3d5ffd5b5050604051623f675f60e91b81526001600160a01b038b811660048301525f93508c169150637ecebe0090602401602060405180830381865afa1580156128b5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128d99190615b70565b90506128e6826001615b4a565b8114611b075760405163ceeaf5db60e01b81526001600160a01b038b166004820152602401611c0c565b61291b838383613d74565b612923613ff2565b1580156129325750600c5460ff165b8015612944575061294283612c0d565b155b1561161157611611836134ef565b5f546001600160a01b03163314611bd65760405163118cdaa760e01b8152336004820152602401611c0c565b60048054610100600160a81b0319166101006001600160a01b038416908102919091179091556040519081527ff6215f245bfd24e51265c56ef650fdd856aa4ece6221ee1ef395bbe0a5558010906020015b60405180910390a150565b5f806129f56004546001600160a01b036101009091041690565b6001600160a01b031614905090565b600260035403612a2757604051633ee5aeb560e01b815260040160405180910390fd5b6002600355565b6001600160a01b0381165f90815260126020526040902054801561125c57612a56828261403a565b60045461125c9061010090046001600160a01b03165b838361406a565b5f80612a7e60115490565b90505f612a8a600f5490565b9050805f03612a9b575f9250505090565b808210612aa85792915050565b5f612ab283612b8b565b905080602001515f03612ac757509092915050565b80604001518160200151612adb9190615b4a565b4210612af457612aec836001615b4a565b935050505090565b8060600151816080015110612b0e57612aec836001615b4a565b509092915050565b60408051602081018a90526001600160a01b03808a1692820192909252606081018890526080810187905290851660a082015260c0810184905260e081018390525f90612b7e90610100015b60405160208183030381529060405280519060200120836140f6565b9998505050505050505050565b612bba6040518060a001604052805f151581526020015f81526020015f81526020015f81526020015f81525090565b505f908152600d6020908152604091829020825160a081018452815460ff161515815260018201549281019290925260028101549282019290925260038201546060820152600490910154608082015290565b6001600160a01b03165f908152600a602052604090205460ff1690565b6119788484848461411e565b612c408282614223565b612c48613ff2565b158015612c575750600c5460ff165b8015612c675750612c6782612c0d565b1561125c5761125c826136ac565b5f6001600160a01b038216612c8b575047919050565b6040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015612ccd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111bd9190615b70565b919050565b5f612d00600f5490565b90911015919050565b5f858152600d60205260409020805460ff191685151517815560115486148015612d3657505f8160010154115b8015612d46575080600101544210155b15612d645760405163795792b760e01b815260040160405180910390fd5b5f86118015612d7257505f84115b15612dc3575f612d8b612d86600189615b5d565b612b8b565b905080604001518160200151612da19190615b4a565b851015612dc15760405163795792b760e01b815260040160405180910390fd5b505b60018101849055600281018390556004810154821015612df657604051632f56c88b60e11b815260040160405180910390fd5b600381018290556040805160a081018252825460ff1615158152600183015460208201526002830154918101919091526060810183905260048201546080820152612e429087906142d7565b505050505050565b8051825114612e6c576040516328e7033560e01b815260040160405180910390fd5b5f5b825181101561197857612e99838281518110612e8c57612e8c615b0a565b602002602001015161271a565b818181518110612eab57612eab615b0a565b6020026020010151600e5f8681526020019081526020015f205f858481518110612ed757612ed7615b0a565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f20819055507f28af1bd3648c38edc812346775bcc36a5f5fc9870d47c8c69a0e2ec0445afaae84848381518110612f3657612f36615b0a565b6020026020010151848481518110612f5057612f50615b0a565b6020026020010151604051612f81939291909283526001600160a01b03919091166020830152604082015260600190565b60405180910390a180612f9381615b32565b915050612e6e565b601580546001600160a01b0319166001600160a01b0383169081179091556040519081527ff4e654cefbf8c2fe608d6868b30dfaf564ff18be7ac7404b5c06b2667f8a4608906020016129d0565b6130026001600160a01b0387168930888888888861277f565b61120b8888888861411e565b5f918252600e602090815260408084206001600160a01b03909316845291905290205490565b61303d816126da565b90505f5b815181101561161157600e5f8481526020019081526020015f205f83838151811061306e5761306e615b0a565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f205f90557f28af1bd3648c38edc812346775bcc36a5f5fc9870d47c8c69a0e2ec0445afaae838383815181106130cc576130cc615b0a565b60200260200101515f6040516130fe939291909283526001600160a01b03919091166020830152604082015260600190565b60405180910390a18061311081615b32565b915050613041565b8060ff165f0361313b57604051638bd9be8160e01b815260040160405180910390fd5b600280546001600160a01b0384166001600160a81b03199091168117600160a01b60ff8516908102919091179092556040805191825260208201929092527fc9cbf5af9e78df9093a676978e83f2a04e258ad3b0d8fa81cb44b357c657606a91015b60405180910390a15050565b6001600160a01b0382166131ca576116116001600160a01b03841682614333565b6116116001600160a01b03831684836143c6565b6131e6614425565b6001600160a01b038087165f9081526014602052604090205460135490916101009091041661323b7f000000000000000000000000000000000000000000000000000000000000000189848a8a8a8a8a612b16565b6001600160a01b0316146132625760405163ea8e4eb560e01b815260040160405180910390fd5b6001600160a01b0387165f90815260146020526040812080546001929061328a908490615b4a565b90915550506040518181526001600160a01b038816907f3e266ebc4c4f97d954ad3c456dd72416a5e3a203f706c4d0682085901b1ad0ed9060200160405180910390a250505050505050565b826132e08161271a565b846132e961386b565b601154811461330b57604051633b47fcf960e11b815260040160405180910390fd5b5f61331582612b8b565b9050806060015181608001511061333f57604051633b47fcf960e11b815260040160405180910390fd5b80602001515f0361336357604051633b47fcf960e11b815260040160405180910390fd5b80602001514210806133885750806040015181602001516133849190615b4a565b4210155b156133a657604051633b47fcf960e11b815260040160405180910390fd5b6133ae613984565b5f806133bb898888614453565b915091506133ca8a898361448d565b611b078a8a8a85858c614646565b5f6133ea826133e5613b43565b6146ba565b6005549091505f906133fe90600190615b5d565b90505f6005828154811061341457613414615b0a565b5f91825260209091200154600580546001600160a01b03909216925082918590811061344257613442615b0a565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b03160217905550600580548061347e5761347e615bff565b5f828152602080822083015f1990810180546001600160a01b03191690559092019092556001600160a01b03959095168152600690945250506040909120805460ff1916905550565b6134d2838383612644565b61161157604051630b094f2760e31b815260040160405180910390fd5b6001600160a01b0381165f818152600a6020526040808220805460ff19166001179055517fd5a6b3454d1aa211b5b7e99c94012bed9883c7695b5c58fd97e20f425ec0b5bf9190a250565b5f61354483614736565b613562828461355d60025460ff600160a01b9091041690565b614756565b90506111bd81614776565b60138054610100600160a81b0319166101006001600160a01b038416908102919091179091556040519081527f5553331329228fbd4123164423717a4a7539f6dfa1c3279a923b98fd681a6c73906020016129d0565b600180546001600160a01b0319169055610ef881614796565b600b81905560405181907f90004c04698bc3322499a575ed3752dd4abf33e0a7294c06a787a0fe01bea941905f90a250565b60118190556040518181527fc7941f8838c3de40641130e81ef820f45856796923e29f642fb8680ea13c1a3b906020016129d0565b5f61364d83614736565b613562828461366660025460ff600160a01b9091041690565b6147e5565b6009805460ff19168215159081179091556040519081527f080b99e58282ae4610da5730d7f94b53b3a2e6c83e0a471b89874fdd2e33515f906020016129d0565b6001600160a01b0381165f818152600a6020526040808220805460ff19169055517f79bc96e8fb893a32b4429ae9fd9084cdb98be910b9c7cdbb4307cc96732c7ec79190a250565b600c805460ff19168215159081179091556040519081527f2e66025596ef0553cb2e878a0f141264297c58f317ab613bf6dbe35a8be44cf3906020016129d0565b6013805460ff19168215159081179091556040519081527fcbb1b2ab6aab49b57a6ea192e0596dffffe7c371b29eb63ef80bd5840daa9bb5906020016129d0565b5f80613782858561300e565b9050612775818461353a565b60408051602081018890526001600160a01b03871691810191909152606081018590526080810184905260a081018390525f906137cd9060c001612b62565b979650505050505050565b6137e0613984565b6004805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586138153390565b6040516001600160a01b03909116815260200160405180910390a1565b61383a6147fc565b6004805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33613815565b5f61387560115490565b90505f613880612a73565b90508181111561125c576138938161360e565b5f818152600d602052604081206001810154909103611611575f6138bb612d86600185615b5d565b9050806060015181608001511061391c5742600183018190556040805160a081018252845460ff1615158152602081019290925260028401549082015260038301546060820152600483015460808201526139179084906142d7565b611978565b602081015115611978578060400151816020015161393a9190615b4a565b600183018190556040805160a081018252845460ff1615158152602081019290925260028401549082015260038301546060820152600483015460808201526119789084906142d7565b60045460ff1615611bd65760405163d93c066560e01b815260040160405180910390fd5b6139b0614425565b6001600160a01b038085165f90815260146020526040902054601354909161010090910416613a037f0000000000000000000000000000000000000000000000000000000000000001878488888861378e565b6001600160a01b031614613a2a5760405163ea8e4eb560e01b815260040160405180910390fd5b6001600160a01b0385165f908152601460205260408120805460019290613a52908490615b4a565b90915550506040518181526001600160a01b038616907f3e266ebc4c4f97d954ad3c456dd72416a5e3a203f706c4d0682085901b1ad0ed9060200160405180910390a25050505050565b5f80613aa78461481f565b905080831115612b0e579392505050565b613ac28282614840565b613aca61490d565b60405163d279792d60e01b81526001600160a01b0385811660048301526024820185905260448201849052919091169063d279792d906064016020604051808303815f875af1158015613b1f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119789190615c13565b60606005805480602002602001604051908101604052809291908181526020018280548015613b9957602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311613b7b575b5050505050905090565b613bbc6001600160a01b0388168a30888888888861277f565b6112d889898989896132d6565b5f5b815181101561125c575f828281518110613be757613be7615b0a565b60200260200101519050613c12816001600160a01b03165f9081526006602052604090205460ff1690565b613cad576005805460018082019092557f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00180546001600160a01b0319166001600160a01b0384169081179091555f81815260066020908152604091829020805460ff1916909417909355519081527fa317c10673baf4f03b3c1041bd5ddbb537d0333a86fec3607c75f9dbb630f48f910160405180910390a15b5080613cb881615b32565b915050613bcb565b6016805461ffff191685151561ff00198116919091176101008615159081029190911763ffff000019166201000086151590810263ff0000001916919091176301000000861515908102919091179094556040805193845260208401929092529082015260608101919091527fae3d63e035fb1d35cb2faa50509104c27a5f07f4fa920f7a05bd31ed4f5e0b0d9060800160405180910390a150505050565b5f82613d6b8584614948565b14949350505050565b81613d7e8161271a565b613d86613984565b60095460ff16613da957604051630e2f42c960e31b815260040160405180910390fd5b815f03613dc957604051632160733960e01b815260040160405180910390fd5b6001600160a01b038316613dfc57813414613df757604051632160733960e01b815260040160405180910390fd5b613f36565b3415613e1b57604051632160733960e01b815260040160405180910390fd5b6040516370a0823160e01b815230600482015283905f906001600160a01b038316906370a0823190602401602060405180830381865afa158015613e61573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613e859190615b70565b9050613e9c6001600160a01b038316873087614994565b6040516370a0823160e01b81523060048201525f9082906001600160a01b038516906370a0823190602401602060405180830381865afa158015613ee2573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613f069190615b70565b613f109190615b5d565b9050848114613f3257604051632160733960e01b815260040160405180910390fd5b5050505b6001600160a01b0383165f9081526007602052604081208054849290613f5d908490615b4a565b90915550506001600160a01b038085165f90815260086020908152604080832093871683529290529081208054849290613f98908490615b4a565b92505081905550826001600160a01b0316846001600160a01b03167fc67994043444b22a702bf75a242bcc1720705569fb433fe91272cbf8d0d99c1084604051613fe491815260200190565b60405180910390a350505050565b5f80613ffd600f5490565b1115614035575f61400d5f612b8b565b90505f8160200151118015614026575080602001514210155b1561403357600191505090565b505b505f90565b6001600160a01b0382165f9081526012602052604081208054839290614061908490615b5d565b90915550505050565b6001600160a01b0382166140905761408b6001600160a01b03841682614333565b6140a4565b6140a46001600160a01b03831684836143c6565b816001600160a01b0316836001600160a01b03167fa77f6af926d34e43c5b52329799fa34e6d594dea2777723745b977d1d6e271c3836040516140e991815260200190565b60405180910390a3505050565b5f805f8061410486866149cd565b9250925092506141148282614a16565b5090949350505050565b816141288161271a565b8361413161386b565b601154811461415357604051633b47fcf960e11b815260040160405180910390fd5b5f61415d82612b8b565b9050806060015181608001511061418757604051633b47fcf960e11b815260040160405180910390fd5b80602001515f036141ab57604051633b47fcf960e11b815260040160405180910390fd5b80602001514210806141d05750806040015181602001516141cc9190615b4a565b4210155b156141ee57604051633b47fcf960e11b815260040160405180910390fd5b6141f6613984565b5f805f614204898989614ace565b9250925092506142158a898461448d565b611b078a8a8a868686614646565b5f5b8151811015611611575f82828151811061424157614241615b0a565b602002602001015190505f61425685836126f0565b905080156142c257614269858383614b11565b6142748583836131a9565b816001600160a01b0316856001600160a01b03167f933380dab6ffd07b472a6e643e8349744d3263e3b449d12dcd616b69c7312f35836040516142b991815260200190565b60405180910390a35b505080806142cf90615b32565b915050614225565b80516020808301516040808501516060808701518351898152961515958701959095529185019290925283015260808201527f600c4518738a76b007c7da3f323d8a50d668dde43b2b5f2fd3cd4689c1e261339060a00161319d565b804710156143565760405163cd78605960e01b8152306004820152602401611c0c565b5f826001600160a01b0316826040515f6040518083038185875af1925050503d805f811461439f576040519150601f19603f3d011682016040523d82523d5f602084013e6143a4565b606091505b505090508061161157604051630a12f52160e11b815260040160405180910390fd5b6040516001600160a01b0383811660248301526044820183905261161191859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050614ba7565b60135461010090046001600160a01b0316611bd65760405163327effa960e11b815260040160405180910390fd5b5f8161445f8482613643565b91505f61446b8661481f565b905080831115610e8557809250614482858461353a565b915050935093915050565b805f61449985856126f0565b905080156144d0575f8382106144b357505f9150826144c3565b6144bd8285615b5d565b92508190505b6144ce868683614b11565b505b81156110aa576001600160a01b03841661452f57813410156145055760405163356680b760e01b815260040160405180910390fd5b8134111561452a5761452a61451a8334615b5d565b6001600160a01b03871690614333565b6110aa565b6040516370a0823160e01b815230600482015284905f906001600160a01b038316906370a0823190602401602060405180830381865afa158015614575573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906145999190615b70565b90506145b06001600160a01b038316883087614994565b6040516370a0823160e01b81523060048201525f9082906001600160a01b038516906370a0823190602401602060405180830381865afa1580156145f6573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061461a9190615b70565b6146249190615b5d565b905084811461120b57604051632160733960e01b815260040160405180910390fd5b6146508483614c08565b61465b868685613ab8565b836001600160a01b031685876001600160a01b03167f62e796e00a8e66154d78da76daae129635b4795a6e1b889f2caa6c5cea22ac6884876040516146aa929190918252602082015260400190565b60405180910390a4505050505050565b5f805b825181101561471157836001600160a01b03168382815181106146e2576146e2615b0a565b60200260200101516001600160a01b0316036146ff5790506111bd565b8061470981615b32565b9150506146bd565b50604051631e50c79f60e01b81526001600160a01b0384166004820152602401611c0c565b805f03610ef85760405163e9589bbd60e01b815260040160405180910390fd5b5f61476282600a615d06565b61476c8486615d14565b610da79190615d2b565b805f03610ef8576040516334cf99ff60e11b815260040160405180910390fd5b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f826147f283600a615d06565b61476c9086615d14565b60045460ff16611bd657604051638dfc202b60e01b815260040160405180910390fd5b5f8061482a83612b8b565b905080608001518160600151610daa9190615b5d565b5f828152600d6020526040812090508160105f8282546148609190615b4a565b9250508190555081816004015f82825461487a9190615b4a565b90915550506003810154600482015410611611575f601154600161489e9190615b4a565b90506148a98161360e565b5f818152600d6020526040812060018101549091036110aa5742600182018190556040805160a081018252835460ff1615158152602081019290925260028301549082015260038201546060820152600482015460808201526110aa9083906142d7565b6015545f906001600160a01b03166149385760405163309a778560e11b815260040160405180910390fd5b506015546001600160a01b031690565b5f81815b845181101561498c576149788286838151811061496b5761496b615b0a565b6020026020010151614ca8565b91508061498481615b32565b91505061494c565b509392505050565b6040516001600160a01b0384811660248301528381166044830152606482018390526119789186918216906323b872dd906084016143f3565b5f805f8351604103614a04576020840151604085015160608601515f1a6149f688828585614cd4565b955095509550505050614a0f565b505081515f91506002905b9250925092565b5f826003811115614a2957614a29615d4a565b03614a32575050565b6001826003811115614a4657614a46615d4a565b03614a645760405163f645eedf60e01b815260040160405180910390fd5b6002826003811115614a7857614a78615d4a565b03614a995760405163fce698f760e01b815260048101829052602401611c0c565b6003826003811115614aad57614aad615d4a565b0361125c576040516335e2f38360e21b815260048101829052602401611c0c565b5f8181614adc86868461275d565b90935090505f614aeb8761481f565b905080841115614b0757809350614b03878786613776565b5092505b5093509350939050565b614b1b83836126f0565b811115614b3b57604051631650c97f60e11b815260040160405180910390fd5b6001600160a01b0382165f9081526007602052604081208054839290614b62908490615b5d565b90915550506001600160a01b038084165f90815260086020908152604080832093861683529290529081208054839290614b9d908490615b5d565b9091555050505050565b5f614bbb6001600160a01b03841683614d9c565b905080515f14158015614bdf575080806020019051810190614bdd9190615c13565b155b1561161157604051635274afe760e01b81526001600160a01b0384166004820152602401611c0c565b816001600160a01b03167fd98279195c58b6b93d53ab20cbd8ad7d353cfdcbcc0f514dc4a0f114f1e3f0e682604051614c4391815260200190565b60405180910390a260135460ff168015614c625750614c606129db565b155b15614c815760045461125c9061010090046001600160a01b0316612a6c565b6001600160a01b0382165f9081526012602052604081208054839290614061908490615b4a565b5f818310614cc2575f828152602084905260409020610daa565b5f838152602083905260409020610daa565b5f80807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115614d0d57505f91506003905082614d92565b604080515f808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015614d5e573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b038116614d8957505f925060019150829050614d92565b92505f91508190505b9450945094915050565b6060610daa83835f845f80856001600160a01b03168486604051614dc09190615d5e565b5f6040518083038185875af1925050503d805f8114614dfa576040519150601f19603f3d011682016040523d82523d5f602084013e614dff565b606091505b509150915061101a868383606082614e1f57614e1a82614e66565b610daa565b8151158015614e3657506001600160a01b0384163b155b15614e5f57604051639996b31560e01b81526001600160a01b0385166004820152602401611c0c565b5080610daa565b805115614e765780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b80356001600160a01b0381168114612cf1575f80fd5b5f8083601f840112614eb5575f80fd5b5081356001600160401b03811115614ecb575f80fd5b6020830191508360208260051b8501011115614ee5575f80fd5b9250929050565b5f805f60408486031215614efe575f80fd5b614f0784614e8f565b925060208401356001600160401b03811115614f21575f80fd5b614f2d86828701614ea5565b9497909650939450505050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b0381118282101715614f7657614f76614f3a565b604052919050565b5f6001600160401b03821115614f9657614f96614f3a565b5060051b60200190565b5f82601f830112614faf575f80fd5b81356020614fc4614fbf83614f7e565b614f4e565b82815260059290921b84018101918181019086841115614fe2575f80fd5b8286015b8481101561500457614ff781614e8f565b8352918301918301614fe6565b509695505050505050565b5f8060408385031215615020575f80fd5b61502983614e8f565b915060208301356001600160401b03811115615043575f80fd5b61504f85828601614fa0565b9150509250929050565b5f8151808452602080850194508084015f5b838110156150905781516001600160a01b03168752958201959082019060010161506b565b509495945050505050565b5f8151808452602080850194508084015f5b83811015615090578151875295820195908201906001016150ad565b604081525f6150db6040830185615059565b82810360208401526126d1818561509b565b5f805f606084860312156150ff575f80fd5b8335925061510f60208501614e8f565b9150604084013590509250925092565b803560ff81168114612cf1575f80fd5b5f805f805f8060c08789031215615144575f80fd5b61514d87614e8f565b955060208701359450604087013593506151696060880161511f565b92506080870135915060a087013590509295509295509295565b5f60208284031215615193575f80fd5b610daa82614e8f565b5f602082840312156151ac575f80fd5b81356001600160401b038111156151c1575f80fd5b611fad84828501614fa0565b5f82601f8301126151dc575f80fd5b81356001600160401b038111156151f5576151f5614f3a565b615208601f8201601f1916602001614f4e565b81815284602083860101111561521c575f80fd5b816020850160208301375f918101602001919091529392505050565b5f805f805f8060c0878903121561524d575f80fd5b61525687614e8f565b95506020870135945061526b60408801614e8f565b9350606087013592506080870135915060a08701356001600160401b03811115615293575f80fd5b61529f89828a016151cd565b9150509295509295509295565b5f5b838110156152c65781810151838201526020016152ae565b50505f910152565b602081525f82518060208401526152ec8160408501602087016152ac565b601f01601f19169190910160400192915050565b8015158114610ef8575f80fd5b5f82601f83011261531c575f80fd5b8135602061532c614fbf83614f7e565b82815260059290921b8401810191818101908684111561534a575f80fd5b8286015b84811015615004578035835291830191830161534e565b5f805f805f805f60e0888a03121561537b575f80fd5b87359650602088013561538d81615300565b955060408801359450606088013593506080880135925060a08801356001600160401b03808211156153bd575f80fd5b6153c98b838c01614fa0565b935060c08a01359150808211156153de575f80fd5b506153eb8a828b0161530d565b91505092959891949750929550565b5f806040838503121561540b575f80fd5b61541483614e8f565b946020939093013593505050565b5f805f805f805f60e0888a031215615438575f80fd5b8735965061544860208901614e8f565b955060408801359450606088013593506154646080890161511f565b925060a0880135915060c0880135905092959891949750929550565b5f8060408385031215615491575f80fd5b82356001600160401b03808211156154a7575f80fd5b6154b386838701614fa0565b935060208501359150808211156154c8575f80fd5b5061504f85828601614fa0565b5f602082840312156154e5575f80fd5b5035919050565b871515815286602082015285604082015284606082015283608082015260e060a08201525f61551e60e0830185615059565b82810360c0840152615530818561509b565b9a9950505050505050505050565b5f806040838503121561554f575f80fd5b61555883614e8f565b91506155666020840161511f565b90509250929050565b5f805f60608486031215615581575f80fd5b61558a84614e8f565b925061510f60208501614e8f565b5f80602083850312156155a9575f80fd5b82356001600160401b038111156155be575f80fd5b6155ca85828601614ea5565b90969095509350505050565b5f805f805f60a086880312156155ea575f80fd5b853594506155fa60208701614e8f565b9350604086013592506060860135915060808601356001600160401b03811115615622575f80fd5b61562e888289016151cd565b9150509295509295909350565b5f805f805f6080868803121561564f575f80fd5b8535945061565f60208701614e8f565b93506040860135925060608601356001600160401b03811115615680575f80fd5b61568c88828901614ea5565b969995985093965092949392505050565b5f80604083850312156156ae575f80fd5b50508035926020909101359150565b5f80604083850312156156ce575f80fd5b8235915060208301356001600160401b03811115615043575f80fd5b5f602082840312156156fa575f80fd5b8135610daa81615300565b5f805f8060808587031215615718575f80fd5b61572185614e8f565b9350602085013592506040850135915060608501356001600160401b03811115615749575f80fd5b615755878288016151cd565b91505092959194509250565b5f805f60608486031215615773575f80fd5b833592506020840135915060408401356001600160401b03811115615796575f80fd5b6157a2868287016151cd565b9150509250925092565b602081525f610daa6020830184615059565b5f6020808301818452808551808352604092508286019150828160051b8701018488015f805b8481101561589957898403603f19018652825180511515855288810151898601528781015188860152606080820151908601526080808201519086015260a08082015160e09187018290528051918701829052610100870191908b0190855b818110156158685782516001600160a01b03168452928c0192918c0191600101615843565b50505060c08083015192508682038188015250615885818361509b565b978a019795505050918701916001016157e4565b50919998505050505050505050565b5f805f606084860312156158ba575f80fd5b8335925060208401356001600160401b03808211156158d7575f80fd5b6158e387838801614fa0565b935060408601359150808211156158f8575f80fd5b506157a28682870161530d565b5f805f805f805f805f60e08a8c03121561591d575f80fd5b8935985060208a013561592f81615300565b975060408a0135965060608a0135955060808a0135945060a08a01356001600160401b038082111561595f575f80fd5b61596b8d838e01614ea5565b909650945060c08c0135915080821115615983575f80fd5b506159908c828d01614ea5565b915080935050809150509295985092959850929598565b5f805f805f805f805f6101208a8c0312156159c0575f80fd5b893598506159d060208b01614e8f565b975060408a0135965060608a0135955060808a013594506159f360a08b0161511f565b935060c08a0135925060e08a013591506101008a01356001600160401b03811115615a1c575f80fd5b615a288c828d016151cd565b9150509295985092959850929598565b5f805f805f805f805f6101008a8c031215615a51575f80fd5b89359850615a6160208b01614e8f565b975060408a0135965060608a01359550615a7d60808b0161511f565b945060a08a0135935060c08a0135925060e08a01356001600160401b03811115615aa5575f80fd5b6159908c828d01614ea5565b5f805f8060808587031215615ac4575f80fd5b8435615acf81615300565b93506020850135615adf81615300565b92506040850135615aef81615300565b91506060850135615aff81615300565b939692955090935050565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b5f60018201615b4357615b43615b1e565b5060010190565b808201808211156111bd576111bd615b1e565b818103818111156111bd576111bd615b1e565b5f60208284031215615b80575f80fd5b5051919050565b5f81615b9557615b95615b1e565b505f190190565b5f823560de19833603018112615bb0575f80fd5b9190910192915050565b5f808335601e19843603018112615bcf575f80fd5b8301803591506001600160401b03821115615be8575f80fd5b6020019150600581901b3603821315614ee5575f80fd5b634e487b7160e01b5f52603160045260245ffd5b5f60208284031215615c23575f80fd5b8151610daa81615300565b600181815b80851115610e6157815f1904821115615c4e57615c4e615b1e565b80851615615c5b57918102915b93841c9390800290615c33565b5f82615c76575060016111bd565b81615c8257505f6111bd565b8160018114615c985760028114615ca257615cbe565b60019150506111bd565b60ff841115615cb357615cb3615b1e565b50506001821b6111bd565b5060208310610133831016604e8410600b8410161715615ce1575081810a6111bd565b615ceb8383615c2e565b805f1904821115615cfe57615cfe615b1e565b029392505050565b5f610daa60ff841683615c68565b80820281158282048414176111bd576111bd615b1e565b5f82615d4557634e487b7160e01b5f52601260045260245ffd5b500490565b634e487b7160e01b5f52602160045260245ffd5b5f8251615bb08184602087016152ac56fea2646970667358221220c52dc068b18ad600a3ef08184aef45b97e64aa1b94a361345300f95f628ef71564736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000b25766a96a90c251c328b73965d6931ea9a469550000000000000000000000000000000000000000000000000000000000000012
-----Decoded View---------------
Arg [0] : sellableToken (address): 0xB25766a96a90c251C328b73965D6931EA9A46955
Arg [1] : sellableTokenDecimals (uint8): 18
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000b25766a96a90c251c328b73965d6931ea9a46955
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000012
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.