Ethanolle
Ethanolle

Reputation: 1223

getting error: Fail with error 'STF' UniswapV3 on swap

Created a contract a basic one to deploy on the Mumbai BC.

The same one of uni swaps the basic one, just with different addresses. Since this is in Mumbai BC, so I created it with wmatic.

then after approving all the addresses, I get this error without any explanation or option to understand where it failed when i try to singleswap.

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;


import "hardhat/console.sol";

import '@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol';
import '@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol';

contract SwapExamples {
    // For the scope of these swap examples,
    // we will detail the design considerations when using
    // `exactInput`, `exactInputSingle`, `exactOutput`, and  `exactOutputSingle`.

    // It should be noted that for the sake of these examples, we purposefully pass in the swap router instead of inherit the swap router for simplicity.
    // More advanced example contracts will detail how to inherit the swap router safely.

    ISwapRouter public immutable swapRouter;


    address public constant WMATIC = 0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889;
    address public constant WETH = 0xA6FA4fB5f76172d178d61B04b0ecd319C5d1C0aa;

    // For this example, we will set the pool fee to 0.3%.
    uint24 public constant poolFee = 3000;

    constructor(ISwapRouter _swapRouter) {
        swapRouter = _swapRouter;
    }

    /// @notice swapExactInputSingle swaps a fixed amount of WMATIC for a maximum possible amount of WETH
    /// using the WMATIC/WETH 0.3% pool by calling `exactInputSingle` in the swap router.
    /// @dev The calling address must approve this contract to spend at least `amountIn` worth of its WMATIC for this function to succeed.
    /// @param amountIn The exact amount of WMATIC that will be swapped for WETH.
    /// @return amountOut The amount of WETH received.

    function swapExactInputSingle(uint256 amountIn) external returns (uint256 amountOut) {
        // msg.sender must approve this contract

        // Transfer the specified amount of WMATIC to this contract.
        TransferHelper.safeTransferFrom(WMATIC, msg.sender, address(this), amountIn);

        // Approve the router to spend WMATIC.
        TransferHelper.safeApprove(WMATIC, address(swapRouter), amountIn);

        // Naively set amountOutMinimum to 0. In production, use an oracle or other data source to choose a safer value for amountOutMinimum.
        // We also set the sqrtPriceLimitx96 to be 0 to ensure we swap our exact input amount.
        ISwapRouter.ExactInputSingleParams memory params =
            ISwapRouter.ExactInputSingleParams({
                tokenIn: WMATIC,
                tokenOut: WETH,
                fee: poolFee,
                recipient: msg.sender,
                deadline: block.timestamp,
                amountIn: amountIn,
                amountOutMinimum: 0,
                sqrtPriceLimitX96: 0
            });

        // The call to `exactInputSingle` executes the swap.
        amountOut = swapRouter.exactInputSingle(params);
    }

    /// @notice swapExactOutputSingle swaps a minimum possible amount of WMATIC for a fixed amount of WETH.
    /// @dev The calling address must approve this contract to spend its WMATIC for this function to succeed. As the amount of input WMATIC is variable,
    /// the calling address will need to approve for a slightly higher amount, anticipating some variance.
    /// @param amountOut The exact amount of WETH to receive from the swap.
    /// @param amountInMaximum The amount of WMATIC we are willing to spend to receive the specified amount of WETH.
    /// @return amountIn The amount of WMATIC actually spent in the swap.
    function swapExactOutputSingle(uint256 amountOut, uint256 amountInMaximum) external returns (uint256 amountIn) {
        // Transfer the specified amount of WMATIC to this contract.
        TransferHelper.safeTransferFrom(WMATIC, msg.sender, address(this), amountInMaximum);

        // Approve the router to spend the specifed `amountInMaximum` of WMATIC.
        // In production, you should choose the maximum amount to spend based on oracles or other data sources to acheive a better swap.
        TransferHelper.safeApprove(WMATIC, address(swapRouter), amountInMaximum);

        ISwapRouter.ExactOutputSingleParams memory params =
            ISwapRouter.ExactOutputSingleParams({
                tokenIn: WMATIC,
                tokenOut: WETH,
                fee: poolFee,
                recipient: msg.sender,
                deadline: block.timestamp,
                amountOut: amountOut,
                amountInMaximum: amountInMaximum,
                sqrtPriceLimitX96: 0
            });

        // Executes the swap returning the amountIn needed to spend to receive the desired amountOut.
        amountIn = swapRouter.exactOutputSingle(params);

        // For exact output swaps, the amountInMaximum may not have all been spent.
        // If the actual amount spent (amountIn) is less than the specified maximum amount, we must refund the msg.sender and approve the swapRouter to spend 0.
        if (amountIn < amountInMaximum) {
            TransferHelper.safeApprove(WMATIC, address(swapRouter), 0);
            TransferHelper.safeTransfer(WMATIC, msg.sender, amountInMaximum - amountIn);
        }
    }
}

Upvotes: 0

Views: 3923

Answers (3)

jun
jun

Reputation: 31

SwapRouter Failed with error 'STF' due to:

SwapRouter inherits PeripheryPaymentsWithFee. sol, thereby also inheriting PeripheryPayments.sol, which includes the pay function, so it was an error when the contract program deducted inputAmount from the account of the original initiator. This error may be: 1. The SwapRouter contract is not approved; 2. Insufficient approved volume; 3. The transaction amount exceeds the account balance (caused by rounding and should be rounded down instead of up)

Upvotes: 3

djchals
djchals

Reputation: 58

ok, finally after several days I achieved do a swap on mumbai with exactInputSingle! This test swap 0.5Wmatic for weth. I attach you my scripts:

TestSwap.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;
import '@uniswap/v3-core/contracts/libraries/LowGasSafeMath.sol';

import "./SwapRouter02lib/ISwapRouter02.sol";
import "./SwapRouter02lib/IV3SwapRouter.sol";
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';

contract TestSwap {
    using LowGasSafeMath for uint256;

    ISwapRouter02 public immutable uniswapRouter;

    constructor(address _uniswapRouter) {
        uniswapRouter = ISwapRouter02(_uniswapRouter);
    }

    function swapUniswap(
        address token0, 
        address token1, 
        uint256 amount, 
        uint256 fee 
    ) external returns (uint256 amountOut) {
        require(IERC20(token0).transferFrom(msg.sender, address(this), amount),"STF v2");
        require(IERC20(token0).approve(address(uniswapRouter), amount),"SA v2");

        uint256 amountMin = LowGasSafeMath.add(amount, fee);
        IV3SwapRouter.ExactInputSingleParams memory params = IV3SwapRouter.ExactInputSingleParams({
            tokenIn: token0,
            tokenOut: token1,
            fee: uint24(fee),
            recipient: msg.sender,
            amountIn: amount,
            amountOutMinimum: 0,
            sqrtPriceLimitX96: 0
        });

        (bool success, bytes memory amountBytes) = address(uniswapRouter).call(
            abi.encodeWithSelector(
                IV3SwapRouter.exactInputSingle.selector,
                params
            )
        );

        return bytesToUint(amountBytes);
    }

    function bytesToUint(bytes memory _bytes) internal pure returns (uint256 value) {
        assembly {
            value := mload(add(_bytes, 0x20))
        }
    }
}

deploy-test.js

const hre = require('hardhat');
const ethers = hre.ethers;
const { abi: abiERC20 } = require("../artifacts/contracts/IERC20.sol/IERC20.json");
const uniswapRouter = "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45"; //ROUTER 02
const uniswapFactory = "0x1F98431c8aD98523631AE4a59f267346ea31F984"; //FACTORY

const wmatic = "0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889"; //wmatic mumbai
const weth = "0xA6FA4fB5f76172d178d61B04b0ecd319C5d1C0aa"; //weth mumbai

const fee1 = 500;

const signerAddress = "0x1234567890123456789012345678901234567890";//this must be one of your address with wmatic tokens (I tested on mumbai)

async function main() {
    
    await hre.network.provider.request({
        method: 'hardhat_impersonateAccount',
        params: [signerAddress],
      });
      const signer = await ethers.provider.getSigner(signerAddress);

    var wmaticContract = new ethers.Contract(
        wmatic,
        abiERC20,
        signer
    );

    var wethContract = new ethers.Contract(
        weth,
        abiERC20,
        signer
    );

    const TestSwap = await ethers.getContractFactory("TestSwap");
    const testSwap = await TestSwap.deploy(
        uniswapRouter
    );
    await testSwap.deployed();

    console.log((await wmaticContract.balanceOf(signer._address)).toString());
    console.log((await wethContract.balanceOf(signer._address)).toString());

    console.log((await wmaticContract.balanceOf(testSwap.address)).toString());
    console.log((await wethContract.balanceOf(testSwap.address)).toString());

    const amountApprove = ethers.utils.parseEther('1'); 
    const amount = ethers.utils.parseEther('0.5');

    const GAS_PARAMS =  {
        gasLimit: "1250000",//exagerate amount of gas, must be adjusted
        gasPrice: ethers.utils.parseUnits('100','gwei').toString(),
    };
    const txApproveWMatic = await wmaticContract.connect(signer).approve(
        testSwap.address, 
        amountApprove, 
        GAS_PARAMS);
    await txApproveWMatic.wait();

    const tx = await testSwap.connect(signer).swapUniswap(
        wmatic,
        weth,
        amount,
        fee1, 
        GAS_PARAMS
    );

    console.log(tx);
    const txResponse = await tx.wait();
    console.log(txResponse);

    console.log((await wmaticContract.balanceOf(signer._address)).toString());
    console.log((await wethContract.balanceOf(signer._address)).toString());
    console.log((await wmaticContract.balanceOf(testSwap.address)).toString());
    console.log((await wethContract.balanceOf(testSwap.address)).toString());
}

main();

To run it:

 $ npx hardhat run scripts/deploy-test.js

Upvotes: 0

djchals
djchals

Reputation: 58

I'm in the same point as you since two weeks. I don't solved the problem yet but I have more data useful for you:

I tried to do a swap on https://app.uniswap.org on mumbai network and I discovered that the SwapRouter address for mumbai maybe is wrong or is not the primary as they have on official uniswap website (I mean: 0xE592427A0AEce92De3Edee1F18E0157C05861564).

I don't know if they are using two addresses, but I tried two times and two times has passed for 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45 as SwapRouter. You can check it if you try to do a swap on the website.

My tx example: https://mumbai.polygonscan.com/tx/0xa7e75b78ec8f5ae311d0252d0ad7412176ebe7f063df72a8f2c347543f8f2103

Upvotes: 1

Related Questions