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
About Author Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc fermentum lectus eget interdum varius. Curabitur ut nibh vel velit cursus molestie. Cras sed sagittis erat. Nullam id ante hendrerit, lobortis justo ac, fermentum neque. Mauris egestas maximus tortor. Nunc non neque a quam sollicitudin facilisis. Maecenas posuere turpis arcu, vel tempor ipsum tincidunt ut.
WHAT'S YOUR OPINION?
Related News