// File: src/PricingV1.sol



// SPDX-License-Identifier: MIT

///@title PricingV1
///@author Koto Protocol
///@notice

pragma solidity 0.8.23;

import {IKotoV3} from "./interfaces/IKotoV3.sol";
import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";
import {IERC20Minimal} from "./interfaces/IERC20Minimal.sol";
import {FullMath} from "./libraries/FullMath.sol";

contract PricingV1 {
    // ====================================================== \\
    //                         CONSTANTS                      \\
    // ====================================================== \\

    IKotoV3 public constant KOTO = IKotoV3(0x64C7d8C8Abf28Daf9D441c507CfE9Be678A0929c);
    address public constant BOND_DEPOSITORY = 0xE58B33c813ac4077bd2519dE90FccB189a19FA71;
    address public constant PAIR = 0x47287d8d7C1a5854Aa11868E7d2186b138069F84;
    uint256 public constant INTERVAL = 86400;
    uint256 public constant LENGTH = 604800;
    address public constant OWNER = 0x946eF43867225695E29241813A8F41519634B36b;
    bool private constant zeroForOne = true;

    // ====================================================== \\
    //                         STRUCTS                        \\
    // ====================================================== \\

    struct Model {
        uint48 interval;
        uint48 last;
        uint48 conclusion;
        uint96 theta;
        uint96 price;
        uint96 capacity;
    }

    // ====================================================== \\
    //                         STORAGE                        \\
    // ====================================================== \\

    Model public ethModel;
    Model public lpModel;
    uint256 private ethCapacity;
    uint256 private lpCapacity;

    constructor() {}

    // ====================================================== \\
    //                   EXTERNAL FUNCTIONS                   \\
    // ====================================================== \\

    ///@notice bond Eth for koto
    function bond() external payable {
        if (block.timestamp > ethModel.conclusion) revert MarketClosed();
        if (ethModel.capacity != 0) {
            SafeTransferLib.safeTransferETH(address(KOTO), msg.value);
            // Cache variables for later use to minimize storage calls
            Model memory eth = ethModel;

            uint48 time = uint48(block.timestamp);
            uint256 theta = eth.theta * (time - eth.last);
            uint256 price = eth.price - theta;
            uint256 payout = (msg.value * 1e18 / price);
            if (payout > _max(eth)) revert MaxPayout();

            // Update market variables
            eth.price = uint96(_marketPrice(eth, payout));
            eth.theta = uint96(_decay(eth));
            eth.capacity -= uint96(payout);
            eth.last = time;
            ethModel = eth;

            bool success = KOTO.transfer(msg.sender, payout);
            if (!success) revert BondFailed();
            emit Bond(msg.sender, payout, price);
        } else {
            //If bonds are not available refund the eth sent to the contract
            SafeTransferLib.safeTransferETH(msg.sender, msg.value);
        }
    }

    ///@notice bond Koto / WETH LP tokens for Koto
    ///@param amount the amount of koto lp tokens to bond
    function bondLp(uint256 amount) external {
        if (block.timestamp > lpModel.conclusion) revert MarketClosed();
        if (lpModel.capacity != 0) {
            IERC20Minimal(PAIR).transferFrom(msg.sender, BOND_DEPOSITORY, amount);
            // Cache variables for later use to minimize storage calls
            Model memory lp = lpModel;
            uint48 time = uint48(block.timestamp);
            uint256 theta = lp.theta * (time - lp.last);

            uint256 price = lp.price - theta;

            uint256 payout = (amount * 1e18 / price);
            if (payout > _max(lp)) revert MaxPayout();

            // Update market variables
            lp.price = uint96(_marketPrice(lp, payout));
            lp.theta = uint96(_decay(lp));
            lp.capacity -= uint96(payout);
            lp.last = time;
            lpModel = lp;
            bool success = KOTO.transfer(msg.sender, payout);
            if (!success) revert BondFailed();
            emit Bond(msg.sender, payout, price);
        } else {
            revert BondsSoldOut();
        }
    }

    ///@notice transfer in the koto to be sold as bonds
    ///@param amount the amount of koto tokens to transfer in
    function notifyRewardAmount(uint256 amount) external {
        if (msg.sender != BOND_DEPOSITORY) revert OnlyDepository();
        KOTO.transferFrom(BOND_DEPOSITORY, address(this), amount);
    }

    ///@notice transfer eth bonded for koto to the koto contract
    function addReserves() external {
        SafeTransferLib.safeTransferETH(address(KOTO), address(this).balance);
    }

    ///@notice burn any unsold bonds
    function burn() external {
        if (msg.sender != OWNER) revert OnlyOwner();
        uint256 amount;
        if (KOTO.balanceOf(address(this)) - (ethModel.capacity + lpModel.capacity) > 0) {
            amount = KOTO.balanceOf(address(this)) - (ethModel.capacity + lpModel.capacity);
        }
        KOTO.burn(amount);
    }

    function create(uint256 ethBonds, uint256 lpBonds) external {
        if (msg.sender != OWNER) revert OnlyOwner();
        if (ethModel.conclusion > block.timestamp) revert OngoingMarket();
        if (ethBonds + lpBonds > KOTO.balanceOf(address(this))) revert BondOverflow();
        ethCapacity = ethBonds;
        lpCapacity = lpBonds;
        _create();
        _createLpMarket();
    }

    // ====================================================== \\
    //                   EXTERNAL VIEW FUNCTIONS               \\
    // ====================================================== \\

    function market() external view returns (Model memory, Model memory) {
        return (ethModel, lpModel);
    }

    function ethPrice() external view returns (uint256) {
        Model memory model = ethModel;
        return model.price - (model.theta * (block.timestamp - model.last));
    }

    function lpPrice() external view returns (uint256) {
        Model memory model = lpModel;
        return model.price - (model.theta * (block.timestamp - model.last));
    }

    // ====================================================== \\
    //                    INTERNAL FUNCTIONS                  \\
    // ====================================================== \\

    // Set the initial price to the current market price
    function _create() private {
        uint256 _capacity = ethCapacity;
        if (_capacity > 0) {
            uint256 initialPrice = _getPrice();
            uint96 capacity = uint96(_capacity);
            uint48 conclusion = uint48(block.timestamp + LENGTH);
            bool policy = _policy(capacity, initialPrice);

            if (policy) {
                Model memory _ethModel = Model(
                    uint48(INTERVAL),
                    uint48(block.timestamp),
                    uint48(conclusion),
                    0,
                    uint96(initialPrice),
                    uint96(capacity)
                );
                _ethModel.theta = uint96(_decay(_ethModel));
                ethModel = _ethModel;
                emit CreateMarket(capacity, block.timestamp, conclusion);
            } else {
                KOTO.burn(capacity);
                // Set the markets so that they will be closed for the next interval. Important step to make sure
                // that if anyone accidently tries to buy a bond they get refunded their eth.
                ethModel.conclusion = uint48(block.timestamp + INTERVAL);
                ethModel.capacity = 0;
            }
            ethCapacity = 0;
        } else {
            ethModel.conclusion = uint48(block.timestamp + LENGTH);
        }
    }

    function _createLpMarket() private {
        uint256 _capacity = lpCapacity;
        if (_capacity > 0) {
            uint256 initialPrice = _getLpPrice();
            uint96 capacity = uint96(_capacity);
            uint48 conclusion = uint48(block.timestamp + LENGTH);
            Model memory _lpModel = Model(
                uint48(INTERVAL), uint48(block.timestamp), uint48(conclusion), 0, uint96(initialPrice), uint96(capacity)
            );
            _lpModel.theta = uint96(_decay(_lpModel));
            lpModel = _lpModel;
            emit CreateMarket(capacity, block.timestamp, conclusion);
            lpCapacity = 0;
        } else {
            lpModel.conclusion = uint48(block.timestamp + LENGTH);
        }
    }

    // ====================================================== \\
    //                 INTERNAL VIEW FUNCTIONS                \\
    // ====================================================== \\

    function _policy(uint256 capacity, uint256 price) private view returns (bool decision) {
        uint256 supply = KOTO.totalSupply();
        uint256 burnRelative = (address(KOTO).balance * 1e18) / (supply - capacity);
        uint256 bondRelative = ((address(KOTO).balance * 1e18) + ((capacity * price))) / supply;
        decision = burnRelative >= bondRelative ? false : true;
    }

    ///@notice calculate the current decay per second required to sell all the bonds within the
    /// remaining amount of time.
    function _decay(Model memory model) private view returns (uint256) {
        uint256 delta = model.conclusion - block.timestamp;
        uint256 decay = model.price / delta;
        return decay;
    }

    ///@notice calculate the new market price based off of the previous bond sell.
    function _marketPrice(Model memory model, uint256 sold) private pure returns (uint256) {
        uint256 price = FullMath.mulDiv(model.price, model.capacity, model.capacity - sold);
        return price;
    }

    function _max(Model memory model) private view returns (uint256) {
        uint256 remaining = model.conclusion - block.timestamp;
        uint256 max = (model.capacity * model.interval) / remaining;
        return max;
    }

    ///@notice calculate the current market price based on the reserves of the Uniswap Pair
    ///@dev price is returned as the amount of ETH you would get back for 1 full (1e18) Koto tokens
    function _getPrice() private view returns (uint256 price) {
        uint112 reserve0;
        uint112 reserve1;
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x0902f1ac00000000000000000000000000000000000000000000000000000000)
            let success := staticcall(gas(), PAIR, ptr, 4, 0, 0)
            if iszero(success) { revert(0, 0) }
            returndatacopy(0x00, 0, 32)
            returndatacopy(0x20, 0x20, 32)
            reserve0 := mload(0x00)
            reserve1 := mload(0x20)
        }

        if (zeroForOne) {
            price = FullMath.mulDiv(uint256(reserve1), 1e18, uint256(reserve0));
        } else {
            price = FullMath.mulDiv(uint256(reserve0), 1e18, uint256(reserve1));
        }
    }

    ///@notice return the current price in koto for 1 LP token
    function _getLpPrice() private view returns (uint256 _lpPrice) {
        uint112 reserve0;
        uint112 reserve1;
        uint256 lpTotalSupply;
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x0902f1ac00000000000000000000000000000000000000000000000000000000)
            let success := staticcall(gas(), PAIR, ptr, 4, 0, 0)
            if iszero(success) { revert(0, 0) }
            returndatacopy(0x00, 0, 32)
            returndatacopy(0x20, 0x20, 32)
            reserve0 := mload(0x00)
            reserve1 := mload(0x20)
            mstore(add(ptr, 0x20), 0x18160ddd00000000000000000000000000000000000000000000000000000000)
            let result := staticcall(gas(), PAIR, add(ptr, 0x20), 4, 0, 32)
            lpTotalSupply := mload(0x00)
        }
        ///@dev with uniswap v2 we simply treat the other token total as equal value to simplify the pricing mechanism
        if (zeroForOne) {
            _lpPrice = FullMath.mulDiv(reserve0 * 2, 1e18, lpTotalSupply);
        } else {
            _lpPrice = FullMath.mulDiv(reserve1 * 2, 1e18, lpTotalSupply);
        }
    }

    // ====================================================== \\
    //                     ERROR AND EVENTS                   \\
    // ====================================================== \\

    error OnlyDepository();
    error MarketClosed();
    error MaxPayout();
    error BondFailed();
    error BondsSoldOut();
    error OngoingMarket();
    error OnlyOwner();
    error BondOverflow();

    event Bond(address indexed caller, uint256 payout, uint256 price);
    event CreateMarket(uint256 bonds, uint256 start, uint256 end);

    receive() external payable {}
}

// File: src/interfaces/IKotoV3.sol



// SPDX-License-Identifier: MIT

pragma solidity 0.8.23;

import {PricingLibrary} from "../PricingLibrary.sol";

interface IKotoV3 {
    // ==================== EXTERNAL FUNCTIONS ===================== \\

    function transfer(address _to, uint256 _value) external returns (bool success);

    function transferFrom(address _from, address _to, uint256 _value) external returns (bool success);

    function approve(address _spender, uint256 _value) external returns (bool success);

    ///@notice exchange ETH for Koto tokens at the current bonding price
    ///@dev bonds are set on 1 day intervals with 4 hour deposit intervals and 30 minute tune intervals.
    function bond() external payable returns (uint256 payout);

    function bondLp(uint256 _lpAmount) external returns (uint256 payout);

    ///@notice burn Koto tokens in exchange for a piece of the underlying reserves
    ///@param amount The amount of Koto tokens to redeem
    ///@return payout The amount of ETH received in exchange for the Koto tokens
    function redeem(uint256 amount) external returns (uint256 payout);

    ///@notice burn Koto tokens, without redemption
    ///@param amount the amount of Koto to burn
    function burn(uint256 amount) external returns (bool success);

    // ==================== EXTERNAL VIEW FUNCTIONS ===================== \\

    ///@notice get the tokens name
    function name() external pure returns (string memory);

    ///@notice get the tokens symbol
    function symbol() external pure returns (string memory);

    ///@notice get the tokens decimals
    function decimals() external pure returns (uint8);

    ///@notice get the tokens total supply
    function totalSupply() external view returns (uint256);

    ///@notice get the current balance of a user
    ///@param _owner the user whos balance you want to check
    function balanceOf(address _owner) external view returns (uint256);

    ///@notice get current approved amount for transfer from another party
    ///@param owner the current owner of the tokens
    ///@param spender the user who has approval (or not) to spend the owners tokens
    function allowance(address owner, address spender) external view returns (uint256);

    ///@notice return the Uniswap V2 Pair address
    function pool() external view returns (address);

    ///@notice get the owner of the contract
    ///@dev ownership is nontransferable and limited to opening trade, exclusion / inclusion,s and increasing liquidity
    function ownership() external pure returns (address);

    ///@notice the current price a bond
    function bondPrice() external view returns (uint256);

    function bondPriceLp() external view returns (uint256);

    ///@notice return the current redemption price for 1 uint of Koto.
    function redemptionPrice() external view returns (uint256);

    function marketInfo()
        external
        view
        returns (PricingLibrary.Market memory, PricingLibrary.Term memory, PricingLibrary.Data memory);

    function lpMarketInfo()
        external
        view
        returns (PricingLibrary.Market memory, PricingLibrary.Term memory, PricingLibrary.Data memory);

    function depository() external pure returns (address);

    function create(uint256 ethBondAmount, uint256 lpBondAmount) external;

    // ========================= EVENTS ========================= \\

    event AmmAdded(address poolAdded);
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);
    event Bond(address indexed buyer, uint256 amount, uint256 bondPrice);
    event CreateMarket(uint256 bonds, uint256 start, uint48 end);
    event IncreaseLiquidity(uint256 kotoAdded, uint256 ethAdded);
    event Launched(uint256 time);
    event LimitsRemoved(uint256 time);
    event OpenBondMarket(uint256 openingTime);
    event Redeem(address indexed sender, uint256 burned, uint256 payout, uint256 floorPrice);
    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event UserExcluded(address indexed userToExclude);

    // ========================= ERRORS ========================= \\

    error AlreadyLaunched();
    error BondFailed();
    error InsufficentAllowance();
    error InsufficentBalance();
    error InsufficentBondsAvailable();
    error InvalidSender();
    error InvalidTransfer();
    error LimitsReached();
    error MarketClosed();
    error MaxPayout();
    error OngoingBonds();
    error OnlyOwner();
    error RedeemFailed();
    error Reentrancy();
}

// File: lib/solmate/src/utils/SafeTransferLib.sol



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

import {ERC20} from "../tokens/ERC20.sol";

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferETH(address to, uint256 amount) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and store if it succeeded or not.
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }

        require(success, "ETH_TRANSFER_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                            ERC20 OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferFrom(
        ERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
            mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "TRANSFER_FAILED");
    }

    function safeApprove(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "APPROVE_FAILED");
    }
}

// File: src/interfaces/IERC20Minimal.sol



// SPDX-License-Identifier: MIT

pragma solidity 0.8.23;

interface IERC20Minimal {
    function transferFrom(address from, address to, uint256 value) external returns (bool);
    function transfer(address, uint256) external returns (bool);
    function balanceOf(address) external view returns (uint256);
    function totalSupply() external view returns (uint256);
}

// File: src/libraries/FullMath.sol



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

/// @title Contains 512-bit math functions
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
library FullMath {
    /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
    /// @param a The multiplicand
    /// @param b The multiplier
    /// @param denominator The divisor
    /// @return result The 256-bit result
    /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
    function mulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = a * b
            // Compute the product mod 2**256 and mod 2**256 - 1
            // then use the Chinese Remainder Theorem to reconstruct
            // the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2**256 + prod0
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(a, b, not(0))
                prod0 := mul(a, b)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division
            if (prod1 == 0) {
                require(denominator > 0);
                assembly {
                    result := div(prod0, denominator)
                }
                return result;
            }

            // Make sure the result is less than 2**256.
            // Also prevents denominator == 0
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0]
            // Compute remainder using mulmod
            uint256 remainder;
            assembly {
                remainder := mulmod(a, b, denominator)
            }
            // Subtract 256 bit number from 512 bit number
            assembly {
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator
            // Compute largest power of two divisor of denominator.
            // Always >= 1.
            uint256 twos = (0 - denominator) & denominator;
            // Divide denominator by power of two
            assembly {
                denominator := div(denominator, twos)
            }

            // Divide [prod1 prod0] by the factors of two
            assembly {
                prod0 := div(prod0, twos)
            }
            // Shift in bits from prod1 into prod0. For this we need
            // to flip `twos` such that it is 2**256 / twos.
            // If twos is zero, then it becomes one
            assembly {
                twos := add(div(sub(0, twos), twos), 1)
            }
            prod0 |= prod1 * twos;

            // Invert denominator mod 2**256
            // Now that denominator is an odd number, it has an inverse
            // modulo 2**256 such that denominator * inv = 1 mod 2**256.
            // Compute the inverse by starting with a seed that is correct
            // correct for four bits. That is, denominator * inv = 1 mod 2**4
            uint256 inv = (3 * denominator) ^ 2;
            // Now use Newton-Raphson iteration to improve the precision.
            // Thanks to Hensel's lifting lemma, this also works in modular
            // arithmetic, doubling the correct bits in each step.
            inv *= 2 - denominator * inv; // inverse mod 2**8
            inv *= 2 - denominator * inv; // inverse mod 2**16
            inv *= 2 - denominator * inv; // inverse mod 2**32
            inv *= 2 - denominator * inv; // inverse mod 2**64
            inv *= 2 - denominator * inv; // inverse mod 2**128
            inv *= 2 - denominator * inv; // inverse mod 2**256

            // Because the division is now exact we can divide by multiplying
            // with the modular inverse of denominator. This will give us the
            // correct result modulo 2**256. Since the precoditions guarantee
            // that the outcome is less than 2**256, this is the final result.
            // We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inv;
            return result;
        }
    }

    /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
    /// @param a The multiplicand
    /// @param b The multiplier
    /// @param denominator The divisor
    /// @return result The 256-bit result
    function mulDivRoundingUp(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            result = mulDiv(a, b, denominator);
            if (mulmod(a, b, denominator) > 0) {
                require(result < type(uint256).max);
                result++;
            }
        }
    }
}

// File: src/PricingLibrary.sol



// SPDX-License-Identifier: MIT

pragma solidity 0.8.23;

library PricingLibrary {
    // 1 Slot
    struct Data {
        uint48 lastTune;
        uint48 lastDecay; // last timestamp when market was created and debt was decayed
        uint48 length; // time from creation to conclusion. used as speed to decay debt.
        uint48 depositInterval; // target frequency of deposits
        uint48 tuneInterval; // frequency of tuning
    }

    // 2 Storage slots
    struct Market {
        uint96 capacity; // capacity remaining
        uint96 totalDebt; // total debt from market
        uint96 maxPayout; // max tokens in/out
        uint96 sold; // Koto out
        uint96 purchased; // Eth in
    }

    // 1 Storage Slot
    struct Adjustment {
        uint128 change;
        uint48 lastAdjustment;
        uint48 timeToAdjusted;
        bool active;
    }

    // 2 Storage slots
    struct Term {
        uint48 conclusion; // timestamp when the current market will end
        uint256 controlVariable; // scaling variable for price
    }

    function decay(Data memory data, Market memory market, Term memory terms, Adjustment memory adjustments)
        internal
        view
        returns (Market memory, Data memory, Term memory, Adjustment memory)
    {
        uint48 time = uint48(block.timestamp);
        market.totalDebt -= debtDecay(data, market);
        data.lastDecay = time;

        if (adjustments.active) {
            (uint128 adjustby, uint48 dt, bool stillActive) = controlDecay(adjustments);
            terms.controlVariable -= adjustby;
            if (stillActive) {
                adjustments.change -= adjustby;
                adjustments.timeToAdjusted -= dt;
                adjustments.lastAdjustment = time;
            } else {
                adjustments.active = false;
            }
        }
        return (market, data, terms, adjustments);
    }

    function controlDecay(Adjustment memory info) internal view returns (uint128, uint48, bool) {
        if (!info.active) return (0, 0, false);

        uint48 secondsSince = uint48(block.timestamp) - info.lastAdjustment;
        bool active = secondsSince < info.timeToAdjusted;
        uint128 _decay = active ? (info.change * secondsSince) / info.timeToAdjusted : info.change;
        return (_decay, secondsSince, active);
    }

    function marketPrice(uint256 _controlVariable, uint256 _totalDebt, uint256 _totalSupply)
        internal
        pure
        returns (uint256)
    {
        return ((_controlVariable * debtRatio(_totalDebt, _totalSupply)) / 1e18);
    }

    function debtRatio(uint256 _totalDebt, uint256 _totalSupply) internal pure returns (uint256) {
        return ((_totalDebt * 1e18) / _totalSupply);
    }

    function debtDecay(Data memory data, Market memory market) internal view returns (uint64) {
        uint256 secondsSince = block.timestamp - data.lastDecay;
        return uint64((market.totalDebt * secondsSince) / data.length);
    }

    struct TuneCache {
        uint256 remaining;
        uint256 price;
        uint256 capacity;
        uint256 targetDebt;
        uint256 ncv;
    }

    function tune(
        uint48 time,
        Market memory market,
        Term memory term,
        Data memory data,
        Adjustment memory adjustment,
        uint256 _totalSupply
    ) internal pure returns (Market memory, Term memory, Data memory, Adjustment memory) {
        TuneCache memory cache;
        if (time >= data.lastTune + data.tuneInterval) {
            cache.remaining = term.conclusion - time;
            cache.price = marketPrice(term.controlVariable, market.totalDebt, _totalSupply);
            cache.capacity = market.capacity;
            market.maxPayout = uint96((cache.capacity * data.depositInterval / cache.remaining));
            cache.targetDebt = cache.capacity * data.length / cache.remaining;
            cache.ncv = (cache.price * _totalSupply) / cache.targetDebt;

            if (cache.ncv < term.controlVariable) {
                uint128 change = uint128(term.controlVariable - cache.ncv);
                adjustment = Adjustment(change, time, data.tuneInterval, true);
            } else {
                term.controlVariable = cache.ncv;
            }
            data.lastTune = time;
        }
        return (market, term, data, adjustment);
    }
}

// File: lib/solmate/src/tokens/ERC20.sol



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

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

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

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}