How to Programmatically Multi-Swap a V3 DEX

05-Aug-2025 Medium » Coinmonks

Using Viem

Overview

Swaps can be done through a website or programmatically through a standard Uniswap V3 API interface.

Below are the requirements

  • nodejs
  • viem

Below is the full source code:

import {http, createWalletClient, encodePacked} from "viem";
import {privateKeyToAccount} from "viem/accounts";
import {sepolia} from "viem/chains";

// 0x101010e6Cc358616FB6Cc69e4443Fb54E6582F1f
const PRIVATE_KEY = "0x6fd707ca7385cdfb0093be77c37a1d6de57566c48cc7bf6463abbe5f003be464";

// https://sepolia.etherscan.io/address/0x3bFA4769FB09eefC5a80d6E87c3B9C650f7Ae48E#code
const swapRouter = "0x3bFA4769FB09eefC5a80d6E87c3B9C650f7Ae48E";

const walletClient = createWalletClient({
account: privateKeyToAccount(PRIVATE_KEY),
chain: sepolia,
transport: http(),
});

const swapRouterAbi = [{ "inputs": [ { "components": [ { "internalType": "bytes", "name": "path", "type": "bytes" }, { "internalType": "address", "name": "recipient", "type": "address" }, { "internalType": "uint256", "name": "amountIn", "type": "uint256" }, { "internalType": "uint256", "name": "amountOutMinimum", "type": "uint256" } ], "internalType": "struct IV3SwapRouter.ExactInputParams", "name": "params", "type": "tuple" } ], "name": "exactInput", "outputs": [ { "internalType": "uint256", "name": "amountOut", "type": "uint256" } ], "stateMutability": "payable", "type": "function" }]

// https://app.uniswap.org/positions/v3/ethereum_sepolia/201908
const path = encodePacked(
["address", "uint24", "address"],
[
"0xa652ca947878766bd54813e8e9609ec35dbb2e79", // token A
3000, // A -> B fee (0.3%)
"0x7832e346ef46a888118800807b2de9240288d40e", // token B
]
);

walletClient
.writeContract({
abi: swapRouterAbi,
address: swapRouter,
account: walletClient.account,
functionName: "exactInput",
args: [
{
path, // encoded bytes path
recipient: walletClient.account.address,
amountIn: 1 * 10 ** 18, // token A
amountOutMinimum: 0n,
},
],
})
.then((hash) => console.log({hash}));

Define Variables

  • PRIVATE_KEY of your wallet
  • swapRouter address
  • walletClient to broadcast the transaction
  • swapRouterAbi to interface with the smart contract
// 0x101010e6Cc358616FB6Cc69e4443Fb54E6582F1f
const PRIVATE_KEY = "0x6fd707ca7385cdfb0093be77c37a1d6de57566c48cc7bf6463abbe5f003be464";

// https://sepolia.etherscan.io/address/0x3bFA4769FB09eefC5a80d6E87c3B9C650f7Ae48E#code
const swapRouter = "0x3bFA4769FB09eefC5a80d6E87c3B9C650f7Ae48E";

const walletClient = createWalletClient({
account: privateKeyToAccount(PRIVATE_KEY),
chain: sepolia,
transport: http(),
});

const swapRouterAbi = [{ "inputs": [ { "components": [ { "internalType": "bytes", "name": "path", "type": "bytes" }, { "internalType": "address", "name": "recipient", "type": "address" }, { "internalType": "uint256", "name": "amountIn", "type": "uint256" }, { "internalType": "uint256", "name": "amountOutMinimum", "type": "uint256" } ], "internalType": "struct IV3SwapRouter.ExactInputParams", "name": "params", "type": "tuple" } ], "name": "exactInput", "outputs": [ { "internalType": "uint256", "name": "amountOut", "type": "uint256" } ], "stateMutability": "payable", "type": "function" }]

Define Paths

Paths can be a single hop, or multihop. In the example below, it’s doing a single hop from token A to token B.

const path = encodePacked(
["address", "uint24", "address"],
[
"0xa652ca947878766bd54813e8e9609ec35dbb2e79", // token A
3000, // fee (0.3%)
"0x7832e346ef46a888118800807b2de9240288d40e", // token B
]
);

You can do multihops if there’s no direct route from A → C

const path = encodePacked(
["address", "uint24", "address", "uint24", "address"],
[
"0xa652ca947878766bd54813e8e9609ec35dbb2e79", // token A
3000, // A -> B fee
"0x7832e346ef46a888118800807b2de9240288d40e", // token B
3000, // B -> C fee
"0xdAC17F958D2ee523a2206206994597C13D831ec7", // token C
]
);

Write “exactInput”

To execute the multihop swap, call exactInput

walletClient
.writeContract({
abi: swapRouterAbi,
address: swapRouter,
account: walletClient.account,
functionName: "exactInput",
args: [
{
path, // encoded bytes path
recipient: walletClient.account.address,
amountIn: 1 * 10 ** 18, // token A
amountOutMinimum: 0n,
},
],
})
.then((hash) => console.log({hash}));

output should look like

{
hash: '0x993a07efa0994a542206fbebd87674babb1d464e35cdd1a843f7c7609ff3262c'
}

Approve Spending (prerequisite)

Make sure the wallet has approved spending. This allows the swapRouter to spend token A

const erc20Abi = [{ "inputs": [ { "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "value", "type": "uint256" } ], "name": "approve", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" }]

// allow swapRouter to spend token A
const approveHash = await walletClient.writeContract({
abi: erc20Abi,
address: "0xa652ca947878766bd54813e8e9609ec35dbb2e79", // token A
functionName: "approve",
args: [swapRouter, BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")],
});
console.log({approveHash});

How to Programmatically Multi-Swap a V3 DEX was originally published in Coinmonks on Medium, where people are continuing the conversation by highlighting and responding to this story.

Also read: Precision Over Pump: Building a Swap Sniper in the Fixed/Float Jungle
WHAT'S YOUR OPINION?
Related News