Skip to content

Commit a66f3df

Browse files
authored
Add acceptance test support for hts precompile (#424)
* Add base contracts Signed-off-by: nikolay <[email protected]> * Add tests Signed-off-by: nikolay <[email protected]> * Run all tests Signed-off-by: nikolay <[email protected]> * Remove unused code Signed-off-by: nikolay <[email protected]> * Edit initial balance Signed-off-by: nikolay <[email protected]> * Reduce initial hbars balance Signed-off-by: nikolay <[email protected]> * Edit initial balance Signed-off-by: nikolay <[email protected]> * Edit tests Signed-off-by: nikolay <[email protected]> * Edit tests Signed-off-by: nikolay <[email protected]> * Edit tests Signed-off-by: nikolay <[email protected]> Signed-off-by: nikolay <[email protected]>
1 parent 76b75e8 commit a66f3df

13 files changed

+4481
-0
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*-
2+
*
3+
* Hedera JSON RPC Relay
4+
*
5+
* Copyright (C) 2022 Hedera Hashgraph, LLC
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*
19+
*/
20+
21+
// external resources
22+
import { solidity } from 'ethereum-waffle';
23+
import chai, { expect } from 'chai';
24+
25+
chai.use(solidity);
26+
27+
import { AliasAccount } from '../clients/servicesClient';
28+
import { ethers, BigNumber } from 'ethers';
29+
import ERC20MockJson from '../contracts/ERC20Mock.json';
30+
import BaseHTSJson from '../contracts/BaseHTS.json';
31+
32+
33+
describe('HTS Precompile Acceptance Tests', async function() {
34+
this.timeout(240 * 1000); // 240 seconds
35+
const { servicesNode, relay } = global;
36+
37+
const accounts: AliasAccount[] = [];
38+
let baseHTSContract;
39+
let HTSTokenContract;
40+
41+
before(async () => {
42+
accounts[0] = await servicesNode.createAliasAccount(30, relay.provider);
43+
accounts[1] = await servicesNode.createAliasAccount(30, relay.provider);
44+
accounts[2] = await servicesNode.createAliasAccount(30, relay.provider);
45+
46+
baseHTSContract = await deployBaseHTSContract();
47+
HTSTokenContract = await createHTSToken();
48+
});
49+
50+
async function deployBaseHTSContract() {
51+
const baseHTSFactory = new ethers.ContractFactory(BaseHTSJson.abi, BaseHTSJson.bytecode, accounts[0].wallet);
52+
const baseHTS = await baseHTSFactory.deploy();
53+
const { contractAddress } = await baseHTS.deployTransaction.wait();
54+
55+
return new ethers.Contract(contractAddress, BaseHTSJson.abi, accounts[0].wallet);
56+
}
57+
58+
async function createHTSToken() {
59+
const tx = await baseHTSContract.createToken(accounts[0].wallet.address, {
60+
value: ethers.BigNumber.from('20000000000000000000'),
61+
gasLimit: 1000000
62+
});
63+
const { tokenAddress } = (await tx.wait()).events.filter(e => e.event = 'CreatedToken')[0].args;
64+
65+
return new ethers.Contract(tokenAddress, ERC20MockJson.abi, accounts[0].wallet);
66+
}
67+
68+
it('should associate to a token', async function() {
69+
const baseHTSContractReceiverWalletFirst = new ethers.Contract(baseHTSContract.address, BaseHTSJson.abi, accounts[1].wallet);
70+
expect(baseHTSContractReceiverWalletFirst.associateTokenTo(accounts[1].wallet.address, HTSTokenContract.address, { gasLimit: 10000000 }))
71+
.to.not.be.reverted;
72+
73+
const baseHTSContractReceiverWalletSecond = new ethers.Contract(baseHTSContract.address, BaseHTSJson.abi, accounts[2].wallet);
74+
expect(baseHTSContractReceiverWalletSecond.associateTokenTo(accounts[2].wallet.address, HTSTokenContract.address, { gasLimit: 10000000 }))
75+
.to.not.be.reverted;
76+
});
77+
78+
it('should check initial balances', async function() {
79+
expect(await HTSTokenContract.balanceOf(accounts[0].wallet.address)).to.equal(1000);
80+
expect(await HTSTokenContract.balanceOf(accounts[1].wallet.address)).to.equal(0);
81+
expect(await HTSTokenContract.balanceOf(accounts[2].wallet.address)).to.equal(0);
82+
});
83+
84+
it('should be able to transfer hts tokens between accounts', async function() {
85+
const amount = 10;
86+
const balanceBefore = await HTSTokenContract.balanceOf(accounts[1].wallet.address);
87+
88+
await baseHTSContract.transferTokenTo(accounts[1].wallet.address, HTSTokenContract.address, amount);
89+
90+
const balanceAfter = await HTSTokenContract.balanceOf(accounts[1].wallet.address);
91+
expect(balanceBefore + amount).to.equal(balanceAfter);
92+
});
93+
});

packages/server/tests/contracts/BaseHTS.json

Lines changed: 128 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity >=0.5.0 <0.9.0;
3+
pragma experimental ABIEncoderV2;
4+
5+
import "./FeeHelper.sol";
6+
7+
contract BaseHTS is FeeHelper {
8+
9+
string name = "tokenName";
10+
string symbol = "tokenSymbol";
11+
string memo = "memo";
12+
uint initialTotalSupply = 1000;
13+
uint32 maxSupply = 1000;
14+
uint decimals = 8;
15+
16+
event CreatedToken(address tokenAddress);
17+
18+
function createToken(
19+
address treasury
20+
) public payable {
21+
IHederaTokenService.TokenKey[] memory keys = new IHederaTokenService.TokenKey[](1);
22+
keys[0] = getSingleKey(0, 0, 1, bytes(""));
23+
24+
IHederaTokenService.Expiry memory expiry = IHederaTokenService.Expiry(
25+
0, treasury, 8000000
26+
);
27+
28+
IHederaTokenService.HederaToken memory token = IHederaTokenService.HederaToken(
29+
name, symbol, treasury, memo, true, maxSupply, false, keys, expiry
30+
);
31+
32+
(int responseCode, address tokenAddress) =
33+
HederaTokenService.createFungibleToken(token, initialTotalSupply, decimals);
34+
35+
if (responseCode != HederaResponseCodes.SUCCESS) {
36+
revert ();
37+
}
38+
39+
emit CreatedToken(tokenAddress);
40+
}
41+
42+
function associateTokenTo(address account, address token) public returns (int responseCode) {
43+
responseCode = HederaTokenService.associateToken(account, token);
44+
if (responseCode != HederaResponseCodes.SUCCESS) {
45+
revert ();
46+
}
47+
}
48+
49+
function transferTokenTo(address account, address token, int64 amount) public returns (int responseCode) {
50+
IHederaTokenService.NftTransfer[] memory nftTransfers = new IHederaTokenService.NftTransfer[](0);
51+
52+
IHederaTokenService.AccountAmount memory accountAmountNegative =
53+
IHederaTokenService.AccountAmount(msg.sender, - amount);
54+
IHederaTokenService.AccountAmount memory accountAmountPositive =
55+
IHederaTokenService.AccountAmount(account, amount);
56+
IHederaTokenService.AccountAmount[] memory transfers = new IHederaTokenService.AccountAmount[](2);
57+
transfers[0] = accountAmountNegative;
58+
transfers[1] = accountAmountPositive;
59+
60+
IHederaTokenService.TokenTransferList memory tokenTransfer =
61+
IHederaTokenService.TokenTransferList(token, transfers, nftTransfers);
62+
IHederaTokenService.TokenTransferList[] memory tokenTransferList = new IHederaTokenService.TokenTransferList[](1);
63+
tokenTransferList[0] = tokenTransfer;
64+
65+
responseCode = HederaTokenService.cryptoTransfer(tokenTransferList);
66+
if (responseCode != HederaResponseCodes.SUCCESS) {
67+
revert();
68+
}
69+
}
70+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"_format": "hh-sol-artifact-1",
3+
"contractName": "FeeHelper",
4+
"sourceName": "contracts/FeeHelper.sol",
5+
"abi": [
6+
{
7+
"inputs": [
8+
{
9+
"internalType": "address",
10+
"name": "token",
11+
"type": "address"
12+
}
13+
],
14+
"name": "pauseToken",
15+
"outputs": [
16+
{
17+
"internalType": "int256",
18+
"name": "responseCode",
19+
"type": "int256"
20+
}
21+
],
22+
"stateMutability": "nonpayable",
23+
"type": "function"
24+
},
25+
{
26+
"inputs": [
27+
{
28+
"internalType": "address",
29+
"name": "token",
30+
"type": "address"
31+
}
32+
],
33+
"name": "unpauseToken",
34+
"outputs": [
35+
{
36+
"internalType": "int256",
37+
"name": "responseCode",
38+
"type": "int256"
39+
}
40+
],
41+
"stateMutability": "nonpayable",
42+
"type": "function"
43+
}
44+
],
45+
"bytecode": "0x",
46+
"deployedBytecode": "0x",
47+
"linkReferences": {},
48+
"deployedLinkReferences": {}
49+
}

0 commit comments

Comments
 (0)