0

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

2 Answers 2

0

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())
}
1
  • Main problem was with selector, I posted my own solution
    – Fuga14
    Commented Sep 17, 2023 at 19:51
1

So, the main problem was selector. The function getSelector gives incorrect selector, so I decided to get selector in another way

bytes4 selector = uniswapRouter.swapExactTokensForTokens.selector;

Also, before calling the function we need to make a transfer from our wallet to contract -> approve from contract to uniswap to use amountIn In contract it looks like this:

IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
IERC20(tokenIn).approve(address(uniswapRouter), amountIn);

Full contract:

// 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 uniswapRouter;
    address public routerUniswap = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;

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

    function swapTokens(
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 amountOutMin
    ) external {
        IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
        IERC20(tokenIn).approve(address(uniswapRouter), amountIn);

        bytes4 selector = uniswapRouter.swapExactTokensForTokens.selector;

        // Prepare the input data for the function call
        bytes memory data = abi.encodeWithSelector(
            selector,
            amountIn,
            amountOutMin,
            getPathForTokens(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)
            )
            if iszero(success) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
        }
    }

    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;
    }
}

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.