Quickstart: your first swap
We'll swap 0.5 zkLTC for ZKUSDC using the Swap API and viem. The API finds the route and builds the transaction, you just sign and send it.
Install viem
npm install viemAny library that can send a transaction works (ethers, web3.py, a raw eth_sendTransaction). We use viem because
it's small and typed.
Describe the chain
viem needs to know about LitVM once. Save this as litvm.ts:
import { defineChain } from 'viem'
export const litvm = defineChain({
id: 4441,
name: 'LitVM LiteForge',
nativeCurrency: { name: 'zkLTC', symbol: 'zkLTC', decimals: 18 },
rpcUrls: { default: { http: ['https://liteforge.rpc.caldera.xyz/http'] } },
blockExplorers: {
default: { name: 'LiteForge Explorer', url: 'https://liteforge.explorer.caldera.xyz' },
},
contracts: {
multicall3: { address: '0xcA11bde05977b3631167028862bE2a173976CA11' },
},
})Get a quote
A quote is a free read. Great for showing a price while the user is still typing.
const API = 'https://www.litvmswap.com/api'
const NATIVE = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' // native zkLTC
const ZKUSDC = '0xdf69970B2fE416339187aA41D39882e864984CE9'
const params = new URLSearchParams({
fromToken: NATIVE,
toToken: ZKUSDC,
amount: '0.5', // human units, not wei
partnerFeeBps: '0', // no integrator fee for this example
})
const quote = await fetch(`${API}/partner/quote?${params}`).then((r) => r.json())
if (quote.error) throw new Error(quote.error)
console.log(`0.5 zkLTC ≈ ${quote.toAmount} ${quote.toToken.symbol} via ${quote.route.dexName}`)Build the transaction
When the user hits the button, ask for a real transaction. This one is built for their address and their slippage, so it's the number to show right before they sign.
const account = '0xYourWalletAddress'
const swap = await fetch(`${API}/partner/swap`, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
fromToken: NATIVE,
toToken: ZKUSDC,
amount: '0.5',
userAddress: account,
slippage: 0.5, // percent
partnerFeeBps: 0,
}),
}).then((r) => r.json())
if (swap.error) throw new Error(swap.error)
console.log(`You get about ${swap.estimate.toAmount}, at least ${swap.estimate.minAmountOut}`)Approve (only when selling an ERC-20)
Selling native zkLTC? Skip this step, the value rides along with the transaction. Selling a token? The swap
contract needs an allowance first. Approve the exact amount and only the spender the API returned.
import { createPublicClient, createWalletClient, custom, erc20Abi, http, parseUnits } from 'viem'
import 'viem/window' // types for window.ethereum
import { litvm } from './litvm'
const publicClient = createPublicClient({ chain: litvm, transport: http() })
const wallet = createWalletClient({ chain: litvm, transport: custom(window.ethereum!) })
async function ensureAllowance(token: `0x${string}`, owner: `0x${string}`, spender: `0x${string}`, amount: string, decimals: number) {
const needed = parseUnits(amount, decimals)
const current = await publicClient.readContract({
address: token,
abi: erc20Abi,
functionName: 'allowance',
args: [owner, spender],
})
if (current >= needed) return
const hash = await wallet.writeContract({
account: owner,
address: token,
abi: erc20Abi,
functionName: 'approve',
args: [spender, needed],
})
await publicClient.waitForTransactionReceipt({ hash })
}Send it
const hash = await wallet.sendTransaction({
account,
to: swap.transaction.to,
data: swap.transaction.data,
value: BigInt(swap.transaction.value), // "0" for token sells
})
const receipt = await publicClient.waitForTransactionReceipt({ hash })
console.log(receipt.status === 'success' ? 'Swapped 🎉' : 'Reverted, see the explorer')
console.log(`https://liteforge.explorer.caldera.xyz/tx/${hash}`)That's it
You just did a routed swap on LitVM. A few things worth doing next:
- Show users
minAmountOut, not onlytoAmount. That's the worst case they agreed to. - If the transaction reverts because the price moved, fetch a fresh
/partner/swapand try again. Don't just resend the old calldata. - Want to earn a little on the swaps you send? See integrator fees.
Start with tiny swaps while you wire things up. A wrong token address or decimals bug costs real tokens.