{
  "sources": {
    "Squatchi.sol": {
      "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\n/**\n * @title Squatchi ($SQUATCHI) \u2014 Base ERC-20i Dynamic Inscription Token & Pacific Northwest Arcade Asset\n * @notice Fixed 15,000,000 $SQUATCHI supply, 0% buy/sell/transfer tax, no minting, no blacklist,\n *         no direct-sale arbitrage functions, and standard OpenZeppelin Ownable renouncement\n *         (`owner() == address(0)`).\n *         - 86.67% (13,000,000 $SQUATCHI) allocated to Base Uniswap V2 Liquidity Pool (100% LP burned\n *           to 0x000000000000000000000000000000000000dEaD) & Play-to-Earn Victory Pool.\n *         - 10.00% (1,500,000 $SQUATCHI) Genesis Ecosystem & Gameplay Allocation.\n *         - 3.33%  (500,000 $SQUATCHI) Genesis Partner Allocation.\n */\n\nabstract contract Ownable {\n    address private _owner;\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n    event RenounceOwnership();\n\n    constructor(address initialOwner) {\n        _owner = initialOwner;\n        emit OwnershipTransferred(address(0), initialOwner);\n    }\n\n    modifier onlyOwner() {\n        require(_owner == msg.sender, \"Ownable: caller is not the owner\");\n        _;\n    }\n\n    function owner() public view virtual returns (address) {\n        return _owner;\n    }\n\n    function ownershipRenounced() public view returns (bool) {\n        return _owner == address(0);\n    }\n\n    function renounceOwnership() public virtual onlyOwner {\n        address oldOwner = _owner;\n        _owner = address(0);\n        emit OwnershipTransferred(oldOwner, address(0));\n        emit RenounceOwnership();\n    }\n}\n\ncontract Squatchi is Ownable {\n    // \u2500\u2500\u2500 ERC-20i Seed Structure (compatible with Base ERC-20i indexers & Squatchi Arcade) \u2500\u2500\u2500\n    struct SeedData {\n        uint256 seed;\n        uint256 extra;\n    }\n\n    struct SquatchStats {\n        uint8 tier;\n        string className;\n        uint256 sizeBps;      // 100 = 1.00x, 350 = 3.50x\n        uint256 speed;        // Game movement speed rating\n        uint256 magnetRadius; // Item pickup radius in game units\n        uint256 multiplier;   // Score & foraging yield multiplier\n        string furColor;\n        string gearItem;\n        string auraType;\n        bool isStable;\n    }\n\n    // \u2500\u2500\u2500 Token Constants \u2500\u2500\u2500\n    string public constant name = \"Squatchi\";\n    string public constant symbol = \"SQUATCHI\";\n    uint8 public constant decimals = 18;\n\n    uint256 public constant MAX_SUPPLY = 15_000_000 * 1e18; // 15,000,000 SQUATCHI total (fixed, immutable)\n    address public constant DEAD_ADDRESS = 0x000000000000000000000000000000000000dEaD;\n\n    // \u2500\u2500\u2500 Standard ERC-20 State (0% Buy/Sell/Transfer Tax) \u2500\u2500\u2500\n    uint256 public totalSupply;\n    mapping(address => uint256) private _balances;\n    mapping(address => mapping(address => uint256)) private _allowances;\n\n    // \u2500\u2500\u2500 ERC-20i Inscription State \u2500\u2500\u2500\n    mapping(address => SeedData) private _seeds;\n    mapping(address => uint256) public holderNonce;\n    mapping(address => bool) public stableInscription;\n    mapping(address => bool) public isPoolOrExempt;\n\n    address[] private _holders;\n    mapping(address => uint256) private _holderIndex; // 1-based index\n\n    // \u2500\u2500\u2500 Standard ERC-20 Events \u2500\u2500\u2500\n    event Transfer(address indexed from, address indexed to, uint256 value);\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n    event InscriptionStabilized(address indexed holder, bool stable, uint256 lockedSeed);\n\n    constructor(address genesisPartner) Ownable(msg.sender) {\n        require(genesisPartner != address(0), \"Squatchi: invalid partner\");\n\n        isPoolOrExempt[address(this)] = true;\n        isPoolOrExempt[DEAD_ADDRESS] = true;\n\n        // 1. Mint 500,000 SQUATCHI (3.33%) to Genesis Partner\n        _mint(genesisPartner, 500_000 * 1e18);\n\n        // 2. Mint 14,500,000 SQUATCHI to deployer (1,500,000 [10%] Genesis Ecosystem + 13,000,000 [86.67%] DEX LP & P2E Pool)\n        _mint(msg.sender, 14_500_000 * 1e18);\n    }\n\n    // \u2500\u2500\u2500 Standard ERC-20 Functions (0% Tax, No Restrictions) \u2500\u2500\u2500\n\n    function balanceOf(address account) public view returns (uint256) {\n        return _balances[account];\n    }\n\n    function effectiveBalanceOf(address holder) public view returns (uint256) {\n        return _balances[holder];\n    }\n\n    function lockedFounderBalance() public pure returns (uint256) {\n        return 0;\n    }\n\n    function allowance(address holder, address spender) public view returns (uint256) {\n        return _allowances[holder][spender];\n    }\n\n    function approve(address spender, uint256 amount) public returns (bool) {\n        _approve(msg.sender, spender, amount);\n        return true;\n    }\n\n    function transfer(address to, uint256 amount) public returns (bool) {\n        _transfer(msg.sender, to, amount);\n        return true;\n    }\n\n    function transferFrom(address from, address to, uint256 amount) public returns (bool) {\n        uint256 currentAllowance = _allowances[from][msg.sender];\n        if (currentAllowance != type(uint256).max) {\n            require(currentAllowance >= amount, \"Squatchi: insufficient allowance\");\n            unchecked {\n                _approve(from, msg.sender, currentAllowance - amount);\n            }\n        }\n        _transfer(from, to, amount);\n        return true;\n    }\n\n    // \u2500\u2500\u2500 ERC-20i Dynamic Seed & Inscription Mechanics \u2500\u2500\u2500\n\n    function stabilizeInscription(bool stable) external {\n        require(_balances[msg.sender] > 0, \"Squatchi: must hold tokens to stabilize\");\n        stableInscription[msg.sender] = stable;\n        emit InscriptionStabilized(msg.sender, stable, _seeds[msg.sender].seed);\n    }\n\n    function getTier(address holder) public view returns (uint8) {\n        return _tierForBalance(_balances[holder]);\n    }\n\n    function _tierForBalance(uint256 bal) internal pure returns (uint8) {\n        uint256 whole = bal / 1e18;\n        if (whole >= 500_000) return 6; // Mythic Sasquatch King (500k+ SQUATCHI)\n        if (whole >= 250_000) return 5; // Cascadian Elder\n        if (whole >= 50_000)  return 4; // Mountain Alpha\n        if (whole >= 10_000)  return 3; // Timber Strider\n        if (whole >= 1_000)   return 2; // Moss Forager\n        if (bal > 0)          return 1; // Baby Wood Ape\n        return 0;\n    }\n\n    function getSeed(address holder) public view returns (SeedData memory) {\n        return _seeds[holder];\n    }\n\n    function getHolderCount() external view returns (uint256) {\n        return _holders.length;\n    }\n\n    function getHolderByIndex(uint256 index) external view returns (address) {\n        require(index < _holders.length, \"Squatchi: index out of bounds\");\n        return _holders[index];\n    }\n\n    function getHoldersList(uint256 startIndex, uint256 count) external view returns (address[] memory list) {\n        uint256 len = _holders.length;\n        if (startIndex >= len) return new address[](0);\n        uint256 end = startIndex + count;\n        if (end > len) end = len;\n        uint256 outLen = end - startIndex;\n        list = new address[](outLen);\n        for (uint256 i = 0; i < outLen; i++) {\n            list[i] = _holders[startIndex + i];\n        }\n    }\n\n    function getSquatchStats(address holder) public view returns (SquatchStats memory stats) {\n        SeedData memory sd = _seeds[holder];\n        uint8 tier = _tierForBalance(_balances[holder]);\n        return _computeStats(sd.seed, tier, stableInscription[holder]);\n    }\n\n    function _computeStats(uint256 seed, uint8 tier, bool isStable) internal pure returns (SquatchStats memory s) {\n        s.tier = tier;\n        s.isStable = isStable;\n\n        if (tier == 0) {\n            s.className = \"Uninscribed\";\n            s.furColor = \"None\";\n            s.gearItem = \"None\";\n            s.auraType = \"None\";\n            return s;\n        }\n\n        if (tier == 1) {\n            s.className = \"Baby Wood Ape\";\n            s.sizeBps = 100;\n            s.speed = 100 + (seed % 15);\n            s.magnetRadius = 60 + (seed % 12);\n            s.multiplier = 1;\n        } else if (tier == 2) {\n            s.className = \"Moss Forager\";\n            s.sizeBps = 135;\n            s.speed = 125 + (seed % 20);\n            s.magnetRadius = 95 + (seed % 18);\n            s.multiplier = 2;\n        } else if (tier == 3) {\n            s.className = \"Timber Strider\";\n            s.sizeBps = 175;\n            s.speed = 150 + (seed % 25);\n            s.magnetRadius = 135 + (seed % 22);\n            s.multiplier = 3;\n        } else if (tier == 4) {\n            s.className = \"Mountain Alpha\";\n            s.sizeBps = 225;\n            s.speed = 180 + (seed % 30);\n            s.magnetRadius = 180 + (seed % 28);\n            s.multiplier = 4;\n        } else if (tier == 5) {\n            s.className = \"Cascadian Elder\";\n            s.sizeBps = 280;\n            s.speed = 215 + (seed % 35);\n            s.magnetRadius = 235 + (seed % 35);\n            s.multiplier = 4;\n        } else {\n            s.className = \"Mythic Sasquatch King\";\n            s.sizeBps = 350;\n            s.speed = 260 + (seed % 40);\n            s.magnetRadius = 310 + (seed % 50);\n            s.multiplier = 5;\n        }\n\n        string[8] memory furs = [\n            \"Pine Bark Brown\",\n            \"Cascadian Moss\",\n            \"Redwood Umber\",\n            \"Glacier Frost\",\n            \"Obsidian Silverback\",\n            \"Bioluminescent Mycelium\",\n            \"Ancient Amber\",\n            \"Cosmic Auroral Gold\"\n        ];\n        string[8] memory gears = [\n            \"Woven Twig Pouch\",\n            \"Forager Berry Basket\",\n            \"Glowing Spore Lantern\",\n            \"Carved Cedar Totem\",\n            \"Golden Pinecone Satchel\",\n            \"Elder Mycelium Staff\",\n            \"Emerald Relic Horn\",\n            \"Crown of the Old Growth\"\n        ];\n        string[6] memory auras = [\n            \"Pine Mist\",\n            \"Emerald Spore Ring\",\n            \"Frost Wind Swirl\",\n            \"Thunder Stomp Shockwave\",\n            \"Bioluminescent Halo\",\n            \"Sovereign Auroral Crown\"\n        ];\n\n        if (tier == 6) {\n            s.furColor = furs[7];\n            s.gearItem = gears[7];\n            s.auraType = auras[5];\n        } else {\n            s.furColor = furs[(seed >> 8) % (tier + 2)];\n            s.gearItem = gears[(seed >> 16) % (tier + 2)];\n            s.auraType = auras[tier - 1];\n        }\n    }\n\n    function getMeta(SeedData memory seed_data) public pure returns (string memory) {\n        uint8 tier = uint8(seed_data.extra & 0xFF);\n        if (tier == 0 && seed_data.seed > 0) {\n            tier = 1;\n        }\n        bool isStable = ((seed_data.extra >> 8) & 1) == 1;\n        uint256 wholeTokens = seed_data.extra >> 16;\n        return _buildMetadataJSON(seed_data.seed, tier, isStable, wholeTokens);\n    }\n\n    function getInscription(address holder) external view returns (string memory) {\n        SeedData memory sd = _seeds[holder];\n        uint8 tier = _tierForBalance(_balances[holder]);\n        return _buildMetadataJSON(sd.seed, tier, stableInscription[holder], _balances[holder] / 1e18);\n    }\n\n    function _buildMetadataJSON(\n        uint256 seed,\n        uint8 tier,\n        bool isStable,\n        uint256 wholeTokens\n    ) internal pure returns (string memory) {\n        SquatchStats memory st = _computeStats(seed, tier, isStable);\n        string memory svg = _buildSVG(seed, tier, st);\n\n        string memory header = string(\n            abi.encodePacked(\n                '{\"name\":\"Squatchi #',\n                _toString(seed % 100000),\n                \" - \",\n                st.className,\n                '\",\"description\":\"Squatchi ($SQUATCHI) dynamic ERC-20i on-chain Sasquatch inscription on Base.\",\"balance_tokens\":',\n                _toString(wholeTokens),\n                ',\"tier\":',\n                _toString(tier),\n                ',\"image\":\"data:image/svg+xml;utf8,',\n                svg\n            )\n        );\n\n        string memory attrs = string(\n            abi.encodePacked(\n                '\",\"attributes\":[{\"trait_type\":\"Tier\",\"value\":\"Tier ',\n                _toString(tier),\n                '\"},{\"trait_type\":\"Class\",\"value\":\"',\n                st.className,\n                '\"},{\"trait_type\":\"Size Multiplier\",\"value\":\"',\n                _toString(st.sizeBps / 100),\n                \".\",\n                _toString((st.sizeBps % 100) / 10),\n                'x\"},{\"trait_type\":\"Speed\",\"value\":',\n                _toString(st.speed),\n                '},{\"trait_type\":\"Magnet Radius\",\"value\":',\n                _toString(st.magnetRadius),\n                '},{\"trait_type\":\"Yield Multiplier\",\"value\":\"',\n                _toString(st.multiplier),\n                'x\"},{\"trait_type\":\"Fur Coat\",\"value\":\"',\n                st.furColor,\n                '\"},{\"trait_type\":\"Woods Gear\",\"value\":\"',\n                st.gearItem,\n                '\"},{\"trait_type\":\"Forest Aura\",\"value\":\"',\n                st.auraType,\n                '\"}]}'\n            )\n        );\n\n        return string(abi.encodePacked(header, attrs));\n    }\n\n    function _buildSVG(\n        uint256 seed,\n        uint8 tier,\n        SquatchStats memory st\n    ) internal pure returns (string memory) {\n        string[7] memory bodyColors = [\"#334155\",\"#784c2a\",\"#4a6b3b\",\"#8c3f2b\",\"#64748b\",\"#14b8a6\",\"#eab308\"];\n        string[7] memory auraColors = [\"#1e293b\",\"#38bdf8\",\"#4ade80\",\"#60a5fa\",\"#f59e0b\",\"#2dd4bf\",\"#facc15\"];\n        string memory furHex = bodyColors[tier > 6 ? 6 : tier];\n        string memory auraHex = auraColors[tier > 6 ? 6 : tier];\n        uint256 scalePct = 60 + (uint256(tier) * 10);\n\n        string memory part1 = string(\n            abi.encodePacked(\n                \"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='480' height='480' shape-rendering='crispEdges'>\",\n                \"<rect width='24' height='24' fill='#0b1d13'/>\",\n                \"<circle cx='12' cy='12' r='\", _toString(4 + tier), \"' fill='none' stroke='\", auraHex, \"' stroke-width='1' opacity='0.55'/>\",\n                \"<g transform='translate(12,13) scale(\", _toString(scalePct / 100), \".\", _toString((scalePct % 100) / 10), \") translate(-12,-13)'>\",\n                \"<rect x='8' y='4' width='8' height='6' fill='\", furHex, \"'/>\",\n                \"<rect x='6' y='9' width='12' height='9' fill='\", furHex, \"'/>\",\n                \"<rect x='5' y='10' width='2' height='7' fill='\", furHex, \"'/>\",\n                \"<rect x='17' y='10' width='2' height='7' fill='\", furHex, \"'/>\"\n            )\n        );\n        string memory crown = tier >= 6\n            ? \"<polygon points='8,4 10,1 12,3 14,1 16,4' fill='#facc15'/>\"\n            : (tier >= 3 ? \"<rect x='9' y='3' width='6' height='1' fill='#4ade80'/>\" : \"\");\n        string memory part2 = string(\n            abi.encodePacked(\n                \"<rect x='8' y='18' width='3' height='4' fill='\", furHex, \"'/>\",\n                \"<rect x='13' y='18' width='3' height='4' fill='\", furHex, \"'/>\",\n                \"<rect x='9' y='6' width='6' height='2' fill='#1e293b'/>\",\n                \"<rect x='10' y='6' width='1' height='1' fill='\", auraHex, \"'/>\",\n                \"<rect x='13' y='6' width='1' height='1' fill='\", auraHex, \"'/>\",\n                crown,\n                \"</g><text x='1' y='23' font-size='2' fill='#a7f3d0' font-family='monospace'>T\",\n                _toString(tier), \" \", st.className, \" #\", _toString(seed % 1000), \"</text></svg>\"\n            )\n        );\n        return string(abi.encodePacked(part1, part2));\n    }\n\n    // \u2500\u2500\u2500 Internal Transfer & Seed Evolution Logic (Standard Transfer Event Only) \u2500\u2500\u2500\n\n    function _mint(address to, uint256 amount) internal {\n        require(to != address(0), \"Squatchi: mint to zero\");\n        totalSupply += amount;\n        _balances[to] += amount;\n\n        _updateHolderSet(to);\n        if (!isPoolOrExempt[to]) {\n            _evolveSeed(address(0), to, amount);\n        }\n\n        emit Transfer(address(0), to, amount);\n    }\n\n    function _transfer(address from, address to, uint256 amount) internal {\n        require(from != address(0) && to != address(0), \"Squatchi: transfer from/to zero\");\n        uint256 fromBal = _balances[from];\n        require(fromBal >= amount, \"Squatchi: transfer exceeds balance\");\n\n        unchecked {\n            _balances[from] = fromBal - amount;\n        }\n        _balances[to] += amount;\n\n        _updateHolderSet(from);\n        _updateHolderSet(to);\n\n        if (!isPoolOrExempt[from]) {\n            _evolveSeed(to, from, amount);\n        }\n        if (!isPoolOrExempt[to]) {\n            _evolveSeed(from, to, amount);\n        }\n\n        emit Transfer(from, to, amount);\n    }\n\n    function _evolveSeed(address counterparty, address holder, uint256 amount) internal {\n        uint256 bal = _balances[holder];\n        if (bal == 0) {\n            _seeds[holder] = SeedData(0, 0);\n            stableInscription[holder] = false;\n            return;\n        }\n\n        uint8 tier = _tierForBalance(bal);\n        uint256 nonce = ++holderNonce[holder];\n\n        uint256 newSeedVal = _seeds[holder].seed;\n        if (!stableInscription[holder] || newSeedVal == 0) {\n            newSeedVal = uint256(\n                keccak256(\n                    abi.encodePacked(\n                        block.timestamp,\n                        block.prevrandao,\n                        holder,\n                        counterparty,\n                        amount,\n                        bal,\n                        nonce\n                    )\n                )\n            );\n            if (newSeedVal == 0) newSeedVal = 1;\n        }\n\n        uint256 extraVal = uint256(tier)\n            | (stableInscription[holder] ? (uint256(1) << 8) : 0)\n            | ((bal / 1e18) << 16);\n\n        _seeds[holder] = SeedData({ seed: newSeedVal, extra: extraVal });\n    }\n\n    function _updateHolderSet(address account) internal {\n        if (isPoolOrExempt[account]) return;\n        uint256 idx = _holderIndex[account];\n        uint256 bal = _balances[account];\n\n        if (bal > 0 && idx == 0) {\n            _holders.push(account);\n            _holderIndex[account] = _holders.length;\n        } else if (bal == 0 && idx > 0) {\n            uint256 lastIdx = _holders.length;\n            if (idx != lastIdx) {\n                address lastHolder = _holders[lastIdx - 1];\n                _holders[idx - 1] = lastHolder;\n                _holderIndex[lastHolder] = idx;\n            }\n            _holders.pop();\n            delete _holderIndex[account];\n        }\n    }\n\n    function _approve(address holder, address spender, uint256 amount) internal {\n        require(holder != address(0) && spender != address(0), \"Squatchi: approve zero\");\n        _allowances[holder][spender] = amount;\n        emit Approval(holder, spender, amount);\n    }\n\n    function _toString(uint256 value) internal pure returns (string memory) {\n        if (value == 0) return \"0\";\n        uint256 temp = value;\n        uint256 digits;\n        while (temp != 0) {\n            digits++;\n            temp /= 10;\n        }\n        bytes memory buffer = new bytes(digits);\n        while (value != 0) {\n            digits -= 1;\n            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\n            value /= 10;\n        }\n        return string(buffer);\n    }\n}\n"
    }
  },
  "language": "Solidity",
  "settings": {
    "viaIR": true,
    "optimizer": {
      "runs": 200,
      "enabled": true
    },
    "evmVersion": "paris"
  }
}