Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add acceptance tests for cryptoTransfer function #485

Merged
merged 7 commits into from
Sep 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
212 changes: 208 additions & 4 deletions packages/server/tests/acceptance/htsPrecompile.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ describe('HTS Precompile Acceptance Tests', async function () {
let HTSTokenWithCustomFeesContractAddress;

this.beforeAll(async () => {
accounts[0] = await servicesNode.createAliasAccount(160, relay.provider);
accounts[0] = await servicesNode.createAliasAccount(170, relay.provider);
accounts[1] = await servicesNode.createAliasAccount(30, relay.provider);
accounts[2] = await servicesNode.createAliasAccount(30, relay.provider);

Expand All @@ -76,7 +76,7 @@ describe('HTS Precompile Acceptance Tests', async function () {

async function deployBaseHTSContract() {
const baseHTSFactory = new ethers.ContractFactory(BaseHTSJson.abi, BaseHTSJson.bytecode, accounts[0].wallet);
const baseHTS = await baseHTSFactory.deploy({gasLimit: 10000000});
const baseHTS = await baseHTSFactory.deploy({gasLimit: 15000000});
const { contractAddress } = await baseHTS.deployTransaction.wait();

return contractAddress;
Expand Down Expand Up @@ -424,7 +424,7 @@ describe('HTS Precompile Acceptance Tests', async function () {

before(async function() {
const amount = 5;
await baseHTSContract.transferTokenPublic(accounts[1].wallet.address, HTSTokenContractAddress, amount);
await baseHTSContract.cryptoTransferTokenPublic(accounts[1].wallet.address, HTSTokenContractAddress, amount);
});

it('should revert if attempting to wipe more tokens than the owned amount', async function() {
Expand Down Expand Up @@ -551,7 +551,7 @@ describe('HTS Precompile Acceptance Tests', async function () {
// transfer hts
const amount = 10;
const balanceBefore = await HTSTokenContract.balanceOf(accounts[1].wallet.address);
await baseHTSContract.transferTokenPublic(accounts[1].wallet.address, HTSTokenContractAddress, amount);
await baseHTSContract.cryptoTransferTokenPublic(accounts[1].wallet.address, HTSTokenContractAddress, amount);
const balanceAfter = await HTSTokenContract.balanceOf(accounts[1].wallet.address);

expect(balanceBefore + amount).to.equal(balanceAfter);
Expand Down Expand Up @@ -604,4 +604,208 @@ describe('HTS Precompile Acceptance Tests', async function () {
expect(tokenInfoAfter.deleted).to.equal(true);
});
});

describe('CryptoTransfer Tests', async function() {
let NftSerialNumber;
let NftSerialNumber2;

async function setKyc(tokenAddress) {
const grantKycTx = await baseHTSContractOwner.grantTokenKycPublic(tokenAddress, accounts[1].wallet.address, { gasLimit: 1_000_000 });
expect((await grantKycTx.wait()).events.filter(e => e.event === 'ResponseCode')[0].args.responseCode).to.equal(TX_SUCCESS_CODE);

const grantKycTx2 = await baseHTSContractOwner.grantTokenKycPublic(tokenAddress, accounts[2].wallet.address, { gasLimit: 1_000_000 });
expect((await grantKycTx2.wait()).events.filter(e => e.event === 'ResponseCode')[0].args.responseCode).to.equal(TX_SUCCESS_CODE);
}

it('should be able to transfer fungible tokens', async function() {
await setKyc(HTSTokenContractAddress);

// setup the transfer
const tokenTransferList = [{
token: `${HTSTokenContractAddress}`,
transfers: [
{
accountID: `${accounts[1].wallet.address}`,
amount: 4,
},
{
accountID: `${accounts[2].wallet.address}`,
amount: 6,
},
{
accountID: `${accounts[0].wallet.address}`,
amount: -10,
},
],
nftTransfers: [],
}];
const txXfer = await baseHTSContract.cryptoTransferPublic(tokenTransferList);
expect((await txXfer.wait()).events.filter(e => e.event === 'ResponseCode')[0].args.responseCode).to.equal(TX_SUCCESS_CODE);
});

it('should be able to transfer non-fungible tokens', async function() {
// Mint an NFT
const txMint = await baseHTSContract.mintTokenPublic(NftHTSTokenContractAddress, 0, ['0x03', '0x04'], { gasLimit: 1_000_000 });
expect((await txMint.wait()).events.filter(e => e.event === 'ResponseCode')[0].args.responseCode).to.be.equal(TX_SUCCESS_CODE);
const { serialNumbers } = (await txMint.wait()).events.filter(e => e.event === 'MintedToken')[0].args;
NftSerialNumber = serialNumbers[0];
NftSerialNumber2 = serialNumbers[1];

await setKyc(NftHTSTokenContractAddress);

// setup the transfer
const tokenTransferList = [{
token: `${NftHTSTokenContractAddress}`,
transfers: [],
nftTransfers: [{
senderAccountID: `${accounts[0].wallet.address}`,
receiverAccountID: `${accounts[1].wallet.address}`,
serialNumber: NftSerialNumber.toNumber(),
},
{
senderAccountID: `${accounts[0].wallet.address}`,
receiverAccountID: `${accounts[2].wallet.address}`,
serialNumber: NftSerialNumber2.toNumber(),
}],
}];
const txXfer = await baseHTSContract.cryptoTransferPublic(tokenTransferList);
expect((await txXfer.wait()).events.filter(e => e.event === 'ResponseCode')[0].args.responseCode).to.equal(TX_SUCCESS_CODE);
});

it('should be able to transfer both fungible and non-fungible tokens in single cryptoTransfer', async function() {
// Mint an NFT
const txMint = await baseHTSContract.mintTokenPublic(NftHTSTokenContractAddress, 0, ['0x05'], { gasLimit: 1_000_000 });
expect((await txMint.wait()).events.filter(e => e.event === 'ResponseCode')[0].args.responseCode).to.be.equal(TX_SUCCESS_CODE);
const { serialNumbers } = (await txMint.wait()).events.filter(e => e.event === 'MintedToken')[0].args;
const NftSerialNumber = serialNumbers[0];

// setup the transfer
const tokenTransferList = [{
token: `${NftHTSTokenContractAddress}`,
transfers: [],
nftTransfers: [{
senderAccountID: `${accounts[0].wallet.address}`,
receiverAccountID: `${accounts[1].wallet.address}`,
serialNumber: NftSerialNumber.toNumber(),
}],
},
{
token: `${HTSTokenContractAddress}`,
transfers: [
{
accountID: `${accounts[1].wallet.address}`,
amount: 10,
},
{
accountID: `${accounts[0].wallet.address}`,
amount: -10,
},
],
nftTransfers: [],
}];
const txXfer = await baseHTSContract.cryptoTransferPublic(tokenTransferList);
expect((await txXfer.wait()).events.filter(e => e.event === 'ResponseCode')[0].args.responseCode).to.equal(TX_SUCCESS_CODE);
});

it('should fail to swap approved fungible tokens', async function() {
const txApproval1 = await baseHTSContract.setApprovalForAllPublic(NftHTSTokenContractAddress, accounts[1].wallet.address, true, { gasLimit: 1_000_000 });
expect((await txApproval1.wait()).events.filter(e => e.event === 'ResponseCode')[0].args.responseCode).to.equal(TX_SUCCESS_CODE);

const txApproval2 = await baseHTSContract.setApprovalForAllPublic(NftHTSTokenContractAddress, accounts[2].wallet.address, true, { gasLimit: 1_000_000 });
expect((await txApproval2.wait()).events.filter(e => e.event === 'ResponseCode')[0].args.responseCode).to.equal(TX_SUCCESS_CODE);

// setup the transfer
const tokenTransferList = [{
token: `${HTSTokenContractAddress}`,
transfers: [
{
accountID: `${accounts[1].wallet.address}`,
amount: 2,
},
{
accountID: `${accounts[2].wallet.address}`,
amount: -2,
},
{
accountID: `${accounts[1].wallet.address}`,
amount: -2,
},
{
accountID: `${accounts[2].wallet.address}`,
amount: 2,
},
],
nftTransfers: [],
}];

try{
const txXfer = await baseHTSContract.cryptoTransferPublic(tokenTransferList);
expect((await txXfer.wait()).events.filter(e => e.event === 'ResponseCode')[0].args.responseCode).to.equal(TX_SUCCESS_CODE);
} catch (error: any) {
expect(error.code).to.equal("CALL_EXCEPTION");
expect(error.reason).to.equal("transaction failed");
}
});

it('should fail to swap approved non-fungible tokens', async function() {
const txApprove1 = await baseHTSContract.setApprovalForAllPublic(NftHTSTokenContractAddress, accounts[1].wallet.address, true, { gasLimit: 1_000_000 });
expect((await txApprove1.wait()).events.filter(e => e.event === 'ResponseCode')[0].args.responseCode).to.equal(TX_SUCCESS_CODE);

const txApprove2 = await baseHTSContract.setApprovalForAllPublic(NftHTSTokenContractAddress, accounts[2].wallet.address, true, { gasLimit: 1_000_000 });
expect((await txApprove2.wait()).events.filter(e => e.event === 'ResponseCode')[0].args.responseCode).to.equal(TX_SUCCESS_CODE);

const tokenTransferList = [{
token: `${NftHTSTokenContractAddress}`,
transfers: [],
nftTransfers: [{
senderAccountID: `${accounts[1].wallet.address}`,
receiverAccountID: `${accounts[2].wallet.address}`,
serialNumber: NftSerialNumber.toNumber(),
},
{
senderAccountID: `${accounts[2].wallet.address}`,
receiverAccountID: `${accounts[1].wallet.address}`,
serialNumber: NftSerialNumber2.toNumber(),
}],
}];

try{
const txXfer = await baseHTSContract.cryptoTransferPublic(tokenTransferList);
expect((await txXfer.wait()).events.filter(e => e.event === 'ResponseCode')[0].args.responseCode).to.equal(TX_SUCCESS_CODE);
} catch (error: any) {
expect(error.code).to.equal("CALL_EXCEPTION");
expect(error.reason).to.equal("transaction failed");
}
});

it('should fail to transfer fungible and non-fungible tokens in a single tokenTransferList', async function() {
// setup the transfer
const xferAmount = 10;
const tokenTransferList = [{
token: `${NftHTSTokenContractAddress}`,
transfers: [
{
accountID: `${accounts[1].wallet.address}`,
amount: `${xferAmount}`,
},
{
accountID: `${accounts[0].wallet.address}`,
amount: `-${xferAmount}`,
},
],
nftTransfers: [{
senderAccountID: `${accounts[0].wallet.address}`,
receiverAccountID: `${accounts[1].wallet.address}`,
serialNumber: NftSerialNumber.toNumber(),
}],
}];
try {
const txXfer = await baseHTSContract.cryptoTransferPublic(tokenTransferList);
const response = (await txXfer.wait()).events.filter(e => e.event === 'ResponseCode')[0].args.responseCode;
} catch (error: any) {
expect(error.code).to.equal("CALL_EXCEPTION");
expect(error.reason).to.equal("transaction failed");
}
});
});
});
6 changes: 3 additions & 3 deletions packages/server/tests/acceptance/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,9 @@ describe('RPC Server Acceptance Tests', function () {

function runLocalHederaNetwork() {
// set env variables for docker images until local-node is updated
process.env['NETWORK_NODE_IMAGE_TAG'] = '0.29.0-alpha.1';
process.env['HAVEGED_IMAGE_TAG'] = '0.29.0-alpha.1';
process.env['MIRROR_IMAGE_TAG'] = '0.62.0-rc1';
process.env['NETWORK_NODE_IMAGE_TAG'] = '0.30.0-alpha.0';
process.env['HAVEGED_IMAGE_TAG'] = '0.30.0-alpha.0';
process.env['MIRROR_IMAGE_TAG'] = '0.64.0-beta2';
logger.trace(`Docker container versions, services: ${process.env['NETWORK_NODE_IMAGE_TAG']}, mirror: ${process.env['MIRROR_IMAGE_TAG']}`);

// start local-node
Expand Down
107 changes: 103 additions & 4 deletions packages/server/tests/contracts/BaseHTS.json

Large diffs are not rendered by default.

18 changes: 17 additions & 1 deletion packages/server/tests/contracts/BaseHTS.sol
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ contract BaseHTS is FeeHelper {
emit AllowanceValue(amount);
}

function transferTokenPublic(address account, address token, int64 amount) public returns (int responseCode) {
function cryptoTransferTokenPublic(address account, address token, int64 amount) public returns (int responseCode) {
IHederaTokenService.NftTransfer[] memory nftTransfers = new IHederaTokenService.NftTransfer[](0);

IHederaTokenService.AccountAmount memory accountAmountNegative =
Expand All @@ -140,6 +140,22 @@ contract BaseHTS is FeeHelper {
}
}

function transferTokenPublic(address token, address sender, address receiver, int64 amount) public returns (int responseCode) {
responseCode = HederaTokenService.transferToken(token, sender, receiver, amount);
emit ResponseCode(responseCode);
if (responseCode != HederaResponseCodes.SUCCESS) {
revert();
}
}

function cryptoTransferPublic(IHederaTokenService.TokenTransferList[] calldata tokenTransferList) public returns (int responseCode) {
responseCode = HederaTokenService.cryptoTransfer(tokenTransferList);
emit ResponseCode(responseCode);
if (responseCode != HederaResponseCodes.SUCCESS) {
revert();
}
}

function getApprovedPublic(address token, uint256 serialNumber) public returns (int responseCode, address approved)
{
(responseCode, approved) = HederaTokenService.getApproved(token, serialNumber);
Expand Down