Swap HBAR for Tokens

Swap HBAR for HTS fungible tokens.

Below are three methods available to swap HBAR for HTS tokens:

The swapExactETHForTokens and swapETHForExactTokens function trades in HBAR but derives its name from Uniswap on Ethereum. This name was kept to simplify integration for developers versed in Uniswap tools.

Consider the token's decimal places when determining the output amount.

The output values should be in the token's smallest unit. For the SAUCE token, which has 6 decimal places, an input of 123.45 SAUCE should be entered as 123450000 (123.45 multiplied by 10^6).

Ensure that the "to" account has the output token id associated prior to executing the swap. Failure to do so will result in a TOKEN_NOT_ASSOCIATED_TO_ACCOUNT error.

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


Swap Exact HBAR for Tokens

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

Solidity function name: swapExactETHForTokens

Parameter name
Description

uint amountOutMin

The minimum token amount to receive in its smallest unit

address[] calldata path

An ordered list of token EVM addresses

address to

EVM address for the token recipient

uint deadline

Deadline in Unix seconds

Solidity Interface & Function Body
//IUniswapV2Router01.sol

function swapExactETHForTokens(
  uint amountOutMin, 
  address[] calldata path, 
  address to, 
  uint deadline
) external payable returns (uint[] memory amounts);
//UniswapV2Router02.sol

function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
  external
  virtual
  override
  payable
  ensure(deadline)
  returns (uint[] memory amounts)
{
  require(path[0] == whbar, 'UniswapV2Router: INVALID_PATH');
  amounts = UniswapV2Library.getAmountsOut(factory, msg.value, path);
  require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
  IWHBAR(WHBAR).deposit{value: amounts[0]}(msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]));
  _swap(amounts, path, to);
}

Set the minimum output token amount (amountOutMin) 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:

import { 
  ContractFunctionParameters, 
  ContractExecuteTransaction,
  TokenAssociateTransaction,
  .. 
} from '@hashgraph/sdk';

//Client pre-checks:
// - Output token is associated

const params = new ContractFunctionParameters();
params.addUint256(amountOutMin); //uint amountOutMin
params.addAddressArray(tokenPath); //address[] calldata path
params.addAddress(toAddress); //address to
params.addUint256(deadline); //uint deadline
    
const response = await new ContractExecuteTransaction()
 .setPayableAmount(inputHbar)
 .setContractId(routerContractId)
 .setGas(gasLim)
 .setFunction('swapExactETHForTokens', params)
 .execute(client);
 
const record = await response.getRecord(client);
const result = record.contractFunctionResult!;
const values = result.getResult(['uint[]']);
const amounts = values[0]; //uint[] amounts
const finalOutputAmount = amounts[amounts.length - 1];

Swap HBAR for Exact Tokens

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

Solidity function name: swapETHForExactTokens

Parameter Name
Description

uint amountOut

The exact output amount to receive in its smallest unit

address[] calldata path

An ordered list of token EVM addresses

address to

EVM address for the token recipient

uint deadline

Deadline in Unix seconds

Solidity Interface & Function Body
//IUniswapV2Router01.sol

function swapETHForExactTokens(
  uint amountOut, 
  address[] calldata path, 
  address to, 
  uint deadline
) external payable returns (uint[] memory amounts);

Hedera TypeScript SDK

//UniswapV2Router02.sol

function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
  external
  virtual
  override
  payable
  ensure(deadline)
  returns (uint[] memory amounts)
{
  require(path[0] == whbar, 'UniswapV2Router: INVALID_PATH');
  amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
  require(amounts[0] <= msg.value, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
  IWHBAR(WHBAR).deposit{value: amounts[0]}(msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]));
  _swap(amounts, path, to);
  // refund dust eth, if any
  if (msg.value > amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]);
}

Set the maximum HBAR amount (payable) 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 HBAR than expected.

Code Overview

Resources:

import { 
  ContractFunctionParameters, 
  ContractExecuteTransaction,
  TokenAssociateTransaction,
  .. 
} from '@hashgraph/sdk';

const params = new ContractFunctionParameters();
params.addUint256(amountOut); //uint amountOut
params.addAddressArray(tokenPath); //address[] calldata path
params.addAddress(toAddress); //address to
params.addUint256(deadline); //uint deadline
    
const response = await new ContractExecuteTransaction()
 .setPayableAmount(inputHbar)
 .setContractId(routerContractId)
 .setGas(gasLim)
 .setFunction('swapETHForExactTokens', params)
 .execute(client);
 
const record = await response.getRecord(client);
const result = record.contractFunctionResult!;
const values = result.getResult(['uint[]']);
const amounts = values[0]; //uint[] amounts
const finalInputAmount = amounts[0];

Swap Exact HBAR for Tokens Supporting Custom Fees

Swap an exact amount of HBAR for a minimum token amount, supporting HTS tokens with custom fees on token transfer.

Solidity function name: swapExactETHForTokensSupportingFeeOnTransferTokens

Parameter name
Description

uint amountOutMin

The minimum token amount to receive in its smallest unit

address[] calldata path

An ordered list of token EVM addresses

address to

EVM address for the token recipient

uint deadline

Deadline in Unix seconds

Solidity Interface & Function Body
//IUniswapV2Router02.sol

function swapExactETHForTokensSupportingFeeOnTransferTokens(
  uint amountOutMin,
  address[] calldata path,
  address to,
  uint deadline
) external payable;
//UniswapV2Router02.sol

function swapExactETHForTokensSupportingFeeOnTransferTokens(
  uint amountOutMin,
  address[] calldata path,
  address to,
  uint deadline
)
  external
  virtual
  override
  payable
  ensure(deadline)
{
  require(path[0] == whbar, 'UniswapV2Router: INVALID_PATH');
  uint amountIn = msg.value;
  IWHBAR(WHBAR).deposit{value: amountIn}(msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]));
  uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);
  _swapSupportingFeeOnTransferTokens(path, to);
  require(
    IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin,
    'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'
  );
}

Set the minimum output token amount (amountOutMin) 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:

import { 
  ContractFunctionParameters, 
  ContractExecuteTransaction,
  TokenAssociateTransaction,
  .. 
} from '@hashgraph/sdk';

const params = new ContractFunctionParameters();
params.addUint256(amountOutMin); //uint amountOutMin
params.addAddressArray(tokenPath); //address[] calldata path
params.addAddress(toAddress); //address to
params.addUint256(deadline); //uint deadline
    
await new ContractExecuteTransaction()
 .setPayableAmount(inputHbar)
 .setContractId(routerContractId)
 .setGas(gasLim)
 .setFunction('swapExactETHForTokensSupportingFeeOnTransferTokens', params)
 .execute(client);

Last updated