Uniswap / v2-periphery

🎚 Peripheral smart contracts for interacting with Uniswap V2
https://uniswap.org/docs
GNU General Public License v3.0
1.14k stars 1.69k forks source link

UniswapV2 swapExactTokensForTokens Question #173

Open Xardesso opened 11 months ago

Xardesso commented 11 months ago

I have question does UniswapV2 swapExactTokensForTokens function charge fees ? And if yes does expected amount input should be like this Expected price after fee subtraction?

Saswankar1 commented 11 months ago

Yes, UniswapV2's swapExactTokensForTokens function does charge fees. The fee is approx 0.3% of the transaction amount and it's automatically deducted from the amount of output tokens. Therefore, the expected amount input should be the expected price after fee subtraction.

When you use the swapExactTokensForTokens function, you specify the exact amount of input tokens you are willing to spend, and the minimum amount of output tokens you expect to receive. The function will revert (fail) if the amount of output tokens is less than the minimum specified due to price changes or fees.

As an example, if you're swapping 100 tokens A for tokens B, and the current price of token A in terms of token B is 1 (1 token A = 1 token B), you would expect to receive 100 tokens B. However, Uniswap will charge a 0.3% fee, so you will actually receive 99.7 tokens B.

If you want to ensure that you receive at least 99 tokens B, you would set your amountOutMin parameter to 99. If the actual amount of tokens B you would receive after fees is less than this (due to price changes or fees), the transaction will revert.

In summary, when using swapExactTokensForTokens, you provide the exact input amount and specify the minimum acceptable output amount, and the Uniswap contract handles the fee calculation during the swap.

jwbda commented 5 months ago

Hi @Saswankar1, thanks for detaied explanation.

And Why the fee not equal 0.3% when the input amount greater than 1000 ETH in WETH-USDT PAIR (in test).

Normal Part

bellow is my solidity code:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "../IERC20.sol";
import "../IWETH.sol";
import "../IUniswapV2Pair.sol";
import "../IUniswapV2Factory.sol";
import "../IUniswapV2Router02.sol";
import "../TransferHelper.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "../UniswapV2Library.sol";

import "hardhat/console.sol";

contract ForTest {
    address public owner;
    address public WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    address public USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
    address public WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;
    address public UniRouter = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
    address public UniWETHUSDT = 0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852;

    modifier isOnwer() {
        require(msg.sender == owner, "caller is not owner");
        _;
    }

    constructor() {
        console.log(">>> run in constructor > ", type(uint).max);
        owner = msg.sender;

        TransferHelper.safeApprove(WETH, UniRouter, type(uint).max);
        TransferHelper.safeApprove(USDT, UniRouter, type(uint).max);
    }

    receive() external payable {}

    fallback() external {}

    function deposit() public payable isOnwer {
        // IWETH(WETH).transfer(WETH, msg.value);
        IWETH(WETH).deposit{value: msg.value}();
    }

    function change(
        uint256 amount1,
        uint256 amount2,
        // address[] memory path,
        address p1,
        address p2,
        address r
    ) private returns (uint256[] memory) {
        address[] memory path = new address[](2);
        path[0] = p1;
        path[1] = p2;
        return
            IUniswapV2Router02(r).swapExactTokensForTokens(
                amount1,
                amount2,
                path,
                address(this),
                block.timestamp + 1800
            );
    }

    // 1 swap WETH => USDT
    function flashLoan() public isOnwer {
        console.log(">>> run in flashLoan");
        (uint a1, uint a2, ) = IUniswapV2Pair(UniWETHUSDT).getReserves();
        uint256 wethBalance = IWETH(WETH).balanceOf(address(this));
        uint256[] memory amount1 = change(
            IWETH(WETH).balanceOf(address(this)),
            uint(0),
            WETH,
            USDT,
            UniRouter
        );
        console.log(
            ">>>",
            a1,
            a2,
            amount1[1] // USDT NUM
        );
        console.log(wethBalance, "to", IERC20(USDT).balanceOf(address(this)));
    }

    function withdraw() public isOnwer {
        if (IERC20(WETH).balanceOf(address(this)) > 0) {
            IERC20(WETH).transfer(owner, IERC20(WETH).balanceOf(address(this)));
        }

        if (IERC20(USDT).balanceOf(address(this)) > 0) {
            IERC20(USDT).transfer(owner, IERC20(USDT).balanceOf(address(this)));
        }

        if (owner.balance > 0) {
            payable(owner).transfer(owner.balance);
        }
    }
}

Deployment

And the environment is fork the ethereum main net in hardhat. And the deploy code is bellow :


const i = await commonTask.deploycontract("ForTest");
    const iAddress = await i.getAddress();
    await commonTask.contractapi("ForTest", iAddress, "deposit", "value:1");
    await commonTask.contractapi("ForTest", iAddress, "flashLoan");

Console log in fork console

And the console log in fork console is :


eth_sendRawTransaction
  Contract call:       ForTest#flashLoan
  Transaction:         0xe0f8dac374326abb00ee2afc48fd34cc1e65c955eeb486c87b3fa596bdc08f46
  From:                0x40b191f50ccd8d00fa7afaf284a66664aec60d8f
  To:                  0x16ffa9bdb249ed0e04ce1ad101188fbf5b77ac89
  Value:               0 ETH
  Gas used:            145087 of 153588
  Block #20105390:     0xe9bdc53f82721cf8524071c7fb58e089806fa7c974761e482600415678c8ba50

  console.log:
    >>> run in flashLoan
    >>> 17205994118699203745752 61751887511655 3578001024
    1000000000000000000 to 3578001024

so before swap: the price of each Eth is = 61751887.511655 / 17205994118699203745752 = 3588.975277199707 (USDT);

and swap expect usdt amount is (value: 1) 1*3588.975277199707 = 3588.975277199707 (USDT);

and the actual value is 3578.001024, which fee = (3588.975277199707- 3578.001024)/3588.975277199707 ≈ 0.003057767845163134 ≈ 0.3%. That is what I want in my mind.



Not normal Part

But when the input value greater than 1000ETH( in test), the fee up to 5.76%

deploy code 

const i = await commonTask.deploycontract("ForTest");
    const iAddress = await i.getAddress();
    // await commonTask.contractapi("ForTest", iAddress, "deposit", "value:1");  // work
    // await commonTask.contractapi("ForTest", iAddress, "deposit", "value:2");  // work 
    await commonTask.contractapi("ForTest", iAddress, "deposit", "value:1000"); // not work
    await commonTask.contractapi("ForTest", iAddress, "flashLoan");

console log in fork


eth_sendRawTransaction
  Contract call:       ForTest#flashLoan
  Transaction:         0xe0f8dac374326abb00ee2afc48fd34cc1e65c955eeb486c87b3fa596bdc08f46
  From:                0x40b191f50ccd8d00fa7afaf284a66664aec60d8f
  To:                  0x16ffa9bdb249ed0e04ce1ad101188fbf5b77ac89
  Value:               0 ETH
  Gas used:            145087 of 153588
  Block #20105390:     0x4415d29e0ced876fa9a778bec0bea8ea2b030b5c0f1ca5b349da1d5a7cb98fdd

  console.log:
    >>> run in flashLoan
    >>> 17205994118699203745752 61751887511655 3382225552986
    1000000000000000000000 to 3382225552986

expect swap usdt amount = 61751887.511655 / 17205.994118699203745752 *1000 = 3588975.277199707.

actual value = 3382225.552986.

so, the fee = (3588975.277199707 - 3382225.552986) / 3588975.277199707 = 0.057606895630393694 ≈ 5.76%

This is really expensive fee. 

Please help me solve this problem, looking forword to your reply. Thanks in advance.