const UNIVERSAL_ROUTER_ADDRESS = "0x8876789976dEcBfCbBbe364623C63652db8C0904"; const ARB_WALLET = "0xd064E892A3BBb758053922C80098b7f4130444c6"; const USDG = "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168"; const PONS = "0x39dbed3a2bd333467115de45665cc57f813c4571"; const UNIVERSAL_ROUTER_ABI = [ "function execute(bytes commands, bytes[] inputs, uint256 deadline)" ]; async function executeRecommendedArb() { try { if (!window.ethereum) return alert("MetaMask missing."); // 1. Extract pool data and prices from your UI state const priceA = parseFloat(document.getElementById("priceA_display").innerText); const priceB = parseFloat(document.getElementById("priceB_display").innerText); const inputAmount = ethers.utils.parseUnits(document.getElementById("amount_input").value, 18); if (isNaN(priceA) || isNaN(priceB) || priceA === priceB) { return alert("Invalid or identical pool prices. Aborting."); } // 2. Read structural parameters from your hidden inputs // (Ensure your row-selection logic populates these from the table row data-attributes!) const poolA = { addr: document.getElementById("poolA_input").value, hook: document.getElementById("poolA_hook").value || "0x0000000000000000000000000000000000000000", spacing: parseInt(document.getElementById("poolA_tickSpacing").value) || 50, fee: parseInt(document.getElementById("poolA_fee").value) || 2500 }; const poolB = { addr: document.getElementById("poolB_input").value, hook: document.getElementById("poolB_hook").value || "0x0000000000000000000000000000000000000000", spacing: parseInt(document.getElementById("poolB_tickSpacing").value) || 50, fee: parseInt(document.getElementById("poolB_fee").value) || 2500 }; // 3. Dynamic Arb Evaluation (Determine optimal execution path) let buyPool, sellPool; if (priceA < priceB) { // Pool A is cheaper: Buy on A (USDG -> PONS), Sell on B (PONS -> USDG) buyPool = poolA; sellPool = poolB; console.log(`Recommendation: Buy Pool A (${priceA}), Sell Pool B (${priceB})`); } else { // Pool B is cheaper: Buy on B (USDG -> PONS), Sell on A (PONS -> USDG) buyPool = poolB; sellPool = poolA; console.log(`Recommendation: Buy Pool B (${priceB}), Sell Pool A (${priceA})`); } // 4. Set accurate Uniswap V4 token0/token1 ordering constraints const isUsdgToken0 = BigInt(USDG) < BigInt(PONS); const token0 = isUsdgToken0 ? USDG : PONS; const token1 = isUsdgToken0 ? PONS : USDG; const provider = new ethers.providers.Web3Provider(window.ethereum); await provider.send("eth_requestAccounts", []); const signer = provider.getSigner(); // 5. Encode HOP 1: Buy Swap (Always exchanging USDG for PONS) const poolKey1 = { currency0: token0, currency1: token1, fee: buyPool.fee, tickSpacing: buyPool.spacing, hooks: buyPool.hook }; const params1 = { zeroForOne: isUsdgToken0, // true if selling USDG (token0) for PONS (token1) amountIn: inputAmount, amountOutMinimum: 0, // Low value acceptable for small scale testing hookData: "0x" }; const input1 = ethers.utils.defaultAbiCoder.encode( ["tuple(address currency0, address currency1, uint24 fee, int24 tickSpacing, address hooks)", "tuple(bool zeroForOne, uint256 amountIn, uint256 amountOutMinimum, bytes hookData)"], [poolKey1, params1] ); // 6. Encode HOP 2: Sell Swap (Always exchanging PONS back to USDG) const poolKey2 = { currency0: token0, currency1: token1, fee: sellPool.fee, tickSpacing: sellPool.spacing, hooks: sellPool.hook }; const params2 = { zeroForOne: !isUsdgToken0, // Flipped to sell PONS for USDG amountIn: 0, // 0 flags the Router to swap the exact output from Hop 1 amountOutMinimum: 0, hookData: "0x" }; const input2 = ethers.utils.defaultAbiCoder.encode( ["tuple(address currency0, address currency1, uint24 fee, int24 tickSpacing, address hooks)", "tuple(bool zeroForOne, uint256 amountIn, uint256 amountOutMinimum, bytes hookData)"], [poolKey2, params2] ); // 7. Assemble and Dispatch to MetaMask const commands = "0x1010"; // V4_SWAP -> V4_SWAP const inputs = [input1, input2]; const deadline = Math.floor(Date.now() / 1000) + 1200; const router = new ethers.Contract(UNIVERSAL_ROUTER_ADDRESS, UNIVERSAL_ROUTER_ABI, signer); const txData = await router.populateTransaction.execute(commands, inputs, deadline); const txHash = await window.ethereum.request({ method: 'eth_sendTransaction', params: [{ from: ARB_WALLET, to: UNIVERSAL_ROUTER_ADDRESS, data: txData.data, value: '0x00' }] }); document.getElementById("tx_status_label").innerText = `Dispatched! Hash: ${txHash}`; } catch (err) { console.error("Automation error:", err); alert(`Arb Execution Failed: ${err.message}`); } }