Swap Tokens for Tokens

Swap HTS fungible tokens for HTS fungible tokens.

Below are two methods available to swap tokens for tokens:


Swap Exact Tokens for Tokens

Swap an exact amount of tokens for a minimum token amount.

Solidity function name: exactInput

Struct Parameter Name
Description

bytes path

A bytes array representing a route path including fees data

address recipient

EVM address of the token recipient

uint256 deadline

Deadline in Unix seconds

uint256 amountIn

The exact input token amount in its smallest unit

uint256 amountOutMinimum

The minimum token amount to receive in its smallest unit

Solidity Interface & Function Body
//ISwapRouter.sol

struct ExactInputParams {
  bytes path;
  address recipient;
  uint256 deadline;
  uint256 amountIn;
  uint256 amountOutMinimum;
}

function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);
//SwapRouter.sol

/// @inheritdoc ISwapRouter
function exactInput(ExactInputParams memory params)
  external
  payable
  override
  checkDeadline(params.deadline)
  returns (uint256 amountOut)
{
  address payer = msg.sender; // msg.sender pays for the first hop

  while (true) {
    bool hasMultiplePools = params.path.hasMultiplePools();

    // the outputs of prior swaps become the inputs to subsequent ones
    params.amountIn = exactInputInternal(
      params.amountIn,
      hasMultiplePools ? address(this) : params.recipient, // for intermediate swaps, this contract custodies
      0,
      SwapCallbackData({
        path: params.path.getFirstPool(), // only the first pool in the path is necessary
        payer: payer
      })
    );

    // decide whether to continue or terminate
    if (hasMultiplePools) {
      payer = address(this); // at this point, the caller has paid
      params.path = params.path.skipToken();
    } else {
      amountOut = params.amountIn;
      break;
    }
  }

  require(amountOut >= params.amountOutMinimum, 'Too little received');
}

Set the minimum output token amount (amountOutMinimum) with caution.

A high minimum might lead to a swap failure due to insufficient liquidity or rapid price movements. Conversely, setting the minimum too low can expose you to significant slippage, potentially resulting in a financial loss as you might receive far fewer tokens than expected.

Code Overview

Resources:

Note: The Hedera Typescript SDK currently does not support passing complex contract function parameters. Instead, use Ethers.js or Web3.js to obtain the encoded function data and pass that data as a function parameter.

import * as ethers from 'ethers'; //V6
import { 
  ContractExecuteTransaction, 
  AccountAllowanceApproveTransaction.
  TokenAssociateTransaction,
  .. 
} from '@hashgraph/sdk';

//Client pre-checks:
// - Output token is associated
// - Router contract has spender allowance for the input token

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

//ExactInputParams
const params = {
  path: routeDataWithFee, //'0x...'
  recipient: recipientAddress, //'0x...' - user's recipient address
  deadline: deadline, //Unix seconds
  amountIn: inputAmount, //in token's smallest unit
  amountOutMinimum: outputAmountMin //in token's smallest unit
};

//get encoded hexdecimal string data ('0x...')
const encodedData = abiInterfaces.encodeFunctionData('exactInput', [params]);

//get encoded data as Uint8Array
const encodedDataAsUint8Array = hexToUint8Array(encodedData);

const response = await new ContractExecuteTransaction()
 .setContractId(swapRouterContractId)
 .setGas(gasLim)
 .setFunctionParameters(encodedDataAsUint8Array)
 .execute(client);
 
const record = await response.getRecord(client);
const result = record.contractFunctionResult!;
const values = result.getResult(['uint256']);
const amountOut = values[0]; //uint256 amountOut - in token's smallest unit

Swap Tokens for Exact Tokens

Swap a maximum amount of tokens to receive an exact tokens amount

Solidity function name: exactOutput

Schema parameter name
Description

bytes path

A bytes array representing a route path including fees data

address recipient

EVM address for the token recipient

uint256 deadline

Deadline in Unix seconds

uint256 amountOut

The exact output amount to receive in its smallest unit

uint256 amountInMaximum

The maximum allowed input amount in its smallest unit

Solidity Interface & Function Body
//ISwapRouter.sol

struct ExactOutputParams {
  bytes path;
  address recipient;
  uint256 deadline;
  uint256 amountOut;
  uint256 amountInMaximum;
}

/// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
/// @return amountIn The amount of the input token
function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);
//SwapRouter.sol

/// @inheritdoc ISwapRouter
function exactOutput(ExactOutputParams calldata params)
  external
  payable
  override
  checkDeadline(params.deadline)
  returns (uint256 amountIn)
{
  // it's okay that the payer is fixed to msg.sender here, as they're only paying for the "final" exact output
  // swap, which happens first, and subsequent swaps are paid for within nested callback frames
  exactOutputInternal(
    params.amountOut,
    params.recipient,
    0,
    SwapCallbackData({path: params.path, payer: msg.sender})
  );

  amountIn = amountInCached;
  require(amountIn <= params.amountInMaximum, 'Too much requested');
  amountInCached = DEFAULT_AMOUNT_IN_CACHED;
}

Set the maximum input token amount (amountInMaximum) with caution.

A low maximum might lead to a swap failure if the required liquidity surpasses this limit or due to rapid price movements. Conversely, setting it too high can expose you to significant slippage, potentially leading to a financial loss as you might spend far more tokens than expected.

Code Overview

Resources:

Note: The Hedera Typescript SDK currently does not support passing complex contract function parameters. Instead, use Ethers.js or Web3.js to obtain the encoded function data and pass that data as a function parameter.

import * as ethers from 'ethers'; //V6
import {
  ContractExecuteTransaction, 
  AccountAllowanceApproveTransaction.
  TokenAssociateTransaction,
  .. 
} from '@hashgraph/sdk';

//Client pre-checks:
// - Output token is associated
// - Router contract has spender allowance for the input token

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

//ExactOutputParams
const params = {
  path: routeDataWithFee, //'0x...' (reversed route path)
  recipient: recipientAddress, //'0x...' - user's recipient address
  deadline: deadline, //Unix seconds
  amountOut: outputAmount, //in token's smallest unit
  amountInMaximum: inputAmountMax //in token's smallest unit
};

//get encoded hexdecimal string data ('0x...')
const encodedData = abiInterfaces.encodeFunctionData('exactOutput', [params]);

//get encoded data as Uint8Array
const encodedDataAsUint8Array = hexToUint8Array(encodedData);

const response = await new ContractExecuteTransaction()
 .setContractId(swapRouterContractId)
 .setGas(gasLim)
 .setFunctionParameters(encodedDataAsUint8Array)
 .execute(client);
 
const record = await response.getRecord(client);
const result = record.contractFunctionResult!;
const values = result.getResult(['uint256']);
const amountIn = values[0]; //uint256 amountIn - in token's smallest unit

Last updated