Technical Deep-Dive: Leveraging Hedera Token Service (HTS) for Bonzo Lending & Borrowing

Bonzo Finance Labs's picture
Bonzo Finance Labs
April 22, 2024

In the rapidly evolving world of blockchain and decentralized finance (DeFi), the Hedera stands out with its unique approach to digital consensus and transaction efficiency. A key feature facilitating this innovation is the Hedera Token Service (HTS), which allows for creating and managing native tokens with robust security and performance characteristics.

This blog explores the integration of HTS with Ethereum Virtual Machine (EVM) compatible Smart contracts. This development significantly broadens the scope of DeFi applications on Hedera, particularly for projects like Bonzo's lending and borrowing protocol build on Aave v2.

Understanding Hedera Token Service (HTS)

What is HTS?

HTS offers a streamlined mechanism for configuring, deploying, and managing fungible and non-fungible tokens, based on ERC-20 & 721 standards, directly on the Hedera network. The service is interoperable with the Hedera Smart Contract service (Ethereum Virtual Machine) and eliminates the need for smart contracts to handle basic token functionalities. This not only reduces the operational complexity but also enhances transaction speeds (10,000 TPS) and reduces costs.

Benefits of HTS

Using HTS, developers can leverage features like native compliance checks, automated token management, and built-in security measures. These advantages make HTS attractive to financial services and native web3 protocols looking to deploy their solutions.

HTS and EVM Compatibility

Integration Challenges and Solutions

While HTS tokens offer a range of benefits, their integration into the Ethereum-centric world of smart contracts and DApps requires careful engineering. The introduction of EVM compatibility on Hedera was a game-changer, allowing HTS tokens to be seamlessly used as if they were ERC20 tokens. This was achieved without compromising the native advantages of HTS, although some Hedera-specific idiosyncrasies had to be navigated.

Technical Deep Dive: ERC20 and HTS

Here’s how HTS tokens interact within the EVM:

  1. Obtaining EVM Address: Each HTS token can be associated with an EVM address, derived from its Hedera Account ID or Contract ID.
  2. ERC20 Interaction in Solidity: Developers can directly use this EVM address in their Solidity smart contracts to interact with HTS tokens just like traditional ERC20 tokens, utilizing all standard methods such as transfer, balanceOf, and allowance.

Practical Guide: Using HTS Tokens in Your Projects

Solidity Smart Contracts

  • Step 1: Convert the HTS token’s Account ID to an EVM address or get it from a service like HashScan.
  • Step 2: Use this address in your Solidity contract to call HTS tokens using ERC20 methods.

JavaScript Integration Using Ethers.js

  • Step 1: Utilize a WrapperERC20 contract interface to wrap HTS tokens as ERC20 tokens.
  • Step 2: Use Ethers.js to interact with the HTS token by creating a standard ERC20 token object.

To provide practical insights into integrating Hedera's Token Service (HTS) with Solidity and Ethers.js, here are code examples demonstrating how to use HTS tokens as if they were ERC20 tokens in both environments.

Solidity Example: Interacting with an HTS Token as an ERC20 Token

First, we assume you have your HTS token's EVM address. Here's how you might set up a simple Solidity smart contract to interact with this HTS token using standard ERC20 functions:

Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
 * @title ERC20 interface
 * @dev Standard interface for ERC20 compliant tokens as defined in the EIP
 */
interface IERC20 {
    /// @notice Returns the total token supply
    /// @return Total token supply as a uint256
    function totalSupply() external view returns (uint256);

    /// @notice Returns the balance of the specified address
    /// @param account The address from which the balance will be retrieved
    /// @return Balance of the specified address as a uint256
    function balanceOf(address account) external view returns (uint256);

    /// @notice Transfers tokens to a specified address
    /// @param recipient The address to transfer to
    /// @param amount The amount of tokens to be transferred
    /// @return A boolean that indicates if the operation was successful
    function transfer(address recipient, uint256 amount) external returns (bool);

    /// @notice Returns the remaining number of tokens that the spender will be allowed to spend on behalf of owner
    /// @param owner The address of the token owner
    /// @param spender The address of the token spender
    /// @return Remaining amount the spender is allowed to transfer
    function allowance(address owner, address spender) external view returns (uint256);

    /// @notice Sets the amount of tokens that an address is allowed to spend on behalf of another address
    /// @param spender The address which will spend the tokens
    /// @param amount The amount of tokens to be spent
    /// @return A boolean that indicates if the operation was successful
    function approve(address spender, uint256 amount) external returns (bool);

    /// @notice Transfers tokens from one address to another
    /// @param sender The address which you want to send tokens from
    /// @param recipient The address which you want to transfer to
    /// @param amount The amount of tokens to be transferred
    /// @return A boolean that indicates if the operation was successful
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
}

/**
 * @title Token Wrapper
 * @dev Contract for interacting with HTS (Hedera Token Service) tokens via an ERC20 contract
 */
contract TokenWrapper {
    /// @notice The ERC20 token interface for interacting with HTS token
    IERC20 public htsToken;

    /**
     * @notice Contract constructor that initializes the contract with an HTS token address
     * @param _htsTokenAddress The EVM address of the HTS token
     */
    constructor(address _htsTokenAddress) {
        htsToken = IERC20(_htsTokenAddress);
    }

    /**
     * @notice Transfers HTS tokens to a specified address
     * @param to The recipient's address
     * @param amount The amount of HTS tokens to transfer
     * @return A boolean that indicates if the operation was successful
     */
    function transferHTSToken(address to, uint256 amount) public returns (bool) {
        return htsToken.transfer(to, amount);
    }

    /**
     * @notice Retrieves the token balance of a specified account
     * @param account The address of the token holder
     * @return The current token balance of the account
     */
    function getBalance(address account) public view returns (uint256) {
        return htsToken.balanceOf(account);
    }

    // Add other ERC20 interaction methods here as needed
}

This contract initializes with the EVM address of the HTS token and provides functions to transfer tokens and check balances.

Ethers.js Example: Interacting with an HTS Token Using WrapperERC20 Contract

For this JavaScript example, you need to have Node.js and Ethers.js installed. Here’s how you might interact with an HTS token, assuming you have a WrapperERC20 contract that makes the HTS token behave like an ERC20 token:

JavaScript

// Import ethers library and dotenv for environment variable management
const { ethers } = require('ethers');
import dotenv from 'dotenv';
dotenv.config(); // Load environment variables from .env file into process.env

// Setup the connection to Hedera node using Ethers.js provider
const provider = new ethers.providers.JsonRpcProvider('https://your.hedera.node.url');

// Create a wallet instance by connecting a private key to a provider. This has to be your ECDSA private key
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);

// Define the contract address of the WrapperERC20 for the HTS token - Note: This is the EVM address of the HTS token
const htsTokenAddress = '0xYourHTSTokenEVMAddress';

// Define the ABI (Application Binary Interface) for the ERC20 standard functions
const erc20ABI = [
  'function balanceOf(address) view returns (uint)',
  'function transfer(address to, uint amount) returns (bool)',
  // Other ERC20 functions can be added as needed
];

// Create an instance of the contract, connecting it with the wallet to enable transactions
const htsToken = new ethers.Contract(htsTokenAddress, erc20ABI, wallet);

/**
 * Retrieves the balance of a given address.
 * @param {string} address - The address whose balance will be queried.
 * @returns {Promise<BigNumber>} A promise that resolves to the balance of the given address.
 */
async function getBalance(address) {
  return await htsToken.balanceOf(address);
}

/**
 * Transfers a specified amount of HTS tokens to a given address.
 * @param {string} toAddress - The address to which tokens will be transferred.
 * @param {number} amount - The amount of tokens to transfer.
 * @returns {Promise<boolean>} A promise that resolves to the result of the transfer operation.
 */
async function transferHTSToken(toAddress, amount) {
  return await htsToken.transfer(toAddress, amount);
}

// Example usage of the above functions

// Define the address from which to query the balance
const myAddress = '0xMyAddress';
const balance = await getBalance(myAddress);
console.log(`My balance: ${balance}`); // Log the balance to the console

// Define recipient address and amount for the transfer
const recipientAddress = '0xRecipientAddress';
const transferAmount = 100; // Amount of the tokens to transfer
const transferResult = await transferHTSToken(recipientAddress, transferAmount);
console.log(`Transfer result: ${transferResult}`); // Log the transfer result to the console

In this example, we first set up the provider and define the contract interface using the ABI. We then interact with the contract to check balances and transfer tokens. You must replace "https://your.hedera.node.url" and htsTokenAddress with the actual node URL and the token's contract address.

These examples should provide a foundation for integrating HTS tokens into your Solidity and JavaScript projects, enhancing your blockchain applications with Hedera's capabilities.

Addressing ERC20 Tokens Without HTS Counterparts

A notable issue arises when an ERC20 token created on EVM doesn’t have a corresponding HTS token. This scenario can lead to inconsistencies between token representations and functionalities across platforms.

Solutions

Creating a matching HTS token for every ERC20 token manually can be a viable solution, ensuring alignment and seamless functionality across both ecosystems.

Conclusion

The integration of HTS with EVM paves the way for innovative DeFi applications on Hedera, expanding the utility and accessibility of blockchain solutions. As we continue to explore these technologies, the potential for more sophisticated financial products and services on blockchain seems boundless.




Share this post on