1

I want to programmatically swap using Uniswap Universal Router, but the transaction always gets reverted

Here is my code:

const ethers = require("ethers");
const { abi: V3SwapRouterABI } = require('@uniswap/v3-periphery/artifacts/contracts/SwapRouter.sol/SwapRouter.json')


const ROUTER_ADDRESS = "0x3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad"
const WETH_ADDRESS = "0xf531B8F309Be94191af87605CfBf600D71C2cFe0"
const UNI_ADDRESS = "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984"
privateKey = ""
const provider = new ethers.JsonRpcProvider("https://ethereum-sepolia.rpc.subquery.network/public")
const signer = new ethers.Wallet(privateKey, provider)

const router = new ethers.Contract(ROUTER_ADDRESS, V3SwapRouterABI, signer)

async function main() {
   
    const inputAmount = ethers.parseEther("0.0001")
    const params = {
        tokenIn: WETH_ADDRESS,
        tokenOut: UNI_ADDRESS,
        fee: 3000,
        recipient: signer.address,
        deadline: Math.floor(Date.now() / 1000) + 60 * 10,
        amountIn: ethers.parseEther("0.00001"),
        amountOutMinimum: 0,
        sqrtPriceLimitX96: 0,
      };
    
    const data = router.interface.encodeFunctionData(
        "exactInputSingle", 
        [params]);

    const txArgs = {
        to: ROUTER_ADDRESS,
        value: inputAmount,
        data: data,
        gasLimit: "343433",
    }

    const tx = await signer.sendTransaction(txArgs);
    const reciept = await tx.wait();
    console.log(reciept);
}
main();

Here is the transaction that got reverted: Sepolia Etherscan

What is the problem here, are the parameters not correctly set up?

I have also double-checked my ETH and WETH balance, there are enough to make the swap!

2
  • 3
    Are you calling the correct contract? It doesn't seem to implement the function exactInputSingle. Etherscan shows execute.
    – Ismael
    Commented Apr 16 at 15:18
  • I followed this guys' code, youtube.com/watch?v=hg_BrywT2Cs&t=288s His code worked, but mine doesnt :(
    – duyanhhz
    Commented Apr 16 at 15:45

1 Answer 1

3

As Ismael mentioned in the comments, you're not using the correct contract.

You're using the UniversalRouter contract instead of the SwapRouter.

Since, the SwapRouter is not available on the Sepolia testnet, you can use SwapRouter02 instead. All the parameters would remain the same, you just need to remove the deadline parameter in case of SwapRouter02.

Also, the WETH_ADDRESS should be 0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14, as specified in the Uniswap Docs:

enter image description here

You can find all the available deployed addresses on this page of Uniswap Docs.

Here's the revised code with the changes:

const ethers = require("ethers");

const SWAP_ROUTER02_ABI = [{ "inputs": [{ "internalType": "address", "name": "_factoryV2", "type": "address" }, { "internalType": "address", "name": "factoryV3", "type": "address" }, { "internalType": "address", "name": "_positionManager", "type": "address" }, { "internalType": "address", "name": "_WETH9", "type": "address" }], "stateMutability": "nonpayable", "type": "constructor" }, { "inputs": [], "name": "WETH9", "outputs": [{ "internalType": "address", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "token", "type": "address" }], "name": "approveMax", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "token", "type": "address" }], "name": "approveMaxMinusOne", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "token", "type": "address" }], "name": "approveZeroThenMax", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "token", "type": "address" }], "name": "approveZeroThenMaxMinusOne", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "bytes", "name": "data", "type": "bytes" }], "name": "callPositionManager", "outputs": [{ "internalType": "bytes", "name": "result", "type": "bytes" }], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "bytes[]", "name": "paths", "type": "bytes[]" }, { "internalType": "uint128[]", "name": "amounts", "type": "uint128[]" }, { "internalType": "uint24", "name": "maximumTickDivergence", "type": "uint24" }, { "internalType": "uint32", "name": "secondsAgo", "type": "uint32" }], "name": "checkOracleSlippage", "outputs": [], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "bytes", "name": "path", "type": "bytes" }, { "internalType": "uint24", "name": "maximumTickDivergence", "type": "uint24" }, { "internalType": "uint32", "name": "secondsAgo", "type": "uint32" }], "name": "checkOracleSlippage", "outputs": [], "stateMutability": "view", "type": "function" }, { "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" }, { "inputs": [{ "components": [{ "internalType": "address", "name": "tokenIn", "type": "address" }, { "internalType": "address", "name": "tokenOut", "type": "address" }, { "internalType": "uint24", "name": "fee", "type": "uint24" }, { "internalType": "address", "name": "recipient", "type": "address" }, { "internalType": "uint256", "name": "amountIn", "type": "uint256" }, { "internalType": "uint256", "name": "amountOutMinimum", "type": "uint256" }, { "internalType": "uint160", "name": "sqrtPriceLimitX96", "type": "uint160" }], "internalType": "struct IV3SwapRouter.ExactInputSingleParams", "name": "params", "type": "tuple" }], "name": "exactInputSingle", "outputs": [{ "internalType": "uint256", "name": "amountOut", "type": "uint256" }], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "components": [{ "internalType": "bytes", "name": "path", "type": "bytes" }, { "internalType": "address", "name": "recipient", "type": "address" }, { "internalType": "uint256", "name": "amountOut", "type": "uint256" }, { "internalType": "uint256", "name": "amountInMaximum", "type": "uint256" }], "internalType": "struct IV3SwapRouter.ExactOutputParams", "name": "params", "type": "tuple" }], "name": "exactOutput", "outputs": [{ "internalType": "uint256", "name": "amountIn", "type": "uint256" }], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "components": [{ "internalType": "address", "name": "tokenIn", "type": "address" }, { "internalType": "address", "name": "tokenOut", "type": "address" }, { "internalType": "uint24", "name": "fee", "type": "uint24" }, { "internalType": "address", "name": "recipient", "type": "address" }, { "internalType": "uint256", "name": "amountOut", "type": "uint256" }, { "internalType": "uint256", "name": "amountInMaximum", "type": "uint256" }, { "internalType": "uint160", "name": "sqrtPriceLimitX96", "type": "uint160" }], "internalType": "struct IV3SwapRouter.ExactOutputSingleParams", "name": "params", "type": "tuple" }], "name": "exactOutputSingle", "outputs": [{ "internalType": "uint256", "name": "amountIn", "type": "uint256" }], "stateMutability": "payable", "type": "function" }, { "inputs": [], "name": "factory", "outputs": [{ "internalType": "address", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "factoryV2", "outputs": [{ "internalType": "address", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "token", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" }], "name": "getApprovalType", "outputs": [{ "internalType": "enum IApproveAndCall.ApprovalType", "name": "", "type": "uint8" }], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "components": [{ "internalType": "address", "name": "token0", "type": "address" }, { "internalType": "address", "name": "token1", "type": "address" }, { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, { "internalType": "uint256", "name": "amount0Min", "type": "uint256" }, { "internalType": "uint256", "name": "amount1Min", "type": "uint256" }], "internalType": "struct IApproveAndCall.IncreaseLiquidityParams", "name": "params", "type": "tuple" }], "name": "increaseLiquidity", "outputs": [{ "internalType": "bytes", "name": "result", "type": "bytes" }], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "components": [{ "internalType": "address", "name": "token0", "type": "address" }, { "internalType": "address", "name": "token1", "type": "address" }, { "internalType": "uint24", "name": "fee", "type": "uint24" }, { "internalType": "int24", "name": "tickLower", "type": "int24" }, { "internalType": "int24", "name": "tickUpper", "type": "int24" }, { "internalType": "uint256", "name": "amount0Min", "type": "uint256" }, { "internalType": "uint256", "name": "amount1Min", "type": "uint256" }, { "internalType": "address", "name": "recipient", "type": "address" }], "internalType": "struct IApproveAndCall.MintParams", "name": "params", "type": "tuple" }], "name": "mint", "outputs": [{ "internalType": "bytes", "name": "result", "type": "bytes" }], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "bytes32", "name": "previousBlockhash", "type": "bytes32" }, { "internalType": "bytes[]", "name": "data", "type": "bytes[]" }], "name": "multicall", "outputs": [{ "internalType": "bytes[]", "name": "", "type": "bytes[]" }], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "uint256", "name": "deadline", "type": "uint256" }, { "internalType": "bytes[]", "name": "data", "type": "bytes[]" }], "name": "multicall", "outputs": [{ "internalType": "bytes[]", "name": "", "type": "bytes[]" }], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "bytes[]", "name": "data", "type": "bytes[]" }], "name": "multicall", "outputs": [{ "internalType": "bytes[]", "name": "results", "type": "bytes[]" }], "stateMutability": "payable", "type": "function" }, { "inputs": [], "name": "positionManager", "outputs": [{ "internalType": "address", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "token", "type": "address" }, { "internalType": "uint256", "name": "value", "type": "uint256" }], "name": "pull", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [], "name": "refundETH", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "token", "type": "address" }, { "internalType": "uint256", "name": "value", "type": "uint256" }, { "internalType": "uint256", "name": "deadline", "type": "uint256" }, { "internalType": "uint8", "name": "v", "type": "uint8" }, { "internalType": "bytes32", "name": "r", "type": "bytes32" }, { "internalType": "bytes32", "name": "s", "type": "bytes32" }], "name": "selfPermit", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "token", "type": "address" }, { "internalType": "uint256", "name": "nonce", "type": "uint256" }, { "internalType": "uint256", "name": "expiry", "type": "uint256" }, { "internalType": "uint8", "name": "v", "type": "uint8" }, { "internalType": "bytes32", "name": "r", "type": "bytes32" }, { "internalType": "bytes32", "name": "s", "type": "bytes32" }], "name": "selfPermitAllowed", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "token", "type": "address" }, { "internalType": "uint256", "name": "nonce", "type": "uint256" }, { "internalType": "uint256", "name": "expiry", "type": "uint256" }, { "internalType": "uint8", "name": "v", "type": "uint8" }, { "internalType": "bytes32", "name": "r", "type": "bytes32" }, { "internalType": "bytes32", "name": "s", "type": "bytes32" }], "name": "selfPermitAllowedIfNecessary", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "token", "type": "address" }, { "internalType": "uint256", "name": "value", "type": "uint256" }, { "internalType": "uint256", "name": "deadline", "type": "uint256" }, { "internalType": "uint8", "name": "v", "type": "uint8" }, { "internalType": "bytes32", "name": "r", "type": "bytes32" }, { "internalType": "bytes32", "name": "s", "type": "bytes32" }], "name": "selfPermitIfNecessary", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "uint256", "name": "amountIn", "type": "uint256" }, { "internalType": "uint256", "name": "amountOutMin", "type": "uint256" }, { "internalType": "address[]", "name": "path", "type": "address[]" }, { "internalType": "address", "name": "to", "type": "address" }], "name": "swapExactTokensForTokens", "outputs": [{ "internalType": "uint256", "name": "amountOut", "type": "uint256" }], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "uint256", "name": "amountOut", "type": "uint256" }, { "internalType": "uint256", "name": "amountInMax", "type": "uint256" }, { "internalType": "address[]", "name": "path", "type": "address[]" }, { "internalType": "address", "name": "to", "type": "address" }], "name": "swapTokensForExactTokens", "outputs": [{ "internalType": "uint256", "name": "amountIn", "type": "uint256" }], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "token", "type": "address" }, { "internalType": "uint256", "name": "amountMinimum", "type": "uint256" }, { "internalType": "address", "name": "recipient", "type": "address" }], "name": "sweepToken", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "token", "type": "address" }, { "internalType": "uint256", "name": "amountMinimum", "type": "uint256" }], "name": "sweepToken", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "token", "type": "address" }, { "internalType": "uint256", "name": "amountMinimum", "type": "uint256" }, { "internalType": "uint256", "name": "feeBips", "type": "uint256" }, { "internalType": "address", "name": "feeRecipient", "type": "address" }], "name": "sweepTokenWithFee", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "token", "type": "address" }, { "internalType": "uint256", "name": "amountMinimum", "type": "uint256" }, { "internalType": "address", "name": "recipient", "type": "address" }, { "internalType": "uint256", "name": "feeBips", "type": "uint256" }, { "internalType": "address", "name": "feeRecipient", "type": "address" }], "name": "sweepTokenWithFee", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "int256", "name": "amount0Delta", "type": "int256" }, { "internalType": "int256", "name": "amount1Delta", "type": "int256" }, { "internalType": "bytes", "name": "_data", "type": "bytes" }], "name": "uniswapV3SwapCallback", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "uint256", "name": "amountMinimum", "type": "uint256" }, { "internalType": "address", "name": "recipient", "type": "address" }], "name": "unwrapWETH9", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "uint256", "name": "amountMinimum", "type": "uint256" }], "name": "unwrapWETH9", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "uint256", "name": "amountMinimum", "type": "uint256" }, { "internalType": "address", "name": "recipient", "type": "address" }, { "internalType": "uint256", "name": "feeBips", "type": "uint256" }, { "internalType": "address", "name": "feeRecipient", "type": "address" }], "name": "unwrapWETH9WithFee", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "uint256", "name": "amountMinimum", "type": "uint256" }, { "internalType": "uint256", "name": "feeBips", "type": "uint256" }, { "internalType": "address", "name": "feeRecipient", "type": "address" }], "name": "unwrapWETH9WithFee", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "uint256", "name": "value", "type": "uint256" }], "name": "wrapETH", "outputs": [], "stateMutability": "payable", "type": "function" }, { "stateMutability": "payable", "type": "receive" }]
const SWAP_ROUTER02_ADDRESS = "0x3bFA4769FB09eefC5a80d6E87c3B9C650f7Ae48E"
const WETH_ADDRESS = "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14"
const UNI_ADDRESS = "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984"
const privateKey = "YOUR_PRIVATE_KEY"
const provider = new ethers.JsonRpcProvider("https://ethereum-sepolia.rpc.subquery.network/public")
const signer = new ethers.Wallet(privateKey, provider)

const router = new ethers.Contract(SWAP_ROUTER02_ADDRESS, SWAP_ROUTER02_ABI, signer)

async function main() {

    const inputAmount = ethers.parseEther("0.0001")

    const params = {
        tokenIn: WETH_ADDRESS,
        tokenOut: UNI_ADDRESS,
        fee: 3000,
        recipient: signer.address,
        amountIn: inputAmount,
        amountOutMinimum: 0,
        sqrtPriceLimitX96: 0,
    };

    const data = router.interface.encodeFunctionData(
        "exactInputSingle",
        [params]);

    const txArgs = {
        to: SWAP_ROUTER02_ADDRESS,
        value: inputAmount,
        data: data,
        gasLimit: "343433",
    }

    const tx = await signer.sendTransaction(txArgs);
    const reciept = await tx.wait();
    console.log(reciept.hash);
}

main();

And, here's the successful transaction for the same.


By the way, if I want to swap back (from UNI to ETH), I will have to execute the function ApproveMax, and swapExactTokensToETH, right?

Since swapExactTokensToETH is not available in SwapRouter02, so here's what you can do in order to swap back (from UNI to ETH):

  1. Call the approve function of UNI token contract by passing in the amount of UNI tokens from your balance that you want to swap back.

  2. Then, execute the exactInputSingle function of SwapRouter02 by passing tokenIn as UNI address, tokenOut as WETH address, amountIn as UNI amount and value as 0. 



  3. Finally, call the withdraw function of the WETH token contract, passing in the amount of WETH tokens that you want to convert to ETH.

3
  • Thanks for making it clear! By the way, if I want to swap back (from UNI to ETH), I will have to execute the function ApproveMax, and swapExactTokensToETH, right?
    – duyanhhz
    Commented Apr 16 at 16:11
  • 1
    I've added the response to this, in the answer. Please check. Commented Apr 16 at 17:00
  • 1
    Thank you sir, will be implementing it!!!
    – duyanhhz
    Commented Apr 16 at 17:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.