Skip to content

Commit

Permalink
chore: add hts adapter
Browse files Browse the repository at this point in the history
Signed-off-by: nikolay <[email protected]>
  • Loading branch information
natanasow committed Dec 19, 2024
1 parent 45a2259 commit 447a0ff
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 9 deletions.
28 changes: 26 additions & 2 deletions tools/layer-zero-example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ npx hardhat test --grep "OFTTests @bsc @test" --network bsc_testnet

### OFT Adapter

npx hardhat deploy-erc20 --network hedera_testnet
npx hardhat deploy-erc20 --network bsc_testnet
npx hardhat deploy-erc20 --decimals 18 --mint 10000000000000000000 --network hedera_testnet
npx hardhat deploy-erc20 --decimals 18 --mint 10000000000000000000 --network bsc_testnet

npx hardhat deploy-oft-adapter --token <erc20_hedera_address> --network hedera_testnet
npx hardhat deploy-oft-adapter --token <erc20_bsc_address> --network bsc_testnet
Expand Down Expand Up @@ -125,3 +125,27 @@ wait a couple minutes, the LZ progress can be tracked on https://testnet.layerze

npx hardhat test --grep "HTSConnectorTests @hedera @test" --network hedera_testnet
npx hardhat test --grep "HTSConnectorTests @bsc @test" --network bsc_testnet

### HTS Adapter

npx hardhat create-hts-token --network hedera_testnet
npx hardhat deploy-erc20 --decimals 8 --mint 1000 --network bsc_testnet

npx hardhat deploy-oft-adapter --token <erc20_hedera_address> --network hedera_testnet
npx hardhat deploy-oft-adapter --token <erc20_bsc_address> --network bsc_testnet

npx hardhat set-peer --source <hedera_oft_adapter_address> --target <bsc_oft_adapter_address> --network hedera_testnet
npx hardhat set-peer --source <bsc_oft_adapter_address> --target <hedera_oft_adapter_address> --network bsc_testnet

fill the .env

npx hardhat test --grep "HTSAdapterTests @hedera @fund-and-approve" --network hedera_testnet
npx hardhat test --grep "HTSAdapterTests @bsc @fund-and-approve" --network bsc_testnet

npx hardhat test --grep "HTSAdapterTests @hedera @send" --network hedera_testnet
npx hardhat test --grep "HTSAdapterTests @bsc @send" --network bsc_testnet

wait a couple minutes, the LZ progress can be tracked on https://testnet.layerzeroscan.com/tx/<tx_hash>

npx hardhat test --grep "HTSAdapterTests @hedera @test" --network hedera_testnet
npx hardhat test --grep "HTSAdapterTests @bsc @test" --network bsc_testnet
42 changes: 42 additions & 0 deletions tools/layer-zero-example/contracts/CreateHTS.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/access/Ownable.sol";
import "./hts/HederaTokenService.sol";
import "./hts/IHederaTokenService.sol";
import "./hts/KeyHelper.sol";
import "./hts/ExpiryHelper.sol";

contract CreateHTS is Ownable, KeyHelper, ExpiryHelper, HederaTokenService {
address public htsTokenAddress;

constructor(string memory _name, string memory _symbol, address _delegate) payable Ownable(_delegate) {
IHederaTokenService.TokenKey[] memory keys = new IHederaTokenService.TokenKey[](2);
keys[0] = getSingleKey(
KeyType.ADMIN,
KeyType.PAUSE,
KeyValueType.INHERIT_ACCOUNT_KEY,
bytes("")
);
keys[1] = getSingleKey(
KeyType.SUPPLY,
KeyValueType.INHERIT_ACCOUNT_KEY,
bytes("")
);

IHederaTokenService.Expiry memory expiry = IHederaTokenService.Expiry(0, address(this), 8000000);
IHederaTokenService.HederaToken memory token = IHederaTokenService.HederaToken(
_name, _symbol, address(this), "memo", true, 5000, false, keys, expiry
);

(int responseCode, address tokenAddress) = HederaTokenService.createFungibleToken(
token, 1000, int32(int256(uint256(8)))
);
require(responseCode == HederaResponseCodes.SUCCESS, "Failed to create HTS token");

int256 transferResponse = HederaTokenService.transferToken(tokenAddress, address(this), msg.sender, 1000);
require(transferResponse == HederaResponseCodes.SUCCESS, "HTS: Transfer failed");

htsTokenAddress = tokenAddress;
}
}
11 changes: 9 additions & 2 deletions tools/layer-zero-example/contracts/ERC20Mock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ pragma solidity ^0.8.22;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract ERC20Mock is ERC20 {
constructor() ERC20("ERC20Mock", "E20M") {
_mint(msg.sender, 10000000000000000000);
uint8 decimalsArg = 18;

constructor(uint256 _initialMint, uint8 _decimals) ERC20("ERC20Mock", "E20M") {
_mint(msg.sender, _initialMint);
decimalsArg = _decimals;
}

function decimals() public view override returns (uint8) {
return decimalsArg;
}
}
27 changes: 22 additions & 5 deletions tools/layer-zero-example/hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,11 @@ const getEndpointAddress = (network) => {
}

task('deploy-erc20', "Deploy ERC20 token")
.addParam('mint', 'Initial mint')
.addParam('decimals', 'Decimals')
.setAction(async (taskArgs, hre) => {
const contractFactory = await ethers.getContractFactory('ERC20Mock');
const contract = await contractFactory.deploy();
const contract = await contractFactory.deploy(taskArgs.mint, taskArgs.decimals);
await contract.deployTransaction.wait();

console.log(`(${hre.network.name}) ERC20 deployed to: ` + contract.address);
Expand Down Expand Up @@ -100,7 +102,7 @@ task("deploy-oft", "Deploy OFT contract")
.setAction(async (taskArgs, hre) => {
const ethers = hre.ethers;
const signers = await ethers.getSigners();
const ENDPOINT_V2 = getEndpointAddress(hre.network.name)
const ENDPOINT_V2 = getEndpointAddress(hre.network.name);

const contractFactory = await ethers.getContractFactory('ExampleOFT');
const contract = await contractFactory.deploy('T_NAME', 'T_SYMBOL', ENDPOINT_V2, signers[0].address, taskArgs.mint, taskArgs.decimals);
Expand All @@ -113,7 +115,7 @@ task("deploy-hts-connector", "Deploy HTS connector contract")
.setAction(async (taskArgs, hre) => {
const ethers = hre.ethers;
const signers = await ethers.getSigners();
const ENDPOINT_V2 = getEndpointAddress(hre.network.name)
const ENDPOINT_V2 = getEndpointAddress(hre.network.name);

const contractFactory = await ethers.getContractFactory('ExampleHTSConnector');
const contract = await contractFactory.deploy('T_NAME', 'T_SYMBOL', ENDPOINT_V2, signers[0].address, {
Expand All @@ -125,12 +127,27 @@ task("deploy-hts-connector", "Deploy HTS connector contract")
console.log(`(${hre.network.name}) ExampleHTSConnector deployed to: ` + contract.address);
});

task("create-hts-token", "Create a HTS token")
.setAction(async (taskArgs, hre) => {
const ethers = hre.ethers;
const signers = await ethers.getSigners();

const contractFactory = await ethers.getContractFactory('CreateHTS');
const contract = await contractFactory.deploy('T_NAME', 'T_SYMBOL', signers[0].address, {
gasLimit: 10_000_000,
value: '30000000000000000000' // 30 hbars
});
await contract.deployTransaction.wait();

console.log(`(${hre.network.name}) Token address: ` + await contract.htsTokenAddress());
});

task("deploy-oft-adapter", "Deploy OFT adapter contract")
.addParam('token', 'Token address')
.setAction(async (taskArgs, hre) => {
const ethers = hre.ethers;
const signers = await ethers.getSigners();
const ENDPOINT_V2 = getEndpointAddress(hre.network.name)
const ENDPOINT_V2 = getEndpointAddress(hre.network.name);

const contractFactory = await ethers.getContractFactory('ExampleOFTAdapter');
const contract = await contractFactory.deploy(taskArgs.token, ENDPOINT_V2, signers[0].address);
Expand Down Expand Up @@ -164,7 +181,7 @@ task("deploy-onft-adapter", "Deploy OFT contract")
.setAction(async (taskArgs, hre) => {
const ethers = hre.ethers;
const signers = await ethers.getSigners();
const ENDPOINT_V2 = getEndpointAddress(hre.network.name)
const ENDPOINT_V2 = getEndpointAddress(hre.network.name);

const contractFactory = await ethers.getContractFactory('ExampleONFTAdapter');
const contract = await contractFactory.deploy(taskArgs.token, ENDPOINT_V2, signers[0].address);
Expand Down
139 changes: 139 additions & 0 deletions tools/layer-zero-example/test/htsAdapterTests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*-
*
* 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 HEDERA_EID = 40285;
const BSC_EID = 40102;
const receiverAddress = '0xF51c7a9407217911d74e91642dbC58F18E51Deac';
const amount = '100';

describe('HTSAdapterTests', function() {
it('@hedera @fund-and-approve transfer to adapter', async () => {
const contractERC20 = await ethers.getContractAt('ERC20Mock', process.env.HTS_ADAPTER_HTS_HEDERA_CONTRACT);
const transferTx = await contractERC20.transfer(process.env.HTS_ADAPTER_HEDERA_CONTRACT, amount);
const receipt = await transferTx.wait();
expect(!!receipt.status).to.be.true;
});

it('@bsc @fund-and-approve transfer to adapter', async () => {
const contractERC20 = await ethers.getContractAt('ERC20Mock', process.env.HTS_ADAPTER_ERC20_BSC_CONTRACT);
const transferTx = await contractERC20.transfer(process.env.HTS_ADAPTER_BSC_CONTRACT, amount);
const receipt = await transferTx.wait();
expect(!!receipt.status).to.be.true;
});

it('@hedera @fund-and-approve adapter approval', async () => {
const contractERC20 = await ethers.getContractAt('ERC20Mock', process.env.HTS_ADAPTER_HTS_HEDERA_CONTRACT);
const approveTx = await contractERC20.approve(process.env.HTS_ADAPTER_HEDERA_CONTRACT, amount);
const receipt = await approveTx.wait();
expect(!!receipt.status).to.be.true;
});

it('@bsc @fund-and-approve adapter approval', async () => {
const contractERC20 = await ethers.getContractAt('ERC20Mock', process.env.HTS_ADAPTER_ERC20_BSC_CONTRACT);
const approveTx = await contractERC20.approve(process.env.HTS_ADAPTER_BSC_CONTRACT, amount);
const receipt = await approveTx.wait();
expect(!!receipt.status).to.be.true;
});

it('@hedera @send to bsc', async () => {
const signers = await ethers.getSigners();

const sendParam = {
dstEid: BSC_EID,
to: addressToBytes32(receiverAddress),
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('ExampleOFTAdapter', process.env.HTS_ADAPTER_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(receiverAddress),
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('ExampleOFTAdapter', process.env.HTS_ADAPTER_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 contractERC20 = await ethers.getContractAt('ERC20Mock', process.env.HTS_ADAPTER_HTS_HEDERA_CONTRACT);
const receiverBalance = await contractERC20.balanceOf(receiverAddress);

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)}`);
console.log(`(${hre.network.name}) receiver balance: ${receiverBalance}`);

expect(receiverBalance).to.equal(amount);
});

it('@bsc @test balance', async () => {
const signers = await ethers.getSigners();

const contractERC20 = await ethers.getContractAt('ERC20Mock', process.env.HTS_ADAPTER_ERC20_BSC_CONTRACT);
const receiverBalance = await contractERC20.balanceOf(receiverAddress);

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)}`);
console.log(`(${hre.network.name}) receiver balance: ${receiverBalance}`);

expect(receiverBalance).to.equal(amount);
});
});

0 comments on commit 447a0ff

Please sign in to comment.