// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; /// @title DegenZFunTaxToken — buy/sell tax (max 10%) active from launch; creator + system addresses exempt. /// @notice PinkSale-style: tax when transferring to/from an AMM pair (or bonding curve if flagged). /// Curve, factory, routers, distributor, and creator are excluded so bonding math stays exact. contract DegenZFunTaxToken is ERC20, Ownable { uint256 public constant MAX_TAX_BPS = 1000; // 10% uint256 public buyTaxBps; uint256 public sellTaxBps; address public marketingWallet; address public immutable curve; bool public taxesLocked; mapping(address => bool) public isExcludedFromTax; mapping(address => bool) public automatedMarketMakerPairs; event TaxUpdated(uint256 buyTaxBps, uint256 sellTaxBps); event MarketingWalletUpdated(address indexed wallet); event PairUpdated(address indexed pair, bool value); event ExclusionUpdated(address indexed account, bool excluded); event TaxesLocked(); constructor( string memory name_, string memory symbol_, address curve_, address owner_, address marketingWallet_, uint256 buyTaxBps_, uint256 sellTaxBps_, uint256 totalSupply_ ) ERC20(name_, symbol_) Ownable(owner_) { require(curve_ != address(0) && owner_ != address(0) && marketingWallet_ != address(0), "zero addr"); require(buyTaxBps_ <= MAX_TAX_BPS && sellTaxBps_ <= MAX_TAX_BPS, "tax>10%"); curve = curve_; buyTaxBps = buyTaxBps_; sellTaxBps = sellTaxBps_; marketingWallet = marketingWallet_; isExcludedFromTax[owner_] = true; isExcludedFromTax[curve_] = true; isExcludedFromTax[address(this)] = true; isExcludedFromTax[marketingWallet_] = true; isExcludedFromTax[address(0)] = true; isExcludedFromTax[address(0xdead)] = true; // Flag curve as market so tax *would* apply if it were not excluded (documentation / tooling). automatedMarketMakerPairs[curve_] = true; _mint(curve_, totalSupply_); } function setAutomatedMarketMakerPair(address pair, bool value) external onlyOwner { require(pair != address(0), "pair=0"); automatedMarketMakerPairs[pair] = value; emit PairUpdated(pair, value); } function setMarketingWallet(address wallet) external onlyOwner { require(!taxesLocked, "locked"); require(wallet != address(0), "wallet=0"); isExcludedFromTax[marketingWallet] = false; marketingWallet = wallet; isExcludedFromTax[wallet] = true; emit MarketingWalletUpdated(wallet); } function setTax(uint256 buyBps, uint256 sellBps) external onlyOwner { require(!taxesLocked, "locked"); require(buyBps <= MAX_TAX_BPS && sellBps <= MAX_TAX_BPS, "tax>10%"); buyTaxBps = buyBps; sellTaxBps = sellBps; emit TaxUpdated(buyBps, sellBps); } function setExcludedFromTax(address account, bool excluded) external onlyOwner { require(!taxesLocked || excluded, "locked"); isExcludedFromTax[account] = excluded; emit ExclusionUpdated(account, excluded); } /// @notice Called at graduation to freeze tax params / ownership handoff. function lockTaxes() external onlyOwner { taxesLocked = true; emit TaxesLocked(); } function _update(address from, address to, uint256 amount) internal override { if (amount == 0 || from == address(0) || to == address(0) || isExcludedFromTax[from] || isExcludedFromTax[to]) { super._update(from, to, amount); return; } uint256 taxBps; if (automatedMarketMakerPairs[from]) { taxBps = buyTaxBps; } else if (automatedMarketMakerPairs[to]) { taxBps = sellTaxBps; } if (taxBps == 0) { super._update(from, to, amount); return; } uint256 tax = (amount * taxBps) / 10_000; uint256 send = amount - tax; if (tax > 0) { super._update(from, marketingWallet, tax); } super._update(from, to, send); } }