I know there are many similiar questions but i can not figure out how to implement the 'approve' part in.

pragma solidity ^0.8;
pragma abicoder v2;

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;

    // This example swaps DAI/WETH9 for single path swaps and DAI/USDC/WETH9 for multi path swaps.

    address public constant DAI = 0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1;
    address public constant WETH9 = 0x4200000000000000000000000000000000000006;
    address public constant USDC = 0x4e62882864fB8CE54AFfcAf8D899A286762B011B;

    // 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 DAI for a maximum possible amount of WETH9
    /// using the DAI/WETH9 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 DAI for this function to succeed.
    /// @param amountIn The exact amount of DAI that will be swapped for WETH9.
    /// @return amountOut The amount of WETH9 received.
    function swapExactInputSingle(uint256 amountIn) external returns (uint256 amountOut) {
        // msg.sender must approve this contract

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

        // Approve the router to spend DAI.
        TransferHelper.safeApprove(DAI, 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 =
                tokenIn: DAI,
                tokenOut: WETH9,
                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 DAI for a fixed amount of WETH.
    /// @dev The calling address must approve this contract to spend its DAI for this function to succeed. As the amount of input DAI is variable,
    /// the calling address will need to approve for a slightly higher amount, anticipating some variance.
    /// @param amountOut The exact amount of WETH9 to receive from the swap.
    /// @param amountInMaximum The amount of DAI we are willing to spend to receive the specified amount of WETH9.
    /// @return amountIn The amount of DAI actually spent in the swap.
    function swapExactOutputSingle(uint256 amountOut, uint256 amountInMaximum) external returns (uint256 amountIn) {
        // Transfer the specified amount of DAI to this contract.
        TransferHelper.safeTransferFrom(DAI, msg.sender, address(this), amountInMaximum);

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

        ISwapRouter.ExactOutputSingleParams memory params =
                tokenIn: DAI,
                tokenOut: WETH9,
                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(DAI, address(swapRouter), 0);
            TransferHelper.safeTransfer(DAI, msg.sender, amountInMaximum - amountIn);

Here is my error:

Gas estimation failed
Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending?
Internal JSON-RPC error. { "code": -32000, "message": "execution reverted: STF" }
  • What is the "approve part" ? What have you tried so far ? Commented Jun 2, 2022 at 6:08
  • Trying to let the contract take some DAI from my metamask wallet but i supposedly need to approve the contract Commented Jun 2, 2022 at 18:59
  • @CloudTrauma Do you have the failed transaction hash? Usually to use transferFrom the owner has to call approve first, see the details here ethereum.stackexchange.com/a/46458.
    – Ismael
    Commented Jun 3, 2022 at 12:57

3 Answers 3


execution reverted: STF means execution is reverted by require assertion in TransferHelper.safeTransferFrom function.

Here is the code of Uniswap V3 Swap Router https://etherscan.io/address/0xe592427a0aece92de3edee1f18e0157c05861564#code

If you search this code for "STF", you'll find the implementation:

  function safeTransferFrom(
        address token,
        address from,
        address to,
        uint256 value
    ) internal {
        (bool success, bytes memory data) =
            token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'STF');

Possible cause of this can be simply not enough DAI tokens on msg.sender


Your error code is most likely attributed to not having the smart contract approved to access your DAI tokens.

This must be done MANUALLY, e.g. via Metamask or in a Node.Js file as follows:

const { ethers } = require('ethers')

// save the abi file locally and call it
const ERC20ABI = require('../data/erc20.json')

// Token addresses for Ropsten
const DAI = "0xaD6D458402F60fD3Bd25163575031ACDce07538D"

// import from .env file
const INFURA = process.env.INFURA_ROPSTEN

// input here your deployed contract
const SmartContractAddress = '0x......'

// initiate wallet with private key
const provider = new ethers.providers.JsonRpcProvider(INFURA)
const wallet = new ethers.Wallet(WALLET_SECRET)
const connectedWallet = wallet.connect(provider)

// initiate ERC20 contract
const DAIcontract = new ethers.Contract(

// function to approve token
const approve = async (ERC20contract, toApproveAddress) => {
            gasLimit: ethers.utils.hexlify(1000000)
    ).then(transaction => {

async function main() {
    approve(DAIcontract, SmartContractAddress)


For others who are writing javascript and find this question through google, it's probably worth noting that the STF error will show up if you have not approved the token to be spent by swap router.

await token.approve(swapRouterV3.address, params.amountIn)

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.