{"sources":{"contracts/LoomSale.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.26;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/ReentrancyGuard.sol\";\n\n/// @notice Minimal view of the SPACE ID Agent ID registry (ERC-721).\ninterface IAgentIdRegistry {\n    function ownerOf(uint256 tokenId) external view returns (address);\n    function balanceOf(address owner) external view returns (uint256);\n}\n\n/// @notice Chainlink AggregatorV3 (BNB/USD feed on BSC).\ninterface IAggregatorV3 {\n    function latestRoundData()\n        external\n        view\n        returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);\n    function decimals() external view returns (uint8);\n}\n\n/// @notice Minimal PancakeSwap V2 router surface used by seedLiquidity().\ninterface IPancakeRouter {\n    function factory() external view returns (address);\n    function WETH() external view returns (address);\n    function getAmountsOut(uint256 amountIn, address[] calldata path)\n        external\n        view\n        returns (uint256[] memory amounts);\n    function swapExactETHForTokens(uint256 amountOutMin, address[] calldata path, address to, uint256 deadline)\n        external\n        payable\n        returns (uint256[] memory amounts);\n    function addLiquidity(\n        address tokenA,\n        address tokenB,\n        uint256 amountADesired,\n        uint256 amountBDesired,\n        uint256 amountAMin,\n        uint256 amountBMin,\n        address to,\n        uint256 deadline\n    ) external returns (uint256 amountA, uint256 amountB, uint256 liquidity);\n}\n\ninterface IPancakeFactory {\n    function getPair(address tokenA, address tokenB) external view returns (address pair);\n}\n\ninterface IPancakePair {\n    function token0() external view returns (address);\n    function token1() external view returns (address);\n    function getReserves()\n        external\n        view\n        returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);\n}\n\n/// @title LoomSale — sale, referral, liquidity seeding and reserve burn for LOOM\n/// @notice Holds the entire 1B LOOM supply and distributes it by the locked rules:\n///         - 70% sale (700M LOOM = 70,000 shares): first 1,000 shares free\n///           (1 per Agent ID, gas only), the rest paid in USDT or BNB.\n///         - 5% referral pool (50M LOOM): inviter earns 500 LOOM per referred\n///           share, paid in the same transaction as the claim/purchase.\n///         - 25% LP reserve (250M LOOM): paired with the 80% USDT/BNB revenue\n///           share via seedLiquidity() once 3,000 total shares are sold.\n///         - Treasury receives 20% of paid sales revenue (USDT/BNB) at purchase\n///           time. It receives NO LOOM allocation.\n///         There is NO owner/admin: no function can move user funds, change\n///         prices/tiers, or mint beyond the 1B. The treasury address is immutable.\n///         Leftover LP reserve is burned AUTOMATICALLY by the contract itself\n///         (no callable burn function exists) when the sale sells out (70,000\n///         shares) — the only burn trigger. The burn removes ONLY the LOOM in\n///         excess of what is still needed to pair the contract's remaining\n///         USDT reserve (plus the USDT value of its reserve BNB) at the\n///         current pool price — so seeding can always finish and no USDT is\n///         stranded by the burn. The burn executes inside the next\n///         state-changing call after sellout; with zero activity after\n///         sellout it waits for the next interaction. It re-checks on every\n///         later call, so excess that appears later (e.g. the pool price\n///         rises) burns then. If the sale never sells out, the leftover\n///         reserve simply stays in the contract forever.\ncontract LoomSale is ReentrancyGuard {\n    using SafeERC20 for IERC20;\n\n    // ---------------- Constants ----------------\n    /// @notice LOOM per share.\n    uint256 public constant SHARE_LOOM = 10_000 ether;\n    /// @notice Total shares for sale (70,000 = 700M LOOM).\n    uint256 public constant TOTAL_SHARES = 70_000;\n    /// @notice Free shares available in total (first 1,000).\n    uint256 public constant FREE_QUOTA = 1_000;\n    /// @notice Total shares sold at which liquidity seeding unlocks.\n    uint256 public constant LP_TRIGGER = 3_000;\n    /// @notice Referral reward per referred share, from the referral pool.\n    uint256 public constant REFERRAL_PER_SHARE = 500 ether;\n    /// @notice Allocations of the 1B supply held by this contract.\n    uint256 public constant SALE_ALLOCATION = 700_000_000 ether;\n    uint256 public constant REFERRAL_ALLOCATION = 50_000_000 ether;\n    uint256 public constant LP_ALLOCATION = 250_000_000 ether;\n    /// @notice Max age of a Chainlink answer before BNB buys revert (1 hour).\n    uint256 public constant MAX_FEED_STALENESS = 3600;\n    /// @notice Slippage tolerance for seeding swaps/adds (5% = 500 bps).\n    uint256 public constant SEED_SLIPPAGE_BPS = 500;\n    /// @notice Dead address: LP tokens and burned reserve go here.\n    address public constant DEAD = 0x000000000000000000000000000000000000dEaD;\n\n    // ---------------- Immutables ----------------\n    IERC20 public immutable loom;\n    IERC20 public immutable usdt;\n    IAgentIdRegistry public immutable registry;\n    IAggregatorV3 public immutable priceFeed;\n    IPancakeRouter public immutable router;\n    address public immutable wbnb;\n    /// @notice Receives 20% of paid sales revenue. Set once, never changeable.\n    address public immutable treasury;\n    /// @notice Decimals of the Chainlink feed, read once at deploy.\n    uint8 public immutable feedDecimals;\n\n    // ---------------- State ----------------\n    /// @notice Total shares sold (free + paid).\n    uint256 public totalSharesSold;\n    /// @notice Free shares claimed so far (<= FREE_QUOTA).\n    uint256 public freeClaimedCount;\n    /// @notice Whether an Agent ID already used its free share.\n    mapping(uint256 => bool) public freeClaimedByAgent;\n    /// @notice Agent ID -> bound inviter Agent ID (0 = none). Bound once.\n    mapping(uint256 => uint256) public inviterOf;\n    /// @notice LOOM paid out of the referral pool so far.\n    uint256 public referralPaidTotal;\n    /// @notice LOOM left in the sale allocation (700M at deploy).\n    uint256 public saleLeft;\n    /// @notice LOOM left in the referral pool (50M at deploy).\n    uint256 public referralPoolLeft;\n    /// @notice LOOM left in the LP reserve (250M at deploy).\n    uint256 public lpReserveLeft;\n\n    // ---------------- Events ----------------\n    event FreeClaimed(address indexed buyer, uint256 indexed agentTokenId, uint256 indexed inviterTokenId);\n    event SharesBought(\n        address indexed buyer, uint256 indexed agentTokenId, uint256 count, uint256 usdPaid, uint256 bnbPaid\n    );\n    event ReferralPaid(address indexed inviter, uint256 indexed agentTokenId, uint256 amount);\n    event LiquiditySeeded(uint256 usdtAmount, uint256 loomAmount, uint256 liquidity);\n    event ReserveBurned(uint256 amount);\n\n    // ---------------- Errors ----------------\n    error SoldOut();\n    error FreeQuotaGone();\n    error AlreadyClaimedFree();\n    error NotAgentOwner();\n    error InvalidInviter();\n    error SelfInvite();\n    error InsufficientPayment();\n    error StalePrice();\n    error InvalidPrice();\n    error TriggerNotReached();\n    error NothingToSeed();\n    error ReferralPoolExhausted();\n    error ZeroAddress();\n    error TransferFailed();\n\n    constructor(\n        address _loom,\n        address _usdt,\n        address _registry,\n        address _priceFeed,\n        address _router,\n        address _treasury\n    ) {\n        if (\n            _loom == address(0) || _usdt == address(0) || _registry == address(0) || _priceFeed == address(0)\n                || _router == address(0) || _treasury == address(0)\n        ) revert ZeroAddress();\n        loom = IERC20(_loom);\n        usdt = IERC20(_usdt);\n        registry = IAgentIdRegistry(_registry);\n        priceFeed = IAggregatorV3(_priceFeed);\n        router = IPancakeRouter(_router);\n        wbnb = router.WETH();\n        treasury = _treasury;\n        feedDecimals = priceFeed.decimals();\n        saleLeft = SALE_ALLOCATION;\n        referralPoolLeft = REFERRAL_ALLOCATION;\n        lpReserveLeft = LP_ALLOCATION;\n    }\n\n    // ---------------- Views ----------------\n\n    /// @notice Price in USDT (18 decimals) for `count` shares bought when\n    ///         `alreadySold` shares have been sold. Shares are priced by their\n    ///         own index (cross-tier): indices 1-10,000 cost 1 USDT,\n    ///         10,001-30,000 cost 2 USDT, 30,001-60,000 cost 4 USDT,\n    ///         60,001-70,000 cost 8 USDT. Indices 1-1,000 are the\n    ///         free tranche when claimed via claimFree(); if bought via\n    ///         buyShares before they are claimed, they cost the tier-1 price\n    ///         of 1 USDT (the free tranche is a claim path, not a price wall —\n    ///         paid sales are never blocked waiting for free claims).\n    function priceForShares(uint256 alreadySold, uint256 count) public pure returns (uint256 total) {\n        uint256 start = alreadySold;\n        uint256 end = alreadySold + count;\n        total += _segment(start, end, 0, 10_000, 1 ether);\n        total += _segment(start, end, 10_000, 30_000, 2 ether);\n        total += _segment(start, end, 30_000, 60_000, 4 ether);\n        total += _segment(start, end, 60_000, 70_000, 8 ether);\n    }\n\n    function _segment(uint256 start, uint256 end, uint256 lo, uint256 hi, uint256 price)\n        private\n        pure\n        returns (uint256)\n    {\n        if (end <= lo || start >= hi) return 0;\n        uint256 s = start < lo ? lo : start;\n        uint256 e = end > hi ? hi : end;\n        return (e - s) * price;\n    }\n\n    /// @notice BNB (wei) required to pay `usdAmount` (18-decimal USDT) at the\n    ///         live Chainlink BNB/USD price. Rounds UP so the contract never\n    ///         under-collects. Reverts if the feed price is stale or invalid.\n    ///         Round validation follows Chainlink's recommended checks:\n    ///         the answer must come from the latest round\n    ///         (`answeredInRound >= roundId`), and `updatedAt` must be a\n    ///         past timestamp within the staleness window (a future\n    ///         `updatedAt` would otherwise underflow the age subtraction\n    ///         and panic instead of reverting cleanly).\n    function bnbRequiredFor(uint256 usdAmount) public view returns (uint256) {\n        (uint80 roundId, int256 answer,, uint256 updatedAt, uint80 answeredInRound) =\n            priceFeed.latestRoundData();\n        if (answer <= 0 || answeredInRound < roundId) revert InvalidPrice();\n        if (updatedAt == 0 || updatedAt > block.timestamp || block.timestamp - updatedAt > MAX_FEED_STALENESS) {\n            revert StalePrice();\n        }\n        uint256 price = uint256(answer);\n        // usd (18 dec) / (price / 10^feedDecimals USD per BNB), rounded up.\n        return (usdAmount * (10 ** feedDecimals) + price - 1) / price;\n    }\n\n    /// @notice Free shares still claimable in total (0 once 1,000 total\n    ///         shares have been sold, free or paid).\n    function freeLeft() public view returns (uint256) {\n        return totalSharesSold >= FREE_QUOTA ? 0 : FREE_QUOTA - totalSharesSold;\n    }\n\n    // ---------------- Free claim ----------------\n\n    /// @notice Claim the 1 free share for an Agent ID. Gas only.\n    /// @param agentTokenId The caller's Agent ID token. Must be owned by sender.\n    /// @param inviterTokenId Inviter's Agent ID token (0 = no referrer).\n    function claimFree(uint256 agentTokenId, uint256 inviterTokenId) external nonReentrant {\n        _maybeBurnReserve();\n        if (registry.ownerOf(agentTokenId) != msg.sender) revert NotAgentOwner();\n        if (freeClaimedByAgent[agentTokenId]) revert AlreadyClaimedFree();\n        if (totalSharesSold >= FREE_QUOTA) revert FreeQuotaGone();\n\n        freeClaimedByAgent[agentTokenId] = true;\n        freeClaimedCount += 1;\n        totalSharesSold += 1;\n        saleLeft -= SHARE_LOOM;\n\n        loom.safeTransfer(msg.sender, SHARE_LOOM);\n        _payReferral(agentTokenId, inviterTokenId, 1);\n\n        emit FreeClaimed(msg.sender, agentTokenId, inviterTokenId);\n    }\n\n    // ---------------- Paid buys ----------------\n\n    /// @notice Buy `count` shares with USDT (BSC USDT, 18 decimals).\n    ///         Caller must approve this contract for the full price first.\n    function buyShares(uint256 agentTokenId, uint256 count, uint256 inviterTokenId) external nonReentrant {\n        _maybeBurnReserve();\n        if (count == 0) revert InsufficientPayment();\n        if (totalSharesSold + count > TOTAL_SHARES) revert SoldOut();\n        if (registry.ownerOf(agentTokenId) != msg.sender) revert NotAgentOwner();\n\n        uint256 price = priceForShares(totalSharesSold, count);\n\n        totalSharesSold += count;\n        saleLeft -= count * SHARE_LOOM;\n\n        // 80% of the USDT stays in the contract (liquidity reserve);\n        // 20% goes straight to the treasury.\n        usdt.safeTransferFrom(msg.sender, address(this), price);\n        uint256 treasuryPart = (price * 20) / 100;\n        usdt.safeTransfer(treasury, treasuryPart);\n\n        loom.safeTransfer(msg.sender, count * SHARE_LOOM);\n        _payReferral(agentTokenId, inviterTokenId, count);\n\n        emit SharesBought(msg.sender, agentTokenId, count, price, 0);\n    }\n\n    /// @notice Buy `count` shares with BNB at the USDT-equivalent value\n    ///         (Chainlink BNB/USD at execution time). Excess BNB is refunded.\n    function buySharesBNB(uint256 agentTokenId, uint256 count, uint256 inviterTokenId)\n        external\n        payable\n        nonReentrant\n    {\n        _maybeBurnReserve();\n        if (count == 0) revert InsufficientPayment();\n        if (totalSharesSold + count > TOTAL_SHARES) revert SoldOut();\n        if (registry.ownerOf(agentTokenId) != msg.sender) revert NotAgentOwner();\n\n        uint256 usdPrice = priceForShares(totalSharesSold, count);\n        uint256 requiredBnb = bnbRequiredFor(usdPrice);\n        if (msg.value < requiredBnb) revert InsufficientPayment();\n\n        totalSharesSold += count;\n        saleLeft -= count * SHARE_LOOM;\n\n        loom.safeTransfer(msg.sender, count * SHARE_LOOM);\n        _payReferral(agentTokenId, inviterTokenId, count);\n\n        // 80% of the BNB stays in the contract (liquidity reserve);\n        // 20% goes straight to the treasury, in BNB.\n        uint256 treasuryPart = (requiredBnb * 20) / 100;\n        (bool okT,) = treasury.call{value: treasuryPart}(\"\");\n        if (!okT) revert TransferFailed();\n        uint256 excess = msg.value - requiredBnb;\n        if (excess > 0) {\n            (bool okR,) = msg.sender.call{value: excess}(\"\");\n            if (!okR) revert TransferFailed();\n        }\n\n        emit SharesBought(msg.sender, agentTokenId, count, usdPrice, requiredBnb);\n    }\n\n    // ---------------- Referral ----------------\n\n    /// @notice Pays the inviter 500 LOOM per share from the referral pool.\n    ///         Binds the buyer Agent ID to the inviter once; later purchases\n    ///         always pay the bound inviter. No self-invite. A nonexistent\n    ///         inviter token reverts (InvalidInviter) — but only when the\n    ///         binding is FIRST made, so a mistyped inviter fails loudly.\n    ///         If an ALREADY-BOUND inviter token later disappears (burned),\n    ///         the referral is skipped instead of reverting: otherwise the\n    ///         buyer could never purchase again, with no way to rebind.\n    function _payReferral(uint256 agentTokenId, uint256 inviterTokenId, uint256 shareCount) internal {\n        if (inviterTokenId == 0) return;\n        if (inviterTokenId == agentTokenId) revert SelfInvite();\n\n        address inviter;\n        uint256 bound = inviterOf[agentTokenId];\n        if (bound == 0) {\n            // First binding: validate loudly.\n            try registry.ownerOf(inviterTokenId) returns (address o) {\n                if (o == address(0)) revert InvalidInviter();\n                inviter = o;\n            } catch {\n                revert InvalidInviter();\n            }\n            inviterOf[agentTokenId] = inviterTokenId;\n        } else {\n            // Already bound: a burned inviter token skips the referral\n            // instead of bricking the buyer's future purchases.\n            try registry.ownerOf(bound) returns (address o) {\n                inviter = o;\n            } catch {\n                return;\n            }\n            if (inviter == address(0)) return; // unreachable for a real ERC-721; defensive\n        }\n\n        uint256 payout = shareCount * REFERRAL_PER_SHARE;\n        if (payout > referralPoolLeft) revert ReferralPoolExhausted();\n        referralPoolLeft -= payout;\n        referralPaidTotal += payout;\n\n        loom.safeTransfer(inviter, payout);\n        emit ReferralPaid(inviter, agentTokenId, payout);\n    }\n\n    // ---------------- Liquidity seeding ----------------\n\n    /// @notice Seed LOOM/USDT liquidity on PancakeSwap V2. Callable by anyone\n    ///         once 3,000 total shares are sold. Reserve BNB (from BNB buys)\n    ///         is swapped to USDT inside this same call, then the accumulated\n    ///         reserve USDT is paired with LOOM from the LP reserve. First seed\n    ///         pairs at the initial price (1 USDT = 10,000 LOOM); later seeds\n    ///         pair at the pool's market price and only as much LOOM as the\n    ///         reserve allows. Leftover USDT stays in the contract for later\n    ///         seeds. LP tokens go to the dead address (burned).\n    ///         If the sale has sold out, this call first burns only the LOOM in\n    ///         excess of what seeding still needs (via _maybeBurnReserve) and\n    ///         then seeds normally with the remainder — seeding keeps working\n    ///         after a burn, so no USDT is ever stranded by it.\n    function seedLiquidity() external nonReentrant returns (uint256 loomUsed, uint256 usdtUsed, uint256 liquidity) {\n        _maybeBurnReserve();\n        if (totalSharesSold < LP_TRIGGER) revert TriggerNotReached();\n        if (lpReserveLeft == 0) return (0, 0, 0);\n\n        // 1) Swap reserve BNB -> USDT in this same call.\n        uint256 bnbBal = address(this).balance;\n        if (bnbBal > 0) {\n            address[] memory path = new address[](2);\n            path[0] = wbnb;\n            path[1] = address(usdt);\n            uint256[] memory expected = router.getAmountsOut(bnbBal, path);\n            uint256 minOut = (expected[1] * (10_000 - SEED_SLIPPAGE_BPS)) / 10_000;\n            router.swapExactETHForTokens{value: bnbBal}(minOut, path, address(this), block.timestamp + 600);\n        }\n\n        uint256 usdtBal = usdt.balanceOf(address(this));\n        if (usdtBal == 0) revert NothingToSeed();\n\n        // 2) Work out how much LOOM to pair.\n        address pair = IPancakeFactory(router.factory()).getPair(address(loom), address(usdt));\n        uint256 loomAmount;\n        if (pair == address(0)) {\n            // First seed: initial price, 1 USDT = 10,000 LOOM.\n            loomAmount = usdtBal * 10_000;\n        } else {\n            (uint112 r0, uint112 r1,) = IPancakePair(pair).getReserves();\n            (uint256 reserveLoom, uint256 reserveUsdt) =\n                IPancakePair(pair).token0() == address(loom) ? (uint256(r0), uint256(r1)) : (uint256(r1), uint256(r0));\n            if (reserveUsdt == 0) revert NothingToSeed();\n            loomAmount = (usdtBal * reserveLoom) / reserveUsdt;\n        }\n        if (loomAmount > lpReserveLeft) {\n            // Not enough LOOM left: pair only what the reserve allows, and\n            // only the matching USDT; the rest of the USDT stays for later.\n            loomAmount = lpReserveLeft;\n            if (pair == address(0)) {\n                usdtBal = loomAmount / 10_000;\n            } else {\n                (uint112 r0, uint112 r1,) = IPancakePair(pair).getReserves();\n                (uint256 reserveLoom, uint256 reserveUsdt) =\n                    IPancakePair(pair).token0() == address(loom) ? (uint256(r0), uint256(r1)) : (uint256(r1), uint256(r0));\n                usdtBal = (loomAmount * reserveUsdt) / reserveLoom;\n            }\n        }\n        if (loomAmount == 0 || usdtBal == 0) revert NothingToSeed();\n\n        // 3) Add liquidity; LP tokens go to the dead address (burned).\n        uint256 minLoom = (loomAmount * (10_000 - SEED_SLIPPAGE_BPS)) / 10_000;\n        uint256 minUsdt = (usdtBal * (10_000 - SEED_SLIPPAGE_BPS)) / 10_000;\n        loom.forceApprove(address(router), loomAmount);\n        usdt.forceApprove(address(router), usdtBal);\n        (uint256 usedLoom, uint256 usedUsdt, uint256 lp) = router.addLiquidity(\n            address(loom), address(usdt), loomAmount, usdtBal, minLoom, minUsdt, DEAD, block.timestamp + 600\n        );\n        lpReserveLeft -= usedLoom;\n\n        emit LiquiditySeeded(usedUsdt, usedLoom, lp);\n        return (usedLoom, usedUsdt, lp);\n    }\n\n    // ---------------- Automatic reserve burn ----------------\n\n    /// @notice Burns ONLY the LOOM in excess of what is still needed to pair\n    ///         the contract's remaining USDT reserve (plus the USDT value of\n    ///         its reserve BNB) at the current pool price, once the sale has\n    ///         sold out (70,000 shares) — the only burn trigger. Runs\n    ///         automatically inside the contract's own state-changing calls.\n    ///         There is NO public/external burn function — this is internal\n    ///         only. Returns true if it burned anything.\n    ///         The check re-runs on every later call: if the pool price rises\n    ///         after the first burn, new excess appears and burns then. If the\n    ///         price cannot be read, or every LOOM is still needed for seeding,\n    ///         it burns nothing. Seeding therefore always keeps enough LOOM to\n    ///         finish, and no USDT is stranded by the burn.\n    ///         NOTE: the burn executes inside the NEXT transaction after\n    ///         sellout; with zero activity after sellout it waits for the\n    ///         next interaction (buy / free claim / seedLiquidity).\n    ///         NOTE: the burn can only fire at sellout, after which no more\n    ///         shares can be sold — so it can never strand future sales.\n    ///         If the sale never sells out, the leftover reserve simply stays\n    ///         in the contract forever; there is no time-based cleanup.\n    function _maybeBurnReserve() internal returns (bool) {\n        if (lpReserveLeft == 0) return false;\n        if (totalSharesSold != TOTAL_SHARES) return false;\n        uint256 needed = _loomNeededToPairReserve();\n        if (needed == type(uint256).max) return false; // price unreadable: burn nothing\n        if (needed >= lpReserveLeft) return false; // every LOOM still needed for seeding\n        uint256 excess = lpReserveLeft - needed;\n        lpReserveLeft = needed;\n        loom.safeTransfer(DEAD, excess);\n        emit ReserveBurned(excess);\n        return true;\n    }\n\n    /// @notice LOOM needed to pair the contract's entire remaining USDT\n    ///         reserve — plus the USDT value of its reserve BNB, quoted via\n    ///         the router — at the current pool price. Returns\n    ///         type(uint256).max when the price cannot be read (the caller\n    ///         must then burn nothing).\n    ///         - Pair exists with USDT reserves: need = totalUsdt * reserveLoom\n    ///           / reserveUsdt, rounded UP so the need is never underestimated.\n    ///         - No pair yet: initial price, 1 USDT = 10,000 LOOM.\n    ///         - No USDT/BNB reserve at all: 0.\n    ///         PancakeSwap pairs sort token0 < token1 by address, so the\n    ///         reserves are mapped by comparing the token addresses (no\n    ///         token0() call needed).\n    function _loomNeededToPairReserve() internal view returns (uint256) {\n        uint256 totalUsdt = usdt.balanceOf(address(this));\n        uint256 bnbBal = address(this).balance;\n        if (bnbBal > 0) {\n            address[] memory path = new address[](2);\n            path[0] = wbnb;\n            path[1] = address(usdt);\n            try router.getAmountsOut(bnbBal, path) returns (uint256[] memory amounts) {\n                totalUsdt += amounts[1];\n            } catch {\n                return type(uint256).max;\n            }\n        }\n        if (totalUsdt == 0) return 0;\n        address pair = IPancakeFactory(router.factory()).getPair(address(loom), address(usdt));\n        if (pair == address(0)) {\n            return totalUsdt * 10_000; // initial price\n        }\n        uint112 r0;\n        uint112 r1;\n        try IPancakePair(pair).getReserves() returns (uint112 _r0, uint112 _r1, uint32) {\n            r0 = _r0;\n            r1 = _r1;\n        } catch {\n            return type(uint256).max;\n        }\n        (uint256 reserveLoom, uint256 reserveUsdt) = address(loom) < address(usdt)\n            ? (uint256(r0), uint256(r1))\n            : (uint256(r1), uint256(r0));\n        if (reserveUsdt == 0) return type(uint256).max;\n        // Round UP so the need is never underestimated.\n        return (totalUsdt * reserveLoom + reserveUsdt - 1) / reserveUsdt;\n    }\n\n    // NOTE: no receive() — the contract never needs to accept plain BNB\n    // transfers (router calls return USDT, never BNB; BNB buys arrive via\n    // buySharesBNB's payable call). A direct BNB send reverts instead of\n    // silently locking funds in the contract.\n}\n"},"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)\n\npragma solidity >=0.4.16;\n\n/**\n * @dev Interface of the ERC-20 standard as defined in the ERC.\n */\ninterface IERC20 {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the value of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the value of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves a `value` amount of tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 value) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the\n     * caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 value) external returns (bool);\n\n    /**\n     * @dev Moves a `value` amount of tokens from `from` to `to` using the\n     * allowance mechanism. `value` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 value) external returns (bool);\n}\n"},"lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.7.0) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC20} from \"../IERC20.sol\";\nimport {IERC1363} from \"../../../interfaces/IERC1363.sol\";\nimport {IERC20Metadata} from \"../../../interfaces/IERC20Metadata.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC-20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n    /**\n     * @dev An operation with an ERC-20 token failed.\n     */\n    error SafeERC20FailedOperation(address token);\n\n    /**\n     * @dev Indicates a failed `decreaseAllowance` request.\n     */\n    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);\n\n    /**\n     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeTransfer(IERC20 token, address to, uint256 value) internal {\n        if (!_safeTransfer(token, to, value, true)) {\n            revert SafeERC20FailedOperation(address(token));\n        }\n    }\n\n    /**\n     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the\n     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.\n     */\n    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {\n        if (!_safeTransferFrom(token, from, to, value, true)) {\n            revert SafeERC20FailedOperation(address(token));\n        }\n    }\n\n    /**\n     * @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.\n     */\n    function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {\n        return _safeTransfer(token, to, value, false);\n    }\n\n    /**\n     * @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.\n     */\n    function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {\n        return _safeTransferFrom(token, from, to, value, false);\n    }\n\n    /**\n     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     *\n     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the \"client\"\n     * smart contract uses ERC-7674 to set temporary allowances, then the \"client\" smart contract should avoid using\n     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract\n     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.\n     */\n    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        uint256 oldAllowance = token.allowance(address(this), spender);\n        forceApprove(token, spender, oldAllowance + value);\n    }\n\n    /**\n     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no\n     * value, non-reverting calls are assumed to be successful.\n     *\n     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the \"client\"\n     * smart contract uses ERC-7674 to set temporary allowances, then the \"client\" smart contract should avoid using\n     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract\n     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.\n     */\n    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {\n        unchecked {\n            uint256 currentAllowance = token.allowance(address(this), spender);\n            if (currentAllowance < requestedDecrease) {\n                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);\n            }\n            forceApprove(token, spender, currentAllowance - requestedDecrease);\n        }\n    }\n\n    /**\n     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval\n     * to be set to zero before setting it to a non-zero value, such as USDT.\n     *\n     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function\n     * only sets the \"standard\" allowance. Any temporary allowance will remain active, in addition to the value being\n     * set here.\n     */\n    function forceApprove(IERC20 token, address spender, uint256 value) internal {\n        if (!_safeApprove(token, spender, value, false)) {\n            if (!_safeApprove(token, spender, 0, true)) revert SafeERC20FailedOperation(address(token));\n            if (!_safeApprove(token, spender, value, true)) revert SafeERC20FailedOperation(address(token));\n        }\n    }\n\n    /**\n     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no\n     * code. This can be used to implement an {ERC721}-like safe transfer that relies on {ERC1363} checks when\n     * targeting contracts.\n     *\n     * Reverts if the returned value is other than `true`.\n     */\n    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {\n        if (to.code.length == 0) {\n            safeTransfer(token, to, value);\n        } else if (!token.transferAndCall(to, value, data)) {\n            revert SafeERC20FailedOperation(address(token));\n        }\n    }\n\n    /**\n     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target\n     * has no code. This can be used to implement an {ERC721}-like safe transfer that relies on {ERC1363} checks when\n     * targeting contracts.\n     *\n     * Reverts if the returned value is other than `true`.\n     */\n    function transferFromAndCallRelaxed(\n        IERC1363 token,\n        address from,\n        address to,\n        uint256 value,\n        bytes memory data\n    ) internal {\n        if (to.code.length == 0) {\n            safeTransferFrom(token, from, to, value);\n        } else if (!token.transferFromAndCall(from, to, value, data)) {\n            revert SafeERC20FailedOperation(address(token));\n        }\n    }\n\n    /**\n     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no\n     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when\n     * targeting contracts.\n     *\n     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.\n     * Oppositely, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}\n     * once without retrying, and relies on the returned value to be true.\n     *\n     * Reverts if the returned value is other than `true`.\n     */\n    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {\n        if (to.code.length == 0) {\n            forceApprove(token, to, value);\n        } else if (!token.approveAndCall(to, value, data)) {\n            revert SafeERC20FailedOperation(address(token));\n        }\n    }\n\n    /// @dev Attempts to fetch the token decimals. A return value of false indicates that the attempt failed in some way.\n    function tryGetDecimals(IERC20 token) internal view returns (bool success, uint8 decimals) {\n        bytes4 selector = IERC20Metadata.decimals.selector;\n        assembly (\"memory-safe\") {\n            mstore(0x00, selector)\n            success := staticcall(gas(), token, 0x00, 4, 0x00, 0x20)\n            success := and(and(success, gt(returndatasize(), 0x1f)), lt(mload(0x00), 0x100))\n            decimals := mul(success, mload(0x00))\n        }\n    }\n\n    /**\n     * @dev Imitates a Solidity `token.transfer(to, value)` call, relaxing the requirement on the return value: the\n     * return value is optional (but if data is returned, it must not be false).\n     *\n     * @param token The token targeted by the call.\n     * @param to The recipient of the tokens\n     * @param value The amount of token to transfer\n     * @param bubble Behavior switch if the transfer call reverts: bubble the revert reason or return a false boolean.\n     */\n    function _safeTransfer(IERC20 token, address to, uint256 value, bool bubble) private returns (bool success) {\n        bytes4 selector = IERC20.transfer.selector;\n\n        assembly (\"memory-safe\") {\n            let fmp := mload(0x40)\n            mstore(0x00, selector)\n            mstore(0x04, and(to, shr(96, not(0))))\n            mstore(0x24, value)\n            success := call(gas(), token, 0, 0x00, 0x44, 0x00, 0x20)\n            // if call success and return is true, all is good.\n            // otherwise (not success or return is not true), we need to perform further checks\n            if iszero(and(success, eq(mload(0x00), 1))) {\n                // if the call was a failure and bubble is enabled, bubble the error\n                if and(iszero(success), bubble) {\n                    returndatacopy(fmp, 0x00, returndatasize())\n                    revert(fmp, returndatasize())\n                }\n                // if the return value is not true, then the call is only successful if:\n                // - the token address has code\n                // - the returndata is empty\n                success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))\n            }\n            mstore(0x40, fmp)\n        }\n    }\n\n    /**\n     * @dev Imitates a Solidity `token.transferFrom(from, to, value)` call, relaxing the requirement on the return\n     * value: the return value is optional (but if data is returned, it must not be false).\n     *\n     * @param token The token targeted by the call.\n     * @param from The sender of the tokens\n     * @param to The recipient of the tokens\n     * @param value The amount of token to transfer\n     * @param bubble Behavior switch if the transfer call reverts: bubble the revert reason or return a false boolean.\n     */\n    function _safeTransferFrom(\n        IERC20 token,\n        address from,\n        address to,\n        uint256 value,\n        bool bubble\n    ) private returns (bool success) {\n        bytes4 selector = IERC20.transferFrom.selector;\n\n        assembly (\"memory-safe\") {\n            let fmp := mload(0x40)\n            mstore(0x00, selector)\n            mstore(0x04, and(from, shr(96, not(0))))\n            mstore(0x24, and(to, shr(96, not(0))))\n            mstore(0x44, value)\n            success := call(gas(), token, 0, 0x00, 0x64, 0x00, 0x20)\n            // if call success and return is true, all is good.\n            // otherwise (not success or return is not true), we need to perform further checks\n            if iszero(and(success, eq(mload(0x00), 1))) {\n                // if the call was a failure and bubble is enabled, bubble the error\n                if and(iszero(success), bubble) {\n                    returndatacopy(fmp, 0x00, returndatasize())\n                    revert(fmp, returndatasize())\n                }\n                // if the return value is not true, then the call is only successful if:\n                // - the token address has code\n                // - the returndata is empty\n                success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))\n            }\n            mstore(0x40, fmp)\n            mstore(0x60, 0)\n        }\n    }\n\n    /**\n     * @dev Imitates a Solidity `token.approve(spender, value)` call, relaxing the requirement on the return value:\n     * the return value is optional (but if data is returned, it must not be false).\n     *\n     * @param token The token targeted by the call.\n     * @param spender The spender of the tokens\n     * @param value The amount of token to approve\n     * @param bubble Behavior switch if the approve call reverts: bubble the revert reason or return a false boolean.\n     */\n    function _safeApprove(IERC20 token, address spender, uint256 value, bool bubble) private returns (bool success) {\n        bytes4 selector = IERC20.approve.selector;\n\n        assembly (\"memory-safe\") {\n            let fmp := mload(0x40)\n            mstore(0x00, selector)\n            mstore(0x04, and(spender, shr(96, not(0))))\n            mstore(0x24, value)\n            success := call(gas(), token, 0, 0x00, 0x44, 0x00, 0x20)\n            // if call success and return is true, all is good.\n            // otherwise (not success or return is not true), we need to perform further checks\n            if iszero(and(success, eq(mload(0x00), 1))) {\n                // if the call was a failure and bubble is enabled, bubble the error\n                if and(iszero(success), bubble) {\n                    returndatacopy(fmp, 0x00, returndatasize())\n                    revert(fmp, returndatasize())\n                }\n                // if the return value is not true, then the call is only successful if:\n                // - the token address has code\n                // - the returndata is empty\n                success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))\n            }\n            mstore(0x40, fmp)\n        }\n    }\n}\n"},"lib/openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.5.0) (utils/ReentrancyGuard.sol)\n\npragma solidity ^0.8.20;\n\nimport {StorageSlot} from \"./StorageSlot.sol\";\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,\n * consider using {ReentrancyGuardTransient} instead.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n *\n * IMPORTANT: Deprecated. This storage-based reentrancy guard will be removed and replaced\n * by the {ReentrancyGuardTransient} variant in v6.0.\n *\n * @custom:stateless\n */\nabstract contract ReentrancyGuard {\n    using StorageSlot for bytes32;\n\n    // keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.ReentrancyGuard\")) - 1)) & ~bytes32(uint256(0xff))\n    bytes32 private constant REENTRANCY_GUARD_STORAGE =\n        0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;\n\n    // Booleans are more expensive than uint256 or any type that takes up a full\n    // word because each write operation emits an extra SLOAD to first read the\n    // slot's contents, replace the bits taken up by the boolean, and then write\n    // back. This is the compiler's defense against contract upgrades and\n    // pointer aliasing, and it cannot be disabled.\n\n    // The values being non-zero value makes deployment a bit more expensive,\n    // but in exchange the refund on every call to nonReentrant will be lower in\n    // amount. Since refunds are capped to a percentage of the total\n    // transaction's gas, it is best to keep them low in cases like this one, to\n    // increase the likelihood of the full refund coming into effect.\n    uint256 private constant NOT_ENTERED = 1;\n    uint256 private constant ENTERED = 2;\n\n    /**\n     * @dev Unauthorized reentrant call.\n     */\n    error ReentrancyGuardReentrantCall();\n\n    constructor() {\n        _reentrancyGuardStorageSlot().getUint256Slot().value = NOT_ENTERED;\n    }\n\n    /**\n     * @dev Prevents a contract from calling itself, directly or indirectly.\n     * Calling a `nonReentrant` function from another `nonReentrant`\n     * function is not supported. It is possible to prevent this from happening\n     * by making the `nonReentrant` function external, and making it call a\n     * `private` function that does the actual work.\n     */\n    modifier nonReentrant() {\n        _nonReentrantBefore();\n        _;\n        _nonReentrantAfter();\n    }\n\n    /**\n     * @dev A `view` only version of {nonReentrant}. Use to block view functions\n     * from being called, preventing reading from inconsistent contract state.\n     *\n     * CAUTION: This is a \"view\" modifier and does not change the reentrancy\n     * status. Use it only on view functions. For payable or non-payable functions,\n     * use the standard {nonReentrant} modifier instead.\n     */\n    modifier nonReentrantView() {\n        _nonReentrantBeforeView();\n        _;\n    }\n\n    function _nonReentrantBeforeView() private view {\n        if (_reentrancyGuardEntered()) {\n            revert ReentrancyGuardReentrantCall();\n        }\n    }\n\n    function _nonReentrantBefore() private {\n        // On the first call to nonReentrant, _status will be NOT_ENTERED\n        _nonReentrantBeforeView();\n\n        // Any calls to nonReentrant after this point will fail\n        _reentrancyGuardStorageSlot().getUint256Slot().value = ENTERED;\n    }\n\n    function _nonReentrantAfter() private {\n        // By storing the original value once again, a refund is triggered (see\n        // https://eips.ethereum.org/EIPS/eip-2200)\n        _reentrancyGuardStorageSlot().getUint256Slot().value = NOT_ENTERED;\n    }\n\n    /**\n     * @dev Returns true if the reentrancy guard is currently set to \"entered\", which indicates there is a\n     * `nonReentrant` function in the call stack.\n     */\n    function _reentrancyGuardEntered() internal view returns (bool) {\n        return _reentrancyGuardStorageSlot().getUint256Slot().value == ENTERED;\n    }\n\n    function _reentrancyGuardStorageSlot() internal pure virtual returns (bytes32) {\n        return REENTRANCY_GUARD_STORAGE;\n    }\n}\n"},"lib/openzeppelin-contracts/contracts/interfaces/IERC1363.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC1363.sol)\n\npragma solidity >=0.6.2;\n\nimport {IERC20} from \"./IERC20.sol\";\nimport {IERC165} from \"./IERC165.sol\";\n\n/**\n * @title IERC1363\n * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].\n *\n * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract\n * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.\n */\ninterface IERC1363 is IERC20, IERC165 {\n    /*\n     * Note: the ERC-165 identifier for this interface is 0xb0202a11.\n     * 0xb0202a11 ===\n     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^\n     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^\n     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^\n     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^\n     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^\n     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))\n     */\n\n    /**\n     * @dev Moves a `value` amount of tokens from the caller's account to `to`\n     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.\n     * @param to The address which you want to transfer to.\n     * @param value The amount of tokens to be transferred.\n     * @return A boolean value indicating whether the operation succeeded unless throwing.\n     */\n    function transferAndCall(address to, uint256 value) external returns (bool);\n\n    /**\n     * @dev Moves a `value` amount of tokens from the caller's account to `to`\n     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.\n     * @param to The address which you want to transfer to.\n     * @param value The amount of tokens to be transferred.\n     * @param data Additional data with no specified format, sent in call to `to`.\n     * @return A boolean value indicating whether the operation succeeded unless throwing.\n     */\n    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);\n\n    /**\n     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism\n     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.\n     * @param from The address which you want to send tokens from.\n     * @param to The address which you want to transfer to.\n     * @param value The amount of tokens to be transferred.\n     * @return A boolean value indicating whether the operation succeeded unless throwing.\n     */\n    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);\n\n    /**\n     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism\n     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.\n     * @param from The address which you want to send tokens from.\n     * @param to The address which you want to transfer to.\n     * @param value The amount of tokens to be transferred.\n     * @param data Additional data with no specified format, sent in call to `to`.\n     * @return A boolean value indicating whether the operation succeeded unless throwing.\n     */\n    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);\n\n    /**\n     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the\n     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.\n     * @param spender The address which will spend the funds.\n     * @param value The amount of tokens to be spent.\n     * @return A boolean value indicating whether the operation succeeded unless throwing.\n     */\n    function approveAndCall(address spender, uint256 value) external returns (bool);\n\n    /**\n     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the\n     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.\n     * @param spender The address which will spend the funds.\n     * @param value The amount of tokens to be spent.\n     * @param data Additional data with no specified format, sent in call to `spender`.\n     * @return A boolean value indicating whether the operation succeeded unless throwing.\n     */\n    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);\n}\n"},"lib/openzeppelin-contracts/contracts/interfaces/IERC20Metadata.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC20Metadata.sol)\n\npragma solidity >=0.6.2;\n\nimport {IERC20Metadata} from \"../token/ERC20/extensions/IERC20Metadata.sol\";\n"},"lib/openzeppelin-contracts/contracts/utils/StorageSlot.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol)\n// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Library for reading and writing primitive types to specific storage slots.\n *\n * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.\n * This library helps with reading and writing to such slots without the need for inline assembly.\n *\n * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.\n *\n * Example usage to set ERC-1967 implementation slot:\n * ```solidity\n * contract ERC1967 {\n *     // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.\n *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n *\n *     function _getImplementation() internal view returns (address) {\n *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\n *     }\n *\n *     function _setImplementation(address newImplementation) internal {\n *         require(newImplementation.code.length > 0);\n *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\n *     }\n * }\n * ```\n *\n * TIP: Consider using this library along with {SlotDerivation}.\n */\nlibrary StorageSlot {\n    struct AddressSlot {\n        address value;\n    }\n\n    struct BooleanSlot {\n        bool value;\n    }\n\n    struct Bytes32Slot {\n        bytes32 value;\n    }\n\n    struct Uint256Slot {\n        uint256 value;\n    }\n\n    struct Int256Slot {\n        int256 value;\n    }\n\n    struct StringSlot {\n        string value;\n    }\n\n    struct BytesSlot {\n        bytes value;\n    }\n\n    /**\n     * @dev Returns an `AddressSlot` with member `value` located at `slot`.\n     */\n    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {\n        assembly (\"memory-safe\") {\n            r.slot := slot\n        }\n    }\n\n    /**\n     * @dev Returns a `BooleanSlot` with member `value` located at `slot`.\n     */\n    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {\n        assembly (\"memory-safe\") {\n            r.slot := slot\n        }\n    }\n\n    /**\n     * @dev Returns a `Bytes32Slot` with member `value` located at `slot`.\n     */\n    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {\n        assembly (\"memory-safe\") {\n            r.slot := slot\n        }\n    }\n\n    /**\n     * @dev Returns a `Uint256Slot` with member `value` located at `slot`.\n     */\n    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {\n        assembly (\"memory-safe\") {\n            r.slot := slot\n        }\n    }\n\n    /**\n     * @dev Returns a `Int256Slot` with member `value` located at `slot`.\n     */\n    function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {\n        assembly (\"memory-safe\") {\n            r.slot := slot\n        }\n    }\n\n    /**\n     * @dev Returns a `StringSlot` with member `value` located at `slot`.\n     */\n    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {\n        assembly (\"memory-safe\") {\n            r.slot := slot\n        }\n    }\n\n    /**\n     * @dev Returns an `StringSlot` representation of the string storage pointer `store`.\n     */\n    function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {\n        assembly (\"memory-safe\") {\n            r.slot := store.slot\n        }\n    }\n\n    /**\n     * @dev Returns a `BytesSlot` with member `value` located at `slot`.\n     */\n    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {\n        assembly (\"memory-safe\") {\n            r.slot := slot\n        }\n    }\n\n    /**\n     * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.\n     */\n    function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {\n        assembly (\"memory-safe\") {\n            r.slot := store.slot\n        }\n    }\n}\n"},"lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC20.sol)\n\npragma solidity >=0.4.16;\n\nimport {IERC20} from \"../token/ERC20/IERC20.sol\";\n"},"lib/openzeppelin-contracts/contracts/interfaces/IERC165.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC165.sol)\n\npragma solidity >=0.4.16;\n\nimport {IERC165} from \"../utils/introspection/IERC165.sol\";\n"},"lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity >=0.6.2;\n\nimport {IERC20} from \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC-20 standard.\n */\ninterface IERC20Metadata is IERC20 {\n    /**\n     * @dev Returns the name of the token.\n     */\n    function name() external view returns (string memory);\n\n    /**\n     * @dev Returns the symbol of the token.\n     */\n    function symbol() external view returns (string memory);\n\n    /**\n     * @dev Returns the decimals places of the token.\n     */\n    function decimals() external view returns (uint8);\n}\n"},"lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)\n\npragma solidity >=0.4.16;\n\n/**\n * @dev Interface of the ERC-165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[ERC].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n    /**\n     * @dev Returns true if this contract implements the interface defined by\n     * `interfaceId`. See the corresponding\n     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]\n     * to learn more about how these ids are created.\n     *\n     * This function call must use less than 30 000 gas.\n     */\n    function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"}},"abi":[{"type":"constructor","inputs":[{"name":"_loom","type":"address","internalType":"address"},{"name":"_usdt","type":"address","internalType":"address"},{"name":"_registry","type":"address","internalType":"address"},{"name":"_priceFeed","type":"address","internalType":"address"},{"name":"_router","type":"address","internalType":"address"},{"name":"_treasury","type":"address","internalType":"address"}],"stateMutability":"nonpayable"},{"name":"AlreadyClaimedFree","type":"error","inputs":[]},{"name":"FreeQuotaGone","type":"error","inputs":[]},{"name":"InsufficientPayment","type":"error","inputs":[]},{"name":"InvalidInviter","type":"error","inputs":[]},{"name":"InvalidPrice","type":"error","inputs":[]},{"name":"NotAgentOwner","type":"error","inputs":[]},{"name":"NothingToSeed","type":"error","inputs":[]},{"name":"ReentrancyGuardReentrantCall","type":"error","inputs":[]},{"name":"ReferralPoolExhausted","type":"error","inputs":[]},{"name":"SafeERC20FailedOperation","type":"error","inputs":[{"name":"token","type":"address","internalType":"address"}]},{"name":"SelfInvite","type":"error","inputs":[]},{"name":"SoldOut","type":"error","inputs":[]},{"name":"StalePrice","type":"error","inputs":[]},{"name":"TransferFailed","type":"error","inputs":[]},{"name":"TriggerNotReached","type":"error","inputs":[]},{"name":"ZeroAddress","type":"error","inputs":[]},{"name":"FreeClaimed","type":"event","inputs":[{"name":"buyer","type":"address","indexed":true,"internalType":"address"},{"name":"agentTokenId","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"inviterTokenId","type":"uint256","indexed":true,"internalType":"uint256"}],"anonymous":false},{"name":"LiquiditySeeded","type":"event","inputs":[{"name":"usdtAmount","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"loomAmount","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"liquidity","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"name":"ReferralPaid","type":"event","inputs":[{"name":"inviter","type":"address","indexed":true,"internalType":"address"},{"name":"agentTokenId","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"name":"ReserveBurned","type":"event","inputs":[{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"name":"SharesBought","type":"event","inputs":[{"name":"buyer","type":"address","indexed":true,"internalType":"address"},{"name":"agentTokenId","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"count","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"usdPaid","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"bnbPaid","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"name":"DEAD","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"name":"FREE_QUOTA","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"LP_ALLOCATION","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"LP_TRIGGER","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"MAX_FEED_STALENESS","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"REFERRAL_ALLOCATION","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"REFERRAL_PER_SHARE","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"SALE_ALLOCATION","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"SEED_SLIPPAGE_BPS","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"SHARE_LOOM","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"TOTAL_SHARES","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"bnbRequiredFor","type":"function","inputs":[{"name":"usdAmount","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"buyShares","type":"function","inputs":[{"name":"agentTokenId","type":"uint256","internalType":"uint256"},{"name":"count","type":"uint256","internalType":"uint256"},{"name":"inviterTokenId","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"buySharesBNB","type":"function","inputs":[{"name":"agentTokenId","type":"uint256","internalType":"uint256"},{"name":"count","type":"uint256","internalType":"uint256"},{"name":"inviterTokenId","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"payable"},{"name":"claimFree","type":"function","inputs":[{"name":"agentTokenId","type":"uint256","internalType":"uint256"},{"name":"inviterTokenId","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"feedDecimals","type":"function","inputs":[],"outputs":[{"name":"","type":"uint8","internalType":"uint8"}],"stateMutability":"view"},{"name":"freeClaimedByAgent","type":"function","inputs":[{"name":"","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"name":"freeClaimedCount","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"freeLeft","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"inviterOf","type":"function","inputs":[{"name":"","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"loom","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract IERC20"}],"stateMutability":"view"},{"name":"lpReserveLeft","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"priceFeed","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract IAggregatorV3"}],"stateMutability":"view"},{"name":"priceForShares","type":"function","inputs":[{"name":"alreadySold","type":"uint256","internalType":"uint256"},{"name":"count","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"total","type":"uint256","internalType":"uint256"}],"stateMutability":"pure"},{"name":"referralPaidTotal","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"referralPoolLeft","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"registry","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract IAgentIdRegistry"}],"stateMutability":"view"},{"name":"router","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract IPancakeRouter"}],"stateMutability":"view"},{"name":"saleLeft","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"seedLiquidity","type":"function","inputs":[],"outputs":[{"name":"loomUsed","type":"uint256","internalType":"uint256"},{"name":"usdtUsed","type":"uint256","internalType":"uint256"},{"name":"liquidity","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"name":"totalSharesSold","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"treasury","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"name":"usdt","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract IERC20"}],"stateMutability":"view"},{"name":"wbnb","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"}],"matchId":"59092392","creationMatch":"exact_match","runtimeMatch":"exact_match","verifiedAt":"2026-11-11T07:56:51Z","match":"exact_match","chainId":"56","address":"0x6092e186c28872CeC2C4C6f333b6f6B57E207552"}