Skip to main content
PulseScanner.io

Address

0x7ee5476ae357b02f3f61ba0d8369945d3615e0de
Current Holdings
$0.00
TXs sent
not counted
First Active
2025-11-16
block 25,032,007
Last Active
54 days ago
block 27,099,831
Funded By
0x5591…c90e

Net worth historyi

115 snapshots · to block 27,530,895coverage change 26 Augcoverage change 27 Augcoverage change 27 Augcoverage change 27 Augcoverage change 27 Aug
exact matchDivineManagersolc 0.8.24+commit.e11b9ed9runtime exact · creation exact
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/**
 * @title DivineManager
 * @notice On-chain executor for HolyC / JIT arbitrage tickets generated by the off-chain scanner.
 *         The contract is designed for PulseChain (PLS/WPLS) and follows the execution guidelines
 *         captured in `ContractDesign.md`. It keeps strict guard-rails, tracks vault balances, and
 *         settles profits with optional splits and caller gas top-ups.
 */

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
    function decimals() external view returns (uint8);
}

interface IWPLS is IERC20 {
    function deposit() external payable;
    function withdraw(uint256) external;
}

interface IJustInTimeCompiler {
    function compile(uint256 amount) external;
    function restore(uint256 amount) external;
}

interface IUniswapV2Router02 {
    function factory() external view returns (address);
    function WETH() external view returns (address);
    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external;
    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);
    function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts);
    function getAmountsIn(uint256 amountOut, address[] calldata path) external view returns (uint256[] memory amounts);
}

interface IUniswapV2Pair {
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
}

library SafeERC20 {
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        require(_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)), "TRANSFER_FAIL");
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        require(_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)), "TRANSFER_FROM_FAIL");
    }

    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        require(_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)), "APPROVE_FAIL");
    }

    function _callOptionalReturn(IERC20 token, bytes memory data) private returns (bool) {
        (bool success, bytes memory returndata) = address(token).call(data);
        if (!success) return false;
        if (returndata.length == 0) return true;
        return abi.decode(returndata, (bool));
    }
}

abstract contract ReentrancyGuard {
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;
    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    modifier nonReentrant() {
        require(_status != _ENTERED, "REENTRANCY");
        _status = _ENTERED;
        _;
        _status = _NOT_ENTERED;
    }
}

contract DivineManager is ReentrancyGuard {
    using SafeERC20 for IERC20;

    uint256 private constant BPS = 10_000;
    uint256 private constant MAX_SPLIT_BPS = 9_000; // 90%
    uint256 public constant CALLER_TOP_OFF_THRESHOLD = 500_000 ether; // 500k PLS
    address public constant BURN_ADDRESS = 0x0000000000000000000000000000000000000369;

    enum Asset {
        HC,
        JIT,
        WPLS
    }

    enum LegKey {
        COMPILE,
        RESTORE,
        SWAP_SUPPORTING_FOT,
        SWAP_EXACT
    }

    struct BurnInstruction {
        uint256 owedHolyC;
        uint256 owedJIT;
    }

    struct TicketLeg {
        LegKey key;
        address[] path; // optional for compile/restore
        uint256 amountIn;
        uint256 amountOutMin;
    }

    struct FinalGuard {
        Asset asset;
        uint256 minAmount;
    }

    struct BindingPair {
        address pair;
        uint256 reserve0;
        uint256 reserve1;
    }

    struct BindingData {
        uint256 blockNumberObserved;
        BindingPair[] pairs;
        uint16 reservesToleranceBps;
        bytes32 policyHash;
    }

    struct ExecutionTicket {
        bytes32 strategyId;
        Asset targetAsset;
        uint256 minProfitWPLS;
        uint256 deadline;
        uint256 basefeeCap;
        bytes32 policyHash;
        TicketLeg[] legs;
        FinalGuard finalGuard;
        BindingData binding;
        BurnInstruction burn;
        bytes32 jobNonce;
    }

    struct Policy {
        uint256 minProfitWPLS;
        uint256 basefeeCap;
        uint16 safetyBpsPerLeg;
        uint16 reservesToleranceBps;
        uint32 deadlineSeconds;
        bool executorTaxExempt;
        bool burnAfterEnabled;
        bool denyJitSells;
        bytes32 hash;
    }

    struct VaultSnapshot {
        uint256 native;
        uint256 holyC;
        uint256 jit;
        uint256 wpls;
    }

    address public owner;
    address public pendingOwner;
    address public immutable HOLYC;
    address public immutable JIT;
    address public immutable WPLS;
    IJustInTimeCompiler public immutable compiler;
    IWPLS private immutable wplsWrapper;
    IUniswapV2Router02 public router;

    address public botCaller;

    bool public splitEnabled;
    uint16 public splitBps;
    address public splitDestination;

    Policy public policy;

    mapping(bytes32 => bool) public jobNonceConsumed;

    event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    event BotCallerUpdated(address indexed caller);
    event SplitConfigurationUpdated(bool enabled, uint16 splitBps, address indexed destination);
    event PolicyUpdated(bytes32 indexed policyHash, Policy policy);
    event RouterUpdated(address indexed previousRouter, address indexed newRouter);
    event TopOffExecuted(address indexed caller, uint256 nativeAmount, uint256 wplsUsed);
    event SplitPaid(
        address indexed destination,
        uint256 wplsAmount,
        uint256 holyCAmount,
        uint256 jitAmount
    );
    event TicketExecuted(bytes32 indexed strategyId, bytes32 indexed jobNonce, uint256 profitWPLS);

    modifier onlyOwner() {
        require(msg.sender == owner, "NOT_OWNER");
        _;
    }

    modifier onlyExecutor() {
        require(msg.sender == botCaller || msg.sender == owner, "NOT_EXECUTOR");
        _;
    }

    constructor(
        address owner_,
        address botCaller_,
        address holyc_,
        address jit_,
        address wpls_,
        address compiler_,
        address router_
    ) {
        require(owner_ != address(0), "OWNER_ZERO");
        require(holyc_ != address(0) && jit_ != address(0) && wpls_ != address(0), "TOKEN_ZERO");
        require(compiler_ != address(0) && router_ != address(0), "CONFIG_ZERO");

        owner = owner_;
        botCaller = botCaller_;
        HOLYC = holyc_;
        JIT = jit_;
        WPLS = wpls_;
        compiler = IJustInTimeCompiler(compiler_);
        router = IUniswapV2Router02(router_);
        wplsWrapper = IWPLS(wpls_);

        _setAllowances(router_);
    }

    // ----------------------------
    // Owner functions
    // ----------------------------

    function transferOwnership(address newOwner) external onlyOwner {
        require(newOwner != address(0), "OWNER_ZERO");
        pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner, newOwner);
    }

    function acceptOwnership() external {
        require(msg.sender == pendingOwner, "NOT_PENDING");
        address previous = owner;
        owner = pendingOwner;
        pendingOwner = address(0);
        emit OwnershipTransferred(previous, owner);
    }

    function setBotCaller(address caller) external onlyOwner {
        botCaller = caller;
        emit BotCallerUpdated(caller);
    }

    function setSplitConfiguration(bool enabled, uint16 splitBps_, address destination) external onlyOwner {
        require(!enabled || (destination != address(0) && splitBps_ <= MAX_SPLIT_BPS), "INVALID_SPLIT");
        splitEnabled = enabled;
        splitBps = splitBps_;
        splitDestination = destination;
        emit SplitConfigurationUpdated(enabled, splitBps_, destination);
    }

    function setRouter(address newRouter) external onlyOwner {
        require(newRouter != address(0), "ROUTER_ZERO");
        address previous = address(router);
        if (previous != address(0)) {
            IERC20(HOLYC).safeApprove(previous, 0);
            IERC20(JIT).safeApprove(previous, 0);
            IERC20(WPLS).safeApprove(previous, 0);
        }
        router = IUniswapV2Router02(newRouter);
        _setAllowances(newRouter);
        emit RouterUpdated(previous, newRouter);
    }

    function setPolicy(Policy calldata policy_) external onlyOwner {
        Policy memory newPolicy = policy_;
        newPolicy.hash = _computePolicyHash(policy_);
        policy = newPolicy;
        emit PolicyUpdated(newPolicy.hash, newPolicy);
    }

    // Emergency withdrawal (owner only)
    function withdrawToken(address token, uint256 amount, address to) external onlyOwner {
        require(to != address(0), "WITHDRAW_ZERO");
        IERC20(token).safeTransfer(to, amount);
    }

    function withdrawNative(uint256 amount, address payable to) external onlyOwner {
        require(to != address(0), "WITHDRAW_ZERO");
        (bool ok, ) = to.call{value: amount}("");
        require(ok, "NATIVE_WITHDRAW_FAIL");
    }

    // ----------------------------
    // Execution API
    // ----------------------------

    function execute(bytes calldata payload) external nonReentrant onlyExecutor {
        require(policy.hash != bytes32(0), "POLICY_UNSET");
        ExecutionTicket memory ticket = abi.decode(payload, (ExecutionTicket));
        require(ticket.deadline >= block.timestamp, "DEADLINE_PASSED");
        require(ticket.policyHash == policy.hash, "POLICY_MISMATCH");
        if (ticket.basefeeCap > 0 || policy.basefeeCap > 0) {
            uint256 cap = ticket.basefeeCap > 0 ? ticket.basefeeCap : policy.basefeeCap;
            require(block.basefee <= cap, "BASEFEE_HIGH");
        }
        require(!jobNonceConsumed[ticket.jobNonce], "NONCE_USED");
        jobNonceConsumed[ticket.jobNonce] = true;

        _validateBinding(ticket.binding);

        VaultSnapshot memory beforeSnap = _snapshotVault();

        _executeLegs(ticket.legs, ticket.deadline);

        if (policy.executorTaxExempt && policy.burnAfterEnabled) {
            _settleBurn(ticket.burn);
        } else {
            require(ticket.burn.owedHolyC == 0 && ticket.burn.owedJIT == 0, "BURN_UNEXPECTED");
        }

        VaultSnapshot memory afterSnap = _snapshotVault();

        uint256 profitWPLS = _validateProfit(ticket, beforeSnap, afterSnap);

        // Optional split
        uint256 remainingProfit = profitWPLS;
        uint256 holyCProfit = 0;
        if (afterSnap.holyC > beforeSnap.holyC) {
            holyCProfit = afterSnap.holyC - beforeSnap.holyC;
        }
        uint256 jitProfit = 0;
        if (afterSnap.jit > beforeSnap.jit) {
            jitProfit = afterSnap.jit - beforeSnap.jit;
        }
        uint256 sentWPLS = 0;
        uint256 sentHolyC = 0;
        uint256 sentJIT = 0;
        if (splitEnabled && splitDestination != address(0) && splitBps > 0) {
            uint256 splitAmount = (profitWPLS * splitBps) / BPS;
            if (splitAmount > 0) {
                _distributeWPLS(splitDestination, splitAmount);
                sentWPLS = splitAmount;
                remainingProfit -= splitAmount;
            }
            if (holyCProfit > 0) {
                uint256 splitHolyC = (holyCProfit * splitBps) / BPS;
                if (splitHolyC > 0) {
                    IERC20(HOLYC).safeTransfer(splitDestination, splitHolyC);
                    sentHolyC = splitHolyC;
                }
            }
            if (jitProfit > 0) {
                uint256 splitJIT = (jitProfit * splitBps) / BPS;
                if (splitJIT > 0) {
                    IERC20(JIT).safeTransfer(splitDestination, splitJIT);
                    sentJIT = splitJIT;
                }
            }
            if (sentWPLS > 0 || sentHolyC > 0 || sentJIT > 0) {
                emit SplitPaid(splitDestination, sentWPLS, sentHolyC, sentJIT);
            }
        }

        // Caller top-off
        if (botCaller != address(0)) {
            uint256 spent = _topOffCaller(remainingProfit, ticket.deadline);
            if (spent > remainingProfit) {
                remainingProfit = 0;
            } else {
                remainingProfit -= spent;
            }
        }

        VaultSnapshot memory finalSnap = _snapshotVault();
        uint256 beforeWPLSValue = beforeSnap.wpls + beforeSnap.native;
        uint256 finalWPLSValue = finalSnap.wpls + finalSnap.native;

        // Sanity: even after split + top-off, vault WPLS+PLS didn't go below start-of-tx.
        // HC/JIT movement is governed by the off-chain scanner + ticket.finalGuard.
        require(finalWPLSValue >= beforeWPLSValue, "FINAL_WPLS_NEGATIVE");

        emit TicketExecuted(ticket.strategyId, ticket.jobNonce, profitWPLS);
    }

    // ----------------------------
    // Internal helpers
    // ----------------------------

    function _executeLegs(TicketLeg[] memory legs, uint256 txDeadline) internal {
        uint256 len = legs.length;
        require(len > 0, "NO_LEGS");
        for (uint256 i = 0; i < len; ++i) {
            TicketLeg memory leg = legs[i];
            if (policy.denyJitSells && _isJitSell(leg)) {
                revert("JIT_SELL_DENIED");
            }
            if (leg.key == LegKey.COMPILE) {
                _executeCompile(leg.amountIn, leg.amountOutMin);
            } else if (leg.key == LegKey.RESTORE) {
                _executeRestore(leg.amountIn, leg.amountOutMin);
            } else {
                require(leg.path.length >= 2, "PATH_SHORT");
                if (leg.key == LegKey.SWAP_SUPPORTING_FOT) {
                    _executeSwapSupportingFOT(leg.path, leg.amountIn, leg.amountOutMin, txDeadline);
                } else if (leg.key == LegKey.SWAP_EXACT) {
                    _executeSwapExact(leg.path, leg.amountIn, leg.amountOutMin, txDeadline);
                } else {
                    revert("LEG_UNKNOWN");
                }
            }
        }
    }

    function _isJitSell(TicketLeg memory leg) internal view returns (bool) {
        if (leg.key == LegKey.COMPILE || leg.path.length == 0) return false;
        return leg.path[0] == JIT;
    }

    function _executeCompile(uint256 amountIn, uint256 minOut) internal {
        require(amountIn > 0, "COMPILE_ZERO");
        IERC20 holyC = IERC20(HOLYC);
        require(holyC.balanceOf(address(this)) >= amountIn, "HC_BAL_LOW");
        uint256 beforeBal = IERC20(JIT).balanceOf(address(this));
        compiler.compile(amountIn);
        uint256 delta = IERC20(JIT).balanceOf(address(this)) - beforeBal;
        require(delta >= minOut, "COMPILE_MIN");
    }

    function _executeRestore(uint256 amountIn, uint256 minOut) internal {
        require(amountIn > 0, "RESTORE_ZERO");
        IERC20 holyC = IERC20(HOLYC);
        IERC20 jit = IERC20(JIT);
        require(jit.balanceOf(address(this)) >= amountIn, "JIT_BAL_LOW");
        uint256 beforeBal = holyC.balanceOf(address(this));
        compiler.restore(amountIn);
        uint256 afterBal = holyC.balanceOf(address(this));
        require(afterBal - beforeBal >= minOut, "RESTORE_MIN");
    }

    function _executeSwapSupportingFOT(
        address[] memory path,
        uint256 amountIn,
        uint256 minOut,
        uint256 txDeadline
    ) internal {
        require(IERC20(path[0]).balanceOf(address(this)) >= amountIn, "SWAP_BAL_LOW");
        uint256 beforeBal = IERC20(path[path.length - 1]).balanceOf(address(this));
        router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
            amountIn,
            minOut,
            path,
            address(this),
            txDeadline
        );
        uint256 delta = IERC20(path[path.length - 1]).balanceOf(address(this)) - beforeBal;
        require(delta >= minOut, "SWAP_FOT_MIN");
    }

    function _executeSwapExact(
        address[] memory path,
        uint256 amountIn,
        uint256 minOut,
        uint256 txDeadline
    ) internal {
        require(IERC20(path[0]).balanceOf(address(this)) >= amountIn, "SWAP_BAL_LOW");
        uint256 beforeBal = IERC20(path[path.length - 1]).balanceOf(address(this));
        router.swapExactTokensForTokens(amountIn, minOut, path, address(this), txDeadline);
        uint256 delta = IERC20(path[path.length - 1]).balanceOf(address(this)) - beforeBal;
        require(delta >= minOut, "SWAP_MIN");
    }

    function _settleBurn(BurnInstruction memory burn) internal {
        if (burn.owedHolyC == 0 && burn.owedJIT == 0) return;
        uint256 totalHolyC = burn.owedHolyC;
        if (burn.owedJIT > 0) {
            IERC20 jit = IERC20(JIT);
            require(jit.balanceOf(address(this)) >= burn.owedJIT, "BURN_JIT_BAL");
            uint256 beforeHC = IERC20(HOLYC).balanceOf(address(this));
            compiler.restore(burn.owedJIT);
            uint256 restored = IERC20(HOLYC).balanceOf(address(this)) - beforeHC;
            totalHolyC += restored;
        }
        if (totalHolyC > 0) {
            IERC20(HOLYC).safeTransfer(BURN_ADDRESS, totalHolyC);
        }
    }

    function _validateBinding(BindingData memory binding) internal view {
        require(binding.policyHash == policy.hash, "BIND_POLICY");
        if (binding.blockNumberObserved == 0) return; // optional binding
        require(block.number >= binding.blockNumberObserved, "BLOCK_PAST");
        uint256 len = binding.pairs.length;
        uint16 tolerance = binding.reservesToleranceBps > 0 ? binding.reservesToleranceBps : policy.reservesToleranceBps;
        for (uint256 i = 0; i < len; ++i) {
            BindingPair memory snap = binding.pairs[i];
            (uint112 reserve0, uint112 reserve1, ) = IUniswapV2Pair(snap.pair).getReserves();
            _requireWithinTolerance(reserve0, snap.reserve0, tolerance);
            _requireWithinTolerance(reserve1, snap.reserve1, tolerance);
        }
    }

    function _requireWithinTolerance(uint256 liveValue, uint256 snapValue, uint16 toleranceBps) internal pure {
        if (snapValue == 0) return;
        uint256 diff = liveValue > snapValue ? liveValue - snapValue : snapValue - liveValue;
        require(diff * BPS <= snapValue * toleranceBps, "RESERVE_DRIFT");
    }

    function _snapshotVault() internal view returns (VaultSnapshot memory snap) {
        snap.native = address(this).balance;
        snap.holyC = IERC20(HOLYC).balanceOf(address(this));
        snap.jit = IERC20(JIT).balanceOf(address(this));
        snap.wpls = IERC20(WPLS).balanceOf(address(this));
    }

    function _validateProfit(
        ExecutionTicket memory ticket,
        VaultSnapshot memory beforeSnap,
        VaultSnapshot memory afterSnap
    ) internal view returns (uint256 profitWPLS) {
        uint256 beforeWPLSValue = beforeSnap.wpls + beforeSnap.native;
        uint256 afterWPLSValue = afterSnap.wpls + afterSnap.native;
        require(afterWPLSValue >= beforeWPLSValue, "WPLS_NEGATIVE");
        profitWPLS = afterWPLSValue - beforeWPLSValue;
        require(profitWPLS >= ticket.minProfitWPLS && profitWPLS >= policy.minProfitWPLS, "MIN_PROFIT_FAIL");

        if (ticket.finalGuard.asset == Asset.HC) {
            require(afterSnap.holyC >= beforeSnap.holyC + ticket.finalGuard.minAmount, "HC_GUARD");
        } else if (ticket.finalGuard.asset == Asset.JIT) {
            require(afterSnap.jit >= beforeSnap.jit + ticket.finalGuard.minAmount, "JIT_GUARD");
        } else if (ticket.finalGuard.asset == Asset.WPLS) {
            require(afterWPLSValue >= beforeWPLSValue + ticket.finalGuard.minAmount, "WPLS_GUARD");
        }
    }

    function _distributeWPLS(address destination, uint256 amount) internal {
        uint256 nativeBal = address(this).balance;
        if (nativeBal < amount) {
            uint256 shortage = amount - nativeBal;
            uint256 wplsBal = IERC20(WPLS).balanceOf(address(this));
            require(wplsBal >= shortage, "SPLIT_FUNDS");
            wplsWrapper.withdraw(shortage);
        }
        (bool ok, ) = payable(destination).call{value: amount}("");
        require(ok, "SPLIT_NATIVE_FAIL");
    }

    function _topOffCaller(uint256 maxSpendWPLS, uint256 txDeadline) internal returns (uint256 spent) {
        address target = botCaller;
        if (target == address(0)) return 0;
        uint256 balance = target.balance;
        if (balance >= CALLER_TOP_OFF_THRESHOLD) return 0;
        uint256 deficit = CALLER_TOP_OFF_THRESHOLD - balance;
        if (deficit == 0) return 0;
        if (maxSpendWPLS < deficit) {
            return 0;
        }

        uint256 nativeBal = address(this).balance;
        uint256 remaining = deficit;
        uint256 wplsUsed = 0;

        if (nativeBal >= remaining) {
            (bool ok, ) = payable(target).call{value: remaining}("");
            require(ok, "TOP_OFF_NATIVE_FAIL");
            emit TopOffExecuted(target, remaining, 0);
            return remaining;
        }

        // Use all native first
        if (nativeBal > 0) {
            (bool ok2, ) = payable(target).call{value: nativeBal}("");
            require(ok2, "TOP_OFF_NATIVE_FAIL");
            remaining -= nativeBal;
            spent += nativeBal;
        }

        uint256 wplsBal = IERC20(WPLS).balanceOf(address(this));
        if (wplsBal >= remaining) {
            wplsWrapper.withdraw(remaining);
            wplsUsed += remaining;
            (bool ok3, ) = payable(target).call{value: remaining}("");
            require(ok3, "TOP_OFF_NATIVE_FAIL");
            spent += remaining;
            emit TopOffExecuted(target, deficit, wplsUsed);
            return spent;
        }

        // Need to convert HolyC -> WPLS for the residual amount.
        if (wplsBal > 0) {
            wplsWrapper.withdraw(wplsBal);
            (bool ok4, ) = payable(target).call{value: wplsBal}("");
            require(ok4, "TOP_OFF_NATIVE_FAIL");
            spent += wplsBal;
            remaining -= wplsBal;
            wplsUsed += wplsBal;
        }

        _swapHolyCForWPLSTopOff(remaining, txDeadline);

        wplsWrapper.withdraw(remaining);
        (bool ok5, ) = payable(target).call{value: remaining}("");
        require(ok5, "TOP_OFF_NATIVE_FAIL");
        spent = deficit;
        wplsUsed += remaining;
        emit TopOffExecuted(target, deficit, wplsUsed);
    }

    function _swapHolyCForWPLSTopOff(uint256 amountOut, uint256 txDeadline) internal {
        // Sell HOLYC to source the exact WPLS amount needed for gas top-off.
        address[] memory path = new address[](2);
        path[0] = HOLYC;
        path[1] = WPLS;

        uint256 amountHC = router.getAmountsIn(amountOut, path)[0];
        require(IERC20(HOLYC).balanceOf(address(this)) >= amountHC, "HC_TOP_OFF");

        uint256 before = IERC20(WPLS).balanceOf(address(this));
        router.swapExactTokensForTokens(amountHC, amountOut, path, address(this), txDeadline);
        require(IERC20(WPLS).balanceOf(address(this)) - before >= amountOut, "TOP_OFF_MIN_OUT");
    }

    function _computePolicyHash(Policy calldata p) internal pure returns (bytes32) {
        return keccak256(
            abi.encode(
                p.minProfitWPLS,
                p.basefeeCap,
                p.safetyBpsPerLeg,
                p.reservesToleranceBps,
                p.deadlineSeconds,
                p.executorTaxExempt,
                p.burnAfterEnabled,
                p.denyJitSells
            )
        );
    }

    function _setAllowances(address router_) internal {
        IERC20(HOLYC).safeApprove(router_, 0);
        IERC20(JIT).safeApprove(router_, 0);
        IERC20(WPLS).safeApprove(router_, 0);
        IERC20(HOLYC).safeApprove(address(compiler), 0);
        IERC20(JIT).safeApprove(address(compiler), 0);
        IERC20(HOLYC).safeApprove(router_, type(uint256).max);
        IERC20(JIT).safeApprove(router_, type(uint256).max);
        IERC20(WPLS).safeApprove(router_, type(uint256).max);
        IERC20(HOLYC).safeApprove(address(compiler), type(uint256).max);
        IERC20(JIT).safeApprove(address(compiler), type(uint256).max);
    }

    // Allow the contract to receive native PLS
    receive() external payable {}
}