ETH Price: $2,213.03 (-5.10%)

Contract Diff Checker

Contract Name:
Boobies

Contract Source Code:

File 1 of 1 : Boobies

// File: @uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol


pragma solidity >=0.5.0;

interface IUniswapV2Factory {
    event PairCreated(address indexed token0, address indexed token1, address pair, uint);

    function feeTo() external view returns (address);
    function feeToSetter() external view returns (address);

    function getPair(address tokenA, address tokenB) external view returns (address pair);
    function allPairs(uint) external view returns (address pair);
    function allPairsLength() external view returns (uint);

    function createPair(address tokenA, address tokenB) external returns (address pair);

    function setFeeTo(address) external;
    function setFeeToSetter(address) external;
}

// File: @uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol


pragma solidity >=0.5.0;

interface IUniswapV2Pair {
    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);

    function name() external pure returns (string memory);
    function symbol() external pure returns (string memory);
    function decimals() external pure returns (uint8);
    function totalSupply() external view returns (uint);
    function balanceOf(address owner) external view returns (uint);
    function allowance(address owner, address spender) external view returns (uint);

    function approve(address spender, uint value) external returns (bool);
    function transfer(address to, uint value) external returns (bool);
    function transferFrom(address from, address to, uint value) external returns (bool);

    function DOMAIN_SEPARATOR() external view returns (bytes32);
    function PERMIT_TYPEHASH() external pure returns (bytes32);
    function nonces(address owner) external view returns (uint);

    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;

    event Mint(address indexed sender, uint amount0, uint amount1);
    event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
    event Swap(
        address indexed sender,
        uint amount0In,
        uint amount1In,
        uint amount0Out,
        uint amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    function MINIMUM_LIQUIDITY() external pure returns (uint);
    function factory() external view returns (address);
    function token0() external view returns (address);
    function token1() external view returns (address);
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
    function price0CumulativeLast() external view returns (uint);
    function price1CumulativeLast() external view returns (uint);
    function kLast() external view returns (uint);

    function mint(address to) external returns (uint liquidity);
    function burn(address to) external returns (uint amount0, uint amount1);
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
    function skim(address to) external;
    function sync() external;

    function initialize(address, address) external;
}

// File: @uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router01.sol


pragma solidity >=0.6.2;

interface IUniswapV2Router01 {
    function factory() external pure returns (address);
    function WETH() external pure returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB, uint liquidity);
    function addLiquidityETH(
        address token,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETH(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountToken, uint amountETH);
    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETHWithPermit(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountToken, uint amountETH);
    function swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapTokensForExactTokens(
        uint amountOut,
        uint amountInMax,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);
    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);

    function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
    function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
    function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}

// File: @uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol


pragma solidity >=0.6.2;


interface IUniswapV2Router02 is IUniswapV2Router01 {
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountETH);
    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountETH);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable;
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
}

// File: @chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol


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: @openzeppelin/contracts/token/ERC20/IERC20.sol


// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

// File: @openzeppelin/contracts/security/ReentrancyGuard.sol


// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }
}

// File: @openzeppelin/contracts/utils/Context.sol


// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

// File: @openzeppelin/contracts/access/Ownable.sol


// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;


/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

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

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File: boobies1.sol


pragma solidity ^0.8.26;








    /**
    * THIS CONTRACT IS NOT FULLY AUDITED AND IS STILL IN THE TESTING PHASE.
    * USE AT YOUR OWN RISK.
    * 
    * Twitter: https://twitter.com/5318008eth 
    *
    * TOKENOMICS:
    * Initial Supply: 1,000,000 BOOBS
    *
    * 2.5% of Supply to Dev
    * 2.5% of Supply to Rebase Rewards
    * 25% of Supply to Initial Liquidity Pool
    * 70% of Supply to Tranche Releases
    * 
    * Tranches unlocks at: [4x, 8x, 16x, 32x, 64x, 128x, 256x, 512x, 1024x, 2048x, 4096x, 8192x]
    * Tranche supply sizes are: [5%, 5%, 5%, 5%, 2.5%, 2.5%, 2.5%, 2.5%, 1.25%, 1.25%, 1.25%, 1.25%]
    * Ethereum raised in Tranche sales are immediately paired with unreleased tokens to add liquidity.
    *
    * Token Rebases to maintain peg of 5318008 * 10^y
    *
    * PLEASE READ BEFORE CHANGING ANY ACCOUNTING OR MATH
    * Anytime there is division, there is a risk of numerical instability from rounding errors. In
    * order to minimize this risk, we adhere to the following guidelines:
    * 1) The conversion rate adopted is the number of BaseUnits (BU) that equals 1 PublicUnit (PU).
    *    The inverse rate must not be used—TOTAL_BASE_UNITS is always the numerator and _totalSupply is
    *    always the denominator. (i.e., If you want to convert BU to PU, instead of multiplying by the inverse rate,
    *    you should divide by the normal rate).
    * 2) BaseUnit balances converted into PublicUnits are always rounded down (truncated).
    *
    * We make the following guarantees:
    * - If address 'A' transfers x PublicUnits to address 'B', A's resulting external balance will
    *   be decreased by precisely x PublicUnits, and B's external balance will be precisely
    *   increased by x PublicUnits.
    *
    * We do not guarantee that the sum of all balances equals the result of calling totalSupply().
    * This is because, for any conversion function 'f()' that has non-zero rounding error,
    * f(x0) + f(x1) + ... + f(xn) is not always equal to f(x0 + x1 + ... xn).
    */

contract Boobies is IERC20, ReentrancyGuard, Ownable {
    string private _name = "Boobies";
    string private _symbol = "BOOB";
    uint8 private _decimals = 18;

    // Constants
    uint256 private constant MAX_UINT256 = type(uint256).max;
    uint256 private constant INITIAL_SUPPLY = 1_000_000 * 1e18;
    uint256 private constant TOTAL_BASE_UNITS = MAX_UINT256 - (MAX_UINT256 % INITIAL_SUPPLY); // TOTAL_BASE_UNITS is a multiple of INITIAL_SUPPLY so that _baseUnitsPerPublicUnit is an integer.

    // Token Variables
    uint256 private _totalSupply;
    uint256 private _baseUnitsPerPublicUnit;
    mapping(address => uint256) private _baseUnitBalances;
    mapping(address => mapping(address => uint256)) private _allowedPublicUnits; // This is denominated in PublicUnits (PU), because the BaseUnit-PublicUnit conversion might change before it's fully paid.

    // Rebase Variables
    uint256 public pegValue = 531800800800800800; // $0.5318008
    uint256 public lastRebaseBlock = 0;
    uint256 public maxPriceDifference = 3; // Maximum allowed price difference in percentage (3%)
    bool public launched = false;

    // Tranche Prices
    uint256 public initialPricePerInitialSupply;
    uint256[] public priceMultiples = [4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192];
    uint256[12] public trancheSupplyBaseUnits;
    uint256[12] public trancheSoldBaseUnits;

    // Sniper Protection
    bool private _locked;
    bool public sniperProtection = false;
    mapping(address => bool) private _isExcludedFromSniperProtection;
    uint256 public maxTransaction = TOTAL_BASE_UNITS / 100; // 1% Max Transaction Size
    uint256 public maxPriorityFee = 33 * 1e8; // 3.3 gwei

    // Addresses
    IUniswapV2Router02 public uniswapV2Router;
    IUniswapV2Pair public uniswapV2Pair;
    IUniswapV2Pair public ethUsdcPair;
    AggregatorV3Interface public ethPriceFeed;

    // Rebase Rewards
    uint256 public rewardMultiplier = 250; // 250% by default (should be around 150% of actual gas spent)
    mapping(address => uint256) public successfulRebaseCalls;

    // Events
    event Rebase(uint256 _totalSupply, uint256 pegValue);
    event LiquidityDeployed(address pair, uint256 tokenAmount, uint256 ethAmount);
    event RewardUnpaid(address indexed recipient, uint256 attemptedTokenAmount);
    event WithdrawalRequested(address indexed requester, uint256 amount, uint256 executeAfter);
    event WithdrawalExecuted(address indexed executor, uint256 amount);

    // Withdrawal Struct
    struct Withdrawal {
        uint256 amount;
        uint256 executeAfter;
    }

    // Timelock Mappings for Withdrawals
    mapping(address => Withdrawal) public pendingETHWithdrawals;
    mapping(address => Withdrawal) public pendingTokenWithdrawals;

    // Price and calculation errors
    error DivisionByZeroError(string context);
    error InvalidPriceError(string priceType);
    error PriceOutsideAcceptableRange();
    error PriceDifferenceOutOfRange();

    // Balance and allowance errors
    error InsufficientBalanceError(string balanceType);
    error InvalidRecipient(address recipient);

    // Input and validation errors
    error InvalidInputError(string inputType);
    error LaunchStatus(bool launched);
    error TranchePriceExceedsCurrentPrice();
    error TrancheSoldOut();

    // Access control errors
    error OnlyOwnerAllowed();
    error ReentrantCall();

    // Transaction limit errors
    error MaxTransactionExceeded();
    error MaxPriorityFeeExceeded();

    // Rebase errors
    error RebaseTimeLock();

    // Miscellaneous errors
    error NoETHToWithdraw();

    // Modifiers
    modifier validRecipient(address to) {
        if (to == address(0)) revert InvalidRecipient(to);
        _;
    }

    modifier trancheReentrant() {
        if (_locked) revert ReentrantCall();
        _locked = true;
        _;
        _locked = false;
    }

    /**
     * @dev Constructor initializes the contract with Uniswap V2 Router, ETH-USDC pair, and Chainlink oracle addresses.
     * 
     * Uniswap V2 Router Addresses:
     * Mainnet: 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D (Mainnet Uniswap V2 Router address).
     * Sepolia: 0xC532a74256D3Db42D0Bf7a0400fEFDbad7694008 (Sepolia Uniswap V2 Router address).
     * 
     * ETH-USDC Pair Addresses:
     * Mainnet: 0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc (ETH/USDC Uniswap V2 Pair on Mainnet).
     * Sepolia: 0x6c1bBfD6330CA6c3f27C29b3a3330dB2B6B0980a (ETH/USDC Uniswap V2 Pair on Sepolia).
     * 
     * USDC Token Address:
     * Mainnet: 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 (USDC on Mainnet).
     * Sepolia: 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238 (USDC on Sepolia).
     *
     * Chainlink Oracle Addresses:
     * Mainnet: 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419 (ETH/USD on Mainnet).
     * Sepolia: 0x694AA1769357215DE4FAC081bf1f309aDC325306 (ETH/USD on Sepolia).
     */
    constructor(
        address _uniswapV2Router, 
        address _ethPriceFeed, 
        address _ethUsdcPair
    ) Ownable(msg.sender) {
        uniswapV2Router = IUniswapV2Router02(_uniswapV2Router);
        ethPriceFeed = AggregatorV3Interface(_ethPriceFeed);
        ethUsdcPair = IUniswapV2Pair(_ethUsdcPair);
        uniswapV2Pair  = IUniswapV2Pair(IUniswapV2Factory(uniswapV2Router.factory()).createPair(address(this), uniswapV2Router.WETH()));

        _totalSupply = INITIAL_SUPPLY;
        _baseUnitBalances[address(this)] = TOTAL_BASE_UNITS;
        _baseUnitsPerPublicUnit = TOTAL_BASE_UNITS / _totalSupply;

        emit Transfer(address(0x0), address(this), _totalSupply);

        setExcludedFromSniperProtection(owner(), true);
        setExcludedFromSniperProtection(address(uniswapV2Router), true);
        setExcludedFromSniperProtection(address(this), true);
        setExcludedFromSniperProtection(address(0xdead), true);
        setExcludedFromSniperProtection(address(uniswapV2Pair), true);
    }

    /**
     * @dev Receive function to accept ETH deposits.
     */
    receive() external payable {
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() public view returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     */
    function decimals() public view returns (uint8) {
        return _decimals;
    }

    /**
     * @notice Sets the maximum allowed price difference to allow for rebase.
     * @dev Only callable by the contract owner.
     * @param _maxPriceDifference The new maximum price difference percentage.
     */
    function setMaxPriceDifference(uint256 _maxPriceDifference) external onlyOwner {
        maxPriceDifference = _maxPriceDifference;
    }

    /**
     * @notice Sets the reward multiplier to adjust the payout of rebase rewards.
     * @dev Only callable by the contract owner.
     * @param newRewardMultiplier The new reward multiplier.
     */
    function setRewardMultiplier(uint256 newRewardMultiplier) external onlyOwner {
        if (newRewardMultiplier > 1000) revert InvalidInputError("Reward multiplier");
        rewardMultiplier = newRewardMultiplier;
    }

    /**
     * @notice Toggles sniper protection on or off.
     * @dev Only callable by the contract owner.
     */
    function toggleSniperProtection() external onlyOwner {
        sniperProtection = !sniperProtection;
    }

    /**
     * @notice Excludes or includes an account from sniper protection.
     * @dev Only callable by the contract owner.
     * @param account The address to exclude or include.
     * @param excluded Boolean indicating exclusion status.
     */
    function setExcludedFromSniperProtection(address account, bool excluded) public onlyOwner {
        _isExcludedFromSniperProtection[account] = excluded;
    }

    /**
     * @notice Requests to withdraw a specified amount of ETH from the contract.
     * @dev Only callable by the contract owner. Initiates a timelock of 24 hours.
     * @param amount The amount of ETH to withdraw.
     */
    function requestWithdrawETH(uint256 amount) external onlyOwner {
        if (amount > address(this).balance) revert NoETHToWithdraw();
        pendingETHWithdrawals[msg.sender] = Withdrawal({
            amount: amount,
            executeAfter: block.timestamp + 24 hours
        });
        emit WithdrawalRequested(msg.sender, amount, block.timestamp + 24 hours);
    }

    /**
     * @notice Executes the ETH withdrawal after the timelock period.
     * @dev Only callable by the contract owner once the timelock has passed.
     */
    function executeWithdrawETH() external onlyOwner nonReentrant {
        Withdrawal memory withdrawal = pendingETHWithdrawals[msg.sender];
        if (withdrawal.amount == 0) revert NoETHToWithdraw();
        if (block.timestamp < withdrawal.executeAfter) revert RebaseTimeLock();

        pendingETHWithdrawals[msg.sender].amount = 0;

        payable(owner()).transfer(withdrawal.amount);
        emit WithdrawalExecuted(msg.sender, withdrawal.amount);
    }

    /**
     * @notice Requests to withdraw a specified amount of tokens.
     * @dev Only callable by the contract owner. Initiates a timelock of 24 hours.
     * @param amount The amount of tokens to withdraw.
     */
    function requestWithdrawTokens(uint256 amount) external onlyOwner {
        if (amount == 0) revert InvalidInputError("Withdraw amount");
        uint256 baseUnitValue = amount * _baseUnitsPerPublicUnit;
        if (_baseUnitBalances[address(this)] < baseUnitValue) revert InsufficientBalanceError("Token balance");

        pendingTokenWithdrawals[msg.sender] = Withdrawal({
            amount: amount,
            executeAfter: block.timestamp + 24 hours
        });
        emit WithdrawalRequested(msg.sender, amount, block.timestamp + 24 hours);
    }

    /**
     * @notice Executes the token withdrawal after the timelock period.
     * @dev Only callable by the contract owner once the timelock has passed.
     */
    function executeWithdrawTokens() external onlyOwner nonReentrant {
        Withdrawal memory withdrawal = pendingTokenWithdrawals[msg.sender];
        if (withdrawal.amount == 0) revert NoETHToWithdraw();
        if (block.timestamp < withdrawal.executeAfter) revert RebaseTimeLock();

        pendingTokenWithdrawals[msg.sender].amount = 0;

        uint256 baseUnitValue = withdrawal.amount * _baseUnitsPerPublicUnit;
        _baseUnitBalances[address(this)] -= baseUnitValue;
        _baseUnitBalances[owner()] += baseUnitValue;

        emit Transfer(address(this), owner(), withdrawal.amount);
        emit WithdrawalExecuted(msg.sender, withdrawal.amount);
    }

    /**
     * @dev Rebalances the supply of PublicUnits to adjust for the current market price, bringing
     * the token supply back in line with its peg. The peg and supply adjustments are made based on the
     * current price of the token in USD, retrieved through external oracles and the Uniswap price feed.
     *
     * Internally adjusts the BaseUnit balance by recalculating the conversion rate between PublicUnits and BaseUnits.
     * The `totalSupply()` will be modified in accordance with the price peg.
     * 
     * In addition, the caller of the `rebase()` function will be paid a bonus in PublicUnits equivalent to 
     * cover gas cost incurred, adjusted by the reward multiplier. The token amount is calculated based 
     * on the ETH cost of gas, and the tokens are transferred to the caller's specified recipient.
     *
     * Emits a `Rebase` event reflecting the new total supply and peg value.
     * Emits a `Transfer` event if the reward payment to the caller is successful.
     * Emits a `RewardUnpaid` event if the contract does not have enough tokens to pay the reward.
     */
    function rebase(address paymentRecipient) external nonReentrant {
        if (block.number < (lastRebaseBlock + 300)) revert RebaseTimeLock();

        if (paymentRecipient == address(0)) {
            paymentRecipient = msg.sender;
        }

        uint256 gasStart = gasleft();

        _rebase();

        successfulRebaseCalls[msg.sender] += 1;

        if (rewardMultiplier > 0) {
            uint256 boobsPriceInETH = getBoobsPriceInETH();
            if (boobsPriceInETH == 0) revert InvalidPriceError("BOOBS/ETH");

            uint256 gasUsed = gasStart - gasleft();
            uint256 gasCost = gasUsed * block.basefee;
            uint256 payoutInETH = ((gasCost + (1.5 * 1e9)) * rewardMultiplier) / 100;

            uint256 tokenAmount = (payoutInETH * 1e18) / boobsPriceInETH;
            uint256 baseUnitValue = tokenAmount * _baseUnitsPerPublicUnit;

            if (_baseUnitBalances[address(this)] >= baseUnitValue) {
                _baseUnitBalances[address(this)] -= baseUnitValue;
                _baseUnitBalances[paymentRecipient] += baseUnitValue;

                emit Transfer(address(this), paymentRecipient, tokenAmount);
            } else {
                emit RewardUnpaid(paymentRecipient, tokenAmount);
            }
        }
    }

    /**
     * @dev Internal function that performs the rebase operation, adjusting the supply of PublicUnits 
     * based on the current price in USD and the target peg value.
     *
     * The function fetches the price of ETH from both Chainlink and Uniswap, ensuring that the price
     * discrepancy is within the acceptable range. Then, it calculates the new rebase factor and adjusts
     * the total supply of PublicUnits accordingly.
     * 
     * If the total supply has grown too large or too small, it adjusts the peg value by a factor of 10
     * in either direction. After adjusting the supply, the function syncs the Uniswap liquidity pair.
     * 
     * The `lastRebaseBlock` is updated to the current block after a successful rebase.
     *
     * Emits a `Rebase` event reflecting the new total supply and peg value.
     * 
     * return The updated supply of PublicUnits after the rebase.
     */
    function _rebase() internal {
        uint256 ethPriceFromChainlink = getETHPriceFromChainlink();
        uint256 ethPriceFromUniswap = getETHPriceFromUniswap();

        if (ethPriceFromChainlink == 0 || ethPriceFromUniswap == 0) revert InvalidPriceError("ETH");
        if (!isPriceDifferenceAcceptable(ethPriceFromUniswap, ethPriceFromChainlink)) revert PriceDifferenceOutOfRange();

        uint256 boobsPriceInUSD = getBoobsPriceInUSD(ethPriceFromUniswap);
        if (boobsPriceInUSD == 0) revert InvalidPriceError("BOOBS/USD");

        uint256 rebaseFactor = (boobsPriceInUSD * 1e18) / pegValue;
        uint256 supplyAdjustment = (rebaseFactor * _totalSupply) / 1e18;

        if (supplyAdjustment > (12_000_000 * 1e18)) {
            supplyAdjustment = supplyAdjustment / 10;
            pegValue = pegValue * 10;
        } else if (supplyAdjustment < (800_000 * 1e18)) {
            supplyAdjustment = supplyAdjustment * 10;
            pegValue = pegValue / 10;
        }

        _totalSupply = supplyAdjustment;
        _baseUnitsPerPublicUnit = TOTAL_BASE_UNITS / _totalSupply;
        lastRebaseBlock = block.number;

        emit Rebase(_totalSupply, pegValue);

        IUniswapV2Pair(address(uniswapV2Pair)).sync();
    }

    /**
     * @dev Retrieves the current price of BOOBS in USD based on Uniswap prices.
     * 
     * @param ethPriceFromUniswap The price of ETH from Uniswap.
     * @return The price of BOOBS in USD.
     */
    function getBoobsPriceInUSD(uint256 ethPriceFromUniswap) public view returns (uint256) {
        uint256 boobsPriceInETH = getBoobsPriceInETH();
        if (boobsPriceInETH == 0 || ethPriceFromUniswap == 0) revert InvalidPriceError("BOOBS/ETH or ETH/USD");

        return (boobsPriceInETH * ethPriceFromUniswap) / 1e18;
    }

    /**
     * @dev Retrieves the current price of BOOBS in ETH based on the Uniswap liquidity pool.
     * 
     * @return The price of BOOBS in ETH.
     */
    function getBoobsPriceInETH() public view returns (uint256) {
        IUniswapV2Pair pair = IUniswapV2Pair(address(uniswapV2Pair));
        (uint112 reserve0, uint112 reserve1, ) = pair.getReserves();
        if (reserve0 == 0 || reserve1 == 0) revert DivisionByZeroError("BOOBS/ETH reserves");

        if (pair.token0() == address(this)) {
            return uint256(reserve1) * 1e18 / uint256(reserve0);
        } else {
            return uint256(reserve0) * 1e18 / uint256(reserve1);
        }
    }

    /**
     * @dev Retrieves the current price of ETH in USD from Uniswap's ETH/USDC pool.
     * 
     * @return The price of ETH in USD.
     */
    function getETHPriceFromUniswap() public view returns (uint256) {
        IUniswapV2Pair pair = IUniswapV2Pair(ethUsdcPair);
        (uint112 reserve0, uint112 reserve1, ) = pair.getReserves();
        if (reserve0 == 0 || reserve1 == 0) revert DivisionByZeroError("ETH/USDC reserves");

        if (pair.token0() == uniswapV2Router.WETH()) {
            return uint256(reserve1) * 1e30 / uint256(reserve0);
        } else {
            return uint256(reserve0) * 1e30 / uint256(reserve1);
        }
    }

    /**
     * @dev Retrieves the current price of ETH in USD from Chainlink's price feed.
     * 
     * @return The price of ETH in USD (18 decimal places).
     */
    function getETHPriceFromChainlink() public view returns (uint256) {
        (,int256 price,,,) = ethPriceFeed.latestRoundData();
        if (price <= 0) revert InvalidPriceError("ETH from Chainlink");
        return uint256(price) * 1e10;
    }

    /**
     * @dev Checks if the price difference between Uniswap and Chainlink is within the acceptable range.
     * 
     * @param uniswapPrice The price of ETH from Uniswap.
     * @param chainlinkPrice The price of ETH from Chainlink.
     * @return True if the price difference is acceptable, false otherwise.
     */
    function isPriceDifferenceAcceptable(uint256 uniswapPrice, uint256 chainlinkPrice) public view returns (bool) {
        uint256 difference = uniswapPrice > chainlinkPrice ? uniswapPrice - chainlinkPrice : chainlinkPrice - uniswapPrice;
        uint256 percentageDifference = (difference * 100) / ((uniswapPrice + chainlinkPrice) / 2);
        return percentageDifference <= maxPriceDifference;
    }

    /**
     * @dev Deploys initial liquidity to Uniswap, sets up tranche supplies, and initializes key contract parameters.
     * This function can only be called once by the owner when the contract is unlaunched.
     * It requires ETH to be sent along with the transaction to provide initial liquidity.
     */
    function deployAndSendLiquidity() external payable onlyOwner {
        if (launched) revert LaunchStatus(launched);
        if (msg.value == 0) revert InsufficientBalanceError("ETH");
        

        uint256 ethAmount = msg.value;
        uint256 tokenAmount = _totalSupply;

        uint256 devTokenAmount = (tokenAmount * 25) / 1000; // 2.5%
        uint256 vaultTokenAmount = (tokenAmount * 725) / 1000; // 72.5%
        uint256 tokensToLP = tokenAmount - devTokenAmount - vaultTokenAmount; // 25%

        uint256 baseUnitValueDev = devTokenAmount * _baseUnitsPerPublicUnit;
        _baseUnitBalances[address(this)] -= baseUnitValueDev;
        _baseUnitBalances[owner()] += baseUnitValueDev;
        emit Transfer(address(this), owner(), devTokenAmount);

        if (balanceOf(address(this)) < tokensToLP) revert InsufficientBalanceError("Token balance for liquidity");

        _allowedPublicUnits[address(this)][address(uniswapV2Router)] = tokensToLP;

        uniswapV2Router.addLiquidityETH{value: ethAmount}(
            address(this),
            tokensToLP,
            0,
            0,
            owner(),
            block.timestamp
        );

        // Record initial price per initial supply
        initialPricePerInitialSupply = (ethAmount * 1e18) / tokensToLP;

        // Tranche Sizes (4 tranches of 5%, 4 tranches of 2.5%, and 4 tranches of 1.25%)
        uint256 trancheOne = (TOTAL_BASE_UNITS / 10000) * 500;
        uint256 trancheTwo = (TOTAL_BASE_UNITS / 10000) * 250;
        uint256 trancheThree = (TOTAL_BASE_UNITS / 10000) * 125;

        for (uint256 i = 0; i < priceMultiples.length; i++) {
            if (i < 4) {
                trancheSupplyBaseUnits[i] = trancheOne;
            } else if (i < 8) {
                trancheSupplyBaseUnits[i] = trancheTwo;
            } else {
                trancheSupplyBaseUnits[i] = trancheThree;
            }
        }

        launched = true;

        emit LiquidityDeployed(address(uniswapV2Pair), tokensToLP, ethAmount);
    }

    /**
     * @dev Allows users to purchase tokens from a specific tranche.
     * The tranche must be available for purchase (current price >= tranche price).
     * The transaction will be rejected if the current price differs from the tranche price by more than the specified percentage.
     * Excess ETH is refunded, and purchased tokens are added to Uniswap liquidity.
     * @param trancheIndex The index of the tranche to purchase from.
     * @param maxPriceDifferencePercent The maximum allowed percentage difference between current price and tranche price (must be greater than 1).
     */
    function buyTranche(uint256 trancheIndex, uint256 maxPriceDifferencePercent) external payable trancheReentrant {
        if (msg.value == 0) revert InsufficientBalanceError("ETH sent");
        if (!launched) revert LaunchStatus(launched);
        if (trancheIndex >= priceMultiples.length) revert InvalidInputError("Tranche index");
        if (maxPriceDifferencePercent < 100) revert InvalidInputError("Max price difference");

        uint256 currentPrice = getBoobsPriceInETH();
        uint256 tranchePricePerInitialSupply = initialPricePerInitialSupply * priceMultiples[trancheIndex];
        uint256 tranchePrice = (tranchePricePerInitialSupply * INITIAL_SUPPLY) / totalSupply();

        if (currentPrice < tranchePrice) revert TranchePriceExceedsCurrentPrice();
        if (currentPrice > (tranchePrice + (tranchePrice * maxPriceDifferencePercent / 10000))) revert PriceOutsideAcceptableRange();

        uint256 remainingBaseUnits = trancheSupplyBaseUnits[trancheIndex] - trancheSoldBaseUnits[trancheIndex];
        if (remainingBaseUnits == 0) revert TrancheSoldOut();

        uint256 tokensToBuy = (msg.value * 1e18) / currentPrice;

        uint256 baseUnitsToBuy = tokensToBuy * _baseUnitsPerPublicUnit;
        if (baseUnitsToBuy > remainingBaseUnits) {
            baseUnitsToBuy = remainingBaseUnits;
            tokensToBuy = baseUnitsToBuy / _baseUnitsPerPublicUnit;
        }

        uint256 ethToUse = (tokensToBuy * currentPrice) / 1e18;

        if (_baseUnitBalances[address(this)] < baseUnitsToBuy) revert InsufficientBalanceError("Token balance");

        _baseUnitBalances[address(this)] -= baseUnitsToBuy;
        _baseUnitBalances[msg.sender] += baseUnitsToBuy;

        trancheSoldBaseUnits[trancheIndex] += baseUnitsToBuy;

        if (ethToUse < msg.value) {
            payable(msg.sender).transfer(msg.value - ethToUse);
        }

        _allowedPublicUnits[address(this)][address(uniswapV2Router)] = tokensToBuy;

        uint256 minTokens = (tokensToBuy * 95) / 100; // 5% slippage tolerance
        uint256 minETH = (ethToUse * 95) / 100; // 5% slippage tolerance

        uniswapV2Router.addLiquidityETH{value: ethToUse}(
            address(this),
            tokensToBuy,
            minTokens,
            minETH,
            owner(),
            block.timestamp
        );

        emit Transfer(address(this), msg.sender, tokensToBuy);
    }
    
    /**
     * @dev Returns an array of boolean values indicating which tranches are currently available for purchase.
     * A tranche is available if its price is less than or equal to the current market price
     * and it still has tokens available for sale.
     * @return An array of boolean values where true indicates the tranche is available.
     */
    function getAvailableTranches() public view returns (bool[] memory) {
        bool[] memory availableTranches = new bool[](priceMultiples.length);
        uint256 currentPricePerInitialSupply = (getBoobsPriceInETH() * totalSupply()) / INITIAL_SUPPLY;
        
        for (uint256 i = 0; i < priceMultiples.length; i++) {
            uint256 tranchePricePerInitialSupply = initialPricePerInitialSupply * priceMultiples[i];
            if (currentPricePerInitialSupply >= tranchePricePerInitialSupply &&
                trancheSoldBaseUnits[i] < trancheSupplyBaseUnits[i]) {
                availableTranches[i] = true;
            } else {
                availableTranches[i] = false;
            }
        }
        
        return availableTranches;
    }

    /**
     * @dev Calculates the percentage difference between the current price and the tranche price.
     * @param trancheIndex The index of the tranche to compare against.
     * @return The percentage difference, expressed as a number where 10000 represents 100%.
     * Returns 0 if the current price is lower than or equal to the tranche price.
     */
    function getCurrentPriceDifferencePercent(uint256 trancheIndex) public view returns (uint256) {
        if (trancheIndex >= priceMultiples.length) revert InvalidInputError("Tranche index");
        
        uint256 currentPrice = getBoobsPriceInETH();
        uint256 tranchePricePerInitialSupply = initialPricePerInitialSupply * priceMultiples[trancheIndex];
        uint256 tranchePrice = (tranchePricePerInitialSupply * INITIAL_SUPPLY) / totalSupply();
        
        if (currentPrice <= tranchePrice) {
            return 0;
        }
        
        uint256 priceDifference = currentPrice - tranchePrice;
        uint256 percentageDifference = (priceDifference * 10000) / tranchePrice;
        
        return percentageDifference;
    }

    /**
     * @dev Returns the total supply of PublicUnits in the system.
     * This value is dynamically calculated based on the number of BaseUnits in existence
     * and the conversion rate from BaseUnits to PublicUnits.
     *
     * @return The total number of PublicUnits.
     */
    function totalSupply() public view override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev Returns the number of PublicUnits held by a specific address.
     * This function performs an internal conversion from BaseUnits to PublicUnits.
     * 
     * @param who The address whose PublicUnit balance is being queried.
     * @return The number of PublicUnits held by the specified address.
     */
    function balanceOf(address who) public view override returns (uint256) {
        return _baseUnitBalances[who] / _baseUnitsPerPublicUnit;
    }

    /**
     * @dev Returns the raw balance in BaseUnits for a specific address.
     * This function does not convert BaseUnits to PublicUnits and is used for internal calculations.
     * 
     * @param who The address whose BaseUnit balance is being queried.
     * @return The raw balance of BaseUnits held by the specified address.
     */
    function scaledBalanceOf(address who) public view returns (uint256) {
        return _baseUnitBalances[who];
    }

    /**
     * @dev Returns the total number of BaseUnits in the system.
     * This function is used for internal accounting and is not directly tied to the PublicUnit supply.
     * 
     * @return The total number of BaseUnits.
     */
    function scaledTotalSupply() public pure returns (uint256) {
        return TOTAL_BASE_UNITS;
    }

    /**
     * @dev Transfers PublicUnits from the caller's account to the recipient's account.
     * The actual transfer is done by adjusting the BaseUnit balances internally. The public-facing
     * balance in PublicUnits is updated accordingly by the conversion between BaseUnits and PublicUnits.
     *
     * @param to The address to transfer PublicUnits to.
     * @param value The amount of PublicUnits to be transferred.
     * @return True if the transfer was successful.
     */
    function transfer(address to, uint256 value) public override validRecipient(to) nonReentrant returns (bool) {
        uint256 baseUnitValue = value * _baseUnitsPerPublicUnit;
        
        if (sniperProtection && !_isExcludedFromSniperProtection[to]){
            if (baseUnitValue > maxTransaction) revert MaxTransactionExceeded();
            if ((tx.gasprice - block.basefee) > maxPriorityFee) revert MaxPriorityFeeExceeded();
        }

        _baseUnitBalances[msg.sender] -= baseUnitValue;
        _baseUnitBalances[to] += baseUnitValue;

        emit Transfer(msg.sender, to, value);
        return true;
    }

    /**
     * @dev Transfers the entire PublicUnit balance from the caller's account to the specified recipient.
     * The PublicUnits are converted from BaseUnits before performing the transfer.
     * 
     * The caller's BaseUnit balance is set to zero after the transfer.
     *
     * Emits a `Transfer` event.
     * 
     * @param to The recipient of the entire PublicUnit balance.
     * @return True if the transfer is successful.
     */
    function transferAll(address to) public validRecipient(to) nonReentrant returns (bool) {
        uint256 baseUnitValue = _baseUnitBalances[msg.sender];
        uint256 value = baseUnitValue / _baseUnitsPerPublicUnit;

        if (sniperProtection && !_isExcludedFromSniperProtection[to]){
            if (baseUnitValue > maxTransaction) revert MaxTransactionExceeded();
            if ((tx.gasprice - block.basefee) > maxPriorityFee) revert MaxPriorityFeeExceeded();
        }

        delete _baseUnitBalances[msg.sender];
        _baseUnitBalances[to] += baseUnitValue;

        emit Transfer(msg.sender, to, value);
        return true;
    }

    /**
     * @dev Checks the amount of PublicUnits that an owner has allowed a spender to use on their behalf.
     * This function is part of the ERC20 standard and is used to manage allowances for token transfers.
     *
     * The allowance is denominated in PublicUnits, not BaseUnits, and will always return the remaining
     * amount of PublicUnits the spender can transfer.
     *
     * @param owner_ The address that owns the tokens.
     * @param spender The address that is allowed to spend the tokens.
     * @return The remaining number of PublicUnits that the spender can transfer.
     */
    function allowance(address owner_, address spender) public view override returns (uint256) {
        return _allowedPublicUnits[owner_][spender];
    }

    /**
     * @dev Transfers PublicUnits from one account (`from`) to another (`to`) on behalf of the
     * account holder, provided that the caller has been approved to spend at least `value` PublicUnits.
     * This function adjusts BaseUnit balances internally.
     *
     * @param from The address from which to transfer PublicUnits.
     * @param to The address to which PublicUnits will be transferred.
     * @param value The amount of PublicUnits to be transferred.
     * @return True if the transfer is successful.
     */
    function transferFrom(address from, address to, uint256 value) public override nonReentrant returns (bool) {
        if (_allowedPublicUnits[from][msg.sender] < value) revert InsufficientBalanceError("Allowance");
        _allowedPublicUnits[from][msg.sender] -= value;

        uint256 baseUnitValue = value * _baseUnitsPerPublicUnit;

        if (sniperProtection && !_isExcludedFromSniperProtection[to]){
            if (baseUnitValue > maxTransaction) revert MaxTransactionExceeded();
            if ((tx.gasprice - block.basefee) > maxPriorityFee) revert MaxPriorityFeeExceeded();
        }

        if (_baseUnitBalances[from] < baseUnitValue) revert InsufficientBalanceError("Sender");

        _baseUnitBalances[from] -= baseUnitValue;
        _baseUnitBalances[to] += baseUnitValue;

        emit Transfer(from, to, value);
        return true;
    }

    /**
     * @dev Transfers the entire PublicUnit balance from one account to another, using the allowance mechanism.
     * This function transfers all the BaseUnits held by the sender to the recipient and adjusts the 
     * allowance accordingly.
     *
     * Emits a `Transfer` event.
     * 
     * @param from The account from which PublicUnits are being transferred.
     * @param to The recipient of the PublicUnits.
     * @return True if the transfer is successful.
     */
    function transferAllFrom(address from, address to) public validRecipient(to) nonReentrant returns (bool) {
        uint256 senderBalance = balanceOf(from);
        if (_allowedPublicUnits[from][msg.sender] < senderBalance) revert InsufficientBalanceError("Allowance");

        uint256 baseUnitValue = _baseUnitBalances[from];
        uint256 value = baseUnitValue / _baseUnitsPerPublicUnit;

        if (sniperProtection && !_isExcludedFromSniperProtection[to]){
            if (baseUnitValue > maxTransaction) revert MaxTransactionExceeded();
            if ((tx.gasprice - block.basefee) > maxPriorityFee) revert MaxPriorityFeeExceeded();
        }

        _allowedPublicUnits[from][msg.sender] -= value;

        delete _baseUnitBalances[from];
        _baseUnitBalances[to] += baseUnitValue;

        emit Transfer(from, to, value);
        return true;
    }

    /**
     * @dev Approve the passed address to spend the specified amount of PublicUnits on behalf of
     * msg.sender. This method is included for ERC20 compatibility.
     * increaseAllowance and decreaseAllowance should be used instead.
     * Changing an allowance with this method brings the risk that someone may transfer both
     * the old and the new allowance—if they are both greater than zero—if a transfer
     * transaction is mined before the later approve() call is mined.
     *
     * @param spender The address which will spend the PublicUnits.
     * @param value The amount of PublicUnits to be spent.
     * @return True if the approval was successful.
     */
    function approve(address spender, uint256 value) public override returns (bool) {
        _allowedPublicUnits[msg.sender][spender] = value;

        emit Approval(msg.sender, spender, value);
        return true;
    }

    /**
     * @dev Increases the allowance that `spender` has over the caller's PublicUnits.
     * This is safer than using `approve()` because it prevents the double-spend vulnerability.
     *
     * @param spender The address to which the allowance will be granted.
     * @param addedValue The amount of PublicUnits to add to the allowance.
     * @return True if the operation was successful.
     */
    function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
        _allowedPublicUnits[msg.sender][spender] += addedValue;

        emit Approval(msg.sender, spender, _allowedPublicUnits[msg.sender][spender]);
        return true;
    }

    /**
     * @dev Decreases the allowance that `spender` has over the caller's PublicUnits.
     * This is safer than using `approve()` because it prevents the double-spend vulnerability.
     *
     * @param spender The address for which to decrease the allowance.
     * @param subtractedValue The amount of PublicUnits to subtract from the allowance.
     * @return True if the operation was successful.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
        uint256 oldValue = _allowedPublicUnits[msg.sender][spender];
        _allowedPublicUnits[msg.sender][spender] = (subtractedValue >= oldValue)
            ? 0
            : oldValue - subtractedValue;

        emit Approval(msg.sender, spender, _allowedPublicUnits[msg.sender][spender]);
        return true;
    }
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):