1

I'm trying to create a simple contract using Vyper that calls the Uniswap quoter contract and quoteExactInputSingle method to return the value of output token given a certain input token amount.



quoter: constant(address)= 0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6

@external
@view
def quote(tokenIn: address, tokenOut: address, amountIn: uint256)-> Bytes[32]:
  
  response: Bytes[32] = raw_call(
    quoter,
    concat(
      method_id("quoteExactInputSingle((address,address,uint24uint256,uint160))"),
      convert(tokenIn, bytes32),
      convert(tokenOut, bytes32),
      convert(3000, bytes32),
      convert(amountIn, bytes32),
      convert(0, bytes32)
    ),
    is_delegate_call=False,
  )

  return response

As suggested, I have tried to use the raw_call Vyper functionality to do this. When compiling I get the error: vyper.exceptions.InvalidType: (builtin) raw_call did not return a value. I'm kind of lost and not sure why this is the case. Any help much appreciated.

My second question - this method will return Bytes[32] however, the quoteExactInputSingle interface returns the minout in uint256. How do I obtain the uint256 minout from the Bytes[32] that will be returned from the method ?

1
  • Add max_outsize=32 after "is_delegate_call=False", and decode the return data. If returning an array may need more than 32. Will do an example.
    – Maka
    Sep 7, 2023 at 9:49

1 Answer 1

2

Declare a max size for return bytes max_outsize=32.
If it is just 32 bytes returned, conversion should be as simple as convert(_bytes, uint256).

Can call this simple example on itself in remix to see what it returns or how that differs if you don't convert it.

@external
def foo(_foo: address) -> uint256:
  response: Bytes[32] = raw_call(
    _foo,
    method_id("bar()"),
    max_outsize=32
  )
  return convert(response, uint256)

@external
@view
def bar() -> uint256:
  return 123987456321

I tested the raw_call exactInputSingle() on polygon and that successfully swaps usdc -> weth with fee 3000. So the issue isn't with the raw_call example, if there is a failure at that point. Possibly allowances or existence of the pool/liquidity etc.

edit

Realising you are calling the quoter and not swap function relative to this case, should be pointed out that the quoter contract is not intended to be called on chain. It is designed to "fail".

Static call off chain or use the callback.

Quoter Limitation
This design has one significant limitation: since quote calls swap function of Pool contract, and swap function is not a pure or view function (because it modifies contract state), quote cannot also be pure or view. swap modifies state and so does quote, even if not in Quoter contract.
But we treat quote as a getter, a function that only reads contract data. This inconsistency means that EVM will use CALL opcode instead of STATICCALL when quote is called. This is not a big problem since Quoter reverts in the swap callback, and reverting resets the state modified during a call–this guarantees that quote won’t modify the state of Pool contract (no actual trade will happen).

Quoter Allows getting the expected amount out or amount in for a given swap without executing the swap
These functions are not gas efficient and should not be called on chain. Instead, optimistically execute the swap and check the amounts in the callback.

edit to add context

As I understand, the idea is if you want a quote and are calling off chain call the quoter directly with an eth_call, and the quote will be in the revert message (this is the method I am familiar with).

Else it is similar to a flashloan, you optimistically call swap on the pool directly and in that swap function is an attempt to "callback" a function in your contract. So your contract needs a function that conforms to the callback interface:

  function uniswapV3SwapCallback(
    int256 amount0Delta,
    int256 amount1Delta,
    bytes data
  ) external

If your contract doesn't have a function that matches and there is no accept all fallback, it will revert.

If it does, but you don't like the amounts returned in the call back you can revert, or not repay (and have the pool throw).

If you like the numbers then you repay the amount needed for the transaction to not revert.

Called back contract should check that it is a safe pool (from the correct factory) making the callback.

2
  • Thank you for your detailed reply - the question then becomes - how does one call this then with a call back ? Sep 7, 2023 at 11:39
  • You are welcome. I added context, and think anymore would need a new question.
    – Maka
    Sep 7, 2023 at 12:51

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.