// File: src/IPO.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import "@solmate/auth/Owned.sol";

import "../lib/AggregatorV3Interface.sol";
import "./interfaces/IMint.sol";

/**
 * @title Zenith IPO
 * @dev IPO price is 0.06 USD per ZTH, 3M ZTH for sale.
 * ETH price is provided by Chainlink ETH/USD price feed.
 *  https://docs.chain.link/docs/ethereum-addresses/
 *
 */
contract ZTHIPO is Owned(msg.sender) {
    uint256 private constant _MAX_SALE = 19_000_000  * 1e18;
    uint256 private constant _ZTHUSD = 0.01 * 1e8;
    address public immutable zth; 

    bool public started;
    uint256 public totalSold; // total ZTH sold
    uint256 public volume; // total ETH volume

    AggregatorV3Interface private immutable _priceFeed;

    constructor(address _zth, address priceFeed_) {
        zth = _zth;
        _priceFeed = AggregatorV3Interface(priceFeed_); 
    }

    function buy() external payable {
        if (!started) revert NotStarted();

        uint256 amount = getAmountOut(msg.value);
        assert(amount > 0);
        uint256 totalSoldNew = totalSold + amount;
        if (totalSoldNew > _MAX_SALE) revert MAXSaleReached();

        totalSold = totalSoldNew;
        unchecked {
            volume += msg.value;
        }
        IMint(zth).mint(msg.sender, amount);
        emit Buy(msg.sender, msg.value, amount);
    }

    function getAmountOut(uint256 amountIn) public view returns (uint256) {
        uint256 ethUSD = getETHPrice();
        unchecked {
            return amountIn * ethUSD / _ZTHUSD;
        }
    }

    function getETHPrice() public view returns (uint256) {
        (, int256 price,, uint256 updatedAt,) = _priceFeed.latestRoundData();

        // check price last update time
        // chainlink ETH-USD price feed is updated every hour
        unchecked {
            if (updatedAt + 1.2 hours < block.timestamp) revert PriceOutdated();
        }
        return uint256(price);
    }

    // admin functions

    function withdraw() external onlyOwner {
        payable(msg.sender).transfer(address(this).balance); // solhint-disable-line
    }

    function start() external onlyOwner {
        started = true;
    }

    event Buy(address buyer, uint256 amountIn, uint256 amountOut);

    error MAXSaleReached();
    error PriceOutdated();
    error NotStarted();
}

// File: lib/solmate/src/auth/Owned.sol



// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event OwnershipTransferred(address indexed user, address indexed newOwner);

    /*//////////////////////////////////////////////////////////////
                            OWNERSHIP STORAGE
    //////////////////////////////////////////////////////////////*/

    address public owner;

    modifier onlyOwner() virtual {
        require(msg.sender == owner, "UNAUTHORIZED");

        _;
    }

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(address _owner) {
        owner = _owner;

        emit OwnershipTransferred(address(0), _owner);
    }

    /*//////////////////////////////////////////////////////////////
                             OWNERSHIP LOGIC
    //////////////////////////////////////////////////////////////*/

    function transferOwnership(address newOwner) public virtual onlyOwner {
        owner = newOwner;

        emit OwnershipTransferred(msg.sender, newOwner);
    }
}

// File: lib/AggregatorV3Interface.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface AggregatorV3Interface {
    function decimals() external view returns (uint8);

    function description() external view returns (string memory);

    function version() external view returns (uint256);

    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);
}

// File: src/interfaces/IMint.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
 
interface IMint {
    function mint(address to, uint256 amount) external;
}