Created
November 21, 2025 07:36
-
-
Save gaupoit/4bbe6967597527e8fb9552024788a195 to your computer and use it in GitHub Desktop.
MachineX Swap
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { | |
| createPublicClient, | |
| createWalletClient, | |
| http, | |
| parseUnits, | |
| formatUnits, | |
| zeroAddress | |
| } from "viem"; | |
| import { peaq } from "viem/chains"; | |
| import { privateKeyToAccount } from "viem/accounts"; | |
| // --- 1. CONFIGURATION --- | |
| const PRIVATE_KEY = ""; // Replace with your Private Key | |
| // The Pool Address you want to use (e.g., WPEAQ/TOPS 1%) | |
| const POOL_ADDRESS = "0xD5669Ada8FE453A68Aa441C38613d45fe0ae2003"; | |
| // Standard Uniswap V3 SwapRouter02 Address | |
| const ROUTER_ADDRESS = "0x723eA41937d7C1F5a4C5ccFa5bfF82210de17C71"; | |
| // Amount to swap (in human readable units, e.g., "1.5") | |
| // The script will automatically detect decimals. | |
| const AMOUNT_TO_SWAP = "0.001"; | |
| // --- 2. ABIS --- | |
| // Minimal Pool ABI to get details | |
| const POOL_ABI = [ | |
| { inputs: [], name: "token0", outputs: [{ type: "address" }], stateMutability: "view", type: "function" }, | |
| { inputs: [], name: "token1", outputs: [{ type: "address" }], stateMutability: "view", type: "function" }, | |
| { inputs: [], name: "fee", outputs: [{ type: "uint24" }], stateMutability: "view", type: "function" }, | |
| { inputs: [], name: "liquidity", outputs: [{ type: "uint128" }], stateMutability: "view", type: "function" } | |
| ]; | |
| // Minimal ERC20 ABI | |
| const ERC20_ABI = [ | |
| { inputs: [{ name: "account", type: "address" }], name: "balanceOf", outputs: [{ type: "uint256" }], stateMutability: "view", type: "function" }, | |
| { inputs: [{ name: "spender", type: "address" }, { name: "amount", type: "uint256" }], name: "approve", outputs: [{ type: "bool" }], stateMutability: "nonpayable", type: "function" }, | |
| { inputs: [], name: "decimals", outputs: [{ type: "uint8" }], stateMutability: "view", type: "function" }, | |
| { inputs: [], name: "symbol", outputs: [{ type: "string" }], stateMutability: "view", type: "function" } | |
| ]; | |
| // Router ABI for exactInputSingle | |
| const ROUTER_ABI = [ | |
| { | |
| inputs: [ | |
| { | |
| components: [ | |
| { name: "tokenIn", type: "address" }, | |
| { name: "tokenOut", type: "address" }, | |
| { name: "fee", type: "uint24" }, | |
| { name: "recipient", type: "address" }, | |
| { name: "amountIn", type: "uint256" }, | |
| { name: "amountOutMinimum", type: "uint256" }, | |
| { name: "sqrtPriceLimitX96", type: "uint160" }, | |
| ], | |
| name: "params", | |
| type: "tuple", | |
| }, | |
| ], | |
| name: "exactInputSingle", | |
| outputs: [{ name: "amountOut", type: "uint256" }], | |
| stateMutability: "payable", | |
| type: "function", | |
| }, | |
| ]; | |
| // --- 3. MAIN SCRIPT --- | |
| async function main() { | |
| // Setup Clients | |
| const publicClient = createPublicClient({ chain: peaq, transport: http() }); | |
| const account = privateKeyToAccount(PRIVATE_KEY); | |
| const walletClient = createWalletClient({ account, chain: peaq, transport: http() }); | |
| console.log(`--- Uniswap V3 Pool Swap ---`); | |
| console.log(`Wallet: ${account.address}`); | |
| console.log(`Target Pool: ${POOL_ADDRESS}`); | |
| try { | |
| // 1. Fetch Pool Details | |
| // We use multicall to fetch token0, token1, and fee in one go | |
| const [token0Result, token1Result, feeResult, liquidityResult] = await publicClient.multicall({ | |
| contracts: [ | |
| { address: POOL_ADDRESS, abi: POOL_ABI, functionName: "token0" }, | |
| { address: POOL_ADDRESS, abi: POOL_ABI, functionName: "token1" }, | |
| { address: POOL_ADDRESS, abi: POOL_ABI, functionName: "fee" }, | |
| { address: POOL_ADDRESS, abi: POOL_ABI, functionName: "liquidity" } | |
| ], | |
| allowFailure: false | |
| }); | |
| const token0 = token0Result; | |
| const token1 = token1Result; | |
| const fee = feeResult; | |
| const liquidity = liquidityResult; | |
| console.log(`\nPool Data Loaded:`); | |
| console.log(`Token 0: ${token0}`); | |
| console.log(`Token 1: ${token1}`); | |
| console.log(`Fee Tier: ${fee} (${fee / 10000}%)`); | |
| console.log(`Liquidity: ${liquidity}`); | |
| if (liquidity === 0n) { | |
| throw new Error("Pool has no liquidity!"); | |
| } | |
| // 2. Determine Swap Direction (What does the user have?) | |
| // Fetch user balances and decimals for both tokens | |
| const [ | |
| bal0, bal1, | |
| dec0, dec1, | |
| sym0, sym1 | |
| ] = await publicClient.multicall({ | |
| contracts: [ | |
| { address: token0, abi: ERC20_ABI, functionName: "balanceOf", args: [account.address] }, | |
| { address: token1, abi: ERC20_ABI, functionName: "balanceOf", args: [account.address] }, | |
| { address: token0, abi: ERC20_ABI, functionName: "decimals" }, | |
| { address: token1, abi: ERC20_ABI, functionName: "decimals" }, | |
| { address: token0, abi: ERC20_ABI, functionName: "symbol" }, | |
| { address: token1, abi: ERC20_ABI, functionName: "symbol" } | |
| ], | |
| allowFailure: false | |
| }); | |
| console.log(`\nYour Balances:`); | |
| console.log(`- ${sym0}: ${formatUnits(bal0, dec0)}`); | |
| console.log(`- ${sym1}: ${formatUnits(bal1, dec1)}`); | |
| // Logic: We try to swap the token specified in configuration (AMOUNT_TO_SWAP). | |
| // But we need to know which one to treat as input. | |
| // For this example, we'll assume we swap Token 0 -> Token 1. | |
| // You can change this logic to swap based on which balance is higher. | |
| let tokenIn, tokenOut, amountInRaw, decimalsIn, symbolIn; | |
| // Simple decision logic: Check if we have enough Token 0. If yes, swap 0->1. Else check Token 1. | |
| // We parse the target amount based on decimals. | |
| const amountIn0 = parseUnits(AMOUNT_TO_SWAP, dec0); | |
| const amountIn1 = parseUnits(AMOUNT_TO_SWAP, dec1); | |
| if (bal0 >= amountIn0) { | |
| console.log(`\nConfig: Swapping ${AMOUNT_TO_SWAP} ${sym0} -> ${sym1}`); | |
| tokenIn = token0; | |
| tokenOut = token1; | |
| amountInRaw = amountIn0; | |
| decimalsIn = dec0; | |
| symbolIn = sym0; | |
| } else if (bal1 >= amountIn1) { | |
| console.log(`\nConfig: Swapping ${AMOUNT_TO_SWAP} ${sym1} -> ${sym0}`); | |
| tokenIn = token1; | |
| tokenOut = token0; | |
| amountInRaw = amountIn1; | |
| decimalsIn = dec1; | |
| symbolIn = sym1; | |
| } else { | |
| throw new Error(`Insufficient balance in either token to swap ${AMOUNT_TO_SWAP}`); | |
| } | |
| // 3. Approve Router | |
| console.log(`\nStep 1: Approving Router to spend ${symbolIn}...`); | |
| const approveHash = await walletClient.writeContract({ | |
| address: tokenIn, | |
| abi: ERC20_ABI, | |
| functionName: "approve", | |
| args: [ROUTER_ADDRESS, amountInRaw] | |
| }); | |
| console.log(`Approval sent: ${approveHash}`); | |
| await publicClient.waitForTransactionReceipt({ hash: approveHash }); | |
| console.log("Approval Confirmed."); | |
| // 4. Execute Swap (exactInputSingle) | |
| console.log(`\nStep 2: Executing Swap...`); | |
| const params = { | |
| tokenIn: tokenIn, | |
| tokenOut: tokenOut, | |
| fee: fee, // Use the fee we fetched from the pool! | |
| recipient: account.address, | |
| amountIn: amountInRaw, | |
| amountOutMinimum: 0n, // Be careful in production! Calculate slippage. | |
| sqrtPriceLimitX96: 0n, // No limit | |
| }; | |
| const swapHash = await walletClient.writeContract({ | |
| address: ROUTER_ADDRESS, | |
| abi: ROUTER_ABI, | |
| functionName: "exactInputSingle", | |
| args: [params] | |
| }); | |
| console.log(`Swap transaction sent: ${swapHash}`); | |
| const receipt = await publicClient.waitForTransactionReceipt({ hash: swapHash }); | |
| console.log(`Swap Mined! Status: ${receipt.status}`); | |
| console.log(`Transaction: https://etherscan.io/tx/${swapHash}`); | |
| } catch (e) { | |
| console.error("Error:", e); | |
| } | |
| } | |
| main(); |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Error when running this code base