Solidity – How to Make Uniswap v2 Swap Through Inline Assembly

assemblyhardhatsolidityuniswapyul

I'm trying to make swap tokens in smart contract using Uniswap V2. My main goal is to make part where you need to work with Uniswap router as assembly block.
I'm not good in assembly, so I need your help community.

I wrote this smart contract, here is it:

// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity ^0.8.20;

import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract TokenExchangeV2 {
    IUniswapV2Router02 private immutable uniswapRouter;
    address public routerUniswap = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;

    constructor(address _routerAddress) {
        uniswapRouter = IUniswapV2Router02(_routerAddress);
    }

    function swapTokens(
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 amountOutMin
    ) external {
        // Encode the function selector for swapExactTokensForTokens
        bytes4 selector = getSelector();

        // Prepare the input data for the function call
        bytes memory data = abi.encodeWithSelector(
            getSelector(),
            amountIn,
            amountOutMin,
            // getPathForTokens(tokenIn, tokenOut),
            [tokenIn, tokenOut],
            msg.sender,
            block.timestamp
        );

        // Perform the low-level call to the Uniswap Router contract
        bool success;
        assembly {
            success := call(
                gas(), // Gas
                sload(routerUniswap.slot), // To address (Uniswap Router)
                0, // Value (0 ETH)
                add(data, 0x20), // Input data (skip the first 32 bytes)
                mload(data), // Input data length
                0, // Output data pointer (not needed)
                0 // Output data length (not needed)
            )
        }

        require(success, "Swap failed");
    }

    function getPathForTokens(
        address tokenIn,
        address tokenOut
    ) internal pure returns (address[] memory) {
        address[] memory path = new address[](2);
        path[0] = tokenIn;
        path[1] = tokenOut;
        return path;
    }

    function getSelector() internal pure returns (bytes4) {
        return
            bytes4(
                keccak256(
                    bytes("swapExactTokensForTokens(uint256,uint256,address[],address,uint256)")
                )
            );
    }
}

It should work, I wrote test in hardhat with forking mainnet and the function is calling, but I always get "Swap failed" error. It looks like this:

Error: VM Exception while processing transaction: reverted with reason string 'Swap failed'

Can someone help me with it? Mb, there is a bit easier way to do it

Best Answer

I think getPathForTokens(tokenIn, tokenOut) and [tokenIn, tokenOut] inside encodeWithSelector are different, the first is the correct one. So I would fix that for a start.

Also you need to approve the tokens to the router first.

IERC20(tokenIn).approve(routerUniswap, amountIn);

The rest looks ok. I would add this at the end of the assembly block to get the reason why the router reverted the call, so it's easier to debug (instead of doing require(success, "Swap failed");):

if iszero(success) {
    returndatacopy(0, 0, returndatasize())
    revert(0, returndatasize())
}
Related Topic