ETH Price: $3,451.39 (+3.13%)

Transaction Decoder

Block:
23590859 at Oct-16-2025 02:36:35 PM +UTC
Transaction Fee:
0.000182155776473952 ETH $0.63
Gas Used:
88,692 Gas / 2.053801656 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x3DA941CB...9962A247f 0.004957127378047337 Eth0.038957127378047337 Eth0.034
(Titan Builder)
18.256223279112419993 Eth18.256356317112419993 Eth0.000133038
0x85B49048...918Ee6032
0.0355934001886776 Eth
Nonce: 37
0.001411244412203648 Eth
Nonce: 38
0.034182155776473952
0xC09E658B...c52Db0C8f

Execution Trace

ETH 0.034 HEXYDOGPresale.CALL( )
  • 0xdbb8073c55732be20ed708a7944f1daae816659f.STATICCALL( )
    • EACAggregatorProxy.STATICCALL( )
      • 0x7d4e742018fb52e48b08be73d041c18b21de6fb5.STATICCALL( )
      • ETH 0.034 0x3da941cbb07ae91ea4fb170b10a72029962a247f.CALL( )
        File 1 of 2: HEXYDOGPresale
        // SPDX-License-Identifier: MIT
        
        pragma solidity 0.8.24;
        
        // Sources flattened with hardhat v2.12.6 https://hardhat.org
        
        // File @openzeppelin/contracts/utils/[email protected]
        
        // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
        
        /**
         * @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;
            }
        }
        
        // File @openzeppelin/contracts/access/[email protected]
        
        // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.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.
         *
         * By default, the owner account will be the one that deploys the contract. 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;
        
            event OwnershipTransferred(
                address indexed previousOwner,
                address indexed newOwner
            );
        
            /**
             * @dev Initializes the contract setting the deployer as the initial owner.
             */
            constructor() {
                _transferOwnership(_msgSender());
            }
        
            /**
             * @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 {
                require(owner() == _msgSender(), "Ownable: caller is not the owner");
            }
        
            /**
             * @dev Leaves the contract without owner. It will not be possible to call
             * `onlyOwner` functions anymore. Can only be called by the current owner.
             *
             * NOTE: Renouncing ownership will leave the contract without an owner,
             * thereby removing 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 {
                require(
                    newOwner != address(0),
                    "Ownable: new owner is the zero address"
                );
                _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);
            }
        }
        
        // File @openzeppelin/contracts/token/ERC20/[email protected]
        
        // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
        
        /**
         * @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 amount of tokens in existence.
             */
            function totalSupply() external view returns (uint256);
        
            /**
             * @dev Returns the amount of tokens owned by `account`.
             */
            function balanceOf(address account) external view returns (uint256);
        
            /**
             * @dev Moves `amount` tokens from the caller's account to `to`.
             *
             * Returns a boolean value indicating whether the operation succeeded.
             *
             * Emits a {Transfer} event.
             */
            function transfer(address to, uint256 amount) external returns (bool);
        
            /**
             * @dev Returns the remaining number of tokens that `spender` will be
             * allowed to spend on behalf of `owner` through {transferFrom}. This is
             * zero by default.
             *
             * This value changes when {approve} or {transferFrom} are called.
             */
            function allowance(
                address owner,
                address spender
            ) external view returns (uint256);
        
            /**
             * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
             *
             * Returns a boolean value indicating whether the operation succeeded.
             *
             * IMPORTANT: Beware that changing an allowance with this method brings the risk
             * that someone may use both the old and the new allowance by unfortunate
             * transaction ordering. One possible solution to mitigate this race
             * condition is to first reduce the spender's allowance to 0 and set the
             * desired value afterwards:
             * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
             *
             * Emits an {Approval} event.
             */
            function approve(address spender, uint256 amount) external returns (bool);
        
            /**
             * @dev Moves `amount` tokens from `from` to `to` using the
             * allowance mechanism. `amount` is then deducted from the caller's
             * allowance.
             *
             * Returns a boolean value indicating whether the operation succeeded.
             *
             * Emits a {Transfer} event.
             */
            function transferFrom(
                address from,
                address to,
                uint256 amount
            ) external returns (bool);
        }
        
        library Address {
        
            function isContract(address account) internal view returns (bool) {
                // This method relies on extcodesize/address.code.length, which returns 0
                // for contracts in construction, since the code is only stored at the end
                // of the constructor execution.
        
                return account.code.length > 0;
            }
        
            function sendValue(address payable recipient, uint256 amount) internal {
                require(address(this).balance >= amount, "Address: insufficient balance");
        
                (bool success, ) = recipient.call{value: amount}("");
                require(success, "Address: unable to send value, recipient may have reverted");
            }
        
            function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                return functionCallWithValue(target, data, 0, "Address: low-level call failed");
            }
        
            function functionCall(
                address target,
                bytes memory data,
                string memory errorMessage
            ) internal returns (bytes memory) {
                return functionCallWithValue(target, data, 0, errorMessage);
            }
        
            function functionCallWithValue(
                address target,
                bytes memory data,
                uint256 value
            ) internal returns (bytes memory) {
                return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
            }
        
            function functionCallWithValue(
                address target,
                bytes memory data,
                uint256 value,
                string memory errorMessage
            ) internal returns (bytes memory) {
                require(address(this).balance >= value, "Address: insufficient balance for call");
                (bool success, bytes memory returndata) = target.call{value: value}(data);
                return verifyCallResultFromTarget(target, success, returndata, errorMessage);
            }
        
            function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                return functionStaticCall(target, data, "Address: low-level static call failed");
            }
        
            function functionStaticCall(
                address target,
                bytes memory data,
                string memory errorMessage
            ) internal view returns (bytes memory) {
                (bool success, bytes memory returndata) = target.staticcall(data);
                return verifyCallResultFromTarget(target, success, returndata, errorMessage);
            }
        
            function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                return functionDelegateCall(target, data, "Address: low-level delegate call failed");
            }
        
            function functionDelegateCall(
                address target,
                bytes memory data,
                string memory errorMessage
            ) internal returns (bytes memory) {
                (bool success, bytes memory returndata) = target.delegatecall(data);
                return verifyCallResultFromTarget(target, success, returndata, errorMessage);
            }
        
            function verifyCallResultFromTarget(
                address target,
                bool success,
                bytes memory returndata,
                string memory errorMessage
            ) internal view returns (bytes memory) {
                if (success) {
                    if (returndata.length == 0) {
                        // only check isContract if the call was successful and the return data is empty
                        // otherwise we already know that it was a contract
                        require(isContract(target), "Address: call to non-contract");
                    }
                    return returndata;
                } else {
                    _revert(returndata, errorMessage);
                }
            }
        
            function verifyCallResult(
                bool success,
                bytes memory returndata,
                string memory errorMessage
            ) internal pure returns (bytes memory) {
                if (success) {
                    return returndata;
                } else {
                    _revert(returndata, errorMessage);
                }
        
            }
        
            function _revert(bytes memory returndata, string memory errorMessage) 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(errorMessage);
                }
            }
        }
        
        interface IERC20Permit {
        
            function permit(
                address owner,
                address spender,
                uint256 value,
                uint256 deadline,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) external;
        
            function nonces(address owner) external view returns (uint256);
        
            function DOMAIN_SEPARATOR() external view returns (bytes32);
        }
        
        library SafeERC20 {
            using Address for address;
        
            function safeTransfer(
                IERC20 token,
                address to,
                uint256 value
            ) internal {
                _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
            }
        
            function safeTransferFrom(
                IERC20 token,
                address from,
                address to,
                uint256 value
            ) internal {
                _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
            }
        
            function safeApprove(
                IERC20 token,
                address spender,
                uint256 value
            ) internal {
                // safeApprove should only be called when setting an initial allowance,
                // or when resetting it to zero. To increase and decrease it, use
                // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                require(
                    (value == 0) || (token.allowance(address(this), spender) == 0),
                    "SafeERC20: approve from non-zero to non-zero allowance"
                );
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
            }
        
            function safeIncreaseAllowance(
                IERC20 token,
                address spender,
                uint256 value
            ) internal {
                uint256 newAllowance = token.allowance(address(this), spender) + value;
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
            }
        
            function safeDecreaseAllowance(
                IERC20 token,
                address spender,
                uint256 value
            ) internal {
                unchecked {
                    uint256 oldAllowance = token.allowance(address(this), spender);
                    require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                    uint256 newAllowance = oldAllowance - value;
                    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                }
            }
        
            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);
                require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
            }
        
            function _callOptionalReturn(IERC20 token, bytes memory data) private {
                // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
                // the target address contains contract code and also asserts for success in the low-level call.
        
                bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                if (returndata.length > 0) {
                    // Return data is optional
                    require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                }
            }
        }
        
        // File @openzeppelin/contracts/utils/math/[email protected]
        
        // OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol)
        
        // CAUTION
        // This version of SafeMath should only be used with Solidity 0.8 or later,
        // because it relies on the compiler's built in overflow checks.
        
        /**
         * @dev Wrappers over Solidity's arithmetic operations.
         *
         * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
         * now has built in overflow checking.
         */
        library SafeMath {
            /**
             * @dev Returns the addition of two unsigned integers, with an overflow flag.
             *
             * _Available since v3.4._
             */
            function tryAdd(
                uint256 a,
                uint256 b
            ) internal pure returns (bool, uint256) {
                unchecked {
                    uint256 c = a + b;
                    if (c < a) return (false, 0);
                    return (true, c);
                }
            }
        
            /**
             * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
             *
             * _Available since v3.4._
             */
            function trySub(
                uint256 a,
                uint256 b
            ) internal pure returns (bool, uint256) {
                unchecked {
                    if (b > a) return (false, 0);
                    return (true, a - b);
                }
            }
        
            /**
             * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
             *
             * _Available since v3.4._
             */
            function tryMul(
                uint256 a,
                uint256 b
            ) internal pure returns (bool, uint256) {
                unchecked {
                    // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                    // benefit is lost if 'b' is also tested.
                    // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                    if (a == 0) return (true, 0);
                    uint256 c = a * b;
                    if (c / a != b) return (false, 0);
                    return (true, c);
                }
            }
        
            /**
             * @dev Returns the division of two unsigned integers, with a division by zero flag.
             *
             * _Available since v3.4._
             */
            function tryDiv(
                uint256 a,
                uint256 b
            ) internal pure returns (bool, uint256) {
                unchecked {
                    if (b == 0) return (false, 0);
                    return (true, a / b);
                }
            }
        
            /**
             * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
             *
             * _Available since v3.4._
             */
            function tryMod(
                uint256 a,
                uint256 b
            ) internal pure returns (bool, uint256) {
                unchecked {
                    if (b == 0) return (false, 0);
                    return (true, a % b);
                }
            }
        
            /**
             * @dev Returns the addition of two unsigned integers, reverting on
             * overflow.
             *
             * Counterpart to Solidity's `+` operator.
             *
             * Requirements:
             *
             * - Addition cannot overflow.
             */
            function add(uint256 a, uint256 b) internal pure returns (uint256) {
                return a + b;
            }
        
            /**
             * @dev Returns the subtraction of two unsigned integers, reverting on
             * overflow (when the result is negative).
             *
             * Counterpart to Solidity's `-` operator.
             *
             * Requirements:
             *
             * - Subtraction cannot overflow.
             */
            function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                return a - b;
            }
        
            /**
             * @dev Returns the multiplication of two unsigned integers, reverting on
             * overflow.
             *
             * Counterpart to Solidity's `*` operator.
             *
             * Requirements:
             *
             * - Multiplication cannot overflow.
             */
            function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                return a * b;
            }
        
            /**
             * @dev Returns the integer division of two unsigned integers, reverting on
             * division by zero. The result is rounded towards zero.
             *
             * Counterpart to Solidity's `/` operator.
             *
             * Requirements:
             *
             * - The divisor cannot be zero.
             */
            function div(uint256 a, uint256 b) internal pure returns (uint256) {
                return a / b;
            }
        
            /**
             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
             * reverting when dividing by zero.
             *
             * Counterpart to Solidity's `%` operator. This function uses a `revert`
             * opcode (which leaves remaining gas untouched) while Solidity uses an
             * invalid opcode to revert (consuming all remaining gas).
             *
             * Requirements:
             *
             * - The divisor cannot be zero.
             */
            function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                return a % b;
            }
        
            /**
             * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
             * overflow (when the result is negative).
             *
             * CAUTION: This function is deprecated because it requires allocating memory for the error
             * message unnecessarily. For custom revert reasons use {trySub}.
             *
             * Counterpart to Solidity's `-` operator.
             *
             * Requirements:
             *
             * - Subtraction cannot overflow.
             */
            function sub(
                uint256 a,
                uint256 b,
                string memory errorMessage
            ) internal pure returns (uint256) {
                unchecked {
                    require(b <= a, errorMessage);
                    return a - b;
                }
            }
        
            /**
             * @dev Returns the integer division of two unsigned integers, reverting with custom message on
             * division by zero. The result is rounded towards zero.
             *
             * Counterpart to Solidity's `/` operator. Note: this function uses a
             * `revert` opcode (which leaves remaining gas untouched) while Solidity
             * uses an invalid opcode to revert (consuming all remaining gas).
             *
             * Requirements:
             *
             * - The divisor cannot be zero.
             */
            function div(
                uint256 a,
                uint256 b,
                string memory errorMessage
            ) internal pure returns (uint256) {
                unchecked {
                    require(b > 0, errorMessage);
                    return a / b;
                }
            }
        
            /**
             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
             * reverting with custom message when dividing by zero.
             *
             * CAUTION: This function is deprecated because it requires allocating memory for the error
             * message unnecessarily. For custom revert reasons use {tryMod}.
             *
             * Counterpart to Solidity's `%` operator. This function uses a `revert`
             * opcode (which leaves remaining gas untouched) while Solidity uses an
             * invalid opcode to revert (consuming all remaining gas).
             *
             * Requirements:
             *
             * - The divisor cannot be zero.
             */
            function mod(
                uint256 a,
                uint256 b,
                string memory errorMessage
            ) internal pure returns (uint256) {
                unchecked {
                    require(b > 0, errorMessage);
                    return a % b;
                }
            }
        }
        
        contract ChainlinkPriceOracle {
            AggregatorV3Interface internal priceFeed;
        
            constructor() {
                // BNB / USD
                priceFeed = AggregatorV3Interface(0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419);
            }
        
            function getLatestPrice() public view returns (int) {
                (
                    ,
                    int price,
                    ,
                    ,
                ) = priceFeed.latestRoundData();
                // for BNB / USD price is scaled up by 10 ** 8
                return price / 1e8;
            }
        }
        
        interface AggregatorV3Interface {
            function latestRoundData()
                external
                view
                returns (
                    uint80 roundId,
                    int answer,
                    uint startedAt,
                    uint updatedAt,
                    uint80 answeredInRound
                );
        }
        
        contract HEXYDOGPresale is Ownable {
            using SafeERC20 for IERC20;
            using SafeMath for uint256;
        
            ChainlinkPriceOracle public priceOracle;
        
            //Struct for Buyers
            struct tokenBuyer{
                uint256 tokensBought;
            }
        
            //Mapping for Buyers
            mapping (address => tokenBuyer) public Customer;
        
            // The token being sold
            IERC20 public immutable token = IERC20(0x2b97D5D3229124298681aC3b4D0A0DB2BB88756d);
        
            //USDT token address
            IERC20 public immutable usdt = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7);
        
            //USDC token address
            IERC20 public immutable usdc = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
        
            //tokens that can be claimed
            uint256 private claimableTokens;
        
            // Address where funds are collected
            address public wallet = payable(0x3DA941CBB07AE91ea4fB170B10A72029962A247f);
        
            // How many token units a buyer gets per ETH & USDT
            uint256 public tokenPriceUSD = 21;
            uint256 public ethDivider = 10000;
            uint256 private usdtRate = 47619;
            uint256 private usdtDivider = 100;
        
            // Amount of wei raised
            uint256 private weiRaised;
            uint256 private usdtRaised;
            uint256 private usdcRaised;
        
            //Amount of tokens sold
            uint256 private tokensSold;
        
            //Presale status
            bool private hasPresaleStarted;
            bool private hasPresaleEnded;
        
            constructor() {
                priceOracle = new ChainlinkPriceOracle();
            }
        
            receive() external payable {
                buyTokens();
            }
            
            //START PRESALE
            function startPresale() public onlyOwner{
                hasPresaleStarted = true;
            }
        
            //END PRESALE
            function endPresale() public onlyOwner{
                hasPresaleEnded = true;
            }
        
            // BUY TOKENS WITH ETH
            function buyTokens() public payable {
                require(hasPresaleStarted,"Presale not started");
                require(!hasPresaleEnded, "Presale has ended");
                require(msg.value >= 0.001 ether, "cant buy less with than 0.001 ETH");
                uint256 weiAmount = msg.value;
                //uint256 tokens = weiAmount * ethRate;
        
                // Get the ETH price from the ChainlinkPriceOracle
                int256 ethPrice = priceOracle.getLatestPrice();
                require(ethPrice > 0, "Invalid ETH price");
        
                uint256 tokens = weiAmount.mul(uint256(ethPrice)).mul(ethDivider).div(tokenPriceUSD);
        
        
                require(claimableTokens >= tokens, "Not enough tokens availible");
        
                weiRaised += weiAmount;
        
                Customer[msg.sender].tokensBought += tokens;
                tokensSold += tokens;
                claimableTokens -= tokens;
        
                (bool callSuccess, ) = payable(wallet).call{value: msg.value}("");
                require(callSuccess, "Call failed");
            }
        
            //BUY TOKENS WITH USDT
            function buyTokensWithUSDT(uint256 amount) public {
                require(hasPresaleStarted,"Presale not started");
                require(!hasPresaleEnded, "Presale has ended");
                require(amount >= 1 * 10 ** 6, "cant buy less with than 1 USDT");
                uint256 weiAmount = amount * 10 ** 12;
                uint256 tokens = weiAmount.mul(usdtRate).div(usdtDivider);
                require(claimableTokens >= tokens, "Not enough tokens availible");
        
                usdtRaised += amount;
        
                Customer[msg.sender].tokensBought += tokens;
                tokensSold += tokens;
                claimableTokens -= tokens;
        
                usdt.safeTransferFrom(msg.sender, wallet, amount);
            }
        
            //BUY TOKENS WITH USDC
            function buyTokensWithUSDC(uint256 amount) public {
                require(hasPresaleStarted,"Presale not started");
                require(!hasPresaleEnded, "Presale has ended");
                require(amount >= 1 * 10 ** 6, "cant buy less with than 1 USDC");
                uint256 weiAmount = amount * 10 ** 12;
                uint256 tokens = weiAmount.mul(usdtRate).div(usdtDivider);
                require(claimableTokens >= tokens, "Not enough tokens availible");
        
                usdcRaised += amount;
        
                Customer[msg.sender].tokensBought += tokens;
                tokensSold += tokens;
                claimableTokens -= tokens;
        
                usdc.safeTransferFrom(msg.sender, wallet, amount);
            }
        
            //DEPOSIT TOKENS FOR PRESALE
            function deposit(uint amount) external onlyOwner {
                require(amount > 0, "Deposit value must be greater than 0");
                token.safeTransferFrom(msg.sender, address(this), amount);
                claimableTokens += amount;
            }
        
            //WITHDRAW UNSOLD TOKENS IF NEEDED
            function withdraw(uint amount) external onlyOwner {
                require(
                    token.balanceOf(address(this)) > 0,
                    "There are not enough tokens in contract"
                );
                require(claimableTokens >= amount, "not enough tokens to withdraw");
                token.safeTransfer(msg.sender, amount);
                claimableTokens -= amount;
            }
        
            // CLAIM FUNCTION
            function claimTokens() public {
                require(hasPresaleEnded,"Presale has not ended");
                require(Customer[msg.sender].tokensBought > 0, "No tokens to be claimed");
        
                uint256 amount = Customer[msg.sender].tokensBought;
        
                token.safeTransfer(msg.sender, amount);
        
                Customer[msg.sender].tokensBought = 0;
            }
        
            //TO CHANGE COLLECTION WALLET
            function changeWallet(address payable _wallet) external onlyOwner {
                wallet = _wallet;
            }
        
            //TO UPDATE RATES
            function changeRate(uint256 _usdtRate, uint256 _ethToUsd, uint256 divider, uint256 usdtDiv) public onlyOwner {
                require(_usdtRate > 0, "Rate cannot be 0");
                require(_ethToUsd > 0, "Rate cannot be 0");
                tokenPriceUSD = _ethToUsd;
                usdtRate = _usdtRate;
                ethDivider = divider;
                usdtDivider = usdtDiv;
            }
        
            // ALL GETTER FUNCTIONS TO RETRIEVE DATA
        
            function checkbalance() external view returns (uint256) {
                return token.balanceOf(address(this));
            }
        
           function getETHPriceInUSD() public view returns (int256) {
                return priceOracle.getLatestPrice();
            }
        
            function getDivider() public view returns (uint256) {
                return ethDivider;
            }
        
            function getUsdtDivider() public view returns (uint256) {
                return usdtDivider;
            }
        
            function getTokenUsdPrice() public view returns (uint256) {
                return tokenPriceUSD;
            }
        
            function getUsdtRate() public view returns (uint256) {
                return usdtRate;
            }
        
            function progressETH() public view returns (uint256) {
                return weiRaised;
            }
        
            function progressUSDT() public view returns (uint256) {
                return usdtRaised;
            }
        
            function progressUSDC() public view returns (uint256) {
                return usdcRaised;
            }
        
            function soldTokens() public view returns (uint256) {
                return tokensSold;
            }
        
            function checkPresaleStatus() public view returns (bool) {
                return hasPresaleStarted;
            }
        
            function checkPresaleEnd() public view returns (bool) {
                return hasPresaleEnded;
            }
        
            function getClaimableTokens() public view returns (uint256) {
                return claimableTokens;
            }
        
        }

        File 2 of 2: EACAggregatorProxy
        pragma solidity 0.6.6;
        
        
        /**
         * @title The Owned contract
         * @notice A contract with helpers for basic contract ownership.
         */
        contract Owned {
        
          address payable public owner;
          address private pendingOwner;
        
          event OwnershipTransferRequested(
            address indexed from,
            address indexed to
          );
          event OwnershipTransferred(
            address indexed from,
            address indexed to
          );
        
          constructor() public {
            owner = msg.sender;
          }
        
          /**
           * @dev Allows an owner to begin transferring ownership to a new address,
           * pending.
           */
          function transferOwnership(address _to)
            external
            onlyOwner()
          {
            pendingOwner = _to;
        
            emit OwnershipTransferRequested(owner, _to);
          }
        
          /**
           * @dev Allows an ownership transfer to be completed by the recipient.
           */
          function acceptOwnership()
            external
          {
            require(msg.sender == pendingOwner, "Must be proposed owner");
        
            address oldOwner = owner;
            owner = msg.sender;
            pendingOwner = address(0);
        
            emit OwnershipTransferred(oldOwner, msg.sender);
          }
        
          /**
           * @dev Reverts if called by anyone other than the contract owner.
           */
          modifier onlyOwner() {
            require(msg.sender == owner, "Only callable by owner");
            _;
          }
        
        }
        
        interface AggregatorInterface {
          function latestAnswer() external view returns (int256);
          function latestTimestamp() external view returns (uint256);
          function latestRound() external view returns (uint256);
          function getAnswer(uint256 roundId) external view returns (int256);
          function getTimestamp(uint256 roundId) external view returns (uint256);
        
          event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt);
          event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);
        }
        
        interface AggregatorV3Interface {
        
          function decimals() external view returns (uint8);
          function description() external view returns (string memory);
          function version() external view returns (uint256);
        
          // getRoundData and latestRoundData should both raise "No data present"
          // if they do not have data to report, instead of returning unset values
          // which could be misinterpreted as actual reported values.
          function getRoundData(uint80 _roundId)
            external
            view
            returns (
              uint80 roundId,
              int256 answer,
              uint256 startedAt,
              uint256 updatedAt,
              uint80 answeredInRound
            );
          function latestRoundData()
            external
            view
            returns (
              uint80 roundId,
              int256 answer,
              uint256 startedAt,
              uint256 updatedAt,
              uint80 answeredInRound
            );
        
        }
        
        interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface
        {
        }
        
        /**
         * @title A trusted proxy for updating where current answers are read from
         * @notice This contract provides a consistent address for the
         * CurrentAnwerInterface but delegates where it reads from to the owner, who is
         * trusted to update it.
         */
        contract AggregatorProxy is AggregatorV2V3Interface, Owned {
        
          struct Phase {
            uint16 id;
            AggregatorV2V3Interface aggregator;
          }
          Phase private currentPhase;
          AggregatorV2V3Interface public proposedAggregator;
          mapping(uint16 => AggregatorV2V3Interface) public phaseAggregators;
        
          uint256 constant private PHASE_OFFSET = 64;
          uint256 constant private PHASE_SIZE = 16;
          uint256 constant private MAX_ID = 2**(PHASE_OFFSET+PHASE_SIZE) - 1;
        
          constructor(address _aggregator) public Owned() {
            setAggregator(_aggregator);
          }
        
          /**
           * @notice Reads the current answer from aggregator delegated to.
           *
           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
           * answer has been reached, it will simply return 0. Either wait to point to
           * an already answered Aggregator or use the recommended latestRoundData
           * instead which includes better verification information.
           */
          function latestAnswer()
            public
            view
            virtual
            override
            returns (int256 answer)
          {
            return currentPhase.aggregator.latestAnswer();
          }
        
          /**
           * @notice Reads the last updated height from aggregator delegated to.
           *
           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
           * answer has been reached, it will simply return 0. Either wait to point to
           * an already answered Aggregator or use the recommended latestRoundData
           * instead which includes better verification information.
           */
          function latestTimestamp()
            public
            view
            virtual
            override
            returns (uint256 updatedAt)
          {
            return currentPhase.aggregator.latestTimestamp();
          }
        
          /**
           * @notice get past rounds answers
           * @param _roundId the answer number to retrieve the answer for
           *
           * @dev #[deprecated] Use getRoundData instead. This does not error if no
           * answer has been reached, it will simply return 0. Either wait to point to
           * an already answered Aggregator or use the recommended getRoundData
           * instead which includes better verification information.
           */
          function getAnswer(uint256 _roundId)
            public
            view
            virtual
            override
            returns (int256 answer)
          {
            if (_roundId > MAX_ID) return 0;
        
            (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId);
            AggregatorV2V3Interface aggregator = phaseAggregators[phaseId];
            if (address(aggregator) == address(0)) return 0;
        
            return aggregator.getAnswer(aggregatorRoundId);
          }
        
          /**
           * @notice get block timestamp when an answer was last updated
           * @param _roundId the answer number to retrieve the updated timestamp for
           *
           * @dev #[deprecated] Use getRoundData instead. This does not error if no
           * answer has been reached, it will simply return 0. Either wait to point to
           * an already answered Aggregator or use the recommended getRoundData
           * instead which includes better verification information.
           */
          function getTimestamp(uint256 _roundId)
            public
            view
            virtual
            override
            returns (uint256 updatedAt)
          {
            if (_roundId > MAX_ID) return 0;
        
            (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId);
            AggregatorV2V3Interface aggregator = phaseAggregators[phaseId];
            if (address(aggregator) == address(0)) return 0;
        
            return aggregator.getTimestamp(aggregatorRoundId);
          }
        
          /**
           * @notice get the latest completed round where the answer was updated. This
           * ID includes the proxy's phase, to make sure round IDs increase even when
           * switching to a newly deployed aggregator.
           *
           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
           * answer has been reached, it will simply return 0. Either wait to point to
           * an already answered Aggregator or use the recommended latestRoundData
           * instead which includes better verification information.
           */
          function latestRound()
            public
            view
            virtual
            override
            returns (uint256 roundId)
          {
            Phase memory phase = currentPhase; // cache storage reads
            return addPhase(phase.id, uint64(phase.aggregator.latestRound()));
          }
        
          /**
           * @notice get data about a round. Consumers are encouraged to check
           * that they're receiving fresh data by inspecting the updatedAt and
           * answeredInRound return values.
           * Note that different underlying implementations of AggregatorV3Interface
           * have slightly different semantics for some of the return values. Consumers
           * should determine what implementations they expect to receive
           * data from and validate that they can properly handle return data from all
           * of them.
           * @param _roundId the requested round ID as presented through the proxy, this
           * is made up of the aggregator's round ID with the phase ID encoded in the
           * two highest order bytes
           * @return roundId is the round ID from the aggregator for which the data was
           * retrieved combined with an phase to ensure that round IDs get larger as
           * time moves forward.
           * @return answer is the answer for the given round
           * @return startedAt is the timestamp when the round was started.
           * (Only some AggregatorV3Interface implementations return meaningful values)
           * @return updatedAt is the timestamp when the round last was updated (i.e.
           * answer was last computed)
           * @return answeredInRound is the round ID of the round in which the answer
           * was computed.
           * (Only some AggregatorV3Interface implementations return meaningful values)
           * @dev Note that answer and updatedAt may change between queries.
           */
          function getRoundData(uint80 _roundId)
            public
            view
            virtual
            override
            returns (
              uint80 roundId,
              int256 answer,
              uint256 startedAt,
              uint256 updatedAt,
              uint80 answeredInRound
            )
          {
            (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId);
        
            (
              uint80 roundId,
              int256 answer,
              uint256 startedAt,
              uint256 updatedAt,
              uint80 ansIn
            ) = phaseAggregators[phaseId].getRoundData(aggregatorRoundId);
        
            return addPhaseIds(roundId, answer, startedAt, updatedAt, ansIn, phaseId);
          }
        
          /**
           * @notice get data about the latest round. Consumers are encouraged to check
           * that they're receiving fresh data by inspecting the updatedAt and
           * answeredInRound return values.
           * Note that different underlying implementations of AggregatorV3Interface
           * have slightly different semantics for some of the return values. Consumers
           * should determine what implementations they expect to receive
           * data from and validate that they can properly handle return data from all
           * of them.
           * @return roundId is the round ID from the aggregator for which the data was
           * retrieved combined with an phase to ensure that round IDs get larger as
           * time moves forward.
           * @return answer is the answer for the given round
           * @return startedAt is the timestamp when the round was started.
           * (Only some AggregatorV3Interface implementations return meaningful values)
           * @return updatedAt is the timestamp when the round last was updated (i.e.
           * answer was last computed)
           * @return answeredInRound is the round ID of the round in which the answer
           * was computed.
           * (Only some AggregatorV3Interface implementations return meaningful values)
           * @dev Note that answer and updatedAt may change between queries.
           */
          function latestRoundData()
            public
            view
            virtual
            override
            returns (
              uint80 roundId,
              int256 answer,
              uint256 startedAt,
              uint256 updatedAt,
              uint80 answeredInRound
            )
          {
            Phase memory current = currentPhase; // cache storage reads
        
            (
              uint80 roundId,
              int256 answer,
              uint256 startedAt,
              uint256 updatedAt,
              uint80 ansIn
            ) = current.aggregator.latestRoundData();
        
            return addPhaseIds(roundId, answer, startedAt, updatedAt, ansIn, current.id);
          }
        
          /**
           * @notice Used if an aggregator contract has been proposed.
           * @param _roundId the round ID to retrieve the round data for
           * @return roundId is the round ID for which data was retrieved
           * @return answer is the answer for the given round
           * @return startedAt is the timestamp when the round was started.
           * (Only some AggregatorV3Interface implementations return meaningful values)
           * @return updatedAt is the timestamp when the round last was updated (i.e.
           * answer was last computed)
           * @return answeredInRound is the round ID of the round in which the answer
           * was computed.
          */
          function proposedGetRoundData(uint80 _roundId)
            public
            view
            virtual
            hasProposal()
            returns (
              uint80 roundId,
              int256 answer,
              uint256 startedAt,
              uint256 updatedAt,
              uint80 answeredInRound
            )
          {
            return proposedAggregator.getRoundData(_roundId);
          }
        
          /**
           * @notice Used if an aggregator contract has been proposed.
           * @return roundId is the round ID for which data was retrieved
           * @return answer is the answer for the given round
           * @return startedAt is the timestamp when the round was started.
           * (Only some AggregatorV3Interface implementations return meaningful values)
           * @return updatedAt is the timestamp when the round last was updated (i.e.
           * answer was last computed)
           * @return answeredInRound is the round ID of the round in which the answer
           * was computed.
          */
          function proposedLatestRoundData()
            public
            view
            virtual
            hasProposal()
            returns (
              uint80 roundId,
              int256 answer,
              uint256 startedAt,
              uint256 updatedAt,
              uint80 answeredInRound
            )
          {
            return proposedAggregator.latestRoundData();
          }
        
          /**
           * @notice returns the current phase's aggregator address.
           */
          function aggregator()
            external
            view
            returns (address)
          {
            return address(currentPhase.aggregator);
          }
        
          /**
           * @notice returns the current phase's ID.
           */
          function phaseId()
            external
            view
            returns (uint16)
          {
            return currentPhase.id;
          }
        
          /**
           * @notice represents the number of decimals the aggregator responses represent.
           */
          function decimals()
            external
            view
            override
            returns (uint8)
          {
            return currentPhase.aggregator.decimals();
          }
        
          /**
           * @notice the version number representing the type of aggregator the proxy
           * points to.
           */
          function version()
            external
            view
            override
            returns (uint256)
          {
            return currentPhase.aggregator.version();
          }
        
          /**
           * @notice returns the description of the aggregator the proxy points to.
           */
          function description()
            external
            view
            override
            returns (string memory)
          {
            return currentPhase.aggregator.description();
          }
        
          /**
           * @notice Allows the owner to propose a new address for the aggregator
           * @param _aggregator The new address for the aggregator contract
           */
          function proposeAggregator(address _aggregator)
            external
            onlyOwner()
          {
            proposedAggregator = AggregatorV2V3Interface(_aggregator);
          }
        
          /**
           * @notice Allows the owner to confirm and change the address
           * to the proposed aggregator
           * @dev Reverts if the given address doesn't match what was previously
           * proposed
           * @param _aggregator The new address for the aggregator contract
           */
          function confirmAggregator(address _aggregator)
            external
            onlyOwner()
          {
            require(_aggregator == address(proposedAggregator), "Invalid proposed aggregator");
            delete proposedAggregator;
            setAggregator(_aggregator);
          }
        
        
          /*
           * Internal
           */
        
          function setAggregator(address _aggregator)
            internal
          {
            uint16 id = currentPhase.id + 1;
            currentPhase = Phase(id, AggregatorV2V3Interface(_aggregator));
            phaseAggregators[id] = AggregatorV2V3Interface(_aggregator);
          }
        
          function addPhase(
            uint16 _phase,
            uint64 _originalId
          )
            internal
            view
            returns (uint80)
          {
            return uint80(uint256(_phase) << PHASE_OFFSET | _originalId);
          }
        
          function parseIds(
            uint256 _roundId
          )
            internal
            view
            returns (uint16, uint64)
          {
            uint16 phaseId = uint16(_roundId >> PHASE_OFFSET);
            uint64 aggregatorRoundId = uint64(_roundId);
        
            return (phaseId, aggregatorRoundId);
          }
        
          function addPhaseIds(
              uint80 roundId,
              int256 answer,
              uint256 startedAt,
              uint256 updatedAt,
              uint80 answeredInRound,
              uint16 phaseId
          )
            internal
            view
            returns (uint80, int256, uint256, uint256, uint80)
          {
            return (
              addPhase(phaseId, uint64(roundId)),
              answer,
              startedAt,
              updatedAt,
              addPhase(phaseId, uint64(answeredInRound))
            );
          }
        
          /*
           * Modifiers
           */
        
          modifier hasProposal() {
            require(address(proposedAggregator) != address(0), "No proposed aggregator present");
            _;
          }
        
        }
        
        interface AccessControllerInterface {
          function hasAccess(address user, bytes calldata data) external view returns (bool);
        }
        
        /**
         * @title External Access Controlled Aggregator Proxy
         * @notice A trusted proxy for updating where current answers are read from
         * @notice This contract provides a consistent address for the
         * Aggregator and AggregatorV3Interface but delegates where it reads from to the owner, who is
         * trusted to update it.
         * @notice Only access enabled addresses are allowed to access getters for
         * aggregated answers and round information.
         */
        contract EACAggregatorProxy is AggregatorProxy {
        
          AccessControllerInterface public accessController;
        
          constructor(
            address _aggregator,
            address _accessController
          )
            public
            AggregatorProxy(_aggregator)
          {
            setController(_accessController);
          }
        
          /**
           * @notice Allows the owner to update the accessController contract address.
           * @param _accessController The new address for the accessController contract
           */
          function setController(address _accessController)
            public
            onlyOwner()
          {
            accessController = AccessControllerInterface(_accessController);
          }
        
          /**
           * @notice Reads the current answer from aggregator delegated to.
           * @dev overridden function to add the checkAccess() modifier
           *
           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
           * answer has been reached, it will simply return 0. Either wait to point to
           * an already answered Aggregator or use the recommended latestRoundData
           * instead which includes better verification information.
           */
          function latestAnswer()
            public
            view
            override
            checkAccess()
            returns (int256)
          {
            return super.latestAnswer();
          }
        
          /**
           * @notice get the latest completed round where the answer was updated. This
           * ID includes the proxy's phase, to make sure round IDs increase even when
           * switching to a newly deployed aggregator.
           *
           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
           * answer has been reached, it will simply return 0. Either wait to point to
           * an already answered Aggregator or use the recommended latestRoundData
           * instead which includes better verification information.
           */
          function latestTimestamp()
            public
            view
            override
            checkAccess()
            returns (uint256)
          {
            return super.latestTimestamp();
          }
        
          /**
           * @notice get past rounds answers
           * @param _roundId the answer number to retrieve the answer for
           * @dev overridden function to add the checkAccess() modifier
           *
           * @dev #[deprecated] Use getRoundData instead. This does not error if no
           * answer has been reached, it will simply return 0. Either wait to point to
           * an already answered Aggregator or use the recommended getRoundData
           * instead which includes better verification information.
           */
          function getAnswer(uint256 _roundId)
            public
            view
            override
            checkAccess()
            returns (int256)
          {
            return super.getAnswer(_roundId);
          }
        
          /**
           * @notice get block timestamp when an answer was last updated
           * @param _roundId the answer number to retrieve the updated timestamp for
           * @dev overridden function to add the checkAccess() modifier
           *
           * @dev #[deprecated] Use getRoundData instead. This does not error if no
           * answer has been reached, it will simply return 0. Either wait to point to
           * an already answered Aggregator or use the recommended getRoundData
           * instead which includes better verification information.
           */
          function getTimestamp(uint256 _roundId)
            public
            view
            override
            checkAccess()
            returns (uint256)
          {
            return super.getTimestamp(_roundId);
          }
        
          /**
           * @notice get the latest completed round where the answer was updated
           * @dev overridden function to add the checkAccess() modifier
           *
           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
           * answer has been reached, it will simply return 0. Either wait to point to
           * an already answered Aggregator or use the recommended latestRoundData
           * instead which includes better verification information.
           */
          function latestRound()
            public
            view
            override
            checkAccess()
            returns (uint256)
          {
            return super.latestRound();
          }
        
          /**
           * @notice get data about a round. Consumers are encouraged to check
           * that they're receiving fresh data by inspecting the updatedAt and
           * answeredInRound return values.
           * Note that different underlying implementations of AggregatorV3Interface
           * have slightly different semantics for some of the return values. Consumers
           * should determine what implementations they expect to receive
           * data from and validate that they can properly handle return data from all
           * of them.
           * @param _roundId the round ID to retrieve the round data for
           * @return roundId is the round ID from the aggregator for which the data was
           * retrieved combined with a phase to ensure that round IDs get larger as
           * time moves forward.
           * @return answer is the answer for the given round
           * @return startedAt is the timestamp when the round was started.
           * (Only some AggregatorV3Interface implementations return meaningful values)
           * @return updatedAt is the timestamp when the round last was updated (i.e.
           * answer was last computed)
           * @return answeredInRound is the round ID of the round in which the answer
           * was computed.
           * (Only some AggregatorV3Interface implementations return meaningful values)
           * @dev Note that answer and updatedAt may change between queries.
           */
          function getRoundData(uint80 _roundId)
            public
            view
            checkAccess()
            override
            returns (
              uint80 roundId,
              int256 answer,
              uint256 startedAt,
              uint256 updatedAt,
              uint80 answeredInRound
            )
          {
            return super.getRoundData(_roundId);
          }
        
          /**
           * @notice get data about the latest round. Consumers are encouraged to check
           * that they're receiving fresh data by inspecting the updatedAt and
           * answeredInRound return values.
           * Note that different underlying implementations of AggregatorV3Interface
           * have slightly different semantics for some of the return values. Consumers
           * should determine what implementations they expect to receive
           * data from and validate that they can properly handle return data from all
           * of them.
           * @return roundId is the round ID from the aggregator for which the data was
           * retrieved combined with a phase to ensure that round IDs get larger as
           * time moves forward.
           * @return answer is the answer for the given round
           * @return startedAt is the timestamp when the round was started.
           * (Only some AggregatorV3Interface implementations return meaningful values)
           * @return updatedAt is the timestamp when the round last was updated (i.e.
           * answer was last computed)
           * @return answeredInRound is the round ID of the round in which the answer
           * was computed.
           * (Only some AggregatorV3Interface implementations return meaningful values)
           * @dev Note that answer and updatedAt may change between queries.
           */
          function latestRoundData()
            public
            view
            checkAccess()
            override
            returns (
              uint80 roundId,
              int256 answer,
              uint256 startedAt,
              uint256 updatedAt,
              uint80 answeredInRound
            )
          {
            return super.latestRoundData();
          }
        
          /**
           * @notice Used if an aggregator contract has been proposed.
           * @param _roundId the round ID to retrieve the round data for
           * @return roundId is the round ID for which data was retrieved
           * @return answer is the answer for the given round
           * @return startedAt is the timestamp when the round was started.
           * (Only some AggregatorV3Interface implementations return meaningful values)
           * @return updatedAt is the timestamp when the round last was updated (i.e.
           * answer was last computed)
           * @return answeredInRound is the round ID of the round in which the answer
           * was computed.
          */
          function proposedGetRoundData(uint80 _roundId)
            public
            view
            checkAccess()
            hasProposal()
            override
            returns (
              uint80 roundId,
              int256 answer,
              uint256 startedAt,
              uint256 updatedAt,
              uint80 answeredInRound
            )
          {
            return super.proposedGetRoundData(_roundId);
          }
        
          /**
           * @notice Used if an aggregator contract has been proposed.
           * @return roundId is the round ID for which data was retrieved
           * @return answer is the answer for the given round
           * @return startedAt is the timestamp when the round was started.
           * (Only some AggregatorV3Interface implementations return meaningful values)
           * @return updatedAt is the timestamp when the round last was updated (i.e.
           * answer was last computed)
           * @return answeredInRound is the round ID of the round in which the answer
           * was computed.
          */
          function proposedLatestRoundData()
            public
            view
            checkAccess()
            hasProposal()
            override
            returns (
              uint80 roundId,
              int256 answer,
              uint256 startedAt,
              uint256 updatedAt,
              uint80 answeredInRound
            )
          {
            return super.proposedLatestRoundData();
          }
        
          /**
           * @dev reverts if the caller does not have access by the accessController
           * contract or is the contract itself.
           */
          modifier checkAccess() {
            AccessControllerInterface ac = accessController;
            require(address(ac) == address(0) || ac.hasAccess(msg.sender, msg.data), "No access");
            _;
          }
        }