Open Xardesso opened 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.
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).
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);
    }
  }
}
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");
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.
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.
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?