Uniswap/docs

VM Exception while processing transaction: reverted with reason string 'STF'

jweboy opened this issue · 0 comments

I started the local network from the fork mainnet through the hardhat node script, and then continued to execute the hardhat test --network localhost script to perform swap testing on the local network. When I executed the line simpleSwap.swapExactInputSingle, I I encountered the error VM Exception while processing transaction: reverted with reason string 'STF'. Please tell me how I should handle and solve this error.

Error stack

  1) Should provide a caller with more UST than they started with after a swap


  10 passing (5s)
  1 failing

  1) SimpleSwap
       Should provide a caller with more UST than they started with after a swap:
     ProviderError: Error: VM Exception while processing transaction: reverted with reason string 'STF'
      at HttpProvider.request (/Users/biubiubiu/GithubGroup/uvwstor/hanrdhat_swap_contract/node_modules/.pnpm/hardhat@2.22.4_ts-node@10.9.2_typescript@5.0.4/node_modules/hardhat/src/internal/core/providers/http.ts:90:21)
      at processTicksAndRejections (node:internal/process/task_queues:95:5)
      at async HardhatEthersSigner.sendTransaction (/Users/biubiubiu/GithubGroup/uvwstor/hanrdhat_swap_contract/node_modules/.pnpm/@nomicfoundation+hardhat-ethers@3.0.6_ethers@6.12.1_hardhat@2.22.4/node_modules/@nomicfoundation/hardhat-ethers/src/signers.ts:125:18)
      at async send (/Users/biubiubiu/GithubGroup/uvwstor/hanrdhat_swap_contract/node_modules/.pnpm/ethers@6.12.1/node_modules/ethers/src.ts/contract/contract.ts:313:20)
      at async Proxy.swapExactInputSingle (/Users/biubiubiu/GithubGroup/uvwstor/hanrdhat_swap_contract/node_modules/.pnpm/ethers@6.12.1/node_modules/ethers/src.ts/contract/contract.ts:352:16)
      at async Context.<anonymous> (/Users/biubiubiu/GithubGroup/uvwstor/hanrdhat_swap_contract/test/SimpleSwap.test.ts:43:18)

hardhat.config.ts

import { vars } from "hardhat/config";
import type { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox-viem";
import "@nomicfoundation/hardhat-ethers";

const ALCHEMY_API_KEY = vars.get("ALCHEMY_API_KEY");
const SEPOLIA_PRIVATE_KEY = vars.get("SEPOLIA_PRIVATE_KEY");
const ETHERSCAN_API_KEY = vars.get("ETHERSCAN_API_KEY");

const config: HardhatUserConfig = {
  solidity: {
    compilers: [
      {
        version: "0.7.6",
      },
      {
        version: "0.8.24",
        settings: {},
      },
    ],
  },
  networks: {
    sepolia: {
      url: `https://eth-sepolia.g.alchemy.com/v2/${ALCHEMY_API_KEY}`,
      accounts: [SEPOLIA_PRIVATE_KEY],
    },
    hardhat: {
      forking: {
        url: `https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}`,
      },
    },
  },
  etherscan: { apiKey: ETHERSCAN_API_KEY },
  sourcify: { enabled: true },
};

export default config;

My swap contract

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

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

contract SimpleSwap {
    ISwapRouter public immutable swapRouter;
    address public constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
    address public constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    // 交换池的费用等级,如以下就是 0.3%
    uint24 public constant poolFee = 3000;

    constructor(ISwapRouter _swapRouter) {
        swapRouter = _swapRouter;
    }

    // 将固定数量的 DAI 与最大可能数量的 WETH9 互换。
    function swapExactInputSingle(uint256 amountIn) external returns (uint256 amountOut) {
        // 将指定金额的 DAI 转入到合约中
        TransferHelper.safeTransferFrom(DAI, msg.sender, address(this), amountIn);

        // 必须批准 Uniswap 协议路由合约,才能访问我们的合约账户,从调用账户中提取合约代币
        TransferHelper.safeApprove(DAI, address(swapRouter), amountIn);

        // 创建用于执行交换的参数
        ISwapRouter.ExactInputSingleParams memory params = 
            ISwapRouter.ExactInputSingleParams({
                // 入站代币的合约地址
                tokenIn: DAI,
                // 出站代币的合约地址
                tokenOut: WETH9,
                // 交换池的费用等级,用于确定执行交换的正确池合约
                fee: poolFee,
                // 出站 token 的目标地址
                recipient: msg.sender,
                // 交换截止时间,超过该时间后交换将失败,以防止长期未决交易和价格剧烈波动
                deadline: block.timestamp,
                amountIn: amountIn,
                amountOutMinimum: 0,
                // 用于设置交换将推动池的价格限制
                sqrtPriceLimitX96: 0
            });

        // 执行 swap 交换
        amountOut = swapRouter.exactInputSingle(params);
        return amountOut; 
    }
}

Test code

import hre, { ethers } from "hardhat";

const WETH_ADDRESS = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
const DAI_ADDRESS = "0x6B175474E89094C44Da98b954EedeAC495271d0F";
const DAI_DECIMALS = 18;
const SwapRouterAddress = "0xE592427A0AEce92De3Edee1F18E0157C05861564";

const ercAbi = [
  // Read-Only Functions
  "function balanceOf(address owner) view returns (uint256)",
  // Authenticated Functions
  "function transfer(address to, uint amount) returns (bool)",
  "function deposit() public payable",
  "function approve(address spender, uint256 amount) returns (bool)",
];

describe("SimpleSwap", () => {
  it("Should provide a caller with more UST than they started with after a swap", async () => {
    // 合约部署
    const simpleSwapFactory = await ethers.getContractFactory("SimpleSwap");
    const simpleSwap = await simpleSwapFactory.deploy(SwapRouterAddress);
    await simpleSwap.waitForDeployment();
    const signers = await ethers.getSigners();

    // 包装一部分的 ETH 到 WETH
    const WETH = new ethers.Contract(WETH_ADDRESS, ercAbi, signers[0]);
    const deposit = await WETH.deposit({ value: ethers.parseEther("10") });
    await deposit.wait();

    // 检查 DAI 余额
    const DAI = new ethers.Contract(DAI_ADDRESS, ercAbi, signers[0]);
    const expandedDAIBalanceBefore = await DAI.balanceOf(signers[0].address);
    const DAIBalanceBefore = Number(
      ethers.formatUnits(expandedDAIBalanceBefore, DAI_DECIMALS)
    );
    console.log(await simpleSwap.getAddress(), simpleSwap.target);
    // 审批 WETH 转移到 swap 合约
    await WETH.approve(await simpleSwap.getAddress(), ethers.parseEther("1"));

    // 执行 swap
    const amountIn = ethers.parseEther("0.1");
    console.log(amountIn);
    const swap = await simpleSwap.swapExactInputSingle(amountIn, {
      gasLimit: 300000,
    });
    swap.wait();

    // const expandedDAIBalanceAfter = await DAI.balanceOf(signers[0].address);
    // const DAIBalanceAfter = Number(
    //   hre.ethers.formatUnits(expandedDAIBalanceAfter, DAI_DECIMALS)
    // );
    // console.log(DAIBalanceBefore, DAIBalanceAfter);
  });
});