Swap Quote

Get a token swap quote based on an input or output value.

Below are the two methods to get a quote for a swap from a given route:

When providing HBAR in the path array, use the wrapped HBAR token ID (WHBAR).

For production environments, it's highly recommended to use a paid Mirror Node provider for commercial and high-traffic purposes. While Hedera's public mirror node offers free REST API and JSON API endpoints, they have global rate limits. These are best suited for development or low rate usage scenarios.


Get Output Quote from Exact Input Amount

Get the output amount from a given exact input amount and a swap route.

Function name: quoteExactInput No gas cost

Parameter Name
Description

bytes path

Route path containing pair swap fees

uint256 amountIn

Exact input amount in token's smallest unit

Solidity Interface & Function Body
//IQuoterV2.sol

/// @notice Returns the amount out received for a given exact input swap without executing the swap
/// @param path The path of the swap, i.e. each token pair and the pool fee
/// @param amountIn The amount of the first token to swap
/// @return amountOut The amount of the last token that would be received
/// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path
/// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for each pool in the path
/// @return gasEstimate The estimate of the gas that the swap consumes
function quoteExactInput(bytes memory path, uint256 amountIn)
  external
  returns (
    uint256 amountOut,
    uint160[] memory sqrtPriceX96AfterList,
    uint32[] memory initializedTicksCrossedList,
    uint256 gasEstimate
  );
//QuoterV2.sol

function quoteExactOutput(bytes memory path, uint256 amountOut)
  public
  override
  returns (
    uint256 amountIn,
    uint160[] memory sqrtPriceX96AfterList,
    uint32[] memory initializedTicksCrossedList,
    uint256 gasEstimate
  )
{
  sqrtPriceX96AfterList = new uint160[](path.numPools());
  initializedTicksCrossedList = new uint32[](path.numPools());

  uint256 i = 0;
  while (true) {
    (address tokenOut, address tokenIn, uint24 fee) = path.decodeFirstPool();

    // the inputs of prior swaps become the outputs of subsequent ones
    (
      uint256 _amountIn,
      uint160 _sqrtPriceX96After,
      uint32 _initializedTicksCrossed,
      uint256 _gasEstimate
    ) = quoteExactOutputSingle(
        QuoteExactOutputSingleParams({
          tokenIn: tokenIn,
          tokenOut: tokenOut,
          amount: amountOut,
          fee: fee,
          sqrtPriceLimitX96: 0
        })
      );

    sqrtPriceX96AfterList[i] = _sqrtPriceX96After;
    initializedTicksCrossedList[i] = _initializedTicksCrossed;
    amountOut = _amountIn;
    gasEstimate += _gasEstimate;
    i++;

    // decide whether to continue or terminate
    if (path.hasMultiplePools()) {
      path = path.skipToken();
    } else {
      return (amountOut, sqrtPriceX96AfterList, initializedTicksCrossedList, gasEstimate);
    }
  }
}

The data passed to the 'path' parameter follows this format: [token, fee, token, fee, token, ...], with each 'token' in the route being 20 bytes long and each 'fee' being 3 bytes long. Example, 0x0001F4 for a 0.05% fee.

Code Overview

Resources:

//Typescript
import * as ethers from 'ethers'; //V6

//Set one of Hedera's JSON RPC Relay as the provider
const provider = new ethers.JsonRpcProvider(hederaJsonRelayUrl, '', {
  batchMaxCount: 1, //workaround for V6
});

//load ABI data containing QuoterV2 functions
const abiInterfaces = new ethers.Interface(abi);

//QuoterV2.sol contract
const quoterEvmAddress = `0x${ContractId.fromString(config.quoter).toSolidityAddress()}`;  

//swap path
const pathData:string[] = [];
pathData.push(inputToken.toSolidityAddress()); 
pathData.push(feeHexStr);
pathData.push(outputToken.toSolidityAddress());

const encodedPathData = hexToUint8Array(pathData.join(''));

const data = abiInterfaces.encodeFunctionData('quoteExactInput', [
  encodedPathData, 
  inputAmountInSmallestUnit.toString()
]);

//Send a call to the JSON-RPC provider directly
const result = await provider.call({
  to: quoterEvmAddress,
  data: data,
});

const decoded = abiInterfaces.decodeFunctionResult('quoteExactInput', result); 
const finalOutputAmount = decoded.amountOut; //in token's smallest unit

Get Input Quote from Exact Output Amount

Get the input amount from a given exact output amount and a swap route.

Function name: quoteExactOutput No gas cost

Parameter Name
Description

bytes path

Route path containing pair swap fees, reversed

uint256 amountOut

Exact output amount in token's smallest unit

Solidity Interface & Function Body
//IQuoterV2.sol

/// @notice Returns the amount in required for a given exact output swap without executing the swap
/// @param path The path of the swap, i.e. each token pair and the pool fee. Path must be provided in reverse order
/// @param amountOut The amount of the last token to receive
/// @return amountIn The amount of first token required to be paid
/// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path
/// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for each pool in the path
/// @return gasEstimate The estimate of the gas that the swap consumes
function quoteExactOutput(bytes memory path, uint256 amountOut)
  external
  returns (
    uint256 amountIn,
    uint160[] memory sqrtPriceX96AfterList,
    uint32[] memory initializedTicksCrossedList,
    uint256 gasEstimate
  );

  struct QuoteExactOutputSingleParams {
    address tokenIn;
    address tokenOut;
    uint256 amount;
    uint24 fee;
    uint160 sqrtPriceLimitX96;
  }
//QuoterV2.sol

function quoteExactOutput(bytes memory path, uint256 amountOut)
  public
  override
  returns (
    uint256 amountIn,
    uint160[] memory sqrtPriceX96AfterList,
    uint32[] memory initializedTicksCrossedList,
    uint256 gasEstimate
  )
{
  sqrtPriceX96AfterList = new uint160[](path.numPools());
  initializedTicksCrossedList = new uint32[](path.numPools());

  uint256 i = 0;
  while (true) {
    (address tokenOut, address tokenIn, uint24 fee) = path.decodeFirstPool();

    // the inputs of prior swaps become the outputs of subsequent ones
    (
      uint256 _amountIn,
      uint160 _sqrtPriceX96After,
      uint32 _initializedTicksCrossed,
      uint256 _gasEstimate
    ) = quoteExactOutputSingle(
        QuoteExactOutputSingleParams({
          tokenIn: tokenIn,
          tokenOut: tokenOut,
          amount: amountOut,
          fee: fee,
          sqrtPriceLimitX96: 0
        })
      );

    sqrtPriceX96AfterList[i] = _sqrtPriceX96After;
    initializedTicksCrossedList[i] = _initializedTicksCrossed;
    amountOut = _amountIn;
    gasEstimate += _gasEstimate;
    i++;

    // decide whether to continue or terminate
    if (path.hasMultiplePools()) {
      path = path.skipToken();
    } else {
      return (amountOut, sqrtPriceX96AfterList, initializedTicksCrossedList, gasEstimate);
    }
  }
}

The data passed to the 'path' parameter follows this format: [token, fee, token, fee, token, ...], but reversed (i.e. the first token in the array should be output token), with each 'token' in the route being 20 bytes long and each 'fee' being 3 bytes long. Example, 0x000BB8 for a 0.30% fee.

Code Overview

Resources:

//Typescript
import * as ethers from 'ethers'; //V6

//Set one of Hedera's JSON RPC Relay as the provider
const provider = new ethers.JsonRpcProvider(hederaJsonRelayUrl, '', {
  batchMaxCount: 1, //workaround for V6
});

//load ABI data containing QuoterV2 functions
const abiInterfaces = new ethers.Interface(abi);

//QuoterV2.sol contract
const quoterEvmAddress = `0x${ContractId.fromString(config.quoter).toSolidityAddress()}`;  

//swap path
const pathData:string[] = [];
pathData.push(inputToken.toSolidityAddress());  
pathData.push(feeHexStr);
pathData.push(outputToken.toSolidityAddress());

//reverse the path
pathData.reverse();

//get encoded Uint8Array data for path hex
const encodedPathData = hexToUint8Array(pathData.join(''));

const data = abiInterfaces.encodeFunctionData('quoteExactOutput', [
  encodedPathData, 
  outputAmountInSmallestUnit
]);

//Send a call to the JSON-RPC provider directly
const result = await provider.call({
  to: quoterEvmAddress,
  data: data,
});

const decoded = abiInterfaces.decodeFunctionResult('quoteExactOutput', result); 
const finalAmountIn = decoded.amountIn; //in token's smallest unit

Last updated