examples icon indicating copy to clipboard operation
examples copied to clipboard

Calculation of Quoting for WETH to USDC is Wrong

Open arjunaskykok opened this issue 2 years ago • 3 comments

Using this configuration in v3-sdk/quoting/config.ts:

  tokens: {
    in: USDC_TOKEN,
    amountIn: 1000,
    out: WETH_TOKEN,
    poolFee: FeeAmount.MEDIUM,
  },

I got this result (which is correct):

Selection_140

On Mar 4th, 2023, 1 ETH is $1570. So with $1000, we would get around 0.63 ETH.

But when I switched the tokens, I got the wrong result. I used this configuration:

  tokens: {
    in: WETH_TOKEN,
    amountIn: 1,
    out: USDC_TOKEN,
    poolFee: FeeAmount.MEDIUM,
  },

Selection_141

The result should be ~1570 USDC.

arjunaskykok avatar Mar 04 '23 08:03 arjunaskykok

Hey @arjunaskykok ,

I've ran into the same problem, and here's what I've found after a bit of debugging:

In the example, you're using the token addresses returned by the poolContract to call the quoteExactInputSingle function.

Like so:

 const [token0, token1, fee] = await Promise.all([
    poolContract.token0(),
    poolContract.token1(),
    poolContract.fee(),
  ])

Whereas, what appears to be the case, is that the order in which the contract returns token0 and token1 isn't guaranteed, in fact it appears to be returning the tokens in alphabetical order. One might call it consistently inconsistent.

The solution is quite simple, albeit, the examples should be updated to reflect this.

Simply don't use the token addresses returned by the pool contract when calling quoteExactInputSingle, as you already know the token addresses anyways.

  const quotedAmountOut = await quoterContract.callStatic.quoteExactInputSingle(
    CurrentConfig.tokens.in.address, // instead of: await poolContract.token0()
    CurrentConfig.tokens.out.address, // instead of: await poolContract.token1()
    poolConstants.fee,
    fromReadableAmount(
      CurrentConfig.tokens.amountIn,
      CurrentConfig.tokens.in.decimals
    ).toString(),
    0
  )

balazsotakomaiya avatar Mar 24 '23 17:03 balazsotakomaiya

@balazsotakomaiya , Thanks for the solution. It works!

arjunaskykok avatar Mar 25 '23 07:03 arjunaskykok

@balazsotakomaiya fantastic, great investigation and thank you for this. However, I'd like to find out: what are token0() and token1() calls used for then, if we still need to pass them into computePoolAddress call? What's the point of this roundabout? The documentation says:

This guide fetches this information first in order to show how to fetch any metadata, which will be expanded on in future guides.

But it's weird, what data might me needed that we don't already have? Is it possible to get token address for example without knowing it beforehand?

MetaMmodern avatar Feb 26 '24 23:02 MetaMmodern