From bc38bcd5138256ec0ba32f37300598bd421cb2b1 Mon Sep 17 00:00:00 2001 From: nikolay Date: Wed, 8 Jan 2025 12:05:30 +0200 Subject: [PATCH 1/2] chore: resolve comments Signed-off-by: nikolay --- .../contracts/ExampleOApp.sol | 6 ++-- tools/layer-zero-example/test/constants.js | 30 +++++++++++++++++++ .../test/htsAdapterTests.js | 13 ++++---- .../test/htsConnectorTests.js | 13 ++++---- tools/layer-zero-example/test/oappTests.js | 4 +-- .../test/oftAdapterTests.js | 13 ++++---- tools/layer-zero-example/test/oftTests.js | 13 ++++---- .../test/onftAdapterTests.js | 13 ++++---- tools/layer-zero-example/test/onftTests.js | 13 ++++---- tools/layer-zero-example/test/whbarTests.js | 13 ++++---- 10 files changed, 78 insertions(+), 53 deletions(-) create mode 100644 tools/layer-zero-example/test/constants.js diff --git a/tools/layer-zero-example/contracts/ExampleOApp.sol b/tools/layer-zero-example/contracts/ExampleOApp.sol index 7899bd62d1..401008ffc4 100644 --- a/tools/layer-zero-example/contracts/ExampleOApp.sol +++ b/tools/layer-zero-example/contracts/ExampleOApp.sol @@ -41,13 +41,15 @@ contract ExampleOApp is OApp { * @param _origin A struct containing information about where the packet came from. * @param _guid A global unique identifier for tracking the packet. * @param payload Encoded message. + * @param address Executor address as specified by the OApp. + * @param calldata Any extra data or options to trigger on receipt. */ function _lzReceive( Origin calldata _origin, bytes32 _guid, bytes calldata payload, - address, // Executor address as specified by the OApp. - bytes calldata // Any extra data or options to trigger on receipt. + address, + bytes calldata ) internal override { // Decode the payload to get the message // In this case, type is string, but depends on your encoding! diff --git a/tools/layer-zero-example/test/constants.js b/tools/layer-zero-example/test/constants.js new file mode 100644 index 0000000000..95fd28958b --- /dev/null +++ b/tools/layer-zero-example/test/constants.js @@ -0,0 +1,30 @@ +/*- + * + * Hedera JSON RPC Relay - Hardhat Example + * + * Copyright (C) 2025 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +module.exports = { + // we're using the official LZ endpoints + // and a list of all EIDs can be found here + // EIDs are defined in the layer zero documentation https://docs.layerzero.network/v2/developers/evm/technical-reference/deployed-contracts#contract-address-table + HEDERA_EID: 40285, + BSC_EID: 40102, + + // a random account + RECEIVER_ADDRESS: '0xF51c7a9407217911d74e91642dbC58F18E51Deac' +}; diff --git a/tools/layer-zero-example/test/htsAdapterTests.js b/tools/layer-zero-example/test/htsAdapterTests.js index 2381a580a8..d638097d88 100644 --- a/tools/layer-zero-example/test/htsAdapterTests.js +++ b/tools/layer-zero-example/test/htsAdapterTests.js @@ -22,10 +22,9 @@ const hre = require('hardhat'); const { ethers } = hre; const { Options, addressToBytes32 } = require('@layerzerolabs/lz-v2-utilities'); const { expect } = require('chai'); +const CONSTANTS = require('./constants'); -const HEDERA_EID = 40285; -const BSC_EID = 40102; -const receiverAddress = '0xF51c7a9407217911d74e91642dbC58F18E51Deac'; +const { HEDERA_EID, BSC_EID, RECEIVER_ADDRESS } = CONSTANTS; const amount = '100'; describe('HTSAdapterTests', function() { @@ -66,7 +65,7 @@ describe('HTSAdapterTests', function() { const sendParam = { dstEid: BSC_EID, - to: addressToBytes32(receiverAddress), + to: addressToBytes32(RECEIVER_ADDRESS), amountLD: amount, minAmountLD: amount, extraOptions: Options.newOptions().addExecutorLzReceiveOption(3000000, 0).toBytes(), @@ -93,7 +92,7 @@ describe('HTSAdapterTests', function() { const sendParam = { dstEid: HEDERA_EID, - to: addressToBytes32(receiverAddress), + to: addressToBytes32(RECEIVER_ADDRESS), amountLD: amount, minAmountLD: amount, extraOptions: Options.newOptions().addExecutorLzReceiveOption(3000000, 0).toBytes(), @@ -119,7 +118,7 @@ describe('HTSAdapterTests', function() { const signers = await ethers.getSigners(); const contractERC20 = await ethers.getContractAt('ERC20Mock', process.env.HTS_ADAPTER_HTS_HEDERA_CONTRACT); - const receiverBalance = await contractERC20.balanceOf(receiverAddress); + const receiverBalance = await contractERC20.balanceOf(RECEIVER_ADDRESS); console.log(`(${hre.network.name}) signer balance: ${await contractERC20.balanceOf(signers[0].address)}`); console.log(`(${hre.network.name}) adapter balance: ${await contractERC20.balanceOf(process.env.HTS_ADAPTER_HEDERA_CONTRACT)}`); @@ -132,7 +131,7 @@ describe('HTSAdapterTests', function() { const signers = await ethers.getSigners(); const contractERC20 = await ethers.getContractAt('ERC20Mock', process.env.HTS_ADAPTER_ERC20_BSC_CONTRACT); - const receiverBalance = await contractERC20.balanceOf(receiverAddress); + const receiverBalance = await contractERC20.balanceOf(RECEIVER_ADDRESS); console.log(`(${hre.network.name}) signer balance: ${await contractERC20.balanceOf(signers[0].address)}`); console.log(`(${hre.network.name}) adapter balance: ${await contractERC20.balanceOf(process.env.HTS_ADAPTER_BSC_CONTRACT)}`); diff --git a/tools/layer-zero-example/test/htsConnectorTests.js b/tools/layer-zero-example/test/htsConnectorTests.js index 22f03bf6c2..e555f28fb6 100644 --- a/tools/layer-zero-example/test/htsConnectorTests.js +++ b/tools/layer-zero-example/test/htsConnectorTests.js @@ -22,10 +22,9 @@ const hre = require('hardhat'); const { ethers } = hre; const { Options, addressToBytes32 } = require('@layerzerolabs/lz-v2-utilities'); const { expect } = require('chai'); +const CONSTANTS = require('./constants'); -const HEDERA_EID = 40285; -const BSC_EID = 40102; -const receiverAddress = '0xF51c7a9407217911d74e91642dbC58F18E51Deac'; +const { HEDERA_EID, BSC_EID, RECEIVER_ADDRESS } = CONSTANTS; const amount = '100'; describe('HTSConnectorTests', function() { @@ -46,7 +45,7 @@ describe('HTSConnectorTests', function() { const sendParam = { dstEid: BSC_EID, - to: addressToBytes32(receiverAddress), + to: addressToBytes32(RECEIVER_ADDRESS), amountLD: amount, minAmountLD: amount, extraOptions: Options.newOptions().addExecutorLzReceiveOption(3000000, 0).toBytes(), @@ -73,7 +72,7 @@ describe('HTSConnectorTests', function() { const sendParam = { dstEid: HEDERA_EID, - to: addressToBytes32(receiverAddress), + to: addressToBytes32(RECEIVER_ADDRESS), amountLD: amount, minAmountLD: amount, extraOptions: Options.newOptions().addExecutorLzReceiveOption(3000000, 0).toBytes(), @@ -102,7 +101,7 @@ describe('HTSConnectorTests', function() { const tokenAddress = await oftHts.htsTokenAddress(); const contract = await ethers.getContractAt('ERC20', tokenAddress); - const receiverBalance = await contract.balanceOf(receiverAddress); + const receiverBalance = await contract.balanceOf(RECEIVER_ADDRESS); console.log(`(${hre.network.name}) oft contract balance: ${await contract.balanceOf(process.env.HTS_CONNECTOR_HEDERA_CONTRACT)}`); console.log(`(${hre.network.name}) signer balance: ${await contract.balanceOf(signers[0].address)}`); @@ -116,7 +115,7 @@ describe('HTSConnectorTests', function() { const signers = await ethers.getSigners(); const contract = await ethers.getContractAt('ERC20', process.env.HTS_CONNECTOR_BSC_CONTRACT); - const receiverBalance = await contract.balanceOf(receiverAddress); + const receiverBalance = await contract.balanceOf(RECEIVER_ADDRESS); console.log(`(${hre.network.name}) oft contract balance: ${await contract.balanceOf(process.env.HTS_CONNECTOR_BSC_CONTRACT)}`); console.log(`(${hre.network.name}) signer balance: ${await contract.balanceOf(signers[0].address)}`); diff --git a/tools/layer-zero-example/test/oappTests.js b/tools/layer-zero-example/test/oappTests.js index 14a295c306..5bf2837f84 100644 --- a/tools/layer-zero-example/test/oappTests.js +++ b/tools/layer-zero-example/test/oappTests.js @@ -22,9 +22,9 @@ const hre = require('hardhat'); const { ethers } = hre; const { Options } = require('@layerzerolabs/lz-v2-utilities'); const { expect } = require('chai'); +const CONSTANTS = require('./constants'); -const HEDERA_EID = 40285; -const BSC_EID = 40102; +const { HEDERA_EID, BSC_EID } = CONSTANTS; const DATA_FROM_HEDERA = 'dataFromHedera'; const DATA_FROM_BSC = 'dataFromBsc'; diff --git a/tools/layer-zero-example/test/oftAdapterTests.js b/tools/layer-zero-example/test/oftAdapterTests.js index 0020bd8a44..2bf6ef3d9b 100644 --- a/tools/layer-zero-example/test/oftAdapterTests.js +++ b/tools/layer-zero-example/test/oftAdapterTests.js @@ -22,10 +22,9 @@ const hre = require('hardhat'); const { ethers } = hre; const { Options, addressToBytes32 } = require('@layerzerolabs/lz-v2-utilities'); const { expect } = require('chai'); +const CONSTANTS = require('./constants'); -const HEDERA_EID = 40285; -const BSC_EID = 40102; -const receiverAddress = '0xF51c7a9407217911d74e91642dbC58F18E51Deac'; +const { HEDERA_EID, BSC_EID, RECEIVER_ADDRESS } = CONSTANTS; const amount = '100000000000000000'; describe('OFTAdapterTests', function() { @@ -66,7 +65,7 @@ describe('OFTAdapterTests', function() { const sendParam = { dstEid: BSC_EID, - to: addressToBytes32(receiverAddress), + to: addressToBytes32(RECEIVER_ADDRESS), amountLD: amount, minAmountLD: amount, extraOptions: Options.newOptions().addExecutorLzReceiveOption(3000000, 0).toBytes(), @@ -93,7 +92,7 @@ describe('OFTAdapterTests', function() { const sendParam = { dstEid: HEDERA_EID, - to: addressToBytes32(receiverAddress), + to: addressToBytes32(RECEIVER_ADDRESS), amountLD: amount, minAmountLD: amount, extraOptions: Options.newOptions().addExecutorLzReceiveOption(3000000, 0).toBytes(), @@ -119,7 +118,7 @@ describe('OFTAdapterTests', function() { const signers = await ethers.getSigners(); const contractERC20 = await ethers.getContractAt('ERC20Mock', process.env.ERC20_HEDERA_CONTRACT); - const receiverBalance = await contractERC20.balanceOf(receiverAddress); + const receiverBalance = await contractERC20.balanceOf(RECEIVER_ADDRESS); console.log(`(${hre.network.name}) signer balance: ${await contractERC20.balanceOf(signers[0].address)}`); console.log(`(${hre.network.name}) oft adapter balance: ${await contractERC20.balanceOf(process.env.OFT_ADAPTER_HEDERA_CONTRACT)}`); @@ -132,7 +131,7 @@ describe('OFTAdapterTests', function() { const signers = await ethers.getSigners(); const contractERC20 = await ethers.getContractAt('ERC20Mock', process.env.ERC20_BSC_CONTRACT); - const receiverBalance = await contractERC20.balanceOf(receiverAddress); + const receiverBalance = await contractERC20.balanceOf(RECEIVER_ADDRESS); console.log(`(${hre.network.name}) signer balance: ${await contractERC20.balanceOf(signers[0].address)}`); console.log(`(${hre.network.name}) oft adapter balance: ${await contractERC20.balanceOf(process.env.OFT_ADAPTER_BSC_CONTRACT)}`); diff --git a/tools/layer-zero-example/test/oftTests.js b/tools/layer-zero-example/test/oftTests.js index aadfa513e9..64669404e1 100644 --- a/tools/layer-zero-example/test/oftTests.js +++ b/tools/layer-zero-example/test/oftTests.js @@ -22,10 +22,9 @@ const hre = require('hardhat'); const { ethers } = hre; const { Options, addressToBytes32 } = require('@layerzerolabs/lz-v2-utilities'); const { expect } = require('chai'); +const CONSTANTS = require('./constants'); -const HEDERA_EID = 40285; -const BSC_EID = 40102; -const receiverAddress = '0xF51c7a9407217911d74e91642dbC58F18E51Deac'; +const { HEDERA_EID, BSC_EID, RECEIVER_ADDRESS } = CONSTANTS; const amount = '100000000000000000'; describe('OFTTests', function() { @@ -34,7 +33,7 @@ describe('OFTTests', function() { const sendParam = { dstEid: BSC_EID, - to: addressToBytes32(receiverAddress), + to: addressToBytes32(RECEIVER_ADDRESS), amountLD: amount, minAmountLD: amount, extraOptions: Options.newOptions().addExecutorLzReceiveOption(3000000, 0).toBytes(), @@ -61,7 +60,7 @@ describe('OFTTests', function() { const sendParam = { dstEid: HEDERA_EID, - to: addressToBytes32(receiverAddress), + to: addressToBytes32(RECEIVER_ADDRESS), amountLD: amount, minAmountLD: amount, extraOptions: Options.newOptions().addExecutorLzReceiveOption(3000000, 0).toBytes(), @@ -87,7 +86,7 @@ describe('OFTTests', function() { const signers = await ethers.getSigners(); const contract = await ethers.getContractAt('ExampleOFT', process.env.OFT_HEDERA_CONTRACT); - const receiverBalance = await contract.balanceOf(receiverAddress); + const receiverBalance = await contract.balanceOf(RECEIVER_ADDRESS); console.log(`(${hre.network.name}) signer balance: ${await contract.balanceOf(signers[0].address)}`); console.log(`(${hre.network.name}) receiver balance: ${receiverBalance}`); @@ -99,7 +98,7 @@ describe('OFTTests', function() { const signers = await ethers.getSigners(); const contract = await ethers.getContractAt('ExampleOFT', process.env.OFT_BSC_CONTRACT); - const receiverBalance = await contract.balanceOf(receiverAddress); + const receiverBalance = await contract.balanceOf(RECEIVER_ADDRESS); console.log(`(${hre.network.name}) signer balance: ${await contract.balanceOf(signers[0].address)}`); console.log(`(${hre.network.name}) receiver balance: ${receiverBalance}`); diff --git a/tools/layer-zero-example/test/onftAdapterTests.js b/tools/layer-zero-example/test/onftAdapterTests.js index 1268e8303e..4c2ffa4c7d 100644 --- a/tools/layer-zero-example/test/onftAdapterTests.js +++ b/tools/layer-zero-example/test/onftAdapterTests.js @@ -22,10 +22,9 @@ const hre = require('hardhat'); const { ethers } = hre; const { Options, addressToBytes32 } = require('@layerzerolabs/lz-v2-utilities'); const { expect } = require('chai'); +const CONSTANTS = require('./constants'); -const HEDERA_EID = 40285; -const BSC_EID = 40102; -const receiverAddress = '0xF51c7a9407217911d74e91642dbC58F18E51Deac'; +const { HEDERA_EID, BSC_EID, RECEIVER_ADDRESS } = CONSTANTS; describe('ONFTAdapterTests', function() { it('@hedera @mint', async () => { @@ -79,7 +78,7 @@ describe('ONFTAdapterTests', function() { const sendParam = { dstEid: BSC_EID, - to: addressToBytes32(receiverAddress), + to: addressToBytes32(RECEIVER_ADDRESS), tokenId: 1, extraOptions: Options.newOptions().addExecutorLzReceiveOption(3000000, 0).toBytes(), composeMsg: ethers.utils.arrayify('0x'), @@ -105,7 +104,7 @@ describe('ONFTAdapterTests', function() { const sendParam = { dstEid: HEDERA_EID, - to: addressToBytes32(receiverAddress), + to: addressToBytes32(RECEIVER_ADDRESS), tokenId: 2, extraOptions: Options.newOptions().addExecutorLzReceiveOption(3000000, 0).toBytes(), composeMsg: ethers.utils.arrayify('0x'), @@ -129,12 +128,12 @@ describe('ONFTAdapterTests', function() { it('@hedera @test get owner', async () => { const contract = await ethers.getContractAt('ERC721Mock', process.env.ERC721_HEDERA_CONTRACT); const owner = await contract.ownerOf(2); - expect(owner).to.equal(receiverAddress); + expect(owner).to.equal(RECEIVER_ADDRESS); }); it('@bsc @test get owner', async () => { const contract = await ethers.getContractAt('ERC721Mock', process.env.ERC721_BSC_CONTRACT); const owner = await contract.ownerOf(1); - expect(owner).to.equal(receiverAddress); + expect(owner).to.equal(RECEIVER_ADDRESS); }); }); diff --git a/tools/layer-zero-example/test/onftTests.js b/tools/layer-zero-example/test/onftTests.js index bb5257251c..26c42ed05f 100644 --- a/tools/layer-zero-example/test/onftTests.js +++ b/tools/layer-zero-example/test/onftTests.js @@ -22,10 +22,9 @@ const hre = require('hardhat'); const { ethers } = hre; const { Options, addressToBytes32 } = require('@layerzerolabs/lz-v2-utilities'); const { expect } = require('chai'); +const CONSTANTS = require('./constants'); -const HEDERA_EID = 40285; -const BSC_EID = 40102; -const receiverAddress = '0xF51c7a9407217911d74e91642dbC58F18E51Deac'; +const { HEDERA_EID, BSC_EID, RECEIVER_ADDRESS } = CONSTANTS; describe('ONFTTests', function() { it('@hedera @send to bsc', async () => { @@ -33,7 +32,7 @@ describe('ONFTTests', function() { const sendParam = { dstEid: BSC_EID, - to: addressToBytes32(receiverAddress), + to: addressToBytes32(RECEIVER_ADDRESS), tokenId: 1, extraOptions: Options.newOptions().addExecutorLzReceiveOption(3000000, 0).toBytes(), composeMsg: ethers.utils.arrayify('0x'), @@ -59,7 +58,7 @@ describe('ONFTTests', function() { const sendParam = { dstEid: HEDERA_EID, - to: addressToBytes32(receiverAddress), + to: addressToBytes32(RECEIVER_ADDRESS), tokenId: 2, extraOptions: Options.newOptions().addExecutorLzReceiveOption(3000000, 0).toBytes(), composeMsg: ethers.utils.arrayify('0x'), @@ -83,12 +82,12 @@ describe('ONFTTests', function() { it('@hedera @test get owner', async () => { const contract = await ethers.getContractAt('ExampleONFT', process.env.ONFT_HEDERA_CONTRACT); const owner = await contract.ownerOf(2); - expect(owner).to.equal(receiverAddress); + expect(owner).to.equal(RECEIVER_ADDRESS); }); it('@bsc @test get owner', async () => { const contract = await ethers.getContractAt('ExampleONFT', process.env.ONFT_BSC_CONTRACT); const owner = await contract.ownerOf(1); - expect(owner).to.equal(receiverAddress); + expect(owner).to.equal(RECEIVER_ADDRESS); }); }); diff --git a/tools/layer-zero-example/test/whbarTests.js b/tools/layer-zero-example/test/whbarTests.js index 50adf90e37..14b6668fa0 100644 --- a/tools/layer-zero-example/test/whbarTests.js +++ b/tools/layer-zero-example/test/whbarTests.js @@ -22,10 +22,9 @@ const hre = require('hardhat'); const { ethers } = hre; const { Options, addressToBytes32 } = require('@layerzerolabs/lz-v2-utilities'); const { expect } = require('chai'); +const CONSTANTS = require('./constants'); -const HEDERA_EID = 40285; -const BSC_EID = 40102; -const receiverAddress = '0xF51c7a9407217911d74e91642dbC58F18E51Deac'; +const { HEDERA_EID, BSC_EID, RECEIVER_ADDRESS } = CONSTANTS; const amount = '50000000'; describe('WHBARTests', function() { @@ -79,7 +78,7 @@ describe('WHBARTests', function() { const sendParam = { dstEid: BSC_EID, - to: addressToBytes32(receiverAddress), + to: addressToBytes32(RECEIVER_ADDRESS), amountLD: amount, minAmountLD: amount, extraOptions: Options.newOptions().addExecutorLzReceiveOption(3000000, 0).toBytes(), @@ -106,7 +105,7 @@ describe('WHBARTests', function() { const sendParam = { dstEid: HEDERA_EID, - to: addressToBytes32(receiverAddress), + to: addressToBytes32(RECEIVER_ADDRESS), amountLD: amount, minAmountLD: amount, extraOptions: Options.newOptions().addExecutorLzReceiveOption(3000000, 0).toBytes(), @@ -132,7 +131,7 @@ describe('WHBARTests', function() { const signers = await ethers.getSigners(); const contractERC20 = await ethers.getContractAt('ERC20Mock', process.env.WHBAR_HEDERA_CONTRACT); - const receiverBalance = await contractERC20.balanceOf(receiverAddress); + const receiverBalance = await contractERC20.balanceOf(RECEIVER_ADDRESS); console.log(`(${hre.network.name}) signer balance: ${await contractERC20.balanceOf(signers[0].address)}`); console.log(`(${hre.network.name}) adapter balance: ${await contractERC20.balanceOf(process.env.WHBAR_HEDERA_ADAPTER_CONTRACT)}`); @@ -145,7 +144,7 @@ describe('WHBARTests', function() { const signers = await ethers.getSigners(); const contractERC20 = await ethers.getContractAt('ERC20Mock', process.env.WHBAR_BSC_CONTRACT); - const receiverBalance = await contractERC20.balanceOf(receiverAddress); + const receiverBalance = await contractERC20.balanceOf(RECEIVER_ADDRESS); console.log(`(${hre.network.name}) signer balance: ${await contractERC20.balanceOf(signers[0].address)}`); console.log(`(${hre.network.name}) adapter balance: ${await contractERC20.balanceOf(process.env.WHBAR_BSC_ADAPTER_CONTRACT)}`); From a4c5e5897df2f9b7eadea8a090163a65f11c6613 Mon Sep 17 00:00:00 2001 From: nikolay Date: Fri, 10 Jan 2025 14:59:11 +0200 Subject: [PATCH 2/2] chore: add hts connector for existing token example Signed-off-by: nikolay --- tools/layer-zero-example/.env.example | 5 + tools/layer-zero-example/README.md | 52 ++++++ .../contracts/CreateHTS.sol | 13 +- .../ExampleHTSConnectorExistingToken.sol | 16 ++ .../contracts/ExampleOApp.sol | 6 +- .../contracts/HTSConnectorExistingToken.sol | 90 ++++++++++ .../contracts/hts/HederaTokenService.sol | 12 ++ .../contracts/hts/IHederaTokenService.sol | 6 + tools/layer-zero-example/hardhat.config.js | 36 ++-- .../test/htsConnectorExistingTokenTests.js | 160 ++++++++++++++++++ 10 files changed, 381 insertions(+), 15 deletions(-) create mode 100644 tools/layer-zero-example/contracts/ExampleHTSConnectorExistingToken.sol create mode 100644 tools/layer-zero-example/contracts/HTSConnectorExistingToken.sol create mode 100644 tools/layer-zero-example/test/htsConnectorExistingTokenTests.js diff --git a/tools/layer-zero-example/.env.example b/tools/layer-zero-example/.env.example index 3d47c98e8a..a17e1c51c6 100644 --- a/tools/layer-zero-example/.env.example +++ b/tools/layer-zero-example/.env.example @@ -30,6 +30,11 @@ ONFT_ADAPTER_BSC_CONTRACT=0x HTS_CONNECTOR_HEDERA_CONTRACT=0x HTS_CONNECTOR_BSC_CONTRACT=0x +# HTS Connector for existing token config +HTS_CONNECTOR_CREATE_HTS_CONTRACT=0x +HTS_CONNECTOR_EXISTING_TOKEN_HEDERA_CONTRACT=0x +HTS_CONNECTOR_EXISTING_TOKEN_BSC_CONTRACT=0x + # HTS Adapter config HTS_ADAPTER_HTS_HEDERA_CONTRACT=0x HTS_ADAPTER_ERC20_BSC_CONTRACT=0x diff --git a/tools/layer-zero-example/README.md b/tools/layer-zero-example/README.md index 02814e7e51..88bee5e3cb 100644 --- a/tools/layer-zero-example/README.md +++ b/tools/layer-zero-example/README.md @@ -227,6 +227,58 @@ npx hardhat test --grep "HTSConnectorTests @hedera @test" --network hedera_testn npx hardhat test --grep "HTSConnectorTests @bsc @test" --network bsc_testnet ``` +### HTS Connector for existing HTS token + +That's a variant of OFT but using an already existing HTS token. Keep in mind that "supply key" of the token must contains the HTS Connector contract's address. + +- Create an HTS token +```typescript +npx hardhat create-hts-token --network hedera_testnet +``` + +- Deploying OFT on an EVM chain and HTS Connector on the Hedera chain. The HTS Connector for existing token extends OFTCore and receives the HTS tokens address as constructor parameter. Also, overrides OFTCore _debit and _credit with related HTS mint and burn precompile calls +``` +npx hardhat deploy-hts-connector-existing-token --token --network hedera_testnet +npx hardhat deploy-oft --decimals 8 --mint 1000 --network bsc_testnet +``` + +- In order to connect OFTs together, we need to set the peer of the target OFT, more info can be found here https://docs.layerzero.network/v2/developers/evm/getting-started#connecting-your-contracts +```typescript +npx hardhat set-peer --source --target --network hedera_testnet +npx hardhat set-peer --source --target --network bsc_testnet +``` + +- Fill the .env + +- Adding the HTSConnectorExistingToken contract's address as a supply key of the existing HTS token +```typescript +npx hardhat test --grep "HTSConnectorExistingToken @hedera @update-keys" --network hedera_testnet +``` + +- Funding the HTSConnectorExistingToken contract +```typescript +npx hardhat test --grep "HTSConnectorExistingToken @hedera @fund" --network hedera_testnet +``` + +- Approving HTS Connector to use some signer's tokens +```typescript +npx hardhat test --grep "HTSConnectorExistingToken @hedera @approve" --network hedera_testnet +``` + +- On these steps, we're sending tokens from an EVM chain to Hedera and receiving HTS tokens and vice versa +```typescript +npx hardhat test --grep "HTSConnectorExistingToken @hedera @send" --network hedera_testnet +npx hardhat test --grep "HTSConnectorExistingToken @bsc @send" --network bsc_testnet +``` + +- Wait a couple of minutes, the LZ progress can be tracked on https://testnet.layerzeroscan.com/tx/ + +- Finally we're checking whether the balances are expected on both source and destination chains +```typescript +npx hardhat test --grep "HTSConnectorExistingToken @hedera @test" --network hedera_testnet +npx hardhat test --grep "HTSConnectorExistingToken @bsc @test" --network bsc_testnet +``` + ### HTS Adapter If your HTS token already exists on Hedera and you want to connect it to another chain, you can deploy the OFT Adapter contract to act as an intermediary lockbox for it. diff --git a/tools/layer-zero-example/contracts/CreateHTS.sol b/tools/layer-zero-example/contracts/CreateHTS.sol index bdc541894a..4e63af1f1a 100644 --- a/tools/layer-zero-example/contracts/CreateHTS.sol +++ b/tools/layer-zero-example/contracts/CreateHTS.sol @@ -10,8 +10,13 @@ contract CreateHTS is Ownable, KeyHelper, HederaTokenService { address public htsTokenAddress; constructor(string memory _name, string memory _symbol, address _delegate) payable Ownable(_delegate) { - IHederaTokenService.TokenKey[] memory keys = new IHederaTokenService.TokenKey[](1); + IHederaTokenService.TokenKey[] memory keys = new IHederaTokenService.TokenKey[](2); keys[0] = getSingleKey( + KeyType.ADMIN, + KeyValueType.INHERIT_ACCOUNT_KEY, + bytes("") + ); + keys[1] = getSingleKey( KeyType.SUPPLY, KeyValueType.INHERIT_ACCOUNT_KEY, bytes("") @@ -32,4 +37,10 @@ contract CreateHTS is Ownable, KeyHelper, HederaTokenService { htsTokenAddress = tokenAddress; } + + function updateTokenKeysPublic(IHederaTokenService.TokenKey[] memory keys) public returns (int64 responseCode) { + (responseCode) = HederaTokenService.updateTokenKeys(htsTokenAddress, keys); + + require(responseCode == HederaTokenService.SUCCESS_CODE, "HTS: Update keys reverted"); + } } diff --git a/tools/layer-zero-example/contracts/ExampleHTSConnectorExistingToken.sol b/tools/layer-zero-example/contracts/ExampleHTSConnectorExistingToken.sol new file mode 100644 index 0000000000..416deb29a1 --- /dev/null +++ b/tools/layer-zero-example/contracts/ExampleHTSConnectorExistingToken.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/access/Ownable.sol"; +import "./hts/HederaTokenService.sol"; +import "./hts/IHederaTokenService.sol"; +import "./hts/KeyHelper.sol"; +import "./HTSConnectorExistingToken.sol"; + +contract ExampleHTSConnectorExistingToken is Ownable, HTSConnectorExistingToken { + constructor( + address _tokenAddress, + address _lzEndpoint, + address _delegate + ) payable HTSConnectorExistingToken(_tokenAddress, _lzEndpoint, _delegate) Ownable(_delegate) {} +} diff --git a/tools/layer-zero-example/contracts/ExampleOApp.sol b/tools/layer-zero-example/contracts/ExampleOApp.sol index 401008ffc4..7899bd62d1 100644 --- a/tools/layer-zero-example/contracts/ExampleOApp.sol +++ b/tools/layer-zero-example/contracts/ExampleOApp.sol @@ -41,15 +41,13 @@ contract ExampleOApp is OApp { * @param _origin A struct containing information about where the packet came from. * @param _guid A global unique identifier for tracking the packet. * @param payload Encoded message. - * @param address Executor address as specified by the OApp. - * @param calldata Any extra data or options to trigger on receipt. */ function _lzReceive( Origin calldata _origin, bytes32 _guid, bytes calldata payload, - address, - bytes calldata + address, // Executor address as specified by the OApp. + bytes calldata // Any extra data or options to trigger on receipt. ) internal override { // Decode the payload to get the message // In this case, type is string, but depends on your encoding! diff --git a/tools/layer-zero-example/contracts/HTSConnectorExistingToken.sol b/tools/layer-zero-example/contracts/HTSConnectorExistingToken.sol new file mode 100644 index 0000000000..51597af3fb --- /dev/null +++ b/tools/layer-zero-example/contracts/HTSConnectorExistingToken.sol @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.20; + +import {OFTCore} from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/OFTCore.sol"; +import "./hts/HederaTokenService.sol"; +import "./hts/IHederaTokenService.sol"; +import "./hts/KeyHelper.sol"; + +/** + * @title HTS Connector for existing token + * @dev HTSConnectorExistingToken is a contract wrapped for already existing HTS token that extends the functionality of the OFTCore contract. + */ +abstract contract HTSConnectorExistingToken is OFTCore, KeyHelper, HederaTokenService { + address public htsTokenAddress; + + /** + * @dev Constructor for the HTSConnectorExistingToken contract. + * @param _tokenAddress Address of already existing HTS token + * @param _lzEndpoint The LayerZero endpoint address. + * @param _delegate The delegate capable of making OApp configurations inside of the endpoint. + */ + constructor( + address _tokenAddress, + address _lzEndpoint, + address _delegate + ) payable OFTCore(8, _lzEndpoint, _delegate) { + htsTokenAddress = _tokenAddress; + } + + /** + * @dev Retrieves the address of the underlying HTS implementation. + * @return The address of the HTS token. + */ + function token() public view returns (address) { + return htsTokenAddress; + } + + /** + * @notice Indicates whether the HTS Connector contract requires approval of the 'token()' to send. + * @return requiresApproval Needs approval of the underlying token implementation. + */ + function approvalRequired() external pure virtual returns (bool) { + return false; + } + + /** + * @dev Burns tokens from the sender's specified balance. + * @param _from The address to debit the tokens from. + * @param _amountLD The amount of tokens to send in local decimals. + * @param _minAmountLD The minimum amount to send in local decimals. + * @param _dstEid The destination chain ID. + * @return amountSentLD The amount sent in local decimals. + * @return amountReceivedLD The amount received in local decimals on the remote. + */ + function _debit( + address _from, + uint256 _amountLD, + uint256 _minAmountLD, + uint32 _dstEid + ) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) { + (amountSentLD, amountReceivedLD) = _debitView(_amountLD, _minAmountLD, _dstEid); + + int256 transferResponse = HederaTokenService.transferToken(htsTokenAddress, _from, address(this), int64(uint64(_amountLD))); + require(transferResponse == HederaTokenService.SUCCESS_CODE, "HTS: Transfer failed"); + + (int256 response,) = HederaTokenService.burnToken(htsTokenAddress, int64(uint64(amountSentLD)), new int64[](0)); + require(response == HederaTokenService.SUCCESS_CODE, "HTS: Burn failed"); + } + + /** + * @dev Credits tokens to the specified address. + * @param _to The address to credit the tokens to. + * @param _amountLD The amount of tokens to credit in local decimals. + * @dev _srcEid The source chain ID. + * @return amountReceivedLD The amount of tokens ACTUALLY received in local decimals. + */ + function _credit( + address _to, + uint256 _amountLD, + uint32 /*_srcEid*/ + ) internal virtual override returns (uint256) { + (int256 response, ,) = HederaTokenService.mintToken(htsTokenAddress, int64(uint64(_amountLD)), new bytes[](0)); + require(response == HederaTokenService.SUCCESS_CODE, "HTS: Mint failed"); + + int256 transferResponse = HederaTokenService.transferToken(htsTokenAddress, address(this), _to, int64(uint64(_amountLD))); + require(transferResponse == HederaTokenService.SUCCESS_CODE, "HTS: Transfer failed"); + + return _amountLD; + } +} diff --git a/tools/layer-zero-example/contracts/hts/HederaTokenService.sol b/tools/layer-zero-example/contracts/hts/HederaTokenService.sol index e52cd4a9f9..c7f4332c72 100644 --- a/tools/layer-zero-example/contracts/hts/HederaTokenService.sol +++ b/tools/layer-zero-example/contracts/hts/HederaTokenService.sol @@ -144,4 +144,16 @@ abstract contract HederaTokenService { ? abi.decode(result, (int32)) : HederaTokenService.UNKNOWN_CODE; } + + /// Operation to update token keys + /// @param token The token address + /// @param keys The token keys + /// @return responseCode The response code for the status of the request. SUCCESS is 22. + function updateTokenKeys(address token, IHederaTokenService.TokenKey[] memory keys) + internal returns (int64 responseCode){ + (bool success, bytes memory result) = precompileAddress.call( + abi.encodeWithSelector(IHederaTokenService.updateTokenKeys.selector, token, keys)); + (responseCode) = success ? abi.decode(result, (int32)) : HederaTokenService.UNKNOWN_CODE; + } + } diff --git a/tools/layer-zero-example/contracts/hts/IHederaTokenService.sol b/tools/layer-zero-example/contracts/hts/IHederaTokenService.sol index c56fd96199..065b700557 100644 --- a/tools/layer-zero-example/contracts/hts/IHederaTokenService.sol +++ b/tools/layer-zero-example/contracts/hts/IHederaTokenService.sol @@ -158,4 +158,10 @@ interface IHederaTokenService { address recipient, int64 amount ) external returns (int64 responseCode); + + /// Operation to update token keys + /// @param token The token address + /// @param keys The token keys + /// @return responseCode The response code for the status of the request. SUCCESS is 22. + function updateTokenKeys(address token, TokenKey[] memory keys) external returns (int64 responseCode); } diff --git a/tools/layer-zero-example/hardhat.config.js b/tools/layer-zero-example/hardhat.config.js index 8fb78198ab..0c28dbf9ac 100644 --- a/tools/layer-zero-example/hardhat.config.js +++ b/tools/layer-zero-example/hardhat.config.js @@ -71,7 +71,7 @@ task('deploy-whbar', 'Deploy WHBAR') const contract = await contractFactory.deploy(); await contract.deployTransaction.wait(); - console.log(`(${hre.network.name}) WHBAR to ${contract.address}, txHash ${contract.deployTransaction.hash}`); + console.log(`(${hre.network.name}) WHBAR to ${contract.address} txHash ${contract.deployTransaction.hash}`); }); task('deploy-erc20', 'Deploy ERC20 token') @@ -82,7 +82,7 @@ task('deploy-erc20', 'Deploy ERC20 token') const contract = await contractFactory.deploy(taskArgs.mint, taskArgs.decimals); await contract.deployTransaction.wait(); - console.log(`(${hre.network.name}) ERC20 deployed to ${contract.address}, txHash ${contract.deployTransaction.hash}`); + console.log(`(${hre.network.name}) ERC20 deployed to ${contract.address} txHash ${contract.deployTransaction.hash}`); }); task('deploy-erc721', 'Deploy ERC721 token') @@ -91,7 +91,7 @@ task('deploy-erc721', 'Deploy ERC721 token') const contract = await contractFactory.deploy(); await contract.deployTransaction.wait(); - console.log(`(${hre.network.name}) ERC721 deployed to ${contract.address}, txHash ${contract.deployTransaction.hash}`); + console.log(`(${hre.network.name}) ERC721 deployed to ${contract.address} txHash ${contract.deployTransaction.hash}`); }); task('deploy-oapp', 'Deploy OApp contract') @@ -104,7 +104,7 @@ task('deploy-oapp', 'Deploy OApp contract') const contract = await contractFactory.deploy(ENDPOINT_V2, signers[0].address); await contract.deployTransaction.wait(); - console.log(`(${hre.network.name}) ExampleOApp deployed to ${contract.address}, txHash ${contract.deployTransaction.hash}`); + console.log(`(${hre.network.name}) ExampleOApp deployed to ${contract.address} txHash ${contract.deployTransaction.hash}`); }); task('deploy-oft', 'Deploy OFT contract') @@ -119,7 +119,7 @@ task('deploy-oft', 'Deploy OFT contract') const contract = await contractFactory.deploy('T_NAME', 'T_SYMBOL', ENDPOINT_V2, signers[0].address, taskArgs.mint, taskArgs.decimals); await contract.deployTransaction.wait(); - console.log(`(${hre.network.name}) ExampleOFT deployed to ${contract.address}, txHash ${contract.deployTransaction.hash}`); + console.log(`(${hre.network.name}) ExampleOFT deployed to ${contract.address} txHash ${contract.deployTransaction.hash}`); }); task('deploy-hts-connector', 'Deploy HTS connector contract') @@ -135,7 +135,23 @@ task('deploy-hts-connector', 'Deploy HTS connector contract') }); await contract.deployTransaction.wait(); - console.log(`(${hre.network.name}) ExampleHTSConnector deployed to ${contract.address}, txHash ${contract.deployTransaction.hash}`); + console.log(`(${hre.network.name}) ExampleHTSConnector deployed to ${contract.address} txHash ${contract.deployTransaction.hash}`); + }); + +task('deploy-hts-connector-existing-token', 'Deploy HTS connector for existing token contract') + .addParam('token', 'Already existing token address') + .setAction(async (taskArgs, hre) => { + const ethers = hre.ethers; + const signers = await ethers.getSigners(); + const ENDPOINT_V2 = getEndpointAddress(hre.network.name); + + const contractFactory = await ethers.getContractFactory('ExampleHTSConnectorExistingToken'); + const contract = await contractFactory.deploy(taskArgs.token, ENDPOINT_V2, signers[0].address, { + gasLimit: 10_000_000, + }); + await contract.deployTransaction.wait(); + + console.log(`(${hre.network.name}) ExampleHTSConnectorExistingToken deployed to ${contract.address} txHash ${contract.deployTransaction.hash}`); }); task('create-hts-token', 'Create a HTS token') @@ -150,7 +166,7 @@ task('create-hts-token', 'Create a HTS token') }); await contract.deployTransaction.wait(); - console.log(`(${hre.network.name}) Token address ${await contract.htsTokenAddress()}, txHash ${contract.deployTransaction.hash}`); + console.log(`(${hre.network.name}) Token address ${await contract.htsTokenAddress()} contract address ${contract.address} txHash ${contract.deployTransaction.hash}`); }); task('deploy-oft-adapter', 'Deploy OFT adapter contract') @@ -164,7 +180,7 @@ task('deploy-oft-adapter', 'Deploy OFT adapter contract') const contract = await contractFactory.deploy(taskArgs.token, ENDPOINT_V2, signers[0].address); await contract.deployTransaction.wait(); - console.log(`(${hre.network.name}) ExampleOFTAdapter for token ${taskArgs.token} deployed to ${contract.address}, txHash ${contract.deployTransaction.hash}`); + console.log(`(${hre.network.name}) ExampleOFTAdapter for token ${taskArgs.token} deployed to ${contract.address} txHash ${contract.deployTransaction.hash}`); }); task('deploy-onft', 'Deploy OFT contract') @@ -184,7 +200,7 @@ task('deploy-onft', 'Deploy OFT contract') const contract = await contractFactory.deploy('T_NAME', 'T_SYMBOL', ENDPOINT_V2, signers[0].address, tokenId); await contract.deployTransaction.wait(); - console.log(`(${hre.network.name}) ExampleONFT deployed to ${contract.address}, txHash ${contract.deployTransaction.hash}`); + console.log(`(${hre.network.name}) ExampleONFT deployed to ${contract.address} txHash ${contract.deployTransaction.hash}`); }); task('deploy-onft-adapter', 'Deploy OFT contract') @@ -198,7 +214,7 @@ task('deploy-onft-adapter', 'Deploy OFT contract') const contract = await contractFactory.deploy(taskArgs.token, ENDPOINT_V2, signers[0].address); await contract.deployTransaction.wait(); - console.log(`(${hre.network.name}) ExampleONFTAdapter deployed to ${contract.address}, txHash ${contract.deployTransaction.hash}`); + console.log(`(${hre.network.name}) ExampleONFTAdapter deployed to ${contract.address} txHash ${contract.deployTransaction.hash}`); }); task('set-peer', 'Set peer') diff --git a/tools/layer-zero-example/test/htsConnectorExistingTokenTests.js b/tools/layer-zero-example/test/htsConnectorExistingTokenTests.js new file mode 100644 index 0000000000..ecc4091a57 --- /dev/null +++ b/tools/layer-zero-example/test/htsConnectorExistingTokenTests.js @@ -0,0 +1,160 @@ +/*- + * + * Hedera JSON RPC Relay - Hardhat Example + * + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +const hre = require('hardhat'); +const { ethers } = hre; +const { Options, addressToBytes32 } = require('@layerzerolabs/lz-v2-utilities'); +const { expect } = require('chai'); +const CONSTANTS = require('./constants'); + +const { HEDERA_EID, BSC_EID, RECEIVER_ADDRESS } = CONSTANTS; +const amount = '100'; + +describe('HTSConnectorExistingToken', function() { + it('@hedera @update-keys', async () => { + const contract = await ethers.getContractAt('CreateHTS', process.env.HTS_CONNECTOR_CREATE_HTS_CONTRACT); + const updatedKey = [ + false, + process.env.HTS_CONNECTOR_EXISTING_TOKEN_HEDERA_CONTRACT, + '0x', + '0x', + '0x0000000000000000000000000000000000000000' + ]; + const tx = await contract.updateTokenKeysPublic( + [ + [16, updatedKey] + ] + ); + const receipt = await tx.wait(); + + console.log(`(${hre.network.name}) successfully sent to Hedera via tx: ${tx.hash}`); + + expect(receipt.status).to.equal(1); + }); + + it('@hedera @fund', async () => { + const oftHts = await ethers.getContractAt('ExampleHTSConnectorExistingToken', process.env.HTS_CONNECTOR_EXISTING_TOKEN_HEDERA_CONTRACT); + const tokenAddress = await oftHts.htsTokenAddress(); + + const contract = await ethers.getContractAt('ERC20', tokenAddress); + const txTransfer = await contract.transfer(process.env.HTS_CONNECTOR_EXISTING_TOKEN_HEDERA_CONTRACT, '200'); + const receipt = await txTransfer.wait(); + console.log(`(${hre.network.name}) successfully sent to Hedera via tx: ${txTransfer.hash}`); + + expect(receipt.status).to.equal(1); + }); + + it('@hedera @approve oft hts contract', async () => { + const oftHts = await ethers.getContractAt('ExampleHTSConnectorExistingToken', process.env.HTS_CONNECTOR_EXISTING_TOKEN_HEDERA_CONTRACT); + const tokenAddress = await oftHts.htsTokenAddress(); + + const contract = await ethers.getContractAt('ERC20', tokenAddress); + const txApprove = await contract.approve(process.env.HTS_CONNECTOR_EXISTING_TOKEN_HEDERA_CONTRACT, amount); + const receipt = await txApprove.wait(); + console.log(`(${hre.network.name}) successfully sent to Hedera via tx: ${txApprove.hash}`); + + expect(receipt.status).to.equal(1); + }); + + it('@hedera @send to bsc', async () => { + const signers = await ethers.getSigners(); + + const sendParam = { + dstEid: BSC_EID, + to: addressToBytes32(RECEIVER_ADDRESS), + amountLD: amount, + minAmountLD: amount, + extraOptions: Options.newOptions().addExecutorLzReceiveOption(3000000, 0).toBytes(), + composeMsg: ethers.utils.arrayify('0x'), + oftCmd: ethers.utils.arrayify('0x') + }; + + const contract = await ethers.getContractAt('ExampleHTSConnectorExistingToken', process.env.HTS_CONNECTOR_EXISTING_TOKEN_HEDERA_CONTRACT); + const tx = await contract.send(sendParam, { nativeFee: '500000000', lzTokenFee: 0 }, signers[0].address, { + gasLimit: 10_000_000, + value: '5000000000000000000' + }); + + const receipt = await tx.wait(); + if (!receipt.status) { + process.exit(`Execution failed. Tx hash: ${tx.hash}`); + } + + console.log(`(${hre.network.name}) successfully sent to BSC via tx: ${tx.hash}`); + }); + + it('@bsc @send to hedera', async () => { + const signers = await ethers.getSigners(); + + const sendParam = { + dstEid: HEDERA_EID, + to: addressToBytes32(RECEIVER_ADDRESS), + amountLD: amount, + minAmountLD: amount, + extraOptions: Options.newOptions().addExecutorLzReceiveOption(3000000, 0).toBytes(), + composeMsg: ethers.utils.arrayify('0x'), + oftCmd: ethers.utils.arrayify('0x') + }; + + const contract = await ethers.getContractAt('ExampleOFT', process.env.HTS_CONNECTOR_EXISTING_TOKEN_BSC_CONTRACT); + const tx = await contract.send(sendParam, { nativeFee: '1000000000000000', lzTokenFee: 0 }, signers[0].address, { + gasLimit: 1_000_000, + value: '1000000000000000' + }); + + const receipt = await tx.wait(); + if (!receipt.status) { + process.exit(`Execution failed. Tx hash: ${tx.hash}`); + } + + console.log(`(${hre.network.name}) successfully sent to Hedera via tx: ${tx.hash}`); + }); + + it('@hedera @test balance', async () => { + const signers = await ethers.getSigners(); + + const oftHts = await ethers.getContractAt('ExampleHTSConnectorExistingToken', process.env.HTS_CONNECTOR_EXISTING_TOKEN_HEDERA_CONTRACT); + const tokenAddress = await oftHts.htsTokenAddress(); + + const contract = await ethers.getContractAt('ERC20', tokenAddress); + const receiverBalance = await contract.balanceOf(RECEIVER_ADDRESS); + + console.log(`(${hre.network.name}) oft contract balance: ${await contract.balanceOf(process.env.HTS_CONNECTOR_EXISTING_TOKEN_HEDERA_CONTRACT)}`); + console.log(`(${hre.network.name}) signer balance: ${await contract.balanceOf(signers[0].address)}`); + console.log(`(${hre.network.name}) total supply: ${await contract.totalSupply()}`); + console.log(`(${hre.network.name}) receiver balance: ${receiverBalance}`); + + expect(receiverBalance).to.equal(amount); + }); + + it('@bsc @test balance', async () => { + const signers = await ethers.getSigners(); + + const contract = await ethers.getContractAt('ERC20', process.env.HTS_CONNECTOR_EXISTING_TOKEN_BSC_CONTRACT); + const receiverBalance = await contract.balanceOf(RECEIVER_ADDRESS); + + console.log(`(${hre.network.name}) oft contract balance: ${await contract.balanceOf(process.env.HTS_CONNECTOR_EXISTING_TOKEN_BSC_CONTRACT)}`); + console.log(`(${hre.network.name}) signer balance: ${await contract.balanceOf(signers[0].address)}`); + console.log(`(${hre.network.name}) total supply: ${await contract.totalSupply()}`); + console.log(`(${hre.network.name}) receiver balance: ${receiverBalance}`); + + expect(receiverBalance).to.equal(amount); + }); +});