Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example trade WETH/USDC fails with STF error on mainnet fork #58

Open
vdamle opened this issue Nov 27, 2023 · 4 comments
Open

Example trade WETH/USDC fails with STF error on mainnet fork #58

vdamle opened this issue Nov 27, 2023 · 4 comments

Comments

@vdamle
Copy link

vdamle commented Nov 27, 2023

I've been attempting to get the example trade to work using a mainnet fork setup with Anvil, as documented here

As far as I can tell, the example doesn't work with the latest version of the libraries:

    "@uniswap/sdk-core": "^4.0.9",
    "@uniswap/v3-sdk": "^3.10.0",

The error is always:

eth_sendRawTransaction

    Transaction: 0x37640ca5d41ca1e7264fff50678cac102cb88ccc20d002c72ef3355b275fe38c
    Gas used: 115902
    Error: reverted with: revert: STF

    Block Number: 18642983
    Block Hash: 0x31395f673ef6640d4e45273ac643840c9f637c9b5ed826c3d6b1e36c295bf5a9
    Block Time: "Fri, 24 Nov 2023 21:25:20 +0000"

Initially, I was seeing an error with eth_estimateGas:

reason: 'cannot estimate gas; transaction may fail or may require manual gas limit',
  code: 'UNPREDICTABLE_GAS_LIMIT'

So I switched to using a pre-defined gasLimit: 5_000_000 in the Tx.

The steps for getting a quote and approving the swap router for token transfers work but the actual swap fails. As a minor detail, I'm not using the UI code but invoking the functions directly like below, so I can run the example from the command line:

async function performTrade() {
  let t = await createTrade();

  let res = await executeTrade(t);

  console.log(`Trade concluded, ${res}`);
}

performTrade()
  .then(() => console.log(`Complete`))
  .catch((e) => console.error(`Error executing trade`, e));

I can see that the quote returns valid data:

Token0 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
Token1 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Fee 3000
Tick spacing 60
Sqrt PriceX96 BigNumber {
  _hex: '0x55528bdd18dd0fa512bb36b96ace',
  _isBigNumber: true
}
Tick 19984

I'd appreciate any input to troubleshoot this issue. I'd be happy to submit a PR to the docs/example code if there is any change required with the latest version of the v3 SDK.

@vdamle
Copy link
Author

vdamle commented Nov 28, 2023

To verify that the approval is correctly done, I wrote a script to check the allowance:


import hre from 'hardhat';
import { JsonRpcProvider, MaxUint256, Wallet } from 'ethers';

const WETH_CONTRACT_ADDRESS = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
const WALLET_ADDRESS = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266';
const SWAP_ROUTER_ADDRESS = '0xE592427A0AEce92De3Edee1F18E0157C05861564';
const SWAP_ROUTER_V2_ADDRESS = '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45';

const ERC20_ABI = [
    'function approve(address _spender,uint256 _value) returns (bool)',
    'function allowance(address _owner,address _spender) view returns (uint256)',
    'function balanceOf(address _owner) view returns (uint256)',
    'function totalSupply() view returns (uint256)'
]
async function main() {

    const rpc =  new JsonRpcProvider( 'http://localhost:8545' ) ;
    const wallet = new Wallet( '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' , rpc);

  const weth_contract = new hre.ethers.Contract(WETH_CONTRACT_ADDRESS, ERC20_ABI, wallet);

  console.log(`checking allowance for ${SWAP_ROUTER_ADDRESS}`)
  const allowance = await weth_contract.allowance(WALLET_ADDRESS, SWAP_ROUTER_ADDRESS);

  console.log(`Allowance: ${allowance}`);
}

main()
  .then(() => console.log('Execution complete'))
  .catch((e) => console.error('unable to run hardhat script', e));

and I see that the swap router allowance is correctly resported:

➜  hardhat-scripts node scripts/allowance.js
checking allowance for 0xE592427A0AEce92De3Edee1F18E0157C05861564
Allowance: 2000000000000000000000
Execution complete

@69popovvlad
Copy link

Same thing, I also got an error with eth_estimateGas at first
But now the STF error

@vdamle did you solve the problem?

@animalconcerts
Copy link

@vdamle @69popovvlad I have the same problem. Did you guys solve?

@69popovvlad
Copy link

@vdamle @69popovvlad I have the same problem. Did you guys solve?

@animalconcerts
Yes, I somehow solved it, but I don’t remember exactly how, because I solved several problems that kept popping up:

  • try increasing the gas limit GAS_LIMIT=2000000

  • then try checking through the allowance method (you can use static call btw) to see how many tokens you have approved

  • and also approve the required amount for the contract, if it is not enough
    Also, if you plan to use uniswap permanently, you can approve the maximum number of tokens

Here is my code below as an example for you:
TransactionState.ts

    export enum TransactionState {
        Failed = 'Failed',
        New = 'New',
        Rejected = 'Rejected',
        Sending = 'Sending',
        Sent = 'Sent',
    }

Some utils script

    import { Wallet, ethers } from "ethers";
    import { Token } from "@uniswap/sdk-core";
    import ERC20 from '@eth-optimism/contracts-bedrock/forge-artifacts/ERC20.sol/ERC20.json' assert { type: 'json' };
    
    /**
     * @param tokenAdress Get ERC20 token contract
     * @param wallet The wallet for which you need to find out the token balance
     * @returns Signed contract of the ERC20 token
     */
    export function getTokenContractERC20(tokenAdress: string, wallet: Wallet): Contract {
        return new ethers.Contract(tokenAdress, ERC20.abi, wallet);
    }

    export function tokenAmountToWei(token: Token, amount: number) {
        return ethers.parseUnits(amount.toString(), token.decimals);
    }

WalletApi.ts

    import { Contract, Wallet, ethers } from "ethers";
    import { Token } from "@uniswap/sdk-core";
    import { TransactionState } from "./TransactionState.js";
    
async approveForUniswap(token0: Token, token1: Token, wallet: Wallet, amount: number) {
    const amountToken0 = amount < 0 ? ethers.MaxUint256 : tokenAmountToWei(token0, amount);
    const amountToken1 = amount < 0 ? ethers.MaxUint256 : tokenAmountToWei(token1, amount);

    const tx0 = await approve(SWAP_ROUTER_ADDRESS, token0, amountToken0, wallet);
    const tx1 = await approve(SWAP_ROUTER_ADDRESS, token1, amountToken1, wallet);
    const tx2 = await approve(NONFUNGIBLE_POSITION_ADDRESS, token0, amountToken0, wallet);
    const tx3 = await approve(NONFUNGIBLE_POSITION_ADDRESS, token1, amountToken1, wallet);

    return tx0 == tx1 && tx2 == tx3 && tx0 == tx3 && tx0 == TransactionState.Sent ? TransactionState.Sent : TransactionState.Failed;
}

async approve(spenderAddress: string, token: Token, amountWei: bigint, wallet: Wallet) {
    try {
        const tokenContract = getTokenContractERC20(token.address, wallet);

        let allowancewei = await tokenContract.allowance.staticCall(
            wallet.address,
            spenderAddress,
        );

        if (amountWei <= allowancewei) {
            return TransactionState.Sent;
        }

        const tx = await tokenContract.approve(
            spenderAddress,
            ethers.MaxUint256
        );
        await tx.wait();

        allowancewei = await tokenContract.allowance.staticCall(
            wallet.address,
            spenderAddress,
        );

        if (amountWei > allowancewei) {
            throw new Error(`Allowance amount of tokens ${allowancewei} is less than the required amount ${amountWei}`);
        }

        return TransactionState.Sent;
    }
    catch (error) {
        console.error(`Approve error:: ${error}`);
        return TransactionState.Failed;
    }
}

My dependencies btw

"dependencies": {
    "@eth-optimism/contracts-bedrock": "0.16.2",
    "@uniswap/sdk-core": "4.0.9",
    "@uniswap/v3-core": "1.0.1",
    "@uniswap/v3-periphery": "1.4.4",
    "@uniswap/v3-sdk": "3.10.0",
    "ethers": "6.9.0",
    "jsbi": "3.2.5"
},

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants