Using Viem

Swaps can be done through a website or programmatically through a standard Uniswap V3 API interface.
Below are the requirements
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}));// 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" }]
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
]
);
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'
}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.