{"sources":{"agent/AIDROPVault.sol":{"content":"// SPDX-License-Identifier: MIT\r\npragma solidity ^0.8.24;\r\n\r\nimport {IERC20} from \"@openzeppelin/contracts@4.9.6/token/ERC20/IERC20.sol\";\r\nimport {SafeERC20} from \"@openzeppelin/contracts@4.9.6/token/ERC20/utils/SafeERC20.sol\";\r\nimport {Math} from \"@openzeppelin/contracts@4.9.6/utils/math/Math.sol\";\r\nimport {Initializable} from \"@openzeppelin/contracts-upgradeable@4.9.6/proxy/utils/Initializable.sol\";\r\nimport {VaultBaseV3} from \"../flap/VaultBaseV3.sol\";\r\nimport {VaultUISchema, VaultMethodSchema} from \"../flap/IVaultSchemasV1.sol\";\r\nimport {IFlapVaultEx, IFlapTriggerService, IAIDROPWBNB} from \"../flap/IAIDROPServices.sol\";\r\nimport {AIDROPRewards} from \"./AIDROPRewards.sol\";\r\n\r\ninterface IAIDROPTaxRate { function taxRate() external view returns (uint256); }\r\n\r\n/// @notice Beacon-backed BNCB revenue vault. Upgrade authority belongs exclusively to Flap Guardian.\r\n/// @dev Fresh deployment only: this layout is NOT an upgrade target for the former EIP-1167 deployment.\r\ncontract AIDROPVault is Initializable, VaultBaseV3 {\r\n    using SafeERC20 for IERC20;\r\n\r\n    uint256 public constant BUY_BNCB = 10 ether;\r\n    uint256 public constant COMMISSION_PAYOUT_THRESHOLD = 20 ether;\r\n    uint256 public constant MIN_BUY_INTERVAL = 1 minutes;\r\n    // The buffer is spendable in emergencies, never a hard spending floor.\r\n    uint256 public constant TRIGGER_FEE_BUFFER = 0.2 ether;\r\n    uint256 public constant MIN_FUNDING_BNCB = 2 ether;\r\n    uint256 public constant MAX_FUNDING_BNCB = 10 ether;\r\n    uint16 public constant MAX_BUY_SLIPPAGE_BPS = 800;\r\n    uint256 private constant FUNDING_REQUEST_GAS_LIMIT = 450_000;\r\n    uint256 private constant FUNDING_QUOTE_GAS_LIMIT = 120_000;\r\n    uint256 private constant TAX_RATE_GAS_LIMIT = 30_000;\r\n    uint256 public constant FUNDING_QUOTE_BUFFER_BPS = 500;\r\n    uint256 public constant REWARD_RETRY_INTERVAL = 1 days;\r\n    uint256 private constant ROUND_START_GAS_LIMIT = 900_000;\r\n    uint256 private constant FEE_QUERY_GAS_LIMIT = 50_000;\r\n    uint256 private constant TRIGGER_REQUEST_GAS_LIMIT = 250_000;\r\n    uint256 private constant BUY_EXECUTION_GAS_LIMIT = 900_000;\r\n    uint256 private constant BUY_QUOTE_GAS_LIMIT = 150_000;\r\n    uint256 public constant BUY_DEADLINE_SECONDS = 300;\r\n    uint256 public constant PAYOUT_CALL_GAS = 150_000;\r\n    address public constant BNCB = 0x4902C5ebc598265Ed2212b559B042De8a5Eeec3f;\r\n    address public constant WBNB = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;\r\n    address public constant VAULT_EX = 0x000048Df69f3d2af22Df6DC2d3B01FB592b90000;\r\n    address public constant TRIGGER_SERVICE = 0xcf4EE25035CF883895110f367F5BA8172416a7F9;\r\n\r\n    bool public initialized;\r\n    uint256 private locked;\r\n    address public taxToken;\r\n    address public rewardsContract;\r\n    address public owner;\r\n    address public feeReceiver;\r\n    uint256 public taxRateBps;\r\n    mapping(address => bool) public operators;\r\n    uint256 public lastBuyAt;\r\n    uint256 public accountedBNCB;\r\n    uint256 public pendingCommission; // BNCB base units, not native BNB.\r\n    bool public conversionPending;\r\n    uint256 public pendingConversionId;\r\n    uint256 public pendingConversionAmount;\r\n    bool public hasPendingTrigger;\r\n    uint256 public pendingTriggerId;\r\n    uint256 public pendingTriggerAt;\r\n    // Locally released requests remain live at VaultEx until settlement or refund.\r\n    // A positive amount authenticates each late callback, including request ID zero.\r\n    mapping(uint256 => uint256) public detachedConversionAmounts;\r\n    uint256 public rewardStartRetryAt;\r\n    bool public commissionPaymentFailed;\r\n    bool public autoBuyEnabled;\r\n    uint16 public autoBuySlippageBps;\r\n    bool public hasPendingBuyTrigger;\r\n    uint256 private deprecatedAutoBuyMinTokenOut; // Preserve the old storage slot; no longer read.\r\n    uint256 public pendingBuyTriggerId;\r\n    uint256 public pendingBuyTriggerAt;\r\n    uint256 public buyRetryAt;\r\n    bool public taxRateResolved;\r\n    // All native funds are dedicated to Trigger fees; operating/buffer portions are\r\n    // derived from this ledger. Forced native transfers are reconciled on use.\r\n    uint256 public triggerFeeFunds;\r\n    uint256[31] private __gap;\r\n\r\n    event Initialized(address indexed taxToken, address indexed owner, address indexed operator,\r\n        address feeReceiver, uint256 taxRateBps, address buyToken);\r\n    event OperatorUpdated(address indexed operator, bool enabled);\r\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\r\n    event BNCBRevenueRecognized(uint256 amount, uint256 accountedBalance);\r\n    event NativeFundingReceived(address indexed from, uint256 amount);\r\n    event TriggerFeeFunded(address indexed from, uint256 amount);\r\n    event TriggerFeeSpent(uint256 amount, uint256 bufferUsed);\r\n    event TriggerFundingDeferred();\r\n    event TaxRateResolved(uint256 taxRateBps);\r\n    event CommissionAccrued(uint256 bncbRevenue, uint256 commissionBNCB, uint256 totalPending);\r\n    event CommissionPaid(address indexed feeReceiver, uint256 amount);\r\n    event CommissionDeferred(address indexed feeReceiver, uint256 amount, uint256 totalPending);\r\n    event ConversionRequested(uint256 indexed requestId, uint256 bncbAmount);\r\n    event ConversionWaitCancelled(uint256 indexed requestId, uint256 bncbAmount);\r\n    event BNCBConverted(uint256 bncbSpent, uint256 receivedBNB);\r\n    event ConversionRefunded(uint256 indexed requestId, uint256 bncbReturned);\r\n    event TriggeredBuyHold(address indexed caller, uint256 bncbSpent, uint256 bncbBalanceAfter,\r\n        uint256 minOut, uint256 deadline, address indexed token);\r\n    event RewardTriggerScheduled(uint256 indexed requestId, uint256 executeAfter, uint256 fee);\r\n    event RewardSchedulingDeferred(uint256 executeAfter);\r\n    event RewardRoundStartDeferred(uint256 nextAttemptAt);\r\n    event RewardTriggerWaitingForCompletion(uint256 indexed requestId, uint256 indexed roundId);\r\n    event EmergencyWithdrawNative(address indexed to, uint256 amount);\r\n    event EmergencyWithdrawQuote(address indexed to, uint256 amount);\r\n    event AutoBuyConfigured(uint16 slippageBps);\r\n    event AutoBuyStopped();\r\n    event BuyTriggerScheduled(uint256 indexed requestId, uint256 executeAfter, uint256 fee);\r\n    event BuySchedulingDeferred(uint256 executeAfter);\r\n    event BuyExecutionDeferred(uint256 indexed requestId, uint256 retryAfter);\r\n\r\n    constructor() { _disableInitializers(); }\r\n\r\n    modifier onlyInitialized() { require(initialized, \"NOT_INIT\"); _; }\r\n    modifier onlyGuardian() { require(msg.sender == _getGuardian(), \"ONLY_GUARDIAN\"); _; }\r\n    modifier onlyOwnerOrGuardian() { require(msg.sender == owner || msg.sender == _getGuardian(), \"AUTH\"); _; }\r\n    modifier onlyOperatorOrGuardian() { require(isOperator(msg.sender), \"NOT_OPERATOR\"); _; }\r\n    modifier nonReentrant() {\r\n        require(locked == 0, \"REENTRANT\");\r\n        if (rewardsContract != address(0)) require(!AIDROPRewards(payable(rewardsContract)).isBusy(), \"REWARDS_BUSY\");\r\n        locked = 1; _; locked = 0;\r\n    }\r\n\r\n    function initialize(address taxToken_, address owner_, address operator_, address rewards_)\r\n        external initializer {\r\n        require(block.chainid == 56, \"BSC_MAINNET_ONLY\");\r\n        require(taxToken_ != address(0) && taxToken_ != WBNB && taxToken_ != BNCB, \"TOKEN\");\r\n        require(owner_ != address(0), \"OWNER\");\r\n        require(operator_ != address(0) && operator_ != address(this) && operator_.code.length == 0, \"OPERATOR_EOA\");\r\n        require(rewards_.code.length > 0 && rewards_ != address(this), \"REWARDS\");\r\n        initialized = true; taxToken = taxToken_; owner = owner_; feeReceiver = operator_;\r\n        rewardsContract = rewards_;\r\n        operators[owner_] = true; operators[operator_] = true;\r\n        emit OwnershipTransferred(address(0), owner_);\r\n        emit OperatorUpdated(owner_, true);\r\n        if (owner_ != operator_) emit OperatorUpdated(operator_, true);\r\n        emit Initialized(taxToken_, owner_, operator_, operator_, 0, taxToken_);\r\n    }\r\n\r\n    function vaultQuoteToken() public pure override returns (address) { return BNCB; }\r\n    function guardian() external view returns (address) { return _getGuardian(); }\r\n    function isBusy() external view returns (bool) { return locked != 0; }\r\n    function isOperator(address account) public view returns (bool) {\r\n        return account == owner || account == _getGuardian() || operators[account];\r\n    }\r\n\r\n    receive() external payable {\r\n        // WBNB.withdraw uses a 2300-gas native transfer; keep this as the first branch.\r\n        if (msg.sender == WBNB) return;\r\n        require(initialized, \"NOT_INIT\");\r\n        // Native deposits exclusively fund Trigger requests and are never charged commission.\r\n        if (msg.value != 0) {\r\n            triggerFeeFunds = address(this).balance;\r\n            emit NativeFundingReceived(msg.sender, msg.value);\r\n            emit TriggerFeeFunded(msg.sender, msg.value);\r\n        }\r\n    }\r\n\r\n    function _syncBNCB() private {\r\n        if (!taxRateResolved) {\r\n            if (taxToken.code.length == 0) return;\r\n            // A successful external call can still return malformed ABI outside a\r\n            // Solidity try/catch. Copy exactly one word, check its original size,\r\n            // and never allocate/copy attacker-controlled returndata.\r\n            address target = taxToken;\r\n            bytes4 selector = IAIDROPTaxRate.taxRate.selector;\r\n            bool ok;\r\n            uint256 size;\r\n            uint256 rate;\r\n            assembly (\"memory-safe\") {\r\n                let ptr := mload(0x40)\r\n                mstore(ptr, selector)\r\n                ok := staticcall(TAX_RATE_GAS_LIMIT, target, ptr, 4, ptr, 32)\r\n                size := returndatasize()\r\n                rate := mload(ptr)\r\n            }\r\n            if (!ok || size != 32 || rate == 0 || rate > 10_000) return;\r\n            taxRateBps = rate;\r\n            taxRateResolved = true;\r\n            emit TaxRateResolved(rate);\r\n        }\r\n        uint256 balance = IERC20(BNCB).balanceOf(address(this));\r\n        if (balance <= accountedBNCB) return;\r\n        uint256 increase = balance - accountedBNCB;\r\n        accountedBNCB = balance;\r\n        uint256 fee = commissionFor(increase);\r\n        pendingCommission += fee;\r\n        emit BNCBRevenueRecognized(increase, balance);\r\n        emit CommissionAccrued(increase, fee, pendingCommission);\r\n    }\r\n    function syncRevenue() external onlyInitialized nonReentrant {\r\n        _syncBNCB();\r\n        _tryTopUpTriggerFee();\r\n    }\r\n\r\n    function commissionFor(uint256 amount) public view returns (uint256) {\r\n        require(taxRateResolved, \"TAX_RATE_UNRESOLVED\");\r\n        if (taxRateBps <= 100) return Math.mulDiv(amount, 600, 10_000);\r\n        return Math.mulDiv(amount, 6, taxRateBps);\r\n    }\r\n\r\n    /// @dev A reverting self-call rolls back even a non-standard token that moves funds\r\n    ///      and then returns false. Catching it keeps the buy live and the debt intact.\r\n    function payCommissionFromSelf(uint256 amount) external {\r\n        require(msg.sender == address(this) && locked == 1, \"ONLY_SELF\");\r\n        uint256 beforeBalance = IERC20(BNCB).balanceOf(address(this));\r\n        IERC20(BNCB).safeTransfer(feeReceiver, amount);\r\n        require(beforeBalance - IERC20(BNCB).balanceOf(address(this)) == amount, \"COMMISSION_AMOUNT\");\r\n    }\r\n\r\n    function _tryPayCommission() private {\r\n        uint256 amount = pendingCommission;\r\n        if (amount == 0) return;\r\n        pendingCommission = 0;\r\n        accountedBNCB -= amount;\r\n        try this.payCommissionFromSelf{gas: PAYOUT_CALL_GAS}(amount) {\r\n            commissionPaymentFailed = false;\r\n            emit CommissionPaid(feeReceiver, amount);\r\n        } catch {\r\n            pendingCommission = amount;\r\n            accountedBNCB += amount;\r\n            commissionPaymentFailed = true;\r\n            emit CommissionDeferred(feeReceiver, amount, amount);\r\n        }\r\n    }\r\n\r\n    function claimCommission() external onlyInitialized nonReentrant {\r\n        require(msg.sender == feeReceiver, \"ONLY_FEE_RECEIVER\");\r\n        require(pendingCommission > 0, \"NO_PENDING_COMMISSION\");\r\n        require(pendingCommission >= COMMISSION_PAYOUT_THRESHOLD || commissionPaymentFailed, \"BELOW_PAYOUT_THRESHOLD\");\r\n        _tryPayCommission();\r\n    }\r\n\r\n    function availableBNB() public view returns (uint256) {\r\n        return address(this).balance;\r\n    }\r\n\r\n    /// @notice Excludes both recorded commission and the commission on unrecognized income.\r\n    function availableBNCB() public view returns (uint256) {\r\n        if (!taxRateResolved) return 0;\r\n        uint256 balance = IERC20(BNCB).balanceOf(address(this));\r\n        uint256 reserved = pendingCommission;\r\n        if (balance > accountedBNCB) reserved += commissionFor(balance - accountedBNCB);\r\n        return balance > reserved ? balance - reserved : 0;\r\n    }\r\n\r\n    /// @notice Buffer plus one live scheduling fee for each of the two chains.\r\n    function requiredTriggerBalance() public view returns (uint256) {\r\n        return TRIGGER_FEE_BUFFER + IFlapTriggerService(TRIGGER_SERVICE).getFee{gas: FEE_QUERY_GAS_LIMIT}() * 2;\r\n    }\r\n\r\n    function triggerFeeBufferBalance() external view returns (uint256) {\r\n        return Math.min(address(this).balance, TRIGGER_FEE_BUFFER);\r\n    }\r\n    function triggerFeeOperatingBalance() external view returns (uint256) {\r\n        uint256 balance = address(this).balance;\r\n        return balance > TRIGGER_FEE_BUFFER ? balance - TRIGGER_FEE_BUFFER : 0;\r\n    }\r\n\r\n    /// @notice Size only the deficit, capped at 10 BNCB and available non-commission revenue.\r\n    /// @dev Spot quote plus 5% is sizing only, not a guaranteed asynchronous fill price.\r\n    function getTriggerFundingQuote() public view returns (uint256 targetBNB, uint256 shortfallBNB, uint256 bncbAmount) {\r\n        targetBNB = requiredTriggerBalance();\r\n        if (address(this).balance >= targetBNB) return (targetBNB, 0, 0);\r\n        shortfallBNB = targetBNB - address(this).balance;\r\n        uint256 free = availableBNCB();\r\n        if (free == 0) return (targetBNB, shortfallBNB, 0);\r\n        uint256 estimate = AIDROPRewards(payable(rewardsContract)).quoteBNCBForBNB{gas: FUNDING_QUOTE_GAS_LIMIT}(shortfallBNB);\r\n        bncbAmount = Math.min(Math.mulDiv(estimate, 10_000 + FUNDING_QUOTE_BUFFER_BPS, 10_000, Math.Rounding.Up),\r\n            Math.min(MAX_FUNDING_BNCB, free));\r\n        if (bncbAmount < MIN_FUNDING_BNCB) bncbAmount = 0;\r\n    }\r\n\r\n    /// @notice Permissionless, best-effort funding; an in-flight order is a harmless no-op.\r\n    function convertBNCBRevenue() external onlyInitialized nonReentrant returns (bool) {\r\n        if (conversionPending) return false;\r\n        // Unlike an optional callback top-up, a direct request propagates failures.\r\n        // Otherwise gas estimation may select a cheap caught-out-of-gas no-op.\r\n        return this.requestFeeTopUpFromSelf();\r\n    }\r\n\r\n    function _tryTopUpTriggerFee() private returns (bool submitted) {\r\n        if (conversionPending) return false;\r\n        try this.requestFeeTopUpFromSelf{gas: FUNDING_REQUEST_GAS_LIMIT}() returns (bool result) {\r\n            return result;\r\n        } catch { emit TriggerFundingDeferred(); }\r\n    }\r\n\r\n    /// @dev Isolated call rolls back sync, input transfer, allowance and order state on failure.\r\n    function requestFeeTopUpFromSelf() external returns (bool) {\r\n        require(msg.sender == address(this) && locked == 1, \"ONLY_SELF\");\r\n        _syncBNCB();\r\n        (,, uint256 amount) = getTriggerFundingQuote();\r\n        if (amount == 0) return false;\r\n        accountedBNCB -= amount;\r\n        conversionPending = true;\r\n        pendingConversionAmount = amount;\r\n        IERC20(BNCB).forceApprove(VAULT_EX, amount);\r\n        pendingConversionId = IFlapVaultEx(VAULT_EX).requestSwap(IFlapVaultEx.ExactInputParams(BNCB, WBNB, amount));\r\n        IERC20(BNCB).forceApprove(VAULT_EX, 0);\r\n        emit ConversionRequested(pendingConversionId, amount);\r\n        return true;\r\n    }\r\n\r\n    /// @notice Deposit succeeds even when either best-effort rescheduling attempt fails.\r\n    function fundTriggerFee() external payable onlyInitialized nonReentrant {\r\n        // Ensure estimators cannot silently select two underfunded, caught self-calls.\r\n        // Unused gas is not charged; zero-value calls may resume after VaultEx settles.\r\n        require(gasleft() >= 1_000_000, \"RESUME_GAS\");\r\n        triggerFeeFunds = address(this).balance;\r\n        emit TriggerFeeFunded(msg.sender, msg.value);\r\n        try this.resumeFeeChainFromSelf{gas: 450_000}(false) {} catch {}\r\n        try this.resumeFeeChainFromSelf{gas: 450_000}(true) {} catch {}\r\n    }\r\n\r\n    function resumeFeeChainFromSelf(bool buy) external {\r\n        require(msg.sender == address(this) && locked == 1, \"ONLY_SELF\");\r\n        if (buy) { _syncBNCB(); _tryScheduleBuy(); }\r\n        else _tryScheduleRound();\r\n    }\r\n\r\n    function _recordTriggerFee(uint256 fee) private {\r\n        // Called after payment: anything beyond the operating portion is buffer use.\r\n        uint256 afterBalance = address(this).balance;\r\n        uint256 beforeBalance = afterBalance + fee;\r\n        uint256 operating = beforeBalance > TRIGGER_FEE_BUFFER ? beforeBalance - TRIGGER_FEE_BUFFER : 0;\r\n        triggerFeeFunds = afterBalance;\r\n        emit TriggerFeeSpent(fee, fee > operating ? fee - operating : 0);\r\n    }\r\n\r\n    /// @notice Guardian releases the current local wait so another conversion can be submitted.\r\n    /// @dev Does NOT cancel the VaultEx order or return its custodied input. Keep the request\r\n    ///      tracked until its eventual callback; cancellation neither refunds nor charges commission.\r\n    function cancelConversion() external onlyInitialized onlyGuardian nonReentrant {\r\n        require(conversionPending, \"NO_PENDING_CONVERSION\");\r\n        uint256 id = pendingConversionId;\r\n        uint256 amount = pendingConversionAmount;\r\n        detachedConversionAmounts[id] = amount;\r\n        conversionPending = false;\r\n        delete pendingConversionId;\r\n        delete pendingConversionAmount;\r\n        emit ConversionWaitCancelled(id, amount);\r\n    }\r\n\r\n    function onFlapVaultExSettled(uint256 requestId, uint8 status, address token, uint256 amount)\r\n        external onlyInitialized nonReentrant {\r\n        require(msg.sender == VAULT_EX, \"ONLY_VAULT_EX\");\r\n        bool isCurrent = conversionPending && requestId == pendingConversionId;\r\n        uint256 spent = isCurrent ? pendingConversionAmount : detachedConversionAmounts[requestId];\r\n        require(spent > 0, \"UNKNOWN_CONVERSION\");\r\n        require((status == 2 && token == WBNB && amount > 0) || (status == 3 && token == BNCB), \"BAD_SETTLEMENT\");\r\n        if (isCurrent) {\r\n            conversionPending = false;\r\n            delete pendingConversionId;\r\n            delete pendingConversionAmount;\r\n        } else {\r\n            delete detachedConversionAmounts[requestId];\r\n        }\r\n        if (status == 2) {\r\n            uint256 beforeBalance = address(this).balance;\r\n            IAIDROPWBNB(WBNB).withdraw(amount);\r\n            require(address(this).balance - beforeBalance == amount, \"UNWRAP_AMOUNT\");\r\n            triggerFeeFunds = address(this).balance;\r\n            emit TriggerFeeFunded(VAULT_EX, amount);\r\n            emit BNCBConverted(spent, amount);\r\n        } else {\r\n            // Returned input has already been assessed before submission. Recognize only\r\n            // unrelated new deposits; a protocol refund must never be taxed a second time.\r\n            uint256 balance = IERC20(BNCB).balanceOf(address(this));\r\n            require(amount <= spent && balance >= accountedBNCB + amount, \"REFUND_AMOUNT\");\r\n            accountedBNCB += amount;\r\n            _syncBNCB();\r\n            emit ConversionRefunded(requestId, amount);\r\n        }\r\n    }\r\n\r\n    function _performBuy(uint256 minTokenOut, uint256 deadline) private {\r\n        require(minTokenOut > 0, \"MIN_TOKEN_ZERO\");\r\n        require(deadline >= block.timestamp, \"DEADLINE\");\r\n        require(lastBuyAt == 0 || block.timestamp >= lastBuyAt + MIN_BUY_INTERVAL, \"BUY_COOLDOWN\");\r\n        _syncBNCB();\r\n        require(availableBNCB() >= BUY_BNCB, \"INSUFFICIENT_BUY_BNCB\");\r\n        if (pendingCommission >= COMMISSION_PAYOUT_THRESHOLD) _tryPayCommission();\r\n        lastBuyAt = block.timestamp;\r\n        accountedBNCB -= BUY_BNCB;\r\n        IERC20(BNCB).forceApprove(rewardsContract, BUY_BNCB);\r\n        AIDROPRewards(payable(rewardsContract)).buyFromVault(minTokenOut, deadline);\r\n        IERC20(BNCB).forceApprove(rewardsContract, 0);\r\n        emit TriggeredBuyHold(msg.sender, BUY_BNCB, IERC20(BNCB).balanceOf(address(this)), minTokenOut, deadline, taxToken);\r\n    }\r\n\r\n    /// @notice Only Guardian may enable/update automatic buys and their price limits.\r\n    /// @dev Returns false if enabled but not yet funded/quotable; anyone may resume scheduling later.\r\n    function startAutoBuy(uint16 slippageBps)\r\n        external onlyInitialized onlyGuardian nonReentrant returns (bool scheduled) {\r\n        require(slippageBps <= MAX_BUY_SLIPPAGE_BPS, \"SLIPPAGE_TOO_HIGH\");\r\n        autoBuyEnabled = true;\r\n        autoBuySlippageBps = slippageBps;\r\n        _syncBNCB();\r\n        emit AutoBuyConfigured(slippageBps);\r\n        _tryTopUpTriggerFee();\r\n        return _tryScheduleBuy();\r\n    }\r\n\r\n    function stopAutoBuy() external onlyInitialized onlyGuardian nonReentrant {\r\n        autoBuyEnabled = false;\r\n        emit AutoBuyStopped();\r\n    }\r\n\r\n    function nextBuyAt() public view returns (uint256 due) {\r\n        due = lastBuyAt == 0 ? block.timestamp : lastBuyAt + MIN_BUY_INTERVAL;\r\n        if (due < buyRetryAt) due = buyRetryAt;\r\n        if (due < block.timestamp) due = block.timestamp;\r\n    }\r\n\r\n    function scheduleBuyback() external onlyInitialized nonReentrant {\r\n        require(autoBuyEnabled, \"AUTO_BUY_DISABLED\");\r\n        require(!hasPendingBuyTrigger, \"BUY_TRIGGER_PENDING\");\r\n        _syncBNCB();\r\n        _tryTopUpTriggerFee();\r\n        _tryScheduleBuy();\r\n    }\r\n\r\n    function _tryScheduleBuy() private returns (bool) {\r\n        if (!autoBuyEnabled) return false;\r\n        if (hasPendingBuyTrigger) return true;\r\n        uint256 due = nextBuyAt();\r\n        if (due > type(uint64).max || availableBNCB() < BUY_BNCB) {\r\n            emit BuySchedulingDeferred(due); return false;\r\n        }\r\n        try AIDROPRewards(payable(rewardsContract)).quoteBuy{gas: BUY_QUOTE_GAS_LIMIT}(autoBuySlippageBps)\r\n            returns (uint256, uint256) {\r\n        } catch { emit BuySchedulingDeferred(due); return false; }\r\n        try IFlapTriggerService(TRIGGER_SERVICE).getFee{gas: FEE_QUERY_GAS_LIMIT}() returns (uint256 fee) {\r\n            if (availableBNB() < fee) { emit BuySchedulingDeferred(due); return false; }\r\n            try IFlapTriggerService(TRIGGER_SERVICE).requestTrigger{value: fee, gas: TRIGGER_REQUEST_GAS_LIMIT}(uint64(due))\r\n                returns (uint256 id) {\r\n                _recordTriggerFee(fee);\r\n                hasPendingBuyTrigger = true;\r\n                pendingBuyTriggerId = id;\r\n                pendingBuyTriggerAt = due;\r\n                emit BuyTriggerScheduled(id, due, fee);\r\n                return true;\r\n            } catch { emit BuySchedulingDeferred(due); return false; }\r\n        } catch { emit BuySchedulingDeferred(due); return false; }\r\n    }\r\n\r\n    /// @dev Isolated self-call: a failed swap rolls back commission and approvals, but the\r\n    ///      outer callback consumes the appointment and leaves a permissionless recovery path.\r\n    function executeBuyFromTrigger() external {\r\n        require(msg.sender == address(this) && locked == 1, \"ONLY_SELF\");\r\n        (, uint256 minOut) = AIDROPRewards(payable(rewardsContract)).quoteBuy{gas: BUY_QUOTE_GAS_LIMIT}(autoBuySlippageBps);\r\n        _performBuy(minOut, block.timestamp + BUY_DEADLINE_SECONDS);\r\n    }\r\n\r\n    function _handleBuyTrigger(uint256 requestId) private {\r\n        require(block.timestamp >= pendingBuyTriggerAt, \"BUY_TRIGGER_TOO_EARLY\");\r\n        hasPendingBuyTrigger = false;\r\n        delete pendingBuyTriggerId;\r\n        delete pendingBuyTriggerAt;\r\n        if (!autoBuyEnabled) return;\r\n        _tryTopUpTriggerFee();\r\n        if (lastBuyAt != 0 && block.timestamp < lastBuyAt + MIN_BUY_INTERVAL) {\r\n            _tryScheduleBuy(); return;\r\n        }\r\n        try this.executeBuyFromTrigger{gas: BUY_EXECUTION_GAS_LIMIT}() {\r\n            buyRetryAt = 0;\r\n            _tryScheduleBuy();\r\n        } catch {\r\n            buyRetryAt = block.timestamp + MIN_BUY_INTERVAL;\r\n            emit BuyExecutionDeferred(requestId, buyRetryAt);\r\n            _tryScheduleBuy();\r\n        }\r\n    }\r\n\r\n    function setOperator(address account, bool enabled) external onlyInitialized onlyGuardian nonReentrant {\r\n        require(account != address(0), \"OPERATOR\");\r\n        operators[account] = enabled;\r\n        emit OperatorUpdated(account, enabled);\r\n    }\r\n    function transferOwnership(address newOwner) external onlyInitialized onlyOwnerOrGuardian nonReentrant {\r\n        require(newOwner != address(0), \"OWNER\");\r\n        address previous = owner;\r\n        operators[previous] = false;\r\n        emit OperatorUpdated(previous, false);\r\n        emit OwnershipTransferred(previous, newOwner);\r\n        owner = newOwner;\r\n    }\r\n\r\n    function registerForRewards() external onlyInitialized nonReentrant {\r\n        AIDROPRewards(payable(rewardsContract)).registerFromVault(msg.sender);\r\n    }\r\n\r\n    /// @notice Start/resume scheduling. First request targets the next Beijing midnight.\r\n    ///         This only requests a callback; it never starts a reward round itself.\r\n    function scheduleRewardRound() external onlyInitialized nonReentrant {\r\n        require(!hasPendingTrigger, \"TRIGGER_PENDING\");\r\n        require(_tryScheduleRound(), \"SCHEDULE_NOT_READY\");\r\n    }\r\n\r\n    /// @notice The only entry that can start a reward round; validates both service and request ID.\r\n    function trigger(uint256 requestId) external onlyInitialized nonReentrant {\r\n        require(msg.sender == TRIGGER_SERVICE, \"ONLY_TRIGGER_SERVICE\");\r\n        if (hasPendingBuyTrigger && requestId == pendingBuyTriggerId) {\r\n            _tryUpdateRewardOracle();\r\n            _handleBuyTrigger(requestId);\r\n            return;\r\n        }\r\n        require(hasPendingTrigger && requestId == pendingTriggerId, \"UNKNOWN_TRIGGER\");\r\n        require(block.timestamp >= pendingTriggerAt, \"TRIGGER_TOO_EARLY\");\r\n        _tryUpdateRewardOracle();\r\n        _tryTopUpTriggerFee();\r\n        hasPendingTrigger = false;\r\n        delete pendingTriggerId;\r\n        delete pendingTriggerAt;\r\n        AIDROPRewards rewards = AIDROPRewards(payable(rewardsContract));\r\n        if (!rewards.isRoundComplete()) {\r\n            emit RewardTriggerWaitingForCompletion(requestId, rewards.currentRewardRound());\r\n            // Completing the final batch will schedule another callback without another 24h wait.\r\n            return;\r\n        }\r\n        bool started;\r\n        // Bounded external work leaves gas for recording a retry and scheduling it.\r\n        try rewards.startRoundFromVault{gas: ROUND_START_GAS_LIMIT}(requestId) returns (bool result) {\r\n            started = result;\r\n        } catch { }\r\n        if (started) {\r\n            rewardStartRetryAt = 0;\r\n        } else {\r\n            uint256 retryAt = block.timestamp + REWARD_RETRY_INTERVAL;\r\n            try rewards.rewardOracleRetryAt{gas: 100_000}() returns (uint256 readyAt) {\r\n                if (readyAt > block.timestamp && readyAt < retryAt) retryAt = readyAt;\r\n            } catch {}\r\n            rewardStartRetryAt = retryAt;\r\n            emit RewardRoundStartDeferred(retryAt);\r\n        }\r\n        // Funding or service failure must not roll back this round or regenerate its seed.\r\n        _tryScheduleRound();\r\n    }\r\n\r\n    function _tryUpdateRewardOracle() private {\r\n        try AIDROPRewards(payable(rewardsContract)).updateRewardOracle{gas: 160_000}() {} catch {}\r\n    }\r\n\r\n    function _tryScheduleRound() private returns (bool scheduled) {\r\n        if (hasPendingTrigger) return true;\r\n        AIDROPRewards rewards = AIDROPRewards(payable(rewardsContract));\r\n        uint256 due = nextRewardRoundAt();\r\n        if (rewards.currentRewardRound() == 0 && due == 0) {\r\n            due = ((block.timestamp + 8 hours) / 1 days + 1) * 1 days - 8 hours;\r\n        } else if (due <= block.timestamp) {\r\n            if (!rewards.isRoundComplete()) return false;\r\n            due = block.timestamp;\r\n        }\r\n        if (due > type(uint64).max) { emit RewardSchedulingDeferred(due); return false; }\r\n        try IFlapTriggerService(TRIGGER_SERVICE).getFee{gas: FEE_QUERY_GAS_LIMIT}() returns (uint256 fee) {\r\n            if (availableBNB() < fee) { emit RewardSchedulingDeferred(due); return false; }\r\n            try IFlapTriggerService(TRIGGER_SERVICE).requestTrigger{value: fee, gas: TRIGGER_REQUEST_GAS_LIMIT}(uint64(due)) returns (uint256 id) {\r\n                _recordTriggerFee(fee);\r\n                hasPendingTrigger = true; pendingTriggerId = id; pendingTriggerAt = due;\r\n                emit RewardTriggerScheduled(id, due, fee);\r\n                return true;\r\n            } catch { emit RewardSchedulingDeferred(due); return false; }\r\n        } catch { emit RewardSchedulingDeferred(due); return false; }\r\n    }\r\n\r\n    /// @notice Permissionless progress; the caller cannot choose recipients, amount or seed.\r\n    function batchDistributeRewards(uint256 count) external onlyInitialized nonReentrant {\r\n        AIDROPRewards rewards = AIDROPRewards(payable(rewardsContract));\r\n        rewards.distributeFromVault(count);\r\n        if (rewards.isRoundComplete()) _tryScheduleRound();\r\n    }\r\n\r\n    function forwardRewardTokens() external onlyInitialized onlyOperatorOrGuardian nonReentrant {\r\n        uint256 amount = IERC20(taxToken).balanceOf(address(this));\r\n        require(amount > 0, \"NO_TOKEN\");\r\n        IERC20(taxToken).safeTransfer(rewardsContract, amount);\r\n    }\r\n\r\n    function emergencyWithdrawNative(address to) external onlyInitialized onlyGuardian nonReentrant {\r\n        require(to != address(0) && to != address(this), \"RECIPIENT\");\r\n        uint256 amount = availableBNB();\r\n        require(amount > 0, \"NO_AVAILABLE_BNB\");\r\n        (bool ok,) = payable(to).call{value: amount}(\"\");\r\n        require(ok, \"RESCUE_SEND\");\r\n        triggerFeeFunds = address(this).balance;\r\n        emit EmergencyWithdrawNative(to, amount);\r\n    }\r\n    function emergencyWithdrawQuote(address to) external onlyInitialized onlyGuardian nonReentrant {\r\n        require(to != address(0) && to != address(this), \"RECIPIENT\");\r\n        _syncBNCB();\r\n        uint256 amount = availableBNCB();\r\n        require(amount > 0, \"NO_BNCB\");\r\n        accountedBNCB -= amount;\r\n        IERC20(BNCB).safeTransfer(to, amount);\r\n        emit EmergencyWithdrawQuote(to, amount);\r\n    }\r\n\r\n    // Views preserve the existing user-facing registration UI's contract boundary.\r\n    function eligibilityNFT() external view returns (address) { return AIDROPRewards(payable(rewardsContract)).eligibilityNFT(); }\r\n    function registrationNFT() external view returns (address) { return AIDROPRewards(payable(rewardsContract)).registrationNFT(); }\r\n    function eligibleCount() external view returns (uint256) { return AIDROPRewards(payable(rewardsContract)).eligibleCount(); }\r\n    function hasWonReward(address user) external view returns (bool) { return AIDROPRewards(payable(rewardsContract)).hasWonReward(user); }\r\n    function registeredCount() external view returns (uint256) { return AIDROPRewards(payable(rewardsContract)).registeredCount(); }\r\n    function isRegistered(address user) external view returns (bool) { return AIDROPRewards(payable(rewardsContract)).isRegistered(user); }\r\n    function registeredUserAt(uint256 index) external view returns (address) { return AIDROPRewards(payable(rewardsContract)).registeredUserAt(index); }\r\n    function getRegisteredUsers(uint256 offset, uint256 limit) external view returns (address[] memory) { return AIDROPRewards(payable(rewardsContract)).getRegisteredUsers(offset, limit); }\r\n    function getRewardQuote() external view returns (uint256 amountEach, uint256 bncbWeiEach) { return AIDROPRewards(payable(rewardsContract)).getRewardQuote(); }\r\n    function currentRewardRound() external view returns (uint256) { return AIDROPRewards(payable(rewardsContract)).currentRewardRound(); }\r\n    function reservedRewardTokens() external view returns (uint256) { return AIDROPRewards(payable(rewardsContract)).reservedRewardTokens(); }\r\n    function rewardRounds(uint256 id) external view returns (uint256, uint256, uint256, uint256, uint256, bytes32) { return AIDROPRewards(payable(rewardsContract)).rewardRounds(id); }\r\n    function nextRewardRoundAt() public view returns (uint256) {\r\n        uint256 due = AIDROPRewards(payable(rewardsContract)).nextRewardRoundAt();\r\n        return rewardStartRetryAt > due ? rewardStartRetryAt : due;\r\n    }\r\n    function rewardPaid(uint256 id, address user) external view returns (bool) { return AIDROPRewards(payable(rewardsContract)).rewardPaid(id, user); }\r\n    function rewardWinners(uint256 id, uint256 index) external view returns (address) { return AIDROPRewards(payable(rewardsContract)).rewardWinners(id, index); }\r\n    function REWARD_USDT() external view returns (address) { return AIDROPRewards(payable(rewardsContract)).REWARD_USDT(); }\r\n    function BNCB_USDT_POOL() external view returns (address) { return AIDROPRewards(payable(rewardsContract)).BNCB_USDT_POOL(); }\r\n    function getBNBBalance() external view returns (uint256) { return address(this).balance; }\r\n    function getBNCBBalance() external view returns (uint256) { return IERC20(BNCB).balanceOf(address(this)); }\r\n    function getBuyTokenBalance() external view returns (uint256) { return taxToken.code.length == 0 ? 0 : IERC20(taxToken).balanceOf(rewardsContract); }\r\n    function getVaultTokenBalance() external view returns (uint256) { return taxToken.code.length == 0 ? 0 : IERC20(taxToken).balanceOf(address(this)); }\r\n    function buyToken() external view returns (address) { return taxToken; }\r\n    function getVaultStats() external view returns (address, address, address, uint256, uint256, address, uint256) { return (owner, taxToken, taxToken, IERC20(BNCB).balanceOf(address(this)), BUY_BNCB, feeReceiver, taxRateBps); }\r\n    /// @notice Public disclosure of commission calculation, recipient and payout rules.\r\n    /// @dev The actual configured commission parameter and immutable recipient are read\r\n    ///      from this vault, rather than relying only on the factory's creation schema.\r\n    function description() public view override returns (string memory) {\r\n        return AIDROPRewards(payable(rewardsContract)).commissionDescription(taxRateBps, taxRateResolved, feeReceiver, pendingCommission);\r\n    }\r\n\r\n    function _commissionDescription() private pure returns (string memory) {\r\n        return \"drop.ai: Commission on new BNCB revenue R: if cached tax rate t <= 100 bps, floor(R*600/10000); otherwise floor(R*6/t). Cache the first valid tax rate; hold income until resolved. feeReceiver is the initial operator and never changes with role removal. At 20 BNCB, attempt full payment during a buy; failed payments remain owed. Only feeReceiver may claim at the threshold or after a failed payment. Reserved commission cannot be spent. No extra commission on native deposits, conversion proceeds or returned assessed principal. See description() for live values.\";\r\n    }\r\n    function vaultUISchema() public pure override returns (VaultUISchema memory schema) {\r\n        schema.vaultType = \"drop.ai\";\r\n        schema.description = _commissionDescription();\r\n        schema.methods = new VaultMethodSchema[](0);\r\n    }\r\n}\r\n"},"@openzeppelin/contracts@4.9.6/token/ERC20/IERC20.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\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 amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` 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 amount) 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 `amount` as the allowance of `spender` over the 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 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `from` to `to` using the\n     * allowance mechanism. `amount` 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 amount) external returns (bool);\n}\n"},"@openzeppelin/contracts@4.9.6/token/ERC20/utils/SafeERC20.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../extensions/IERC20Permit.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 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    using Address for address;\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        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\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        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n    }\n\n    /**\n     * @dev Deprecated. This function has issues similar to the ones found in\n     * {IERC20-approve}, and its usage is discouraged.\n     *\n     * Whenever possible, use {safeIncreaseAllowance} and\n     * {safeDecreaseAllowance} instead.\n     */\n    function safeApprove(IERC20 token, address spender, uint256 value) internal {\n        // safeApprove should only be called when setting an initial allowance,\n        // or when resetting it to zero. To increase and decrease it, use\n        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n        require(\n            (value == 0) || (token.allowance(address(this), spender) == 0),\n            \"SafeERC20: approve from non-zero to non-zero allowance\"\n        );\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\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    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        uint256 oldAllowance = token.allowance(address(this), spender);\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));\n    }\n\n    /**\n     * @dev Decrease 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    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        unchecked {\n            uint256 oldAllowance = token.allowance(address(this), spender);\n            require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));\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    function forceApprove(IERC20 token, address spender, uint256 value) internal {\n        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);\n\n        if (!_callOptionalReturnBool(token, approvalCall)) {\n            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));\n            _callOptionalReturn(token, approvalCall);\n        }\n    }\n\n    /**\n     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.\n     * Revert on invalid signature.\n     */\n    function safePermit(\n        IERC20Permit token,\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) internal {\n        uint256 nonceBefore = token.nonces(owner);\n        token.permit(owner, spender, value, deadline, v, r, s);\n        uint256 nonceAfter = token.nonces(owner);\n        require(nonceAfter == nonceBefore + 1, \"SafeERC20: permit did not succeed\");\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     */\n    function _callOptionalReturn(IERC20 token, bytes memory data) private {\n        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\n        // the target address contains contract code and also asserts for success in the low-level call.\n\n        bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n        require(returndata.length == 0 || abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     *\n     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.\n     */\n    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {\n        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false\n        // and not revert is the subcall reverts.\n\n        (bool success, bytes memory returndata) = address(token).call(data);\n        return\n            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));\n    }\n}\n"},"@openzeppelin/contracts@4.9.6/token/ERC20/extensions/IERC20Permit.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n *\n * ==== Security Considerations\n *\n * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature\n * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be\n * considered as an intention to spend the allowance in any specific way. The second is that because permits have\n * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should\n * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be\n * generally recommended is:\n *\n * ```solidity\n * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {\n *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}\n *     doThing(..., value);\n * }\n *\n * function doThing(..., uint256 value) public {\n *     token.safeTransferFrom(msg.sender, address(this), value);\n *     ...\n * }\n * ```\n *\n * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of\n * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also\n * {SafeERC20-safeTransferFrom}).\n *\n * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so\n * contracts should have entry points that don't rely on permit.\n */\ninterface IERC20Permit {\n    /**\n     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n     * given ``owner``'s signed approval.\n     *\n     * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n     * ordering also apply here.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     * - `deadline` must be a timestamp in the future.\n     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n     * over the EIP712-formatted function arguments.\n     * - the signature must use ``owner``'s current nonce (see {nonces}).\n     *\n     * For more information on the signature format, see the\n     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n     * section].\n     *\n     * CAUTION: See Security Considerations above.\n     */\n    function permit(\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n\n    /**\n     * @dev Returns the current nonce for `owner`. This value must be\n     * included whenever a signature is generated for {permit}.\n     *\n     * Every successful call to {permit} increases ``owner``'s nonce by one. This\n     * prevents a signature from being used multiple times.\n     */\n    function nonces(address owner) external view returns (uint256);\n\n    /**\n     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n     */\n    // solhint-disable-next-line func-name-mixedcase\n    function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n"},"@openzeppelin/contracts@4.9.6/utils/Address.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n    /**\n     * @dev Returns true if `account` is a contract.\n     *\n     * [IMPORTANT]\n     * ====\n     * It is unsafe to assume that an address for which this function returns\n     * false is an externally-owned account (EOA) and not a contract.\n     *\n     * Among others, `isContract` will return false for the following\n     * types of addresses:\n     *\n     *  - an externally-owned account\n     *  - a contract in construction\n     *  - an address where a contract will be created\n     *  - an address where a contract lived, but was destroyed\n     *\n     * Furthermore, `isContract` will also return true if the target contract within\n     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,\n     * which only has an effect at the end of a transaction.\n     * ====\n     *\n     * [IMPORTANT]\n     * ====\n     * You shouldn't rely on `isContract` to protect against flash loan attacks!\n     *\n     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n     * constructor.\n     * ====\n     */\n    function isContract(address account) internal view returns (bool) {\n        // This method relies on extcodesize/address.code.length, which returns 0\n        // for contracts in construction, since the code is only stored at the end\n        // of the constructor execution.\n\n        return account.code.length > 0;\n    }\n\n    /**\n     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n     * `recipient`, forwarding all available gas and reverting on errors.\n     *\n     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n     * of certain opcodes, possibly making contracts go over the 2300 gas limit\n     * imposed by `transfer`, making them unable to receive funds via\n     * `transfer`. {sendValue} removes this limitation.\n     *\n     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n     *\n     * IMPORTANT: because control is transferred to `recipient`, care must be\n     * taken to not create reentrancy vulnerabilities. Consider using\n     * {ReentrancyGuard} or the\n     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n        (bool success, ) = recipient.call{value: amount}(\"\");\n        require(success, \"Address: unable to send value, recipient may have reverted\");\n    }\n\n    /**\n     * @dev Performs a Solidity function call using a low level `call`. A\n     * plain `call` is an unsafe replacement for a function call: use this\n     * function instead.\n     *\n     * If `target` reverts with a revert reason, it is bubbled up by this\n     * function (like regular Solidity function calls).\n     *\n     * Returns the raw returned data. To convert to the expected return value,\n     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n     *\n     * Requirements:\n     *\n     * - `target` must be a contract.\n     * - calling `target` with `data` must not revert.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n     * `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but also transferring `value` wei to `target`.\n     *\n     * Requirements:\n     *\n     * - the calling contract must have an ETH balance of at least `value`.\n     * - the called Solidity function must be `payable`.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n     * with `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(\n        address target,\n        bytes memory data,\n        uint256 value,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        require(address(this).balance >= value, \"Address: insufficient balance for call\");\n        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n        return functionStaticCall(target, data, \"Address: low-level static call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n     *\n     * _Available since v4.8._\n     */\n    function verifyCallResultFromTarget(\n        address target,\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        if (success) {\n            if (returndata.length == 0) {\n                // only check isContract if the call was successful and the return data is empty\n                // otherwise we already know that it was a contract\n                require(isContract(target), \"Address: call to non-contract\");\n            }\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    /**\n     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n     * revert reason or using the provided one.\n     *\n     * _Available since v4.3._\n     */\n    function verifyCallResult(\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal pure returns (bytes memory) {\n        if (success) {\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    function _revert(bytes memory returndata, string memory errorMessage) private pure {\n        // Look for revert reason and bubble it up if present\n        if (returndata.length > 0) {\n            // The easiest way to bubble the revert reason is using memory via assembly\n            /// @solidity memory-safe-assembly\n            assembly {\n                let returndata_size := mload(returndata)\n                revert(add(32, returndata), returndata_size)\n            }\n        } else {\n            revert(errorMessage);\n        }\n    }\n}\n"},"@openzeppelin/contracts@4.9.6/utils/math/Math.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n    enum Rounding {\n        Down, // Toward negative infinity\n        Up, // Toward infinity\n        Zero // Toward zero\n    }\n\n    /**\n     * @dev Returns the largest of two numbers.\n     */\n    function max(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a > b ? a : b;\n    }\n\n    /**\n     * @dev Returns the smallest of two numbers.\n     */\n    function min(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a < b ? a : b;\n    }\n\n    /**\n     * @dev Returns the average of two numbers. The result is rounded towards\n     * zero.\n     */\n    function average(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b) / 2 can overflow.\n        return (a & b) + (a ^ b) / 2;\n    }\n\n    /**\n     * @dev Returns the ceiling of the division of two numbers.\n     *\n     * This differs from standard division with `/` in that it rounds up instead\n     * of rounding down.\n     */\n    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b - 1) / b can overflow on addition, so we distribute.\n        return a == 0 ? 0 : (a - 1) / b + 1;\n    }\n\n    /**\n     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)\n     * with further edits by Uniswap Labs also under MIT license.\n     */\n    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n        unchecked {\n            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use\n            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n            // variables such that product = prod1 * 2^256 + prod0.\n            uint256 prod0; // Least significant 256 bits of the product\n            uint256 prod1; // Most significant 256 bits of the product\n            assembly {\n                let mm := mulmod(x, y, not(0))\n                prod0 := mul(x, y)\n                prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n            }\n\n            // Handle non-overflow cases, 256 by 256 division.\n            if (prod1 == 0) {\n                // Solidity will revert if denominator == 0, unlike the div opcode on its own.\n                // The surrounding unchecked block does not change this fact.\n                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.\n                return prod0 / denominator;\n            }\n\n            // Make sure the result is less than 2^256. Also prevents denominator == 0.\n            require(denominator > prod1, \"Math: mulDiv overflow\");\n\n            ///////////////////////////////////////////////\n            // 512 by 256 division.\n            ///////////////////////////////////////////////\n\n            // Make division exact by subtracting the remainder from [prod1 prod0].\n            uint256 remainder;\n            assembly {\n                // Compute remainder using mulmod.\n                remainder := mulmod(x, y, denominator)\n\n                // Subtract 256 bit number from 512 bit number.\n                prod1 := sub(prod1, gt(remainder, prod0))\n                prod0 := sub(prod0, remainder)\n            }\n\n            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.\n            // See https://cs.stackexchange.com/q/138556/92363.\n\n            // Does not overflow because the denominator cannot be zero at this stage in the function.\n            uint256 twos = denominator & (~denominator + 1);\n            assembly {\n                // Divide denominator by twos.\n                denominator := div(denominator, twos)\n\n                // Divide [prod1 prod0] by twos.\n                prod0 := div(prod0, twos)\n\n                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.\n                twos := add(div(sub(0, twos), twos), 1)\n            }\n\n            // Shift in bits from prod1 into prod0.\n            prod0 |= prod1 * twos;\n\n            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such\n            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for\n            // four bits. That is, denominator * inv = 1 mod 2^4.\n            uint256 inverse = (3 * denominator) ^ 2;\n\n            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works\n            // in modular arithmetic, doubling the correct bits in each step.\n            inverse *= 2 - denominator * inverse; // inverse mod 2^8\n            inverse *= 2 - denominator * inverse; // inverse mod 2^16\n            inverse *= 2 - denominator * inverse; // inverse mod 2^32\n            inverse *= 2 - denominator * inverse; // inverse mod 2^64\n            inverse *= 2 - denominator * inverse; // inverse mod 2^128\n            inverse *= 2 - denominator * inverse; // inverse mod 2^256\n\n            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is\n            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1\n            // is no longer required.\n            result = prod0 * inverse;\n            return result;\n        }\n    }\n\n    /**\n     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.\n     */\n    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {\n        uint256 result = mulDiv(x, y, denominator);\n        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {\n            result += 1;\n        }\n        return result;\n    }\n\n    /**\n     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.\n     *\n     * Inspired by Henry S. Warren, Jr.'s \"Hacker's Delight\" (Chapter 11).\n     */\n    function sqrt(uint256 a) internal pure returns (uint256) {\n        if (a == 0) {\n            return 0;\n        }\n\n        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.\n        //\n        // We know that the \"msb\" (most significant bit) of our target number `a` is a power of 2 such that we have\n        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.\n        //\n        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`\n        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`\n        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`\n        //\n        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.\n        uint256 result = 1 << (log2(a) >> 1);\n\n        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,\n        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at\n        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision\n        // into the expected uint128 result.\n        unchecked {\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            return min(result, a / result);\n        }\n    }\n\n    /**\n     * @notice Calculates sqrt(a), following the selected rounding direction.\n     */\n    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = sqrt(a);\n            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 2, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 128;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 64;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 32;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 16;\n            }\n            if (value >> 8 > 0) {\n                value >>= 8;\n                result += 8;\n            }\n            if (value >> 4 > 0) {\n                value >>= 4;\n                result += 4;\n            }\n            if (value >> 2 > 0) {\n                value >>= 2;\n                result += 2;\n            }\n            if (value >> 1 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log2(value);\n            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 10, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >= 10 ** 64) {\n                value /= 10 ** 64;\n                result += 64;\n            }\n            if (value >= 10 ** 32) {\n                value /= 10 ** 32;\n                result += 32;\n            }\n            if (value >= 10 ** 16) {\n                value /= 10 ** 16;\n                result += 16;\n            }\n            if (value >= 10 ** 8) {\n                value /= 10 ** 8;\n                result += 8;\n            }\n            if (value >= 10 ** 4) {\n                value /= 10 ** 4;\n                result += 4;\n            }\n            if (value >= 10 ** 2) {\n                value /= 10 ** 2;\n                result += 2;\n            }\n            if (value >= 10 ** 1) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log10(value);\n            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 256, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     *\n     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n     */\n    function log256(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 16;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 8;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 4;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 2;\n            }\n            if (value >> 8 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log256(value);\n            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);\n        }\n    }\n}\n"},"@openzeppelin/contracts-upgradeable@4.9.6/proxy/utils/Initializable.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)\n\npragma solidity ^0.8.2;\n\nimport \"../../utils/AddressUpgradeable.sol\";\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be\n * reused. This mechanism prevents re-execution of each \"step\" but allows the creation of new initialization steps in\n * case an upgrade adds a module that needs to be initialized.\n *\n * For example:\n *\n * [.hljs-theme-light.nopadding]\n * ```solidity\n * contract MyToken is ERC20Upgradeable {\n *     function initialize() initializer public {\n *         __ERC20_init(\"MyToken\", \"MTK\");\n *     }\n * }\n *\n * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {\n *     function initializeV2() reinitializer(2) public {\n *         __ERC20Permit_init(\"MyToken\");\n *     }\n * }\n * ```\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n *\n * [CAUTION]\n * ====\n * Avoid leaving a contract uninitialized.\n *\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\n * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke\n * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * /// @custom:oz-upgrades-unsafe-allow constructor\n * constructor() {\n *     _disableInitializers();\n * }\n * ```\n * ====\n */\nabstract contract Initializable {\n    /**\n     * @dev Indicates that the contract has been initialized.\n     * @custom:oz-retyped-from bool\n     */\n    uint8 private _initialized;\n\n    /**\n     * @dev Indicates that the contract is in the process of being initialized.\n     */\n    bool private _initializing;\n\n    /**\n     * @dev Triggered when the contract has been initialized or reinitialized.\n     */\n    event Initialized(uint8 version);\n\n    /**\n     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,\n     * `onlyInitializing` functions can be used to initialize parent contracts.\n     *\n     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a\n     * constructor.\n     *\n     * Emits an {Initialized} event.\n     */\n    modifier initializer() {\n        bool isTopLevelCall = !_initializing;\n        require(\n            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),\n            \"Initializable: contract is already initialized\"\n        );\n        _initialized = 1;\n        if (isTopLevelCall) {\n            _initializing = true;\n        }\n        _;\n        if (isTopLevelCall) {\n            _initializing = false;\n            emit Initialized(1);\n        }\n    }\n\n    /**\n     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the\n     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be\n     * used to initialize parent contracts.\n     *\n     * A reinitializer may be used after the original initialization step. This is essential to configure modules that\n     * are added through upgrades and that require initialization.\n     *\n     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`\n     * cannot be nested. If one is invoked in the context of another, execution will revert.\n     *\n     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in\n     * a contract, executing them in the right order is up to the developer or operator.\n     *\n     * WARNING: setting the version to 255 will prevent any future reinitialization.\n     *\n     * Emits an {Initialized} event.\n     */\n    modifier reinitializer(uint8 version) {\n        require(!_initializing && _initialized < version, \"Initializable: contract is already initialized\");\n        _initialized = version;\n        _initializing = true;\n        _;\n        _initializing = false;\n        emit Initialized(version);\n    }\n\n    /**\n     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\n     * {initializer} and {reinitializer} modifiers, directly or indirectly.\n     */\n    modifier onlyInitializing() {\n        require(_initializing, \"Initializable: contract is not initializing\");\n        _;\n    }\n\n    /**\n     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.\n     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized\n     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called\n     * through proxies.\n     *\n     * Emits an {Initialized} event the first time it is successfully executed.\n     */\n    function _disableInitializers() internal virtual {\n        require(!_initializing, \"Initializable: contract is initializing\");\n        if (_initialized != type(uint8).max) {\n            _initialized = type(uint8).max;\n            emit Initialized(type(uint8).max);\n        }\n    }\n\n    /**\n     * @dev Returns the highest version that has been initialized. See {reinitializer}.\n     */\n    function _getInitializedVersion() internal view returns (uint8) {\n        return _initialized;\n    }\n\n    /**\n     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.\n     */\n    function _isInitializing() internal view returns (bool) {\n        return _initializing;\n    }\n}\n"},"@openzeppelin/contracts-upgradeable@4.9.6/utils/AddressUpgradeable.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary AddressUpgradeable {\n    /**\n     * @dev Returns true if `account` is a contract.\n     *\n     * [IMPORTANT]\n     * ====\n     * It is unsafe to assume that an address for which this function returns\n     * false is an externally-owned account (EOA) and not a contract.\n     *\n     * Among others, `isContract` will return false for the following\n     * types of addresses:\n     *\n     *  - an externally-owned account\n     *  - a contract in construction\n     *  - an address where a contract will be created\n     *  - an address where a contract lived, but was destroyed\n     *\n     * Furthermore, `isContract` will also return true if the target contract within\n     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,\n     * which only has an effect at the end of a transaction.\n     * ====\n     *\n     * [IMPORTANT]\n     * ====\n     * You shouldn't rely on `isContract` to protect against flash loan attacks!\n     *\n     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n     * constructor.\n     * ====\n     */\n    function isContract(address account) internal view returns (bool) {\n        // This method relies on extcodesize/address.code.length, which returns 0\n        // for contracts in construction, since the code is only stored at the end\n        // of the constructor execution.\n\n        return account.code.length > 0;\n    }\n\n    /**\n     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n     * `recipient`, forwarding all available gas and reverting on errors.\n     *\n     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n     * of certain opcodes, possibly making contracts go over the 2300 gas limit\n     * imposed by `transfer`, making them unable to receive funds via\n     * `transfer`. {sendValue} removes this limitation.\n     *\n     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n     *\n     * IMPORTANT: because control is transferred to `recipient`, care must be\n     * taken to not create reentrancy vulnerabilities. Consider using\n     * {ReentrancyGuard} or the\n     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n        (bool success, ) = recipient.call{value: amount}(\"\");\n        require(success, \"Address: unable to send value, recipient may have reverted\");\n    }\n\n    /**\n     * @dev Performs a Solidity function call using a low level `call`. A\n     * plain `call` is an unsafe replacement for a function call: use this\n     * function instead.\n     *\n     * If `target` reverts with a revert reason, it is bubbled up by this\n     * function (like regular Solidity function calls).\n     *\n     * Returns the raw returned data. To convert to the expected return value,\n     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n     *\n     * Requirements:\n     *\n     * - `target` must be a contract.\n     * - calling `target` with `data` must not revert.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n     * `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but also transferring `value` wei to `target`.\n     *\n     * Requirements:\n     *\n     * - the calling contract must have an ETH balance of at least `value`.\n     * - the called Solidity function must be `payable`.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n     * with `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(\n        address target,\n        bytes memory data,\n        uint256 value,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        require(address(this).balance >= value, \"Address: insufficient balance for call\");\n        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n        return functionStaticCall(target, data, \"Address: low-level static call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n     *\n     * _Available since v4.8._\n     */\n    function verifyCallResultFromTarget(\n        address target,\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        if (success) {\n            if (returndata.length == 0) {\n                // only check isContract if the call was successful and the return data is empty\n                // otherwise we already know that it was a contract\n                require(isContract(target), \"Address: call to non-contract\");\n            }\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    /**\n     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n     * revert reason or using the provided one.\n     *\n     * _Available since v4.3._\n     */\n    function verifyCallResult(\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal pure returns (bytes memory) {\n        if (success) {\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    function _revert(bytes memory returndata, string memory errorMessage) private pure {\n        // Look for revert reason and bubble it up if present\n        if (returndata.length > 0) {\n            // The easiest way to bubble the revert reason is using memory via assembly\n            /// @solidity memory-safe-assembly\n            assembly {\n                let returndata_size := mload(returndata)\n                revert(add(32, returndata), returndata_size)\n            }\n        } else {\n            revert(errorMessage);\n        }\n    }\n}\n"},"flap/VaultBaseV3.sol":{"content":"// SPDX-License-Identifier: MIT\r\n\r\npragma solidity ^0.8.13;\r\n\r\nimport {VaultBaseV2} from \"./VaultBaseV2.sol\";\r\n\r\n/// @title VaultBaseV3\r\n/// @author The Flap Team\r\n/// @notice Extended abstract base contract for vault implementations that adds\r\n///         support for ERC20 quote tokens (stablecoins, tokenized equities / RWA)\r\n///         as the vault's revenue currency, via `vaultQuoteToken()` discovery and\r\n///         a normative balance-delta accounting model.\r\n///\r\n/// @dev  ── MOTIVATION ───────────────────────────────────────────────────────\r\n///\r\n/// Every pre-V3 vault is driven by its `receive()` function: when the token's\r\n/// TaxProcessor dispatches tax revenue in the native gas token (BNB / ETH),\r\n/// the value transfer itself invokes `receive()` and the vault's logic runs.\r\n///\r\n/// ERC20 revenue breaks that assumption. An ERC20 `transfer` executes the\r\n/// token contract's code, NOT the recipient's — the vault receives the funds\r\n/// but is never invoked. No `receive()`, no `fallback()`, nothing. This is the\r\n/// single reason vault-enabled launches were historically restricted to native\r\n/// quote tokens.\r\n///\r\n/// V3 closes the gap with two coordinated mechanisms:\r\n///\r\n///   1. **The wake call (\"ping\"), sent by the protocol.** After every ERC20\r\n///      quote payout, the TaxProcessor calls the recipient wallet with empty\r\n///      calldata and zero value — `wallet.call{value: 0}(\"\")` —\r\n///      which invokes the vault's `receive()` exactly like a native transfer\r\n///      would. The vault gets execution; the funds are already there.\r\n///\r\n///   2. **Balance-delta accounting, implemented by the vault (YOU).** Because\r\n///      the wake call carries no value, `msg.value` is useless for ERC20\r\n///      revenue. The vault must instead compare its current quote balance\r\n///      against a stored baseline to recognize new revenue. The normative\r\n///      model is specified below.\r\n///\r\n///\r\n/// ── BACKWARDS COMPATIBILITY ────────────────────────────────────────────────\r\n///\r\n/// `VaultBaseV3` inherits from `VaultBaseV2` and adds only the new\r\n/// `vaultQuoteToken()` abstract method and the `vaultSpecVersion()` marker.\r\n/// Existing vaults that extend `VaultBase` or `VaultBaseV2` are unaffected —\r\n/// they remain native-only and keep working unchanged. New or upgraded vault\r\n/// implementations should extend `VaultBaseV3` instead of `VaultBaseV2` to\r\n/// gain ERC20 quote support; native-only vaults may also adopt V3 (declare\r\n/// `address(0)`) to benefit from the explicit currency declaration.\r\n///\r\n///\r\n/// ── THE PING CONTRACT (what the protocol guarantees) ──────────────────────\r\n///\r\n/// The TaxProcessor's dispatch upholds four invariants; design your vault\r\n/// against them:\r\n///\r\n///   1. **Transfer first, ping after.** When your `receive()` runs, the\r\n///      revenue is already in your balance.\r\n///   2. **Ping failures are ignored.** If your `receive()` reverts, dispatch\r\n///      proceeds without you — the funds still arrive, but your logic did not\r\n///      run and you will only recognize the revenue on the next wake. Do not\r\n///      rely on reverting to refuse revenue.\r\n///   3. **Ping gas is the dispatcher's, not yours.** The protocol does not\r\n///      cap the wake call — it forwards the dispatch caller's remaining gas\r\n///      (EIP-150), and dispatch is permissionless. The protocol's keeper runs\r\n///      the ENTIRE dispatch (swaps, dividends, burn, up to four wallet\r\n///      payouts) under one fixed gas budget; a heavy `receive()` makes the\r\n///      keeper's dispatch of your token fail until someone re-dispatches with\r\n///      a higher gas limit. Keep `receive()` lightweight: update accounting,\r\n///      emit an event, return. Put heavy work — swaps, staking, buybacks —\r\n///      behind explicit public functions driven by your own automation or the\r\n///      protocol's keeper/trigger services.\r\n///   4. **You may be pinged spuriously.** The same wallet can occupy several\r\n///      payout slots and be pinged more than once per dispatch, and anyone\r\n///      can call your `receive()` at any time. A wake that finds no new\r\n///      revenue MUST be a silent no-op (see accounting model, rule 2).\r\n///\r\n/// Native revenue is unchanged: it still arrives as a plain value transfer\r\n/// invoking `receive()`. A V3 vault handles both currencies with the same\r\n/// code path — the accounting model below is currency-agnostic.\r\n///\r\n///\r\n/// ── NORMATIVE ACCOUNTING MODEL (what YOUR vault must implement) ───────────\r\n///\r\n/// Maintain a baseline of already-recognized revenue, e.g.:\r\n///\r\n///   ```solidity\r\n///   uint256 public accountedQuote; // recognized-and-not-yet-spent revenue\r\n///\r\n///   receive() external payable { _syncRevenue(); }\r\n///\r\n///   function _syncRevenue() internal {\r\n///       address quoteToken = vaultQuoteToken();\r\n///       uint256 bal = quoteToken == address(0)\r\n///           ? address(this).balance\r\n///           : IERC20(quoteToken).balanceOf(address(this));\r\n///       if (bal <= accountedQuote) return;            // rule 2: idempotent no-op\r\n///       uint256 newRevenue = bal - accountedQuote;\r\n///       accountedQuote = bal;                          // rule 1: advance baseline\r\n///       _onRevenue(newRevenue);                        // lightweight only (ping law 3)\r\n///   }\r\n///   ```\r\n///\r\n/// Rules:\r\n///\r\n///   1. **Recognize by delta, never by raw balance.** Crediting\r\n///      `balanceOf(this)` as \"new revenue\" double-counts everything already\r\n///      recognized. Only `balance - accountedQuote` is new.\r\n///   2. **Zero-delta wakes are silent no-ops.** Required by ping law 4.\r\n///   3. **Every outflow MUST decrement `accountedQuote`** by the amount\r\n///      spent, in the same transaction. Forgetting this leaves the baseline\r\n///      above the real balance, and `bal <= accountedQuote` then suppresses\r\n///      revenue recognition forever — the vault deadlocks. This is the\r\n///      single most dangerous mistake a V3 vault can make.\r\n///   4. **Direct transfers are credited late, and that is fine.** Funds sent\r\n///      to the vault outside dispatch (donations, airdrops, misdirected\r\n///      transfers) trigger no ping; they are recognized on the next wake and\r\n///      are indistinguishable from revenue. Treat them as donations. If your\r\n///      vault's economics require exact per-payout amounts, do not infer them\r\n///      from deltas — wait for a typed-callback spec revision (see FUTURE\r\n///      EXTENSIONS).\r\n///   5. **Recognize before you act.** Any function whose behavior depends on\r\n///      available revenue — spending it, paying it out, or gating on it —\r\n///      MUST run the sync routine first, so its outcome never depends on\r\n///      whether a ping happened to arrive beforehand. This applies to every\r\n///      custom function you add to your vault. Exposing a permissionless\r\n///      `sync()` wrapper is RECOMMENDED: it is the neutral, side-effect-free\r\n///      recognition entry for keepers and UIs, and the recovery path when\r\n///      revenue arrives without a wake call (direct transfers, or pings\r\n///      administratively disabled on the processor).\r\n///\r\n///\r\n/// ── HOW TO IMPLEMENT ──────────────────────────────────────────────────────\r\n///\r\n/// 1. **Inherit from VaultBaseV3.** All obligations from VaultBase and\r\n///    VaultBaseV2 still apply (implement `description()` and\r\n///    `vaultUISchema()`, use `_getPortal()` / `_getGuardian()`, honor the\r\n///    Guardian mandate).\r\n///\r\n/// 2. **Override `vaultQuoteToken()`** to return the revenue currency this\r\n///    vault instance accounts for — `address(0)` for the native gas token,\r\n///    the ERC20 address otherwise. It MUST equal the quote token of the tax\r\n///    token this vault serves, MUST be immutable for the life of the vault\r\n///    (set it once at initialization from the factory's `newVault`\r\n///    parameters), and MUST NOT revert. VaultPortal cross-checks it at\r\n///    launch; lenses and UIs read it to label the vault and to warn when a\r\n///    vault is wired to a token with a different quote.\r\n///\r\n/// 3. **Implement `receive()` with the accounting model above.** Present even\r\n///    if your quote is an ERC20 — the wake call lands there.\r\n///\r\n/// 4. **Keep `vaultSpecVersion()` at the default** unless a later spec\r\n///    revision instructs otherwise.\r\n///\r\n/// 5. **Factory-side requirements** (see VaultFactoryBaseV2):\r\n///      - `isQuoteTokenSupported(quote)` MUST answer truthfully for your\r\n///        vault implementation. This is enforced on-chain at launch: the\r\n///        VaultPortal rejects any launch whose quote your factory does not\r\n///        support. Misdeclaring support strands your users' tax revenue in a\r\n///        vault that ignores it.\r\n///      - `factorySpecVersion()` MUST return \"v2.3\" (or higher) so the\r\n///        VaultPortal applies the V3 validation flow.\r\n///      - `newVault(taxToken, quoteToken, creator, vaultData)` receives the\r\n///        launch quote token — pass it into your vault's initializer.\r\n///      - Creation-time first buys in an ERC20 quote are funded by the\r\n///        launcher, not the vault: the VaultPortal pulls `quoteAmt` from the\r\n///        launching user (via an EIP-2612 permit signed with the VaultPortal\r\n///        as spender, or a prior approval) and refunds any partial fill.\r\n///        Frontends integrating your factory should request that approval or\r\n///        permit; the vault itself plays no part in launch funding.\r\n///\r\n///\r\n/// ── FUTURE EXTENSIONS (non-normative) ─────────────────────────────────────\r\n///\r\n/// A later spec revision may add a typed revenue callback in the spirit of\r\n/// ERC721's `onERC721Received`:\r\n///\r\n///   ```solidity\r\n///   interface IFlapRevenueReceiver {\r\n///       function onFlapRevenue(address quoteToken, uint256 amount) external;\r\n///   }\r\n///   ```\r\n///\r\n/// Dispatch would prefer the typed callback (exact amounts, no delta\r\n/// ambiguity) and fall back to the zero-value ping. The balance-delta\r\n/// baseline remains the source of truth regardless — transfers can always\r\n/// arrive without a callback — so vaults written against this spec stay\r\n/// correct unchanged.\r\n///\r\nabstract contract VaultBaseV3 is VaultBaseV2 {\r\n    /// @notice The revenue currency this vault accounts for.\r\n    /// @dev MUST equal the quote token of the tax token this vault serves:\r\n    ///      `address(0)` for the native gas token, the ERC20 address\r\n    ///      otherwise. MUST be stable for the life of the vault and MUST NOT\r\n    ///      revert. Consumed by the VaultPortal launch cross-check and by\r\n    ///      lens/UI discovery.\r\n    /// @return quoteToken The revenue currency (`address(0)` = native).\r\n    function vaultQuoteToken() public view virtual returns (address quoteToken);\r\n\r\n    /// @notice The vault-side spec revision this implementation conforms to.\r\n    /// @dev Distinct from the factory-side `factorySpecVersion()` (\"v2.x\"\r\n    ///      series): this string versions the VAULT contract surface.\r\n    ///      Lenses read it to distinguish V3 vaults from legacy ones (which\r\n    ///      implement neither this method nor `vaultQuoteToken()`).\r\n    /// @return The spec version string, \"v3\" for this revision.\r\n    function vaultSpecVersion() public pure virtual returns (string memory) {\r\n        return \"v3\";\r\n    }\r\n}"},"flap/VaultBaseV2.sol":{"content":"// SPDX-License-Identifier: MIT\r\n\r\npragma solidity ^0.8.13;\r\n\r\nimport {VaultBase} from \"./VaultBase.sol\";\r\nimport {VaultUISchema} from \"./IVaultSchemasV1.sol\";\r\n\r\n/// @title VaultBaseV2\r\n/// @author The Flap Team\r\n/// @notice Extended abstract base contract for vault implementations that adds\r\n///         on-chain UI schema discovery via `vaultUISchema()`.\r\n///\r\n/// @dev  ── MOTIVATION ───────────────────────────────────────────────────────\r\n///\r\n/// The protocol is designed so that anyone can introduce new vault types.\r\n/// Each vault implementation exposes a different set of user-facing view\r\n/// and write methods — a generic UI rendering a vault page has no way to\r\n/// know which methods to display, what each parameter means, or how to\r\n/// format the results without inspecting each vault's source code.\r\n///\r\n/// Existing vault types (FlapXVault, SplitVault, SnowBallVault,\r\n/// BlackHoleVault) already have purpose-built UIs.  `VaultBaseV2` exists\r\n/// to support **future** vault implementations: by requiring every new\r\n/// vault to override `vaultUISchema()`, the UI can automatically generate\r\n/// a full interaction page for any vault type — even ones that did not\r\n/// exist when the UI was built.\r\n///\r\n/// `vaultUISchema()` returns a `VaultUISchema` struct (defined in\r\n/// `IVaultSchemasV1.sol`) that fully describes the methods the UI should\r\n/// render.\r\n///\r\n///\r\n/// ── BACKWARDS COMPATIBILITY ────────────────────────────────────────────────\r\n///\r\n/// `VaultBaseV2` inherits from the original `VaultBase` contract and adds only\r\n/// the new `vaultUISchema()` abstract method.  Existing vaults that extend\r\n/// `VaultBase` are unaffected.  New or upgraded vault implementations should\r\n/// extend `VaultBaseV2` instead of `VaultBase` to gain UI schema support.\r\n///\r\n///\r\n/// ── HOW TO IMPLEMENT ───────────────────────────────────────────────────────\r\n///\r\n/// 1. **Inherit from VaultBaseV2** instead of VaultBase.\r\n///    All obligations from VaultBase still apply (implement `description()`,\r\n///    use `_getPortal()`, `_getGuardian()`, handle `receive()`, etc.).\r\n///\r\n/// 2. **Override `vaultUISchema()`** to return a `VaultUISchema` describing\r\n///    every user-facing method your vault exposes.\r\n///\r\n///    For each method include:\r\n///      - `name`          – the Solidity function name (e.g. \"claim\")\r\n///      - `description`   – human-readable explanation\r\n///      - `inputs`        – ordered FieldDescriptor[] for parameters\r\n///      - `outputs`       – ordered FieldDescriptor[] for return values\r\n///      - `approvals`     – ApproveAction[] for any ERC-20 approvals required\r\n///                          before calling a write method\r\n///      - `isInputArray`  – true if input is tuple[]\r\n///      - `isOutputArray` – true if output is tuple[]\r\n///      - `isWriteMethod` – true for state-changing, false for view\r\n///\r\n/// 3. **MANDATE: Guardian access to privileged functions**\r\n///    If your vault exposes any privileged / role-gated functions (e.g.\r\n///    functions that should only be called by an operator or admin), the\r\n///    Guardian address returned by `_getGuardian()` **must** also be\r\n///    granted the required role(s).  Furthermore, the Guardian's role\r\n///    **must not** be revocable by any other account — only the Guardian\r\n///    itself may renounce its own access.\r\n///\r\n///    When using OpenZeppelin's `AccessControl`, override `revokeRole()` to\r\n///    enforce this invariant:\r\n///\r\n///      ```\r\n///      function revokeRole(bytes32 role, address account)\r\n///          public\r\n///          override\r\n///          onlyRole(getRoleAdmin(role))\r\n///      {\r\n///          address guardian = _getGuardian();\r\n///          if (account == guardian) {\r\n///              revert CannotRevokeGuardianRole();\r\n///          }\r\n///          super.revokeRole(role, account);\r\n///      }\r\n///      ```\r\n///\r\n///    This ensures that the Guardian always retains a backup path to call\r\n///    permissioned functions, regardless of any admin action.\r\n///\r\n///    (This mandate is inherited from VaultBase — see its documentation\r\n///    for the canonical requirement.)\r\n///\r\n///\r\n/// ── EXAMPLE: Hypothetical DonationVault (view + write, no approvals) ───\r\n///\r\n///   function vaultUISchema() public pure override returns (VaultUISchema memory schema) {\r\n///       schema.vaultType = \"DonationVault\";\r\n///       schema.description = \"Collects BNB donations and lets a designated charity withdraw.\";\r\n///       schema.methods = new VaultMethodSchema[](2);\r\n///\r\n///       // View: totalDonated()\r\n///       schema.methods[0].name = \"totalDonated\";\r\n///       schema.methods[0].description = \"Returns the total BNB donated so far.\";\r\n///       schema.methods[0].outputs = new FieldDescriptor[](1);\r\n///       schema.methods[0].outputs[0] = FieldDescriptor(\"total\", \"uint256\", \"Total BNB donated\", 18);\r\n///\r\n///       // Write: withdraw()\r\n///       schema.methods[1].name = \"withdraw\";\r\n///       schema.methods[1].description = \"Withdraws accumulated donations to the charity address. Only the charity can call.\";\r\n///       schema.methods[1].approvals = new ApproveAction[](0);\r\n///       schema.methods[1].isWriteMethod = true;\r\n///   }\r\n///\r\n///\r\n/// ── EXAMPLE: Hypothetical StakingVault (illustrating approve actions) ──────\r\n///\r\n///   function vaultUISchema() public pure override returns (VaultUISchema memory schema) {\r\n///       schema.vaultType = \"StakingVault\";\r\n///       schema.description = \"Stakes tax tokens or LP tokens to earn rewards.\";\r\n///       schema.methods = new VaultMethodSchema[](3);\r\n///\r\n///       // View: stakedBalance(address)\r\n///       schema.methods[0].name = \"stakedBalance\";\r\n///       schema.methods[0].description = \"Returns the staked balance for a given user.\";\r\n///       schema.methods[0].inputs = new FieldDescriptor[](1);\r\n///       schema.methods[0].inputs[0] = FieldDescriptor(\"user\", \"address\", \"The user address to query\", 0);\r\n///       schema.methods[0].outputs = new FieldDescriptor[](1);\r\n///       schema.methods[0].outputs[0] = FieldDescriptor(\"balance\", \"uint256\", \"Staked token balance\", 18);\r\n///\r\n///       // Write: deposit(uint256) — requires ERC-20 approval of the tax token\r\n///       schema.methods[1].name = \"deposit\";\r\n///       schema.methods[1].description = \"Stake tax tokens into the vault to earn rewards.\";\r\n///       schema.methods[1].inputs = new FieldDescriptor[](1);\r\n///       schema.methods[1].inputs[0] = FieldDescriptor(\"amount\", \"uint256\", \"Amount of tax tokens to stake\", 18);\r\n///       schema.methods[1].approvals = new ApproveAction[](1);\r\n///       // The UI will:\r\n///       //   (1) call vault.taxToken() to get the token address\r\n///       //   (2) call token.approve(vault, amount) where amount comes from the \"amount\" input field\r\n///       schema.methods[1].approvals[0] = ApproveAction(\"taxToken\", \"amount\");\r\n///       schema.methods[1].isWriteMethod = true;\r\n///\r\n///       // Write: depositLP(uint256) — requires ERC-20 approval of the LP token\r\n///       schema.methods[2].name = \"depositLP\";\r\n///       schema.methods[2].description = \"Stake LP tokens into the vault to earn boosted rewards.\";\r\n///       schema.methods[2].inputs = new FieldDescriptor[](1);\r\n///       schema.methods[2].inputs[0] = FieldDescriptor(\"amount\", \"uint256\", \"Amount of LP tokens to stake\", 18);\r\n///       schema.methods[2].approvals = new ApproveAction[](1);\r\n///       schema.methods[2].approvals[0] = ApproveAction(\"lpToken\", \"amount\");\r\n///       schema.methods[2].isWriteMethod = true;\r\n///   }\r\n///\r\n///\r\n/// ── EXAMPLE: Vault with no user-facing methods ─────────────────────────────\r\n///\r\n///   function vaultUISchema() public pure override returns (VaultUISchema memory schema) {\r\n///       schema.vaultType = \"SinkVault\";\r\n///       schema.description = \"A vault that permanently locks all received BNB. No methods available.\";\r\n///       schema.methods = new VaultMethodSchema[](0);\r\n///   }\r\n///\r\nabstract contract VaultBaseV2 is VaultBase {\r\n    /// @notice Returns the UI schema describing which methods the UI should\r\n    ///         render for this vault.\r\n    ///\r\n    /// @dev Each vault implementation **must** override this to describe its\r\n    ///      own user-facing methods.\r\n    ///\r\n    ///      The returned `VaultUISchema` struct contains:\r\n    ///        - `vaultType`   – e.g. \"FlapXVault\", \"SplitVault\"\r\n    ///        - `description` – overall explanation of the vault\r\n    ///        - `methods`     – ordered array of `VaultMethodSchema`, each\r\n    ///                          describing one view or write method with its\r\n    ///                          inputs, outputs, required approvals, and\r\n    ///                          array flags.\r\n    ///\r\n    ///      The UI renders methods in the order they appear in the array.\r\n    ///      See the VaultMethodSchema and VaultUISchema docs in\r\n    ///      IVaultSchemasV1.sol for the full rendering algorithm.\r\n    ///\r\n    /// @return schema The complete UI schema for this vault.\r\n    function vaultUISchema() public pure virtual returns (VaultUISchema memory schema);\r\n}"},"flap/VaultBase.sol":{"content":"// SPDX-License-Identifier: MIT\r\n\r\npragma solidity ^0.8.13;\r\n\r\n/// @title VaultBase\r\n/// @notice Abstract base contract for all vault implementations\r\n/// @author The Flap Team\r\n/// @dev How to implement your own vault contract:\r\n///\r\n/// 1. **Inherit from this base contract**\r\n///    - All vault implementations must extend this contract\r\n///\r\n/// 2. **Implement the description() method**\r\n///    - This method should return a dynamic string describing the vault's current state\r\n///    - The description should change based on vault state (e.g., balance, streaming status, etc.)\r\n///    - Example: \"Flap Tax Vault for TOKEN, this vault automatically buyback tokens, we have bought back 3.2M tokens. Developed by Alice (twitter.com/alice)\"\r\n///\r\n/// 3. **Use _getPortal() for Portal interactions**\r\n///    - Call this internal method to get the Portal address for the current chain\r\n///    - The method reverts if the current chain is not supported\r\n///    - Currently only BNB Chain (chain ID 56) and BNB Testnet (chain ID 97) are supported\r\n///\r\n/// 4. **Use _getGuardian() for Guardian address**\r\n///    - Call this internal method to get the Guardian address for the current chain\r\n///    - The Guardian is a privileged address that can always call permissioned functions if you have any\r\n///    - The method reverts if the current chain is not supported\r\n///    - Currently only BNB Chain (chain ID 56) and BNB Testnet (chain ID 97) are supported\r\n///\r\n/// 5. **Handle tax token revenue**\r\n///    - Implement a receive() function to accept BNB from the tax token\r\n///    - Process the revenue according to your vault's logic (accumulate, stream, buyback, etc.)\r\n///\r\n/// 6. **MANDATE: Guardian access to permissioned functions**\r\n///    - If you have any permissioned functions that should be triggered by an external address,\r\n///      and it is not suitable to make them public (e.g. buyback which may be sandwich attacked),\r\n///      you MUST ALSO give the guardian address the permissions alongside other allowed addresses as a backup.\r\n///    - The guardian can always call permissioned functions and must have the necessary roles/permissions.\r\n///    - The guardian's role MUST NOT be revocable by any other account — only the guardian itself\r\n///      may renounce its own access.\r\n///\r\n///    When using OpenZeppelin's `AccessControl`, override `revokeRole()` to enforce this:\r\n///\r\n///      ```\r\n///      function revokeRole(bytes32 role, address account)\r\n///          public\r\n///          override\r\n///          onlyRole(getRoleAdmin(role))\r\n///      {\r\n///          address guardian = _getGuardian();\r\n///          if (account == guardian) {\r\n///              revert CannotRevokeGuardianRole();\r\n///          }\r\n///          super.revokeRole(role, account);\r\n///      }\r\n///      ```\r\n///\r\n///    This ensures that the Guardian always retains a backup path to call\r\n///    permissioned functions, regardless of any admin action.\r\n///\r\nabstract contract VaultBase {\r\n    /// @notice Error thrown when the current chain is not supported\r\n    error UnsupportedChain(uint256 chainId);\r\n\r\n    /// @notice Get the Portal address for the current chain\r\n    /// @dev Currently supports:\r\n    ///        - BNB Chain      (chain ID 56)\r\n    ///        - BNB Testnet    (chain ID 97)\r\n    ///        - Robinhood Chain(chain ID 4663 / 46630)\r\n    ///      All other chains use the same addresses (Base, X Layer, ...).\r\n    /// @return portal The Portal contract address\r\n    function _getPortal() internal view returns (address portal) {\r\n        uint256 chainId = block.chainid;\r\n        if (chainId == 56) {\r\n            // BNB Chain Portal\r\n            return 0xe2cE6ab80874Fa9Fa2aAE65D277Dd6B8e65C9De0;\r\n        } else if (chainId == 97) {\r\n            // BNB Testnet Portal\r\n            return 0x5bEacaF7ABCbB3aB280e80D007FD31fcE26510e9;\r\n        } else if (chainId == 4663 || chainId == 46630) {\r\n            // Robinhood Chain Portal\r\n            return 0x26605f322f7fF986f381bB9A6e3f5DAb0bEaEb09;\r\n        } else {\r\n            // Base / X Layer / any other chain\r\n            return 0x0000BC1c4fD15Dd79029AF8F5d77D68ae4490000;\r\n        }\r\n    }\r\n\r\n    /// @notice Get the Guardian address for the current chain\r\n    /// @dev Currently supports:\r\n    ///        - BNB Chain      (chain ID 56)\r\n    ///        - BNB Testnet    (chain ID 97)\r\n    ///        - Robinhood Chain(chain ID 4663 / 46630)\r\n    ///      All other chains use the same addresses (Base, X Layer, ...).\r\n    /// @return guardian The Guardian contract address\r\n    function _getGuardian() internal view returns (address guardian) {\r\n        uint256 chainId = block.chainid;\r\n        if (chainId == 56) {\r\n            // BNB Chain Guardian address\r\n            return 0x9e27098dcD8844bcc6287a557E0b4D09C86B8a4b;\r\n        } else if (chainId == 97) {\r\n            // BNB Testnet Guardian address\r\n            return 0x76Fa8C526f8Bc27ba6958B76DeEf92a0dbE46950;\r\n        } else if (chainId == 4663 || chainId == 46630) {\r\n            // Robinhood Chain Guardian address\r\n            return 0x0000b48720d3B4ED6BC5031768B07F2b59270000;\r\n        } else {\r\n            // Base / X Layer / any other chain\r\n            return 0x00004d58290854c5E9880F4b5B1993c6Ad8b0000;\r\n        }\r\n    }\r\n\r\n    /// @notice Returns a description of the vault\r\n    /// @dev This method should be overridden by implementing contracts to provide\r\n    ///      a dynamic description based on the vault's current state\r\n    /// @return A string describing the vault's current state and configuration\r\n    function description() public view virtual returns (string memory);\r\n}\r\n"},"flap/IVaultSchemasV1.sol":{"content":"// SPDX-License-Identifier: MIT\r\n\r\npragma solidity ^0.8.13;\r\n\r\n/// @title IVaultSchemasV1\r\n/// @author The Flap Team\r\n/// @notice Shared struct definitions used by VaultFactory and Vault UI schema methods.\r\n///\r\n/// @dev  ── WHY THIS FILE EXISTS ──────────────────────────────────────────────\r\n///\r\n/// The Flap protocol allows anyone to create new vault factories.  Any\r\n/// contract that implements `VaultFactoryBaseV2` can be used to launch\r\n/// tokens via VaultPortal — no on-chain registration is required.\r\n/// Each factory decodes the opaque `vaultData` bytes in\r\n/// `newVault()` differently, and each vault exposes a unique set of\r\n/// user-facing view and write methods.\r\n///\r\n/// Existing vault types (FlapXVault, SplitVault, SnowBallVault,\r\n/// BlackHoleVault) already have purpose-built UIs.  However, the protocol\r\n/// is designed to be extended — any third-party developer can create a new\r\n/// vault factory and vault implementation without modifying the core\r\n/// contracts or the UI codebase.\r\n///\r\n/// Without a self-describing mechanism, a new vault type would require a\r\n/// custom UI to be built and deployed before users could interact with it.\r\n/// The structs in this file solve this problem by providing an on-chain\r\n/// schema that enables **automatic UI generation for any future vault type**.\r\n///\r\n/// Both `VaultFactoryBaseV2.vaultDataSchema()` and\r\n/// `VaultBaseV2.vaultUISchema()` return structs defined here, allowing a\r\n/// generic UI to render creation forms and vault interaction pages for\r\n/// vault types that did not exist when the UI was built.\r\n///\r\n///\r\n/// ── STRUCT CONSOLIDATION ───────────────────────────────────────────────────\r\n///\r\n/// `FieldDescriptor` is the unified leaf type shared by both the factory\r\n/// schema (`VaultDataSchema.fields`) and the vault UI schema\r\n/// (`VaultMethodSchema.inputs` / `VaultMethodSchema.outputs`).  Keeping one\r\n/// struct avoids duplication and ensures the type system is consistent across\r\n/// the protocol.\r\n///\r\n///\r\n/// ── HOW THE UI USES VaultDataSchema (factory side) ─────────────────────────\r\n///\r\n///   1. UI obtains the factory address (e.g. from the token-launch event\r\n///      or a directory service).\r\n///   2. UI calls factory.vaultDataSchema() → gets VaultDataSchema.\r\n///   3. For each field in schema.fields, UI renders an input:\r\n///        \"string\"   → text input\r\n///        \"address\"  → address input (with checksum validation)\r\n///        \"uint16\"   → number input (0–65535)\r\n///        \"uint256\"  → big-number input\r\n///        \"time\"     → date/time picker input (alias for uint256;\r\n///                      value is a Unix timestamp in seconds).\r\n///                      When used in outputs, rendered as a\r\n///                      human-readable time string or countdown clock\r\n///        \"bool\"     → checkbox\r\n///        \"bytes\"    → hex input\r\n///        \"bytes32\"  → hex input (32 bytes)\r\n///      For numeric fields, if field.decimals > 0 the UI multiplies the\r\n///      user's input by 10^decimals before encoding.\r\n///        e.g. decimals=18: user types \"1\" → encoded value is 1e18\r\n///      If decimals == 0 the raw value is used as-is.\r\n///   4. If schema.isArray == true, UI renders an \"Add Item\" button for\r\n///      dynamic array entries.\r\n///   5. UI encodes the user input via:\r\n///        If isArray: abi.encode(tuple[])\r\n///        Else:       abi.encode(tuple)\r\n///      using the fieldType values to construct the ABI type string.\r\n///   6. Encoded bytes are passed as `vaultData` in NewTaxTokenWithVaultParams.\r\n///\r\n/// NOTE: The current spec only supports either a single tuple or an array\r\n///       of tuples.\r\n///\r\n///\r\n/// ── HOW THE UI USES VaultUISchema (vault side) ─────────────────────────────\r\n///\r\n///   1. UI calls vault.vaultUISchema() → gets VaultUISchema.\r\n///   2. UI displays schema.vaultType as a badge/header and\r\n///      schema.description as subtitle.\r\n///   3. UI always calls vault.description() and displays the result as a\r\n///      dynamic status banner.\r\n///   4. For each method in schema.methods:\r\n///      a. If method.isWriteMethod == false (VIEW):\r\n///         - If method.inputs is empty: call immediately and display results.\r\n///         - If method.inputs is non-empty: render input fields, add a\r\n///           \"Query\" button.\r\n///           - If method.isInputArray == true, render an \"Add Item\" button.\r\n///         - For outputs: use field.decimals to format numeric values\r\n///           (e.g. decimals=18: raw 1.5e18 → display \"1.5\").\r\n///           For \"time\" fields, display the raw uint256 value as a\r\n///           human-readable time string or a countdown clock.\r\n///         - Derive the ABI type string from outputs[].fieldType +\r\n///           method.isOutputArray.\r\n///         - Use field.name and field.description as column headers/tooltips.\r\n///      b. If method.isWriteMethod == true (WRITE):\r\n///         - Render a form with inputs from method.inputs.\r\n///         - If method.isInputArray == true, render \"Add Item\" button.\r\n///         - Each input renders based on fieldType:\r\n///             \"address\" → address input with checksum validation\r\n///             \"uint256\" → big-number input\r\n///             \"uint128\" → number input\r\n///             \"time\"    → date/time picker input (alias for uint256;\r\n///                          value is a Unix timestamp in seconds)\r\n///             \"string\"  → text input\r\n///             \"bytes\"   → hex input\r\n///             \"bool\"    → checkbox\r\n///         - If field.decimals > 0, the UI multiplies the user's\r\n///           human-readable input by 10^decimals before encoding.\r\n///         - If method.approvals is non-empty, BEFORE sending the write\r\n///           transaction the UI executes each ApproveAction in order:\r\n///             i.   Resolve the token address from approval.tokenType:\r\n///                    \"taxToken\" → call vault.taxToken()\r\n///                    \"lpToken\"  → call vault.lpToken()\r\n///                    unknown    → skip (log warning, forward-compatible)\r\n///             ii.  Read the amount from the user's input field named by\r\n///                  approval.amountFieldName (already scaled by decimals).\r\n///             iii. Check current allowance: token.allowance(user, vault).\r\n///                  If already sufficient, skip.\r\n///             iv.  Send token.approve(vault, amount) and wait for\r\n///                  confirmation.\r\n///             v.   Repeat for the next ApproveAction.\r\n///         - After all approvals succeed, send the write transaction.\r\n///         - Add a \"Submit\" / \"Execute\" button that sends the transaction.\r\n///         - If method.inputs is empty, just render a single action button.\r\n///   5. All methods are displayed in the order returned by vaultUISchema().\r\n///   6. description() output is polled/refreshed periodically as a live\r\n///      status indicator.\r\n\r\n// ──────────────────────────────────────────────────────────────────────────────\r\n//  Shared Struct Definitions\r\n// ──────────────────────────────────────────────────────────────────────────────\r\n\r\n/// @notice Describes a single field (parameter, return value, or vault-data component).\r\n///\r\n/// @dev   This is the unified leaf type used in both:\r\n///        - `VaultDataSchema.fields`   (factory → describes vaultData encoding)\r\n///        - `VaultMethodSchema.inputs` / `VaultMethodSchema.outputs`  (vault UI)\r\n///\r\n///        Keeping one struct avoids duplication and ensures the type system is\r\n///        consistent across the protocol.\r\n///\r\n/// @param name        Machine-readable name of the field.\r\n///                    Examples: \"xHandle\", \"recipient\", \"bps\", \"user\", \"amount\".\r\n///\r\n/// @param fieldType   Solidity ABI type string for the field.\r\n///                    Examples: \"string\", \"address\", \"uint16\", \"uint256\", \"uint128\",\r\n///                    \"bool\", \"bytes\", \"bytes32\".\r\n///                    The special value \"time\" is an alias for \"uint256\".\r\n///                    The encoded value is a Unix timestamp in seconds.\r\n///                    For ABI encoding purposes the UI treats \"time\"\r\n///                    identically to \"uint256\".\r\n///                    - As an **input**, the UI renders a date/time picker.\r\n///                    - As an **output**, the UI renders the value as a\r\n///                      human-readable time string or a countdown clock.\r\n///                    The special value \"msg.value\" indicates that the field\r\n///                    represents the native currency amount (in wei) to send\r\n///                    with the transaction.  Rules for this type:\r\n///                    - This field is **NOT** ABI-encoded into the function\r\n///                      call data.  Instead, the UI uses the value as the\r\n///                     call's `value` (i.e `msg.value`).\r\n///                    - It **MUST** only be used for inputs in write methods\r\n///                      (i.e. when `VaultMethodSchema.isWriteMethod == true`).\r\n///                    - Only one \"msg.value\" field per method is meaningful.\r\n///                    Example – a payable deposit method:\r\n///                    //   function deposit() public payable\r\n///                    //\r\n///                    //   inputs[0] = FieldDescriptor(\r\n///                    //       \"amount\", \"msg.value\",\r\n///                    //       \"Amount to deposit (BNB)\", 18\r\n///                    //   );\r\n///                    The UI uses this to decide which input widget to render\r\n///                    and how to ABI-encode/decode the value.\r\n///\r\n/// @param description Human-readable explanation of the field, shown as a\r\n///                    label or tooltip in the UI.\r\n///                    Examples: \"The Twitter/X handle of the token creator\",\r\n///                    \"Recipient wallet address\",\r\n///                    \"Basis points share (10000 = 100%)\".\r\n///\r\n/// @param decimals    Decimal precision hint for numeric fields.\r\n///                    - For **factory encoding** (VaultDataSchema context):\r\n///                      If decimals > 0, the UI multiplies the user's input\r\n///                      by 10^decimals before ABI-encoding.\r\n///                      e.g. decimals=18: user types \"1\" → encoded as 1e18.\r\n///                      If decimals == 0, the raw value is used as-is.\r\n///                    - For **vault display** (VaultMethodSchema.outputs context):\r\n///                      If decimals > 0, the UI divides the raw on-chain value\r\n///                      by 10^decimals for display.\r\n///                      e.g. decimals=18: raw 1.5e18 → display \"1.5\".\r\n///                      If decimals == 0, the raw value is displayed.\r\n///                    - For non-numeric fields (string, address, bytes, bool),\r\n///                      this should always be 0.\r\nstruct FieldDescriptor {\r\n    string name;\r\n    string fieldType;\r\n    string description;\r\n    uint8 decimals;\r\n}\r\n\r\n// ──────────────────────────────────────────────────────────────────────────────\r\n//  Factory-side: VaultDataSchema\r\n// ──────────────────────────────────────────────────────────────────────────────\r\n\r\n/// @notice Describes the shape of the `vaultData` bytes expected by a\r\n///         VaultFactory's `newVault()` method.\r\n///\r\n/// @dev   A UI calls `factory.vaultDataSchema()` to discover what fields the\r\n///        factory expects, then renders a form accordingly.\r\n///\r\n///        The current spec supports two shapes:\r\n///          • A single tuple   (`isArray == false`)  – e.g. `(string)`\r\n///          • An array of tuples (`isArray == true`)  – e.g. `(address,uint16)[]`\r\n///\r\n///        If `fields` is empty and `isArray` is false, the factory ignores\r\n///        `vaultData` entirely (e.g. SnowBallFactory).\r\n///\r\n/// @param description  Free-form string explaining what the vault does and\r\n///                     what data is required. Shown to the user in the UI.\r\n///\r\n/// @param fields       Ordered list of field descriptors.  Each entry maps to\r\n///                     one component of the ABI-encoded tuple.\r\n///                     For a factory that requires no user input, return an\r\n///                     empty array.\r\n///\r\n/// @param isArray      If true, `vaultData` is ABI-encoded as `tuple[]`\r\n///                     (an array of the tuple described by `fields`).\r\n///                     If false, `vaultData` is ABI-encoded as a single `tuple`.\r\nstruct VaultDataSchema {\r\n    string description;\r\n    FieldDescriptor[] fields;\r\n    bool isArray;\r\n}\r\n\r\n// ──────────────────────────────────────────────────────────────────────────────\r\n//  Vault-side: VaultUISchema & supporting types\r\n// ──────────────────────────────────────────────────────────────────────────────\r\n\r\n/// @notice An ERC-20 `approve` action that the UI must execute **before**\r\n///         calling a write method on the vault.\r\n///\r\n/// @dev   The spender is always the vault contract itself.\r\n///\r\n///        Known `tokenType` values:\r\n///          • \"taxToken\" – the vault's own tax token (resolved via vault.taxToken())\r\n///          • \"lpToken\"  – the associated LP token  (resolved via vault.lpToken())\r\n///\r\n///        New token types can be added in the future without changing this\r\n///        struct.  The UI **MUST** ignore any `tokenType` it does not\r\n///        recognise (forward-compatible).\r\n///\r\n///        Workflow for the UI when processing an ApproveAction:\r\n///          1. Resolve the token address from `tokenType`:\r\n///               \"taxToken\" → call vault.taxToken()\r\n///               \"lpToken\"  → call vault.lpToken()\r\n///               unknown    → skip with a warning (forward-compatible)\r\n///          2. Read the amount from the user's input field whose `name`\r\n///             matches `amountFieldName`.  The value should already be\r\n///             scaled by the field's `decimals`.\r\n///          3. Check current allowance: token.allowance(user, vault).\r\n///             If already sufficient, skip.\r\n///          4. Send token.approve(vault, amount) and wait for confirmation.\r\n///\r\n/// @param tokenType       Which token to approve.\r\n///                        e.g. \"taxToken\", \"lpToken\".\r\n///\r\n/// @param amountFieldName The `name` of the write method's input\r\n///                        FieldDescriptor whose value should be used as\r\n///                        the approve amount.\r\n///                        e.g. \"amount\".\r\nstruct ApproveAction {\r\n    string tokenType;\r\n    string amountFieldName;\r\n}\r\n\r\n/// @notice Describes a single view or write method that the UI should render\r\n///         for a vault.\r\n///\r\n/// @dev   The UI iterates over the `methods` array in `VaultUISchema` and\r\n///        renders each method as either a read-only query panel (view) or an\r\n///        interactive form (write).\r\n///\r\n/// @param name          Solidity method name.\r\n///                      e.g. \"claim\", \"getRecipientsInfo\", \"stats\",\r\n///                      \"dispatch\", \"transitState\", \"manageByProof\".\r\n///\r\n/// @param description   Human-readable explanation of what the method does.\r\n///                      Shown as a subtitle or tooltip in the UI.\r\n///\r\n/// @param inputs        Ordered list of input parameters.\r\n///                      Empty for no-arg methods (e.g. dispatch(), stats()).\r\n///                      Each FieldDescriptor maps to one method parameter.\r\n///\r\n/// @param outputs       Ordered list of return values.\r\n///                      Empty for write methods (tx receipts don't return data\r\n///                      to the UI).\r\n///                      Each FieldDescriptor maps to one return component.\r\n///                      The UI uses `field.decimals` to format numeric values\r\n///                      for display.\r\n///\r\n/// @param approvals     Ordered list of ERC-20 approve actions the UI must\r\n///                      execute **before** sending the write transaction.\r\n///                      The spender is always the vault contract.\r\n///                      Empty for:\r\n///                        • view methods\r\n///                        • write methods that need no prior approval\r\n///\r\n/// @param isInputArray  If true, the method's input is an array of tuples\r\n///                      described by `inputs`.  The UI should render an\r\n///                      \"Add Row\" button for dynamic array entries.\r\n///\r\n/// @param isOutputArray If true, the method's return value is an array of\r\n///                      tuples described by `outputs`.  The UI should render\r\n///                      results as a table with one row per element.\r\n///\r\n/// @param isWriteMethod If true, the method is state-changing and the UI\r\n///                      should send a transaction.\r\n///                      If false, the method is a view call and the UI\r\n///                      should display the returned data.\r\nstruct VaultMethodSchema {\r\n    string name;\r\n    string description;\r\n    FieldDescriptor[] inputs;\r\n    FieldDescriptor[] outputs;\r\n    ApproveAction[] approvals;\r\n    bool isInputArray;\r\n    bool isOutputArray;\r\n    bool isWriteMethod;\r\n}\r\n\r\n/// @notice Top-level schema describing the vault's entire UI surface.\r\n///\r\n/// @dev   Returned by `VaultBaseV2.vaultUISchema()`.  The UI uses this to\r\n///        build the complete vault interaction page.\r\n///\r\n///        Rendering algorithm:\r\n///          1. Display `vaultType` as a badge/header.\r\n///          2. Display `description` as a subtitle.\r\n///          3. Always call `vault.description()` and display the result as a\r\n///             dynamic status banner (polled/refreshed periodically).\r\n///          4. Iterate `methods` and render each one according to its\r\n///             `isWriteMethod` flag (see VaultMethodSchema docs above).\r\n///          5. Methods are displayed in the order returned.\r\n///\r\n/// @param vaultType    Human-readable vault type identifier.\r\n///                     e.g. \"FlapXVault\", \"SplitVault\", \"SnowBallVault\",\r\n///                     \"BlackHoleVault\".\r\n///\r\n/// @param description  Overall explanation of the vault for the UI.\r\n///                     e.g. \"Distributes received BNB among a fixed set of\r\n///                     recipients by basis-point shares.\"\r\n///\r\n/// @param methods      All methods the UI should render.\r\n///                     Ordered — the UI displays them in this order.\r\nstruct VaultUISchema {\r\n    string vaultType;\r\n    string description;\r\n    VaultMethodSchema[] methods;\r\n}\r\n\r\n// ──────────────────────────────────────────────────────────────────────────────\r\n//  Factory-side: FactoryPolicy (VaultFactoryBaseV2 v2.1)\r\n// ──────────────────────────────────────────────────────────────────────────────\r\n\r\n/// @notice A single constraint on the parameters passed to newTokenV6WithVault.\r\n///\r\n/// @dev    Inspired by AWS IAM policy conditions.  Each FactoryPolicy describes\r\n///         one constraint: \"field <target> must satisfy <operator> <value>\".\r\n///\r\n///         These policies are **informational only** — they are intended for\r\n///         UI rendering (inline validation hints, error messages).  The actual\r\n///         enforcement always happens inside `onBeforeNewTokenV6WithVault`.\r\n///\r\n///         Factories that override `onBeforeNewTokenV6WithVault` SHOULD also\r\n///         override `tokenCreationPolicies()` to describe their constraints in\r\n///         this machine-readable form so the UI can surface them proactively.\r\n///\r\n///\r\n/// ── FIELD REFERENCE ────────────────────────────────────────────────────────\r\n///\r\n/// @param target      The Solidity field name in\r\n///                    IVaultPortalTypes.NewTokenV6WithVaultParams that this\r\n///                    constraint applies to.\r\n///                    Examples: \"dividendToken\", \"dividendBps\", \"taxBps\",\r\n///                    \"quoteToken\", \"migratorType\".\r\n///\r\n/// @param operator    The comparison operator.  Supported values:\r\n///                    \"eq\"    – field must equal value\r\n///                    \"neq\"   – field must not equal value\r\n///                    \"gt\"    – field must be strictly greater than value\r\n///                    \"gte\"   – field must be greater than or equal to value\r\n///                    \"lt\"    – field must be strictly less than value\r\n///                    \"lte\"   – field must be less than or equal to value\r\n///                    \"in\"    – field must be one of a set; `value` is an\r\n///                              ABI-encoded dynamic array of that field's type\r\n///                    \"notIn\" – field must not be any of a set (same encoding)\r\n///                    Unknown operators MUST be ignored by the UI (forward-compat).\r\n///\r\n/// @param value       ABI-encoded expected value.\r\n///                    Since `target` identifies a specific field in\r\n///                    NewTokenV6WithVaultParams whose Solidity type is fixed,\r\n///                    the UI already knows the ABI type and can decode `value`\r\n///                    directly without an additional type hint.\r\n///                    For \"in\" / \"notIn\", `value` is an ABI-encoded dynamic\r\n///                    array of that field's element type.\r\n///\r\n/// @param description Human-readable explanation shown as a tooltip or inline\r\n///                    validation hint in the UI.\r\n///                    Examples:\r\n///                    \"Dividend token must equal the quote token (WBNB).\"\r\n///                    \"Dividend BPS must be at least 100 (1%).\"\r\n///\r\n///\r\n/// ── EXAMPLES ───────────────────────────────────────────────────────────────\r\n///\r\n///   // dividendToken must equal WBNB\r\n///   FactoryPolicy({\r\n///       target:      \"dividendToken\",\r\n///       operator:    \"eq\",\r\n///       value:       abi.encode(WBNB_ADDRESS),\r\n///       description: \"Dividend token must equal the quote token (WBNB).\"\r\n///   })\r\n///\r\n///   // dividendBps must be >= 100 (1%)\r\n///   FactoryPolicy({\r\n///       target:      \"dividendBps\",\r\n///       operator:    \"gte\",\r\n///       value:       abi.encode(uint256(100)),\r\n///       description: \"Dividend BPS must be at least 100 (1%).\"\r\n///   })\r\n///\r\n///   // quoteToken must be one of an allowed set\r\n///   FactoryPolicy({\r\n///       target:      \"quoteToken\",\r\n///       operator:    \"in\",\r\n///       value:       abi.encode(allowedAddresses),   // address[]\r\n///       description: \"Quote token must be WBNB or USDT.\"\r\n///   })\r\n///\r\nstruct FactoryPolicy {\r\n    string target;\r\n    string operator;\r\n    bytes value;\r\n    string description;\r\n}"},"flap/IAIDROPServices.sol":{"content":"// SPDX-License-Identifier: MIT\r\npragma solidity ^0.8.24;\r\n\r\ninterface IFlapVaultEx {\r\n    struct ExactInputParams { address inputToken; address outputToken; uint256 inputAmount; }\r\n    function requestSwap(ExactInputParams calldata params) external payable returns (uint256 requestId);\r\n}\r\n\r\ninterface IFlapTriggerService {\r\n    function getFee() external view returns (uint256);\r\n    function getMaxCallbackGas() external view returns (uint256);\r\n    function requestTrigger(uint64 executeAfter) external payable returns (uint256 requestId);\r\n}\r\n\r\ninterface IAIDROPWBNB {\r\n    function withdraw(uint256 amount) external;\r\n}\r\n"},"agent/AIDROPRewards.sol":{"content":"// SPDX-License-Identifier: MIT\r\npragma solidity ^0.8.24;\r\n\r\nimport {IERC20} from \"@openzeppelin/contracts@4.9.6/token/ERC20/IERC20.sol\";\r\nimport {SafeERC20} from \"@openzeppelin/contracts@4.9.6/token/ERC20/utils/SafeERC20.sol\";\r\nimport {Strings} from \"@openzeppelin/contracts@4.9.6/utils/Strings.sol\";\r\nimport {Math} from \"@openzeppelin/contracts@4.9.6/utils/math/Math.sol\";\r\nimport {Initializable} from \"@openzeppelin/contracts-upgradeable@4.9.6/proxy/utils/Initializable.sol\";\r\nimport {IERC165} from \"@openzeppelin/contracts@4.9.6/utils/introspection/IERC165.sol\";\r\nimport {AIDROPRegistrationNFT} from \"./AIDROPRegistrationNFT.sol\";\r\n\r\ninterface IRewardsEligibilityNFT {\r\n    function balanceOf(address user) external view returns (uint256);\r\n}\r\n\r\ninterface IAIDROPRewardsVault {\r\n    function taxToken() external view returns (address);\r\n    function rewardsContract() external view returns (address);\r\n    function initialized() external view returns (bool);\r\n    function guardian() external view returns (address);\r\n    function isBusy() external view returns (bool);\r\n}\r\n\r\ninterface IRewardsV2Router {\r\n    function factory() external view returns (address);\r\n    function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory);\r\n    function swapExactTokensForTokensSupportingFeeOnTransferTokens(uint256 amountIn, uint256 minOut,\r\n        address[] calldata path, address to, uint256 deadline) external;\r\n}\r\n\r\ninterface IRewardsV3Pool {\r\n    function token0() external view returns (address);\r\n    function token1() external view returns (address);\r\n    function factory() external view returns (address);\r\n    function fee() external view returns (uint24);\r\n    function liquidity() external view returns (uint128);\r\n    function slot0() external view returns (uint160, int24, uint16, uint16, uint16, uint32, bool);\r\n}\r\n\r\ninterface IRewardsV2Factory {\r\n    function getPair(address tokenA, address tokenB) external view returns (address);\r\n}\r\n\r\ninterface IRewardsV2Pair {\r\n    function token0() external view returns (address);\r\n    function token1() external view returns (address);\r\n    function getReserves() external view returns (uint112, uint112, uint32);\r\n    function price0CumulativeLast() external view returns (uint256);\r\n    function price1CumulativeLast() external view returns (uint256);\r\n}\r\n\r\n/// @notice Beacon-backed .ai-domain enrollment, funded daily rounds and fixed-route buy execution.\r\n/// @dev Rounds can only start through the bound vault's authenticated Trigger callback.\r\n///      Finalized batches are permissionlessly forwarded by the vault. The project V2 price uses TWAP,\r\n///      the BNCB/USDT price remains spot, and randomness remains influenceable block pseudorandomness. No gas refund is paid here.\r\ncontract AIDROPRewards is Initializable {\r\n    bool public initialized;\r\n    address public vault;\r\n    address public taxToken;\r\n    address public registrationNFT;\r\n    uint256 private locked;\r\n\r\n    event Initialized(address indexed vault, address indexed taxToken);\r\n\r\n    constructor() { _disableInitializers(); }\r\n\r\n    modifier onlyInitialized() { require(initialized, \"NOT_INIT\"); _; }\r\n    modifier onlyVault() { require(msg.sender == vault, \"ONLY_VAULT\"); _; }\r\n    modifier onlyGuardian() { require(msg.sender == IAIDROPRewardsVault(vault).guardian(), \"ONLY_GUARDIAN\"); _; }\r\n    modifier nonReentrant() {\r\n        require(locked == 0, \"REENTRANT\");\r\n        // The vault's forwarding entry is itself locked; external calls must find it idle.\r\n        if (msg.sender != vault) require(!IAIDROPRewardsVault(vault).isBusy(), \"VAULT_BUSY\");\r\n        locked = 1; _; locked = 0;\r\n    }\r\n\r\n    function isBusy() external view returns (bool) { return locked != 0; }\r\n\r\n    function initialize(address vault_, address registrationNFT_) external initializer {\r\n        require(block.chainid == 56, \"BSC_MAINNET_ONLY\");\r\n        require(vault_.code.length > 0, \"VAULT\");\r\n        IAIDROPRewardsVault source = IAIDROPRewardsVault(vault_);\r\n        require(source.initialized() && source.rewardsContract() == address(this), \"VAULT_BINDING\");\r\n        address token = source.taxToken();\r\n        require(token != address(0), \"TOKEN\");\r\n        require(registrationNFT_.code.length > 0, \"REGISTRATION_NFT\");\r\n        AIDROPRegistrationNFT receipt = AIDROPRegistrationNFT(registrationNFT_);\r\n        require(receipt.vault() == vault_ && receipt.rewardsContract() == address(this)\r\n            && receipt.taxToken() == token, \"REGISTRATION_NFT_BINDING\");\r\n        initialized = true; vault = vault_; taxToken = token;\r\n        registrationNFT = registrationNFT_;\r\n        _validateEligibilityNFT();\r\n        emit RewardsNFTConfigured(eligibilityNFT);\r\n        emit Initialized(vault_, token);\r\n    }\r\n\r\n    function registerForRewards() external onlyInitialized nonReentrant {\r\n        _registerRewardsUser(msg.sender);\r\n    }\r\n    function registerFromVault(address user) external onlyInitialized onlyVault nonReentrant {\r\n        _registerRewardsUser(user);\r\n    }\r\n    function startRoundFromVault(uint256 triggerRequestId) external onlyInitialized onlyVault nonReentrant returns (bool) {\r\n        return _startRewardRound(triggerRequestId);\r\n    }\r\n    function distributeFromVault(uint256 count) external onlyInitialized onlyVault nonReentrant {\r\n        _distributeRewardBatch(count);\r\n    }\r\n\r\n    // Fixed BSC mainnet ERC-721; every clone uses the same eligibility collection.\r\n    address public constant eligibilityNFT = 0x60B9262456dE011F7d15a54CdD8c48bAaa8B022F;\r\n    address[] private enrolledUsers;\r\n    // Unpaid, never-winning registrants. Current-round candidates occupy a shrinking prefix.\r\n    address[] private eligibleUsers;\r\n    mapping(address => bool) public hasWonReward;\r\n    mapping(address => bool) public isRegistered;\r\n\r\n    event RewardsNFTConfigured(address indexed nft);\r\n    event RewardsRegistered(address indexed user, uint256 indexed index);\r\n\r\n    function _validateEligibilityNFT() private view {\r\n        require(eligibilityNFT.code.length > 0, \"NFT_CONTRACT\");\r\n        require(IERC165(eligibilityNFT).supportsInterface(0x80ac58cd), \"NOT_ERC721\");\r\n    }\r\n\r\n    function _registerRewardsUser(address user) internal {\r\n        require(!isRegistered[user], \"ALREADY_REGISTERED\");\r\n        require(IRewardsEligibilityNFT(eligibilityNFT).balanceOf(user) > 0, \"NO_ELIGIBLE_NFT\");\r\n        isRegistered[user] = true;\r\n        uint256 index = enrolledUsers.length;\r\n        enrolledUsers.push(user);\r\n        eligibleUsers.push(user);\r\n        AIDROPRegistrationNFT(registrationNFT).mintForRegistration(user);\r\n        emit RewardsRegistered(user, index);\r\n    }\r\n\r\n    function registeredCount() public view returns (uint256) { return enrolledUsers.length; }\r\n\r\n    function registeredUserAt(uint256 index) public view returns (address) { return enrolledUsers[index]; }\r\n\r\n    function eligibleCount() public view returns (uint256) { return eligibleUsers.length; }\r\n\r\n    function getEligibleUsers(uint256 offset, uint256 limit) external view returns (address[] memory users) {\r\n        require(limit <= 1000, \"PAGE_TOO_LARGE\");\r\n        if (offset >= eligibleUsers.length) return new address[](0);\r\n        uint256 size = eligibleUsers.length - offset;\r\n        if (size > limit) size = limit;\r\n        users = new address[](size);\r\n        for (uint256 i; i < size; ++i) users[i] = eligibleUsers[offset + i];\r\n    }\r\n\r\n    function getRegisteredUsers(uint256 offset, uint256 limit) external view returns (address[] memory users) {\r\n        require(limit <= 1000, \"PAGE_TOO_LARGE\");\r\n        uint256 length = enrolledUsers.length;\r\n        if (offset >= length) return new address[](0);\r\n        uint256 size = length - offset;\r\n        if (size > limit) size = limit;\r\n        users = new address[](size);\r\n        for (uint256 i; i < size; ++i) users[i] = enrolledUsers[offset + i];\r\n    }\r\n\r\n    using SafeERC20 for IERC20;\r\n\r\n    uint256 public constant MAX_REWARD_WINNERS = 1000;\r\n    uint256 public constant MAX_REWARD_BATCH_SIZE = 100;\r\n    uint256 public constant REWARD_INTERVAL = 1 days;\r\n    uint256 private constant QUOTE_GAS_LIMIT = 300_000;\r\n    uint256 public constant TWAP_WINDOW = 1 hours;\r\n    uint256 public constant TWAP_MAX_WINDOW = 6 hours;\r\n    uint256 public constant TWAP_SAMPLE_INTERVAL = 1 minutes;\r\n    uint256 private constant TWAP_CAPACITY = 62;\r\n    address public constant REWARD_USDT = 0x55d398326f99059fF775485246999027B3197955;\r\n    address public constant BNCB = 0x4902C5ebc598265Ed2212b559B042De8a5Eeec3f;\r\n    address public constant BNCB_USDT_POOL = 0xb754F10CebF50B8862444cF4D6c1cC31A3B71c18;\r\n    uint24 public constant BNCB_USDT_FEE = 500;\r\n    address private constant V3_FACTORY = 0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865;\r\n    uint256 public constant BUY_BNCB = 10 ether;\r\n    uint256 public constant REWARD_USDT_AMOUNT = 6 ether; // BSC Binance-Peg USDT uses 18 decimals.\r\n    address private constant REWARD_WBNB = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;\r\n    address private constant REWARD_ROUTER = 0x10ED43C718714eb63d5aA57B78B54704E256024E;\r\n\r\n    struct RewardRound {\r\n        uint256 startedAt;\r\n        uint256 participantCount;\r\n        uint256 winnerCount;\r\n        uint256 amountEach;\r\n        uint256 paidCount;\r\n        bytes32 seed;\r\n    }\r\n\r\n    uint256 public currentRewardRound;\r\n    uint256 public reservedRewardTokens;\r\n    mapping(uint256 => RewardRound) public rewardRounds;\r\n    mapping(uint256 => mapping(address => bool)) public rewardPaid;\r\n    mapping(uint256 => mapping(uint256 => address)) public rewardWinners;\r\n\r\n    event RewardRoundStarted(uint256 indexed roundId, uint256 participantCount,\r\n        uint256 winnerCount, uint256 amountEach, uint256 reservedAmount, bytes32 seed);\r\n    event RewardPaid(uint256 indexed roundId, address indexed user, uint256 indexed winnerIndex, uint256 grossAmount);\r\n    event RewardBatchPaid(uint256 indexed roundId, uint256 fromIndex, uint256 count, uint256 paidCount);\r\n    event RewardRoundCompleted(uint256 indexed roundId);\r\n    event RewardQuoteUnavailable(uint256 indexed triggerRequestId);\r\n\r\n    function _rewardTokenAddress() internal view returns (address) { return taxToken; }\r\n\r\n    /// @notice Reward uses project/BNCB V2 TWAP and the BNCB/USDT V3 spot price.\r\n    /// @dev Use the most recent observation that is at least one and at most six hours old.\r\n    ///      No spot-price fallback or per-round price-change cap. Gross token amount only.\r\n    function getRewardQuote() public view returns (uint256 amountEach, uint256 bncbWeiEach) {\r\n        (address pair, uint256 cumulative) = _tokenCumulativePrice();\r\n        require(pair == twapPair && twapObservations.length > 0, \"TWAP_NOT_READY\");\r\n        uint256 length = twapObservations.length;\r\n        uint256 first = length == TWAP_CAPACITY ? twapHead : 0;\r\n        uint256 target = block.timestamp - TWAP_WINDOW;\r\n        uint256 low;\r\n        uint256 high = length;\r\n        // Latest observation at or before the start of the one-hour window.\r\n        while (low < high) {\r\n            uint256 mid = (low + high) / 2;\r\n            if (twapObservations[(first + mid) % length].timestamp <= target) low = mid + 1;\r\n            else high = mid;\r\n        }\r\n        require(low > 0, \"TWAP_NOT_READY\");\r\n        OracleObservation storage past = twapObservations[(first + low - 1) % length];\r\n        uint256 elapsed = block.timestamp - past.timestamp;\r\n        require(elapsed <= TWAP_MAX_WINDOW, \"TWAP_STALE\");\r\n        uint256 delta;\r\n        unchecked { delta = cumulative - past.cumulative; }\r\n        uint256 priceX112 = delta / elapsed;\r\n        require(priceX112 > 0, \"TWAP_ZERO\");\r\n        bncbWeiEach = _quoteBNCBForUSDT(REWARD_USDT_AMOUNT);\r\n        amountEach = Math.mulDiv(bncbWeiEach, priceX112, uint256(1) << 112, Math.Rounding.Up);\r\n        require(amountEach > 0, \"ZERO_REWARD\");\r\n    }\r\n\r\n    /// @notice Earliest time existing project-pool history can satisfy the TWAP window.\r\n    /// @dev Zero means history is ready; other quote failures keep the normal retry policy.\r\n    ///      Reverts on missing/invalid project liquidity. Callbacks seed history beforehand.\r\n    function rewardOracleRetryAt() external view returns (uint256) {\r\n        (address pair,) = _tokenCumulativePrice();\r\n        uint256 length = twapObservations.length;\r\n        if (pair != twapPair || length == 0) return block.timestamp + TWAP_WINDOW;\r\n        uint256 first = length == TWAP_CAPACITY ? twapHead : 0;\r\n        uint256 cutoff = block.timestamp - TWAP_MAX_WINDOW;\r\n        uint256 low;\r\n        uint256 high = length;\r\n        // Ignore checkpoints that have permanently aged beyond the permitted window.\r\n        while (low < high) {\r\n            uint256 mid = (low + high) / 2;\r\n            if (twapObservations[(first + mid) % length].timestamp < cutoff) low = mid + 1;\r\n            else high = mid;\r\n        }\r\n        if (low == length) return block.timestamp + TWAP_WINDOW;\r\n        uint256 readyAt = twapObservations[(first + low) % length].timestamp + TWAP_WINDOW;\r\n        return readyAt > block.timestamp ? readyAt : 0;\r\n    }\r\n\r\n    /// @notice Anyone may maintain the oracle. At most one checkpoint per minute is stored.\r\n    /// @dev Trigger callbacks also attempt this; if buys stop, external keepers can continue.\r\n    function updateRewardOracle() external onlyInitialized nonReentrant {\r\n        (address pair, uint256 cumulative) = _tokenCumulativePrice();\r\n        if (pair != twapPair) {\r\n            twapPair = pair;\r\n            delete twapObservations;\r\n            twapHead = 0;\r\n        }\r\n        uint256 length = twapObservations.length;\r\n        if (length > 0) {\r\n            uint256 last = length < TWAP_CAPACITY ? length - 1 : (twapHead + TWAP_CAPACITY - 1) % TWAP_CAPACITY;\r\n            if (block.timestamp < twapObservations[last].timestamp + TWAP_SAMPLE_INTERVAL) return;\r\n        }\r\n        OracleObservation memory observation = OracleObservation(block.timestamp, cumulative);\r\n        if (length < TWAP_CAPACITY) twapObservations.push(observation);\r\n        else {\r\n            twapObservations[twapHead] = observation;\r\n            twapHead = (twapHead + 1) % TWAP_CAPACITY;\r\n        }\r\n        emit RewardOracleUpdated(pair, block.timestamp, cumulative);\r\n    }\r\n\r\n    /// @dev Token raw units per BNCB, counterfactually accumulated to the current timestamp.\r\n    ///      V2 cumulative prices and uint32 pool timestamps intentionally wrap.\r\n    function _tokenCumulativePrice() private view returns (address pair, uint256 cumulative) {\r\n        pair = IRewardsV2Factory(IRewardsV2Router(REWARD_ROUTER).factory()).getPair(taxToken, BNCB);\r\n        (uint256 tokens, uint256 bncb) = _pairReserves(pair, taxToken, BNCB);\r\n        IRewardsV2Pair source = IRewardsV2Pair(pair);\r\n        bool tokenIsZero = source.token0() == taxToken;\r\n        cumulative = tokenIsZero ? source.price1CumulativeLast() : source.price0CumulativeLast();\r\n        (,, uint32 timestamp) = source.getReserves();\r\n        unchecked {\r\n            uint32 elapsed = uint32(block.timestamp) - timestamp;\r\n            cumulative += ((tokens << 112) / bncb) * elapsed;\r\n        }\r\n    }\r\n\r\n    function _quoteBNCBForUSDT(uint256 usdtAmount) private view returns (uint256 bncbAmount) {\r\n        uint160 sqrtPriceX96 = _bncbPoolPrice();\r\n        // This fixed pool has BNCB as token0 and USDT as token1. Use full-precision\r\n        // mulDiv and round up; token decimal scaling is already in raw pool prices.\r\n        if (sqrtPriceX96 <= type(uint128).max) {\r\n            uint256 ratioX192 = uint256(sqrtPriceX96) * sqrtPriceX96;\r\n            bncbAmount = Math.mulDiv(usdtAmount, uint256(1) << 192, ratioX192, Math.Rounding.Up);\r\n        } else {\r\n            uint256 ratioX128 = Math.mulDiv(sqrtPriceX96, sqrtPriceX96, uint256(1) << 64);\r\n            bncbAmount = Math.mulDiv(usdtAmount, uint256(1) << 128, ratioX128, Math.Rounding.Up);\r\n        }\r\n    }\r\n\r\n    /// @notice Sizing for VaultEx fee top-ups; does not require the project's V2 pair.\r\n    /// @dev Spot prices may be influenced. VaultEx independently executes and checks its own fill.\r\n    function quoteBNCBForBNB(uint256 bnbAmount) external view returns (uint256 bncbAmount) {\r\n        address factory = IRewardsV2Router(REWARD_ROUTER).factory();\r\n        address pair = IRewardsV2Factory(factory).getPair(REWARD_WBNB, REWARD_USDT);\r\n        (uint256 bnbReserve, uint256 usdtReserve) = _pairReserves(pair, REWARD_WBNB, REWARD_USDT);\r\n        uint256 usdtAmount = Math.mulDiv(bnbAmount, usdtReserve, bnbReserve, Math.Rounding.Up);\r\n        bncbAmount = _quoteBNCBForUSDT(usdtAmount);\r\n        require(bncbAmount > 0, \"ZERO_FUNDING_QUOTE\");\r\n    }\r\n\r\n    function _bncbPoolPrice() private view returns (uint160 sqrtPriceX96) {\r\n        IRewardsV3Pool pool = IRewardsV3Pool(BNCB_USDT_POOL);\r\n        require(pool.token0() == BNCB && pool.token1() == REWARD_USDT\r\n            && pool.factory() == V3_FACTORY && pool.fee() == BNCB_USDT_FEE, \"BNCB_POOL_BINDING\");\r\n        bool unlocked;\r\n        (sqrtPriceX96,,,,,, unlocked) = pool.slot0();\r\n        require(unlocked && sqrtPriceX96 > 0 && pool.liquidity() > 0, \"BNCB_POOL_STATE\");\r\n    }\r\n\r\n    function _path(address a, address b) private pure returns (address[] memory path) {\r\n        path = new address[](2); path[0] = a; path[1] = b;\r\n    }\r\n\r\n    /// @notice Current exact-input V2 quote and the configured slippage floor for 10 BNCB.\r\n    /// @dev Spot quotes remain influenceable. Buy tax is read live, separately from cached commission tax.\r\n    function quoteBuy(uint16 slippageBps) external view returns (uint256 quotedOut, uint256 minOut) {\r\n        require(slippageBps <= 800, \"SLIPPAGE_TOO_HIGH\");\r\n        address pair = IRewardsV2Factory(IRewardsV2Router(REWARD_ROUTER).factory()).getPair(BNCB, taxToken);\r\n        _pairReserves(pair, BNCB, taxToken);\r\n        uint256[] memory amounts = IRewardsV2Router(REWARD_ROUTER).getAmountsOut(BUY_BNCB, _path(BNCB, taxToken));\r\n        quotedOut = amounts[1];\r\n        uint256 buyTax = _currentBuyTaxRate();\r\n        uint256 netOut = Math.mulDiv(quotedOut, 10_000 - buyTax, 10_000);\r\n        minOut = Math.mulDiv(netOut, 10_000 - slippageBps, 10_000);\r\n        require(minOut > 0, \"MIN_TOKEN_ZERO\");\r\n    }\r\n\r\n    function _currentBuyTaxRate() private view returns (uint256 rate) {\r\n        address target = taxToken;\r\n        bytes4 selector = bytes4(keccak256(\"buyTaxRate()\"));\r\n        bool ok;\r\n        uint256 size;\r\n        assembly (\"memory-safe\") {\r\n            let ptr := mload(0x40)\r\n            mstore(ptr, selector)\r\n            ok := staticcall(30000, target, ptr, 4, ptr, 32)\r\n            size := returndatasize()\r\n            rate := mload(ptr)\r\n        }\r\n        require(ok && size == 32 && rate < 10_000, \"BUY_TAX_UNAVAILABLE\");\r\n    }\r\n\r\n    /// @notice Pull exactly 10 BNCB from the vault, then buy the project token directly on V2.\r\n    /// @dev Previously held rewards and incidental BNCB balances are not spent.\r\n    function buyFromVault(uint256 minOut, uint256 deadline)\r\n        external onlyInitialized onlyVault nonReentrant {\r\n        require(minOut > 0, \"MIN_TOKEN_ZERO\");\r\n        require(deadline >= block.timestamp, \"DEADLINE\");\r\n        uint256 bncbBefore = IERC20(BNCB).balanceOf(address(this));\r\n        IERC20(BNCB).safeTransferFrom(vault, address(this), BUY_BNCB);\r\n        require(IERC20(BNCB).balanceOf(address(this)) - bncbBefore == BUY_BNCB, \"FIXED_BUY_AMOUNT\");\r\n        uint256 tokensBefore = IERC20(taxToken).balanceOf(address(this));\r\n        IERC20(BNCB).forceApprove(REWARD_ROUTER, BUY_BNCB);\r\n        IRewardsV2Router(REWARD_ROUTER).swapExactTokensForTokensSupportingFeeOnTransferTokens(\r\n            BUY_BNCB, minOut, _path(BNCB, taxToken), address(this), deadline);\r\n        IERC20(BNCB).forceApprove(REWARD_ROUTER, 0);\r\n        require(IERC20(BNCB).balanceOf(address(this)) == bncbBefore, \"BNCB_BUY_BALANCE\");\r\n        require(IERC20(taxToken).balanceOf(address(this)) - tokensBefore >= minOut, \"MIN_TOKEN_OUT\");\r\n    }\r\n\r\n    function _pairReserves(address pair, address base, address quote)\r\n        private view returns (uint256 baseReserve, uint256 quoteReserve) {\r\n        require(pair.code.length > 0, \"REWARD_PAIR_MISSING\");\r\n        address token0 = IRewardsV2Pair(pair).token0();\r\n        address token1 = IRewardsV2Pair(pair).token1();\r\n        require((token0 == base && token1 == quote) || (token0 == quote && token1 == base), \"REWARD_PAIR_TOKENS\");\r\n        (uint112 r0, uint112 r1,) = IRewardsV2Pair(pair).getReserves();\r\n        (baseReserve, quoteReserve) = token0 == base ? (uint256(r0), uint256(r1)) : (uint256(r1), uint256(r0));\r\n        require(baseReserve > 0 && quoteReserve > 0, \"REWARD_LIQUIDITY\");\r\n    }\r\n\r\n    function nextRewardRoundAt() public view returns (uint256) {\r\n        if (currentRewardRound == 0) return 0;\r\n        return rewardRounds[currentRewardRound].startedAt + REWARD_INTERVAL;\r\n    }\r\n\r\n    function _startRewardRound(uint256 triggerRequestId) internal returns (bool) {\r\n        if (currentRewardRound > 0) {\r\n            RewardRound storage previous = rewardRounds[currentRewardRound];\r\n            require(previous.paidCount == previous.winnerCount, \"REWARD_ROUND_ACTIVE\");\r\n            require(block.timestamp >= nextRewardRoundAt(), \"REWARD_INTERVAL\");\r\n        }\r\n        uint256 population = eligibleCount();\r\n        uint256 winners = population < MAX_REWARD_WINNERS ? population : MAX_REWARD_WINNERS;\r\n        uint256 amountEach;\r\n        uint256 total;\r\n        if (winners > 0) {\r\n            uint256 balance = IERC20(taxToken).balanceOf(address(this));\r\n            require(balance >= reservedRewardTokens, \"REWARD_RESERVE\");\r\n            uint256 freeBalance = balance - reservedRewardTokens;\r\n            if (freeBalance == 0) {\r\n                // A definitely empty prize pool can skip even before DEX liquidity exists.\r\n                winners = 0;\r\n            } else {\r\n                try this.getRewardQuote{gas: QUOTE_GAS_LIMIT}() returns (uint256 quotedAmount, uint256) {\r\n                    amountEach = quotedAmount;\r\n                } catch {\r\n                    emit RewardQuoteUnavailable(triggerRequestId);\r\n                    return false;\r\n                }\r\n                uint256 affordable = freeBalance / amountEach;\r\n                if (winners > affordable) winners = affordable;\r\n                total = amountEach * winners;\r\n            }\r\n        }\r\n        // Zero candidates or less than one complete reward creates a completed empty round.\r\n        // Nobody loses their lifetime eligibility; this round still consumes its 24h interval.\r\n        uint256 id = ++currentRewardRound;\r\n        bytes32 seed;\r\n        if (winners > 0) seed = keccak256(abi.encode(blockhash(block.number - 1), block.prevrandao,\r\n            block.timestamp, block.chainid, address(this), triggerRequestId, id, population, winners));\r\n        rewardRounds[id] = RewardRound(block.timestamp, population, winners, amountEach, 0, seed);\r\n        reservedRewardTokens = total;\r\n        emit RewardRoundStarted(id, population, winners, amountEach, total, seed);\r\n        if (winners == 0) emit RewardRoundCompleted(id);\r\n        return true;\r\n    }\r\n\r\n    function isRoundComplete() public view returns (bool) {\r\n        if (currentRewardRound == 0) return true;\r\n        RewardRound storage round = rewardRounds[currentRewardRound];\r\n        return round.paidCount == round.winnerCount;\r\n    }\r\n\r\n    function availableRewardTokens() public view returns (uint256) {\r\n        if (taxToken.code.length == 0) return 0;\r\n        uint256 balance = IERC20(taxToken).balanceOf(address(this));\r\n        return balance > reservedRewardTokens ? balance - reservedRewardTokens : 0;\r\n    }\r\n\r\n    event EmergencyTokenWithdrawn(address indexed token, address indexed to, uint256 amount);\r\n\r\n    /// @notice Guardian may recover only funds that are NOT reserved for the active round.\r\n    function emergencyWithdrawToken(address token, address to, uint256 amount)\r\n        external onlyInitialized onlyGuardian nonReentrant {\r\n        require(token.code.length > 0, \"TOKEN_CONTRACT\");\r\n        require(to != address(0) && to != address(this), \"RECIPIENT\");\r\n        uint256 balance = IERC20(token).balanceOf(address(this));\r\n        uint256 reserved = token == taxToken ? reservedRewardTokens : 0;\r\n        require(balance >= reserved && amount > 0 && amount <= balance - reserved, \"RESERVED_OR_INSUFFICIENT\");\r\n        IERC20(token).safeTransfer(to, amount);\r\n        require(IERC20(token).balanceOf(address(this)) >= reserved, \"REWARD_RESERVE\");\r\n        emit EmergencyTokenWithdrawn(token, to, amount);\r\n    }\r\n\r\n    /// @dev Number/order of batches cannot change winners. No cancellation/reseed or skip hook.\r\n    ///      A failed ERC20 transfer rolls back THIS batch, including permanent-win flags and pool removals.\r\n    function _distributeRewardBatch(uint256 count) internal {\r\n        require(count > 0 && count <= MAX_REWARD_BATCH_SIZE, \"REWARD_BATCH_SIZE\");\r\n        uint256 id = currentRewardRound;\r\n        require(id > 0, \"NO_REWARD_ROUND\");\r\n        RewardRound storage round = rewardRounds[id];\r\n        uint256 from = round.paidCount;\r\n        require(from < round.winnerCount, \"REWARD_ROUND_COMPLETE\");\r\n        uint256 end = from + count;\r\n        if (end > round.winnerCount) end = round.winnerCount;\r\n        IERC20 token = IERC20(_rewardTokenAddress());\r\n\r\n        for (uint256 i = from; i < end; ++i) {\r\n            uint256 remaining = round.participantCount - i;\r\n            uint256 position = uint256(keccak256(abi.encode(round.seed, i))) % remaining;\r\n            address user = eligibleUsers[position];\r\n            require(!hasWonReward[user] && !rewardPaid[id][user], \"REWARD_ALREADY_PAID\");\r\n            // Compact the snapshot prefix first. Move a late registrant only OUTSIDE\r\n            // the next prefix, so registrations during a round cannot enter its draw.\r\n            uint256 lastCandidate = remaining - 1;\r\n            eligibleUsers[position] = eligibleUsers[lastCandidate];\r\n            uint256 lastActual = eligibleUsers.length - 1;\r\n            if (lastCandidate != lastActual) eligibleUsers[lastCandidate] = eligibleUsers[lastActual];\r\n            eligibleUsers.pop();\r\n            hasWonReward[user] = true;\r\n            rewardPaid[id][user] = true;\r\n            rewardWinners[id][i] = user;\r\n            round.paidCount = i + 1;\r\n            reservedRewardTokens -= round.amountEach;\r\n            token.safeTransfer(user, round.amountEach);\r\n            emit RewardPaid(id, user, i, round.amountEach);\r\n        }\r\n        require(token.balanceOf(address(this)) >= reservedRewardTokens, \"REWARD_RESERVE\");\r\n        emit RewardBatchPaid(id, from, end - from, end);\r\n        if (end == round.winnerCount) emit RewardRoundCompleted(id);\r\n    }\r\n\r\n    /// @notice Shared display formatter; keeping it here leaves the vault room for fee safety.\r\n    function commissionDescription(uint256 rate, bool resolved, address receiver, uint256 pending)\r\n        external pure returns (string memory) {\r\n        return string.concat(\r\n            \"drop.ai: Commission on new BNCB revenue R: if cached tax rate t <= 100 bps, floor(R*600/10000); otherwise floor(R*6/t), rounded down in base units. Cache the first valid tax rate; hold income until resolved. The initial operator is the fixed feeReceiver; role removal does not change it. At 20 BNCB, attempt full payment during a buy; failed payments remain owed. Only feeReceiver may claim at the threshold or after a failed payment. Reserved commission cannot be spent. No extra commission on native deposits, conversion proceeds or returned assessed principal. Tax rate status=\",\r\n            resolved ? \"resolved\" : \"pending (income held)\",\r\n            \"; taxRateBps=\", Strings.toString(rate),\r\n            \"; feeReceiver=\", Strings.toHexString(receiver),\r\n            \"; pending commission (BNCB base units)=\", Strings.toString(pending)\r\n        );\r\n    }\r\n\r\n    struct OracleObservation { uint256 timestamp; uint256 cumulative; }\r\n    address public twapPair;\r\n    OracleObservation[] private twapObservations;\r\n    uint256 private twapHead;\r\n    event RewardOracleUpdated(address indexed pair, uint256 timestamp, uint256 cumulative);\r\n\r\n    uint256[37] private __gap;\r\n\r\n}\r\n"},"@openzeppelin/contracts@4.9.6/utils/Strings.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./math/Math.sol\";\nimport \"./math/SignedMath.sol\";\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n    bytes16 private constant _SYMBOLS = \"0123456789abcdef\";\n    uint8 private constant _ADDRESS_LENGTH = 20;\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n     */\n    function toString(uint256 value) internal pure returns (string memory) {\n        unchecked {\n            uint256 length = Math.log10(value) + 1;\n            string memory buffer = new string(length);\n            uint256 ptr;\n            /// @solidity memory-safe-assembly\n            assembly {\n                ptr := add(buffer, add(32, length))\n            }\n            while (true) {\n                ptr--;\n                /// @solidity memory-safe-assembly\n                assembly {\n                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))\n                }\n                value /= 10;\n                if (value == 0) break;\n            }\n            return buffer;\n        }\n    }\n\n    /**\n     * @dev Converts a `int256` to its ASCII `string` decimal representation.\n     */\n    function toString(int256 value) internal pure returns (string memory) {\n        return string(abi.encodePacked(value < 0 ? \"-\" : \"\", toString(SignedMath.abs(value))));\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n     */\n    function toHexString(uint256 value) internal pure returns (string memory) {\n        unchecked {\n            return toHexString(value, Math.log256(value) + 1);\n        }\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n     */\n    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n        bytes memory buffer = new bytes(2 * length + 2);\n        buffer[0] = \"0\";\n        buffer[1] = \"x\";\n        for (uint256 i = 2 * length + 1; i > 1; --i) {\n            buffer[i] = _SYMBOLS[value & 0xf];\n            value >>= 4;\n        }\n        require(value == 0, \"Strings: hex length insufficient\");\n        return string(buffer);\n    }\n\n    /**\n     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n     */\n    function toHexString(address addr) internal pure returns (string memory) {\n        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n    }\n\n    /**\n     * @dev Returns true if the two strings are equal.\n     */\n    function equal(string memory a, string memory b) internal pure returns (bool) {\n        return keccak256(bytes(a)) == keccak256(bytes(b));\n    }\n}\n"},"@openzeppelin/contracts@4.9.6/utils/math/SignedMath.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard signed math utilities missing in the Solidity language.\n */\nlibrary SignedMath {\n    /**\n     * @dev Returns the largest of two signed numbers.\n     */\n    function max(int256 a, int256 b) internal pure returns (int256) {\n        return a > b ? a : b;\n    }\n\n    /**\n     * @dev Returns the smallest of two signed numbers.\n     */\n    function min(int256 a, int256 b) internal pure returns (int256) {\n        return a < b ? a : b;\n    }\n\n    /**\n     * @dev Returns the average of two signed numbers without overflow.\n     * The result is rounded towards zero.\n     */\n    function average(int256 a, int256 b) internal pure returns (int256) {\n        // Formula from the book \"Hacker's Delight\"\n        int256 x = (a & b) + ((a ^ b) >> 1);\n        return x + (int256(uint256(x) >> 255) & (a ^ b));\n    }\n\n    /**\n     * @dev Returns the absolute unsigned value of a signed value.\n     */\n    function abs(int256 n) internal pure returns (uint256) {\n        unchecked {\n            // must be unchecked in order to support `n = type(int256).min`\n            return uint256(n >= 0 ? n : -n);\n        }\n    }\n}\n"},"@openzeppelin/contracts@4.9.6/utils/introspection/IERC165.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\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[EIP 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"},"agent/AIDROPRegistrationNFT.sol":{"content":"// SPDX-License-Identifier: MIT\r\npragma solidity ^0.8.24;\r\n\r\nimport {ERC721Upgradeable} from \"@openzeppelin/contracts-upgradeable@4.9.6/token/ERC721/ERC721Upgradeable.sol\";\r\nimport {Base64} from \"@openzeppelin/contracts@4.9.6/utils/Base64.sol\";\r\nimport {Strings} from \"@openzeppelin/contracts@4.9.6/utils/Strings.sol\";\r\n\r\ninterface IRegistrationVaultSource {\r\n    function initialized() external view returns (bool);\r\n    function taxToken() external view returns (address);\r\n    function rewardsContract() external view returns (address);\r\n}\r\n\r\n/// @notice Transferable ERC-721 receipt, issued once per original registrant and project.\r\n/// @dev Factory deploys the locked implementation and initializes one clone per vault.\r\ncontract AIDROPRegistrationNFT is ERC721Upgradeable {\r\n    using Strings for uint256;\r\n\r\n    address public vault;\r\n    address public rewardsContract;\r\n    address public taxToken;\r\n    uint256 public totalSupply;\r\n    // Historical issuance record; transfers do not change it or allow another registration mint.\r\n    // Current ownership is tracked by ERC-721 ownerOf and balanceOf.\r\n    mapping(address => uint256) public tokenOf;\r\n\r\n    event RegistrationNFTInitialized(address indexed vault, address indexed rewards, address indexed taxToken);\r\n    event RegistrationReceiptMinted(address indexed user, uint256 indexed tokenId);\r\n\r\n    constructor() { _disableInitializers(); }\r\n\r\n    function initialize(address vault_, address rewards_) external initializer {\r\n        require(block.chainid == 56, \"BSC_MAINNET_ONLY\");\r\n        require(vault_.code.length > 0 && rewards_.code.length > 0, \"BINDING_CODE\");\r\n        IRegistrationVaultSource source = IRegistrationVaultSource(vault_);\r\n        require(source.initialized() && source.rewardsContract() == rewards_, \"VAULT_BINDING\");\r\n        address token = source.taxToken();\r\n        require(token != address(0), \"TOKEN\");\r\n        __ERC721_init(\"drop.ai\", \"drop.ai\");\r\n        vault = vault_;\r\n        rewardsContract = rewards_;\r\n        taxToken = token;\r\n        emit RegistrationNFTInitialized(vault_, rewards_, token);\r\n    }\r\n\r\n    function mintForRegistration(address user) external returns (uint256 tokenId) {\r\n        require(msg.sender == rewardsContract, \"ONLY_REWARDS\");\r\n        require(user != address(0) && tokenOf[user] == 0, \"ALREADY_MINTED_OR_ZERO\");\r\n        tokenId = ++totalSupply;\r\n        tokenOf[user] = tokenId;\r\n        // Registration mint also supports smart accounts without ERC721Receiver.\r\n        // This mint deliberately does not call user-controlled receiver hooks.\r\n        _mint(user, tokenId);\r\n        emit RegistrationReceiptMinted(user, tokenId);\r\n    }\r\n\r\n    function tokenURI(uint256 tokenId) public view override returns (string memory) {\r\n        _requireMinted(tokenId);\r\n        string memory id = tokenId.toString();\r\n        string memory project = Strings.toHexString(taxToken);\r\n        // Pure on-chain vector artwork, traced from the supplied blue water-drop logo.\r\n        // No image URL, external font, embedded raster or off-chain fetch is needed.\r\n        bytes memory svg = abi.encodePacked(\r\n            '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1600\" height=\"1600\" viewBox=\"0 0 1600 1600\"><rect width=\"1600\" height=\"1600\" fill=\"white\"/><path fill=\"#02a5e6\" fill-rule=\"evenodd\" transform=\"translate(96 12) scale(.88)\" d=\"M798.0,44.5L800.5,45.0L819.5,106.0L850.5,187.0L881.5,256.0L915.5,324.0L995.5,465.0L1117.5,662.0L1159.5,738.0L1189.5,799.0L1216.5,866.0L1235.5,928.0L1249.5,995.0L1255.5,1051.0L1253.5,1139.0L1248.5,1174.0L1237.5,1220.0L1220.5,1267.0L1201.5,1306.0L1173.5,1351.0L1146.5,1386.0L1118.0,1416.5L1079.0,1450.5L1045.0,1474.5L998.0,1500.5L959.0,1517.5L922.0,1529.5L866.0,1541.5L824.0,1545.5L772.0,1545.5L733.0,1541.5L681.0,1530.5L646.0,1519.5L607.0,1503.5L562.0,1479.5L529.0,1457.5L499.0,1433.5L466.5,1402.0L436.5,1367.0L408.5,1326.0L382.5,1277.0L370.5,1248.0L353.5,1191.0L346.5,1153.0L343.5,1119.0L345.5,1029.0L355.5,963.0L371.5,898.0L398.5,825.0L442.5,733.0L474.5,674.0L616.5,443.0L692.5,306.0L727.5,234.0L756.5,167.0L777.5,112.0ZM865.0,300.5L864.5,307.0L904.5,482.0L926.5,561.0L944.5,611.0L967.5,656.0L983.0,674.5L995.0,683.5L1008.0,689.5L1026.0,691.5L1036.0,688.5L1043.5,683.0L1048.5,673.0L1048.5,660.0L1044.5,648.0L1028.5,618.0L944.5,481.0L927.5,447.0L924.5,445.0L889.5,371.0ZM1037.0,753.5L1024.0,757.5L1009.5,770.0L1004.5,780.0L1002.5,796.0L1008.5,814.0L1019.0,825.5L1035.0,832.5L1050.0,832.5L1065.0,826.5L1072.5,820.0L1079.5,809.0L1082.5,790.0L1079.5,778.0L1070.0,764.5L1058.0,756.5ZM818.0,865.5L796.0,867.5L763.0,876.5L748.0,883.5L729.0,896.5L709.5,917.0L700.5,933.0L693.5,955.0L693.5,972.0L699.0,976.5L749.0,976.5L755.5,972.0L764.5,952.0L781.0,936.5L803.0,928.5L835.0,927.5L857.0,933.5L865.0,938.5L876.5,952.0L882.5,973.0L882.5,999.0L776.0,1007.5L748.0,1014.5L718.0,1029.5L693.5,1054.0L682.5,1079.0L679.5,1113.0L685.5,1140.0L696.5,1161.0L715.0,1180.5L734.0,1192.5L763.0,1202.5L796.0,1205.5L822.0,1201.5L851.0,1189.5L881.0,1166.5L883.5,1192.0L889.0,1198.5L944.0,1198.5L948.5,1195.0L950.5,1189.0L950.5,976.0L948.5,956.0L943.5,937.0L927.5,909.0L914.0,895.5L898.0,884.5L884.0,877.5L859.0,869.5ZM1018.0,871.5L1013.0,873.5L1008.5,880.0L1008.5,1190.0L1011.5,1197.0L1017.0,1199.5L1065.0,1199.5L1071.0,1197.5L1076.5,1189.0L1075.5,879.0L1071.0,873.5L1066.0,871.5ZM432.0,942.5L423.5,985.0L417.5,1051.0L417.5,1093.0L421.5,1142.0L431.5,1195.0L443.5,1234.0L465.5,1282.0L485.5,1314.0L514.5,1349.0L562.0,1390.5L600.0,1414.5L652.0,1438.5L686.0,1448.5L715.0,1451.5L751.0,1445.5L784.0,1429.5L801.5,1413.0L808.5,1399.0L808.5,1384.0L800.0,1370.5L786.0,1363.5L741.0,1357.5L700.0,1347.5L672.0,1337.5L641.0,1322.5L594.0,1290.5L563.5,1261.0L545.5,1239.0L514.5,1190.0L481.5,1118.0L454.5,1037.0L439.5,976.0L434.5,945.0ZM876.0,1055.5L882.5,1057.0L881.5,1086.0L877.5,1100.0L869.5,1114.0L846.0,1134.5L822.0,1143.5L795.0,1145.5L768.0,1137.5L753.5,1124.0L748.5,1112.0L748.5,1094.0L755.5,1080.0L768.0,1069.5L786.0,1062.5L804.0,1059.5ZM589.0,1121.5L570.0,1130.5L560.5,1142.0L555.5,1157.0L556.5,1172.0L562.5,1185.0L574.0,1196.5L588.0,1202.5L605.0,1202.5L624.0,1193.5L633.5,1182.0L638.5,1165.0L637.5,1153.0L630.5,1138.0L619.0,1127.5L605.0,1121.5Z\"/><text x=\"800\" y=\"1515\" text-anchor=\"middle\" fill=\"#02a5e6\" font-family=\"monospace\" font-weight=\"bold\" font-size=\"',\r\n            bytes(id).length > 28 ? '24' : '64',\r\n            '\">#', id, '</text></svg>'\r\n        );\r\n        bytes memory json = abi.encodePacked(\r\n            '{\"name\":\"drop.ai #', id,\r\n            '\",\"description\":\"Transferable registration receipt; eligibility and reward history stay with the original registrant.\",',\r\n            '\"image\":\"data:image/svg+xml;base64,', Base64.encode(svg),\r\n            '\",\"attributes\":[{\"trait_type\":\"Project token\",\"value\":\"', project,\r\n            '\"},{\"trait_type\":\"Vault\",\"value\":\"', Strings.toHexString(vault), '\"}]}'\r\n        );\r\n        return string.concat(\"data:application/json;base64,\", Base64.encode(json));\r\n    }\r\n}\r\n"},"@openzeppelin/contracts-upgradeable@4.9.6/token/ERC721/ERC721Upgradeable.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/ERC721.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC721Upgradeable.sol\";\nimport \"./IERC721ReceiverUpgradeable.sol\";\nimport \"./extensions/IERC721MetadataUpgradeable.sol\";\nimport \"../../utils/AddressUpgradeable.sol\";\nimport \"../../utils/ContextUpgradeable.sol\";\nimport \"../../utils/StringsUpgradeable.sol\";\nimport \"../../utils/introspection/ERC165Upgradeable.sol\";\nimport {Initializable} from \"../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including\n * the Metadata extension, but not including the Enumerable extension, which is available separately as\n * {ERC721Enumerable}.\n */\ncontract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721Upgradeable, IERC721MetadataUpgradeable {\n    using AddressUpgradeable for address;\n    using StringsUpgradeable for uint256;\n\n    // Token name\n    string private _name;\n\n    // Token symbol\n    string private _symbol;\n\n    // Mapping from token ID to owner address\n    mapping(uint256 => address) private _owners;\n\n    // Mapping owner address to token count\n    mapping(address => uint256) private _balances;\n\n    // Mapping from token ID to approved address\n    mapping(uint256 => address) private _tokenApprovals;\n\n    // Mapping from owner to operator approvals\n    mapping(address => mapping(address => bool)) private _operatorApprovals;\n\n    /**\n     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.\n     */\n    function __ERC721_init(string memory name_, string memory symbol_) internal onlyInitializing {\n        __ERC721_init_unchained(name_, symbol_);\n    }\n\n    function __ERC721_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {\n        _name = name_;\n        _symbol = symbol_;\n    }\n\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) {\n        return\n            interfaceId == type(IERC721Upgradeable).interfaceId ||\n            interfaceId == type(IERC721MetadataUpgradeable).interfaceId ||\n            super.supportsInterface(interfaceId);\n    }\n\n    /**\n     * @dev See {IERC721-balanceOf}.\n     */\n    function balanceOf(address owner) public view virtual override returns (uint256) {\n        require(owner != address(0), \"ERC721: address zero is not a valid owner\");\n        return _balances[owner];\n    }\n\n    /**\n     * @dev See {IERC721-ownerOf}.\n     */\n    function ownerOf(uint256 tokenId) public view virtual override returns (address) {\n        address owner = _ownerOf(tokenId);\n        require(owner != address(0), \"ERC721: invalid token ID\");\n        return owner;\n    }\n\n    /**\n     * @dev See {IERC721Metadata-name}.\n     */\n    function name() public view virtual override returns (string memory) {\n        return _name;\n    }\n\n    /**\n     * @dev See {IERC721Metadata-symbol}.\n     */\n    function symbol() public view virtual override returns (string memory) {\n        return _symbol;\n    }\n\n    /**\n     * @dev See {IERC721Metadata-tokenURI}.\n     */\n    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {\n        _requireMinted(tokenId);\n\n        string memory baseURI = _baseURI();\n        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : \"\";\n    }\n\n    /**\n     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each\n     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty\n     * by default, can be overridden in child contracts.\n     */\n    function _baseURI() internal view virtual returns (string memory) {\n        return \"\";\n    }\n\n    /**\n     * @dev See {IERC721-approve}.\n     */\n    function approve(address to, uint256 tokenId) public virtual override {\n        address owner = ERC721Upgradeable.ownerOf(tokenId);\n        require(to != owner, \"ERC721: approval to current owner\");\n\n        require(\n            _msgSender() == owner || isApprovedForAll(owner, _msgSender()),\n            \"ERC721: approve caller is not token owner or approved for all\"\n        );\n\n        _approve(to, tokenId);\n    }\n\n    /**\n     * @dev See {IERC721-getApproved}.\n     */\n    function getApproved(uint256 tokenId) public view virtual override returns (address) {\n        _requireMinted(tokenId);\n\n        return _tokenApprovals[tokenId];\n    }\n\n    /**\n     * @dev See {IERC721-setApprovalForAll}.\n     */\n    function setApprovalForAll(address operator, bool approved) public virtual override {\n        _setApprovalForAll(_msgSender(), operator, approved);\n    }\n\n    /**\n     * @dev See {IERC721-isApprovedForAll}.\n     */\n    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {\n        return _operatorApprovals[owner][operator];\n    }\n\n    /**\n     * @dev See {IERC721-transferFrom}.\n     */\n    function transferFrom(address from, address to, uint256 tokenId) public virtual override {\n        //solhint-disable-next-line max-line-length\n        require(_isApprovedOrOwner(_msgSender(), tokenId), \"ERC721: caller is not token owner or approved\");\n\n        _transfer(from, to, tokenId);\n    }\n\n    /**\n     * @dev See {IERC721-safeTransferFrom}.\n     */\n    function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {\n        safeTransferFrom(from, to, tokenId, \"\");\n    }\n\n    /**\n     * @dev See {IERC721-safeTransferFrom}.\n     */\n    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual override {\n        require(_isApprovedOrOwner(_msgSender(), tokenId), \"ERC721: caller is not token owner or approved\");\n        _safeTransfer(from, to, tokenId, data);\n    }\n\n    /**\n     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n     * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n     *\n     * `data` is additional data, it has no specified format and it is sent in call to `to`.\n     *\n     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.\n     * implement alternative mechanisms to perform token transfer, such as signature-based.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must exist and be owned by `from`.\n     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n     *\n     * Emits a {Transfer} event.\n     */\n    function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {\n        _transfer(from, to, tokenId);\n        require(_checkOnERC721Received(from, to, tokenId, data), \"ERC721: transfer to non ERC721Receiver implementer\");\n    }\n\n    /**\n     * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist\n     */\n    function _ownerOf(uint256 tokenId) internal view virtual returns (address) {\n        return _owners[tokenId];\n    }\n\n    /**\n     * @dev Returns whether `tokenId` exists.\n     *\n     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.\n     *\n     * Tokens start existing when they are minted (`_mint`),\n     * and stop existing when they are burned (`_burn`).\n     */\n    function _exists(uint256 tokenId) internal view virtual returns (bool) {\n        return _ownerOf(tokenId) != address(0);\n    }\n\n    /**\n     * @dev Returns whether `spender` is allowed to manage `tokenId`.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must exist.\n     */\n    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {\n        address owner = ERC721Upgradeable.ownerOf(tokenId);\n        return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);\n    }\n\n    /**\n     * @dev Safely mints `tokenId` and transfers it to `to`.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must not exist.\n     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n     *\n     * Emits a {Transfer} event.\n     */\n    function _safeMint(address to, uint256 tokenId) internal virtual {\n        _safeMint(to, tokenId, \"\");\n    }\n\n    /**\n     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is\n     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.\n     */\n    function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {\n        _mint(to, tokenId);\n        require(\n            _checkOnERC721Received(address(0), to, tokenId, data),\n            \"ERC721: transfer to non ERC721Receiver implementer\"\n        );\n    }\n\n    /**\n     * @dev Mints `tokenId` and transfers it to `to`.\n     *\n     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible\n     *\n     * Requirements:\n     *\n     * - `tokenId` must not exist.\n     * - `to` cannot be the zero address.\n     *\n     * Emits a {Transfer} event.\n     */\n    function _mint(address to, uint256 tokenId) internal virtual {\n        require(to != address(0), \"ERC721: mint to the zero address\");\n        require(!_exists(tokenId), \"ERC721: token already minted\");\n\n        _beforeTokenTransfer(address(0), to, tokenId, 1);\n\n        // Check that tokenId was not minted by `_beforeTokenTransfer` hook\n        require(!_exists(tokenId), \"ERC721: token already minted\");\n\n        unchecked {\n            // Will not overflow unless all 2**256 token ids are minted to the same owner.\n            // Given that tokens are minted one by one, it is impossible in practice that\n            // this ever happens. Might change if we allow batch minting.\n            // The ERC fails to describe this case.\n            _balances[to] += 1;\n        }\n\n        _owners[tokenId] = to;\n\n        emit Transfer(address(0), to, tokenId);\n\n        _afterTokenTransfer(address(0), to, tokenId, 1);\n    }\n\n    /**\n     * @dev Destroys `tokenId`.\n     * The approval is cleared when the token is burned.\n     * This is an internal function that does not check if the sender is authorized to operate on the token.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must exist.\n     *\n     * Emits a {Transfer} event.\n     */\n    function _burn(uint256 tokenId) internal virtual {\n        address owner = ERC721Upgradeable.ownerOf(tokenId);\n\n        _beforeTokenTransfer(owner, address(0), tokenId, 1);\n\n        // Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook\n        owner = ERC721Upgradeable.ownerOf(tokenId);\n\n        // Clear approvals\n        delete _tokenApprovals[tokenId];\n\n        unchecked {\n            // Cannot overflow, as that would require more tokens to be burned/transferred\n            // out than the owner initially received through minting and transferring in.\n            _balances[owner] -= 1;\n        }\n        delete _owners[tokenId];\n\n        emit Transfer(owner, address(0), tokenId);\n\n        _afterTokenTransfer(owner, address(0), tokenId, 1);\n    }\n\n    /**\n     * @dev Transfers `tokenId` from `from` to `to`.\n     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.\n     *\n     * Requirements:\n     *\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must be owned by `from`.\n     *\n     * Emits a {Transfer} event.\n     */\n    function _transfer(address from, address to, uint256 tokenId) internal virtual {\n        require(ERC721Upgradeable.ownerOf(tokenId) == from, \"ERC721: transfer from incorrect owner\");\n        require(to != address(0), \"ERC721: transfer to the zero address\");\n\n        _beforeTokenTransfer(from, to, tokenId, 1);\n\n        // Check that tokenId was not transferred by `_beforeTokenTransfer` hook\n        require(ERC721Upgradeable.ownerOf(tokenId) == from, \"ERC721: transfer from incorrect owner\");\n\n        // Clear approvals from the previous owner\n        delete _tokenApprovals[tokenId];\n\n        unchecked {\n            // `_balances[from]` cannot overflow for the same reason as described in `_burn`:\n            // `from`'s balance is the number of token held, which is at least one before the current\n            // transfer.\n            // `_balances[to]` could overflow in the conditions described in `_mint`. That would require\n            // all 2**256 token ids to be minted, which in practice is impossible.\n            _balances[from] -= 1;\n            _balances[to] += 1;\n        }\n        _owners[tokenId] = to;\n\n        emit Transfer(from, to, tokenId);\n\n        _afterTokenTransfer(from, to, tokenId, 1);\n    }\n\n    /**\n     * @dev Approve `to` to operate on `tokenId`\n     *\n     * Emits an {Approval} event.\n     */\n    function _approve(address to, uint256 tokenId) internal virtual {\n        _tokenApprovals[tokenId] = to;\n        emit Approval(ERC721Upgradeable.ownerOf(tokenId), to, tokenId);\n    }\n\n    /**\n     * @dev Approve `operator` to operate on all of `owner` tokens\n     *\n     * Emits an {ApprovalForAll} event.\n     */\n    function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {\n        require(owner != operator, \"ERC721: approve to caller\");\n        _operatorApprovals[owner][operator] = approved;\n        emit ApprovalForAll(owner, operator, approved);\n    }\n\n    /**\n     * @dev Reverts if the `tokenId` has not been minted yet.\n     */\n    function _requireMinted(uint256 tokenId) internal view virtual {\n        require(_exists(tokenId), \"ERC721: invalid token ID\");\n    }\n\n    /**\n     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.\n     * The call is not executed if the target address is not a contract.\n     *\n     * @param from address representing the previous owner of the given token ID\n     * @param to target address that will receive the tokens\n     * @param tokenId uint256 ID of the token to be transferred\n     * @param data bytes optional data to send along with the call\n     * @return bool whether the call correctly returned the expected magic value\n     */\n    function _checkOnERC721Received(\n        address from,\n        address to,\n        uint256 tokenId,\n        bytes memory data\n    ) private returns (bool) {\n        if (to.isContract()) {\n            try IERC721ReceiverUpgradeable(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {\n                return retval == IERC721ReceiverUpgradeable.onERC721Received.selector;\n            } catch (bytes memory reason) {\n                if (reason.length == 0) {\n                    revert(\"ERC721: transfer to non ERC721Receiver implementer\");\n                } else {\n                    /// @solidity memory-safe-assembly\n                    assembly {\n                        revert(add(32, reason), mload(reason))\n                    }\n                }\n            }\n        } else {\n            return true;\n        }\n    }\n\n    /**\n     * @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is\n     * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.\n     *\n     * Calling conditions:\n     *\n     * - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`.\n     * - When `from` is zero, the tokens will be minted for `to`.\n     * - When `to` is zero, ``from``'s tokens will be burned.\n     * - `from` and `to` are never both zero.\n     * - `batchSize` is non-zero.\n     *\n     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n     */\n    function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}\n\n    /**\n     * @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is\n     * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.\n     *\n     * Calling conditions:\n     *\n     * - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`.\n     * - When `from` is zero, the tokens were minted for `to`.\n     * - When `to` is zero, ``from``'s tokens were burned.\n     * - `from` and `to` are never both zero.\n     * - `batchSize` is non-zero.\n     *\n     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n     */\n    function _afterTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}\n\n    /**\n     * @dev Unsafe write access to the balances, used by extensions that \"mint\" tokens using an {ownerOf} override.\n     *\n     * WARNING: Anyone calling this MUST ensure that the balances remain consistent with the ownership. The invariant\n     * being that for any address `a` the value returned by `balanceOf(a)` must be equal to the number of tokens such\n     * that `ownerOf(tokenId)` is `a`.\n     */\n    // solhint-disable-next-line func-name-mixedcase\n    function __unsafe_increaseBalance(address account, uint256 amount) internal {\n        _balances[account] += amount;\n    }\n\n    /**\n     * @dev This empty reserved space is put in place to allow future versions to add new\n     * variables without shifting down storage in the inheritance chain.\n     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n     */\n    uint256[44] private __gap;\n}\n"},"@openzeppelin/contracts-upgradeable@4.9.6/token/ERC721/IERC721Upgradeable.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../../utils/introspection/IERC165Upgradeable.sol\";\n\n/**\n * @dev Required interface of an ERC721 compliant contract.\n */\ninterface IERC721Upgradeable is IERC165Upgradeable {\n    /**\n     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n    /**\n     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\n     */\n    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n\n    /**\n     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\n     */\n    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n    /**\n     * @dev Returns the number of tokens in ``owner``'s account.\n     */\n    function balanceOf(address owner) external view returns (uint256 balance);\n\n    /**\n     * @dev Returns the owner of the `tokenId` token.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must exist.\n     */\n    function ownerOf(uint256 tokenId) external view returns (address owner);\n\n    /**\n     * @dev Safely transfers `tokenId` token from `from` to `to`.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must exist and be owned by `from`.\n     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n     *\n     * Emits a {Transfer} event.\n     */\n    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;\n\n    /**\n     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n     * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must exist and be owned by `from`.\n     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.\n     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n     *\n     * Emits a {Transfer} event.\n     */\n    function safeTransferFrom(address from, address to, uint256 tokenId) external;\n\n    /**\n     * @dev Transfers `tokenId` token from `from` to `to`.\n     *\n     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721\n     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must\n     * understand this adds an external call which potentially creates a reentrancy vulnerability.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must be owned by `from`.\n     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 tokenId) external;\n\n    /**\n     * @dev Gives permission to `to` to transfer `tokenId` token to another account.\n     * The approval is cleared when the token is transferred.\n     *\n     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\n     *\n     * Requirements:\n     *\n     * - The caller must own the token or be an approved operator.\n     * - `tokenId` must exist.\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address to, uint256 tokenId) external;\n\n    /**\n     * @dev Approve or remove `operator` as an operator for the caller.\n     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\n     *\n     * Requirements:\n     *\n     * - The `operator` cannot be the caller.\n     *\n     * Emits an {ApprovalForAll} event.\n     */\n    function setApprovalForAll(address operator, bool approved) external;\n\n    /**\n     * @dev Returns the account approved for `tokenId` token.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must exist.\n     */\n    function getApproved(uint256 tokenId) external view returns (address operator);\n\n    /**\n     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\n     *\n     * See {setApprovalForAll}\n     */\n    function isApprovedForAll(address owner, address operator) external view returns (bool);\n}\n"},"@openzeppelin/contracts-upgradeable@4.9.6/utils/introspection/IERC165Upgradeable.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\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 IERC165Upgradeable {\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[EIP 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"},"@openzeppelin/contracts-upgradeable@4.9.6/token/ERC721/IERC721ReceiverUpgradeable.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC721 token receiver interface\n * @dev Interface for any contract that wants to support safeTransfers\n * from ERC721 asset contracts.\n */\ninterface IERC721ReceiverUpgradeable {\n    /**\n     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n     * by `operator` from `from`, this function is called.\n     *\n     * It must return its Solidity selector to confirm the token transfer.\n     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.\n     *\n     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.\n     */\n    function onERC721Received(\n        address operator,\n        address from,\n        uint256 tokenId,\n        bytes calldata data\n    ) external returns (bytes4);\n}\n"},"@openzeppelin/contracts-upgradeable@4.9.6/token/ERC721/extensions/IERC721MetadataUpgradeable.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC721Upgradeable.sol\";\n\n/**\n * @title ERC-721 Non-Fungible Token Standard, optional metadata extension\n * @dev See https://eips.ethereum.org/EIPS/eip-721\n */\ninterface IERC721MetadataUpgradeable is IERC721Upgradeable {\n    /**\n     * @dev Returns the token collection name.\n     */\n    function name() external view returns (string memory);\n\n    /**\n     * @dev Returns the token collection symbol.\n     */\n    function symbol() external view returns (string memory);\n\n    /**\n     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.\n     */\n    function tokenURI(uint256 tokenId) external view returns (string memory);\n}\n"},"@openzeppelin/contracts-upgradeable@4.9.6/utils/ContextUpgradeable.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)\n\npragma solidity ^0.8.0;\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract ContextUpgradeable is Initializable {\n    function __Context_init() internal onlyInitializing {\n    }\n\n    function __Context_init_unchained() internal onlyInitializing {\n    }\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        return msg.data;\n    }\n\n    function _contextSuffixLength() internal view virtual returns (uint256) {\n        return 0;\n    }\n\n    /**\n     * @dev This empty reserved space is put in place to allow future versions to add new\n     * variables without shifting down storage in the inheritance chain.\n     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n     */\n    uint256[50] private __gap;\n}\n"},"@openzeppelin/contracts-upgradeable@4.9.6/utils/StringsUpgradeable.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./math/MathUpgradeable.sol\";\nimport \"./math/SignedMathUpgradeable.sol\";\n\n/**\n * @dev String operations.\n */\nlibrary StringsUpgradeable {\n    bytes16 private constant _SYMBOLS = \"0123456789abcdef\";\n    uint8 private constant _ADDRESS_LENGTH = 20;\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n     */\n    function toString(uint256 value) internal pure returns (string memory) {\n        unchecked {\n            uint256 length = MathUpgradeable.log10(value) + 1;\n            string memory buffer = new string(length);\n            uint256 ptr;\n            /// @solidity memory-safe-assembly\n            assembly {\n                ptr := add(buffer, add(32, length))\n            }\n            while (true) {\n                ptr--;\n                /// @solidity memory-safe-assembly\n                assembly {\n                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))\n                }\n                value /= 10;\n                if (value == 0) break;\n            }\n            return buffer;\n        }\n    }\n\n    /**\n     * @dev Converts a `int256` to its ASCII `string` decimal representation.\n     */\n    function toString(int256 value) internal pure returns (string memory) {\n        return string(abi.encodePacked(value < 0 ? \"-\" : \"\", toString(SignedMathUpgradeable.abs(value))));\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n     */\n    function toHexString(uint256 value) internal pure returns (string memory) {\n        unchecked {\n            return toHexString(value, MathUpgradeable.log256(value) + 1);\n        }\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n     */\n    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n        bytes memory buffer = new bytes(2 * length + 2);\n        buffer[0] = \"0\";\n        buffer[1] = \"x\";\n        for (uint256 i = 2 * length + 1; i > 1; --i) {\n            buffer[i] = _SYMBOLS[value & 0xf];\n            value >>= 4;\n        }\n        require(value == 0, \"Strings: hex length insufficient\");\n        return string(buffer);\n    }\n\n    /**\n     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n     */\n    function toHexString(address addr) internal pure returns (string memory) {\n        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n    }\n\n    /**\n     * @dev Returns true if the two strings are equal.\n     */\n    function equal(string memory a, string memory b) internal pure returns (bool) {\n        return keccak256(bytes(a)) == keccak256(bytes(b));\n    }\n}\n"},"@openzeppelin/contracts-upgradeable@4.9.6/utils/math/MathUpgradeable.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary MathUpgradeable {\n    enum Rounding {\n        Down, // Toward negative infinity\n        Up, // Toward infinity\n        Zero // Toward zero\n    }\n\n    /**\n     * @dev Returns the largest of two numbers.\n     */\n    function max(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a > b ? a : b;\n    }\n\n    /**\n     * @dev Returns the smallest of two numbers.\n     */\n    function min(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a < b ? a : b;\n    }\n\n    /**\n     * @dev Returns the average of two numbers. The result is rounded towards\n     * zero.\n     */\n    function average(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b) / 2 can overflow.\n        return (a & b) + (a ^ b) / 2;\n    }\n\n    /**\n     * @dev Returns the ceiling of the division of two numbers.\n     *\n     * This differs from standard division with `/` in that it rounds up instead\n     * of rounding down.\n     */\n    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b - 1) / b can overflow on addition, so we distribute.\n        return a == 0 ? 0 : (a - 1) / b + 1;\n    }\n\n    /**\n     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)\n     * with further edits by Uniswap Labs also under MIT license.\n     */\n    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n        unchecked {\n            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use\n            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n            // variables such that product = prod1 * 2^256 + prod0.\n            uint256 prod0; // Least significant 256 bits of the product\n            uint256 prod1; // Most significant 256 bits of the product\n            assembly {\n                let mm := mulmod(x, y, not(0))\n                prod0 := mul(x, y)\n                prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n            }\n\n            // Handle non-overflow cases, 256 by 256 division.\n            if (prod1 == 0) {\n                // Solidity will revert if denominator == 0, unlike the div opcode on its own.\n                // The surrounding unchecked block does not change this fact.\n                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.\n                return prod0 / denominator;\n            }\n\n            // Make sure the result is less than 2^256. Also prevents denominator == 0.\n            require(denominator > prod1, \"Math: mulDiv overflow\");\n\n            ///////////////////////////////////////////////\n            // 512 by 256 division.\n            ///////////////////////////////////////////////\n\n            // Make division exact by subtracting the remainder from [prod1 prod0].\n            uint256 remainder;\n            assembly {\n                // Compute remainder using mulmod.\n                remainder := mulmod(x, y, denominator)\n\n                // Subtract 256 bit number from 512 bit number.\n                prod1 := sub(prod1, gt(remainder, prod0))\n                prod0 := sub(prod0, remainder)\n            }\n\n            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.\n            // See https://cs.stackexchange.com/q/138556/92363.\n\n            // Does not overflow because the denominator cannot be zero at this stage in the function.\n            uint256 twos = denominator & (~denominator + 1);\n            assembly {\n                // Divide denominator by twos.\n                denominator := div(denominator, twos)\n\n                // Divide [prod1 prod0] by twos.\n                prod0 := div(prod0, twos)\n\n                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.\n                twos := add(div(sub(0, twos), twos), 1)\n            }\n\n            // Shift in bits from prod1 into prod0.\n            prod0 |= prod1 * twos;\n\n            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such\n            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for\n            // four bits. That is, denominator * inv = 1 mod 2^4.\n            uint256 inverse = (3 * denominator) ^ 2;\n\n            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works\n            // in modular arithmetic, doubling the correct bits in each step.\n            inverse *= 2 - denominator * inverse; // inverse mod 2^8\n            inverse *= 2 - denominator * inverse; // inverse mod 2^16\n            inverse *= 2 - denominator * inverse; // inverse mod 2^32\n            inverse *= 2 - denominator * inverse; // inverse mod 2^64\n            inverse *= 2 - denominator * inverse; // inverse mod 2^128\n            inverse *= 2 - denominator * inverse; // inverse mod 2^256\n\n            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is\n            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1\n            // is no longer required.\n            result = prod0 * inverse;\n            return result;\n        }\n    }\n\n    /**\n     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.\n     */\n    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {\n        uint256 result = mulDiv(x, y, denominator);\n        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {\n            result += 1;\n        }\n        return result;\n    }\n\n    /**\n     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.\n     *\n     * Inspired by Henry S. Warren, Jr.'s \"Hacker's Delight\" (Chapter 11).\n     */\n    function sqrt(uint256 a) internal pure returns (uint256) {\n        if (a == 0) {\n            return 0;\n        }\n\n        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.\n        //\n        // We know that the \"msb\" (most significant bit) of our target number `a` is a power of 2 such that we have\n        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.\n        //\n        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`\n        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`\n        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`\n        //\n        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.\n        uint256 result = 1 << (log2(a) >> 1);\n\n        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,\n        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at\n        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision\n        // into the expected uint128 result.\n        unchecked {\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            return min(result, a / result);\n        }\n    }\n\n    /**\n     * @notice Calculates sqrt(a), following the selected rounding direction.\n     */\n    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = sqrt(a);\n            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 2, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 128;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 64;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 32;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 16;\n            }\n            if (value >> 8 > 0) {\n                value >>= 8;\n                result += 8;\n            }\n            if (value >> 4 > 0) {\n                value >>= 4;\n                result += 4;\n            }\n            if (value >> 2 > 0) {\n                value >>= 2;\n                result += 2;\n            }\n            if (value >> 1 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log2(value);\n            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 10, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >= 10 ** 64) {\n                value /= 10 ** 64;\n                result += 64;\n            }\n            if (value >= 10 ** 32) {\n                value /= 10 ** 32;\n                result += 32;\n            }\n            if (value >= 10 ** 16) {\n                value /= 10 ** 16;\n                result += 16;\n            }\n            if (value >= 10 ** 8) {\n                value /= 10 ** 8;\n                result += 8;\n            }\n            if (value >= 10 ** 4) {\n                value /= 10 ** 4;\n                result += 4;\n            }\n            if (value >= 10 ** 2) {\n                value /= 10 ** 2;\n                result += 2;\n            }\n            if (value >= 10 ** 1) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log10(value);\n            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 256, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     *\n     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n     */\n    function log256(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 16;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 8;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 4;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 2;\n            }\n            if (value >> 8 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log256(value);\n            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);\n        }\n    }\n}\n"},"@openzeppelin/contracts-upgradeable@4.9.6/utils/math/SignedMathUpgradeable.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard signed math utilities missing in the Solidity language.\n */\nlibrary SignedMathUpgradeable {\n    /**\n     * @dev Returns the largest of two signed numbers.\n     */\n    function max(int256 a, int256 b) internal pure returns (int256) {\n        return a > b ? a : b;\n    }\n\n    /**\n     * @dev Returns the smallest of two signed numbers.\n     */\n    function min(int256 a, int256 b) internal pure returns (int256) {\n        return a < b ? a : b;\n    }\n\n    /**\n     * @dev Returns the average of two signed numbers without overflow.\n     * The result is rounded towards zero.\n     */\n    function average(int256 a, int256 b) internal pure returns (int256) {\n        // Formula from the book \"Hacker's Delight\"\n        int256 x = (a & b) + ((a ^ b) >> 1);\n        return x + (int256(uint256(x) >> 255) & (a ^ b));\n    }\n\n    /**\n     * @dev Returns the absolute unsigned value of a signed value.\n     */\n    function abs(int256 n) internal pure returns (uint256) {\n        unchecked {\n            // must be unchecked in order to support `n = type(int256).min`\n            return uint256(n >= 0 ? n : -n);\n        }\n    }\n}\n"},"@openzeppelin/contracts-upgradeable@4.9.6/utils/introspection/ERC165Upgradeable.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC165Upgradeable.sol\";\nimport {Initializable} from \"../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n *\n * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\n */\nabstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {\n    function __ERC165_init() internal onlyInitializing {\n    }\n\n    function __ERC165_init_unchained() internal onlyInitializing {\n    }\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n        return interfaceId == type(IERC165Upgradeable).interfaceId;\n    }\n\n    /**\n     * @dev This empty reserved space is put in place to allow future versions to add new\n     * variables without shifting down storage in the inheritance chain.\n     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n     */\n    uint256[50] private __gap;\n}\n"},"@openzeppelin/contracts@4.9.6/utils/Base64.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.6) (utils/Base64.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides a set of functions to operate with Base64 strings.\n *\n * _Available since v4.5._\n */\nlibrary Base64 {\n    /**\n     * @dev Base64 Encoding/Decoding Table\n     */\n    string internal constant _TABLE = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\n    /**\n     * @dev Converts a `bytes` to its Bytes64 `string` representation.\n     */\n    function encode(bytes memory data) internal pure returns (string memory) {\n        /**\n         * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence\n         * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol\n         */\n        if (data.length == 0) return \"\";\n\n        // Loads the table into memory\n        string memory table = _TABLE;\n\n        // Encoding takes 3 bytes chunks of binary data from `bytes` data parameter\n        // and split into 4 numbers of 6 bits.\n        // The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up\n        // - `data.length + 2`  -> Round up\n        // - `/ 3`              -> Number of 3-bytes chunks\n        // - `4 *`              -> 4 characters for each chunk\n        string memory result = new string(4 * ((data.length + 2) / 3));\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Prepare the lookup table (skip the first \"length\" byte)\n            let tablePtr := add(table, 1)\n\n            // Prepare result pointer, jump over length\n            let resultPtr := add(result, 0x20)\n            let dataPtr := data\n            let endPtr := add(data, mload(data))\n\n            // In some cases, the last iteration will read bytes after the end of the data. We cache the value, and\n            // set it to zero to make sure no dirty bytes are read in that section.\n            let afterPtr := add(endPtr, 0x20)\n            let afterCache := mload(afterPtr)\n            mstore(afterPtr, 0x00)\n\n            // Run over the input, 3 bytes at a time\n            for {\n\n            } lt(dataPtr, endPtr) {\n\n            } {\n                // Advance 3 bytes\n                dataPtr := add(dataPtr, 3)\n                let input := mload(dataPtr)\n\n                // To write each character, shift the 3 byte (24 bits) chunk\n                // 4 times in blocks of 6 bits for each character (18, 12, 6, 0)\n                // and apply logical AND with 0x3F to bitmask the least significant 6 bits.\n                // Use this as an index into the lookup table, mload an entire word\n                // so the desired character is in the least significant byte, and\n                // mstore8 this least significant byte into the result and continue.\n\n                mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))\n                resultPtr := add(resultPtr, 1) // Advance\n\n                mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))\n                resultPtr := add(resultPtr, 1) // Advance\n\n                mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))\n                resultPtr := add(resultPtr, 1) // Advance\n\n                mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))\n                resultPtr := add(resultPtr, 1) // Advance\n            }\n\n            // Reset the value that was cached\n            mstore(afterPtr, afterCache)\n\n            // When data `bytes` is not exactly 3 bytes long\n            // it is padded with `=` characters at the end\n            switch mod(mload(data), 3)\n            case 1 {\n                mstore8(sub(resultPtr, 1), 0x3d)\n                mstore8(sub(resultPtr, 2), 0x3d)\n            }\n            case 2 {\n                mstore8(sub(resultPtr, 1), 0x3d)\n            }\n        }\n\n        return result;\n    }\n}\n"}},"matchId":"57382042","creationMatch":"exact_match","runtimeMatch":"exact_match","verifiedAt":"2026-10-28T11:55:28Z","match":"exact_match","chainId":"56","address":"0xc8750BF1962824D01d2CdD1ad4F973c6CF59836d"}