Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 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

Large diffs are not rendered by default.

70 changes: 70 additions & 0 deletions test/src/test/resources/solidity/contracts/ComplexFunctions.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
pragma experimental ABIEncoderV2;

import "./HederaTokenService.sol";
import "./HederaResponseCodes.sol";
import "./ExpiryHelper.sol";
import "./KeyHelper.sol";

contract ComplexFunctions is HederaTokenService, ExpiryHelper, KeyHelper {
function tokenLifecycle(address acc1, address acc2, address treasury) public payable {
IHederaTokenService.TokenKey[] memory keys = new IHederaTokenService.TokenKey[](5);
keys[0] = getSingleKey(KeyType.ADMIN, KeyType.PAUSE, KeyValueType.INHERIT_ACCOUNT_KEY, bytes(""));
keys[1] = getSingleKey(KeyType.KYC, KeyValueType.INHERIT_ACCOUNT_KEY, bytes(""));
keys[2] = getSingleKey(KeyType.FREEZE, KeyValueType.INHERIT_ACCOUNT_KEY, bytes(""));
keys[3] = getSingleKey(KeyType.WIPE, KeyValueType.INHERIT_ACCOUNT_KEY, bytes(""));
keys[4] = getSingleKey(KeyType.SUPPLY, KeyValueType.INHERIT_ACCOUNT_KEY, bytes(""));

IHederaTokenService.Expiry memory expiry = IHederaTokenService.Expiry(0, treasury, 8000000);

IHederaTokenService.HederaToken memory token = IHederaTokenService.HederaToken("TKN", "TK", treasury, "memo", true, 1000000, false, keys, expiry);
(int code, address tokenAddr) = HederaTokenService.createFungibleToken(token, 1000000, 8);
require(code == HederaResponseCodes.SUCCESS, "Token creation failed");

require(HederaTokenService.associateToken(acc1, tokenAddr) == 22, "Token Associate of failed for acc1");
require(HederaTokenService.associateToken(acc2, tokenAddr) == 22, "Token Associate failed for acc2");
require(HederaTokenService.grantTokenKyc(tokenAddr, acc1) == 22, "GrantKyc failed for acc1");
require(HederaTokenService.grantTokenKyc(tokenAddr, acc2) == 22, "GrantKyC failed for acc2");
require(HederaTokenService.transferToken(tokenAddr, treasury, acc1, 100) == 22, "Transfer token failed from treasury to acc1");
require(HederaTokenService.freezeToken(tokenAddr, acc1) == 22, "Freeze token failed for acc1");
require(HederaTokenService.unfreezeToken(tokenAddr, acc1) == 22, "Unfreeze token failed for acc1");
require(HederaTokenService.transferToken(tokenAddr, acc1, acc2, 50) == 22, "Transfer token failed from acc1 to acc2");
require(HederaTokenService.wipeTokenAccount(tokenAddr, acc2, 10) == 22, "Wipe token failed for acc2");
require(HederaTokenService.pauseToken(tokenAddr) == 22, "Pause token failed");
require(HederaTokenService.unpauseToken(tokenAddr) == 22, "Unpause token failed");
}

function nftLifecycle(address acc1, address acc2, address treasury, bytes[] memory metadata) public payable {
IHederaTokenService.TokenKey[] memory keys = new IHederaTokenService.TokenKey[](5);
keys[0] = getSingleKey(KeyType.ADMIN, KeyType.PAUSE, KeyValueType.INHERIT_ACCOUNT_KEY, bytes(""));
keys[1] = getSingleKey(KeyType.KYC, KeyValueType.INHERIT_ACCOUNT_KEY, bytes(""));
keys[2] = getSingleKey(KeyType.FREEZE, KeyValueType.INHERIT_ACCOUNT_KEY, bytes(""));
keys[3] = getSingleKey(KeyType.WIPE, KeyValueType.INHERIT_ACCOUNT_KEY, bytes(""));
keys[4] = getSingleKey(KeyType.SUPPLY, KeyValueType.INHERIT_ACCOUNT_KEY, bytes(""));

IHederaTokenService.HederaToken memory token = IHederaTokenService.HederaToken(
"NFT", "NFT", treasury, "memo", false, 0, false, keys, IHederaTokenService.Expiry(0, treasury, 8000000)
);
(int code, address tokenAddr) = HederaTokenService.createNonFungibleToken(token);
require(code == HederaResponseCodes.SUCCESS, "NFT creation failed");

require(HederaTokenService.associateToken(acc1, tokenAddr) == 22, "Associate failed for acc1");
require(HederaTokenService.associateToken(acc2, tokenAddr) == 22, "Associate failed for acc2");
require(HederaTokenService.grantTokenKyc(tokenAddr, acc1) == 22, "KYC failed for acc1");
require(HederaTokenService.grantTokenKyc(tokenAddr, acc2) == 22, "KYC failed for acc2");

(int mintResponse, , int64[] memory serials) =
HederaTokenService.mintToken(tokenAddr, 0, metadata);

int64 firstSerial = serials[0];

require(HederaTokenService.transferNFT(tokenAddr, treasury, acc1, firstSerial) == 22, "NFT transfer to acc1 failed");
require(HederaTokenService.freezeToken(tokenAddr, acc1) == 22, "Freeze failed for acc1");
require(HederaTokenService.unfreezeToken(tokenAddr, acc1) == 22, "Unfreeze failed for acc1");
require(HederaTokenService.transferNFT(tokenAddr, acc1, acc2, firstSerial) == 22, "NFT transfer to acc2 failed");
require(HederaTokenService.wipeTokenAccountNFT(tokenAddr, acc2, serials) == 22, "Wipe failed for acc2");
require(HederaTokenService.pauseToken(tokenAddr) == 22, "Pause failed");
require(HederaTokenService.unpauseToken(tokenAddr) == 22, "Unpause failed");
}
}
38 changes: 36 additions & 2 deletions tools/k6/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
| ACCOUNT_ADDRESS | | 64 character hex encoded account address without `0x` prefix |
| AMOUNT | | 64 character hex encoded amount without `0x` prefix |
| ASSOCIATED_ACCOUNT | | 64 character hex encoded account evm address without `0x` prefix - associated to TOKEN_ADDRESS and NON_FUNGIBLE_TOKEN_ADDRESS |
| COMPLEX_FUNCTIONS_CONTRACT_ADDRESS | | 40 character hex encoded contract address without `0x` prefix for `ComplexFunctions.sol` |
| DEFAULT_ACCOUNT_ADDRESS | | 40 character hex encoded account address without `0x` prefix |
| DEFAULT_CONTRACT_ADDRESS | | 40 character hex encoded contract address without `0x` prefix for `Parent.sol` |
| ERC_CONTRACT_ADDRESS | | 40 character hex encoded contract address without `0x` prefix for `ErcTestContract.sol` |
Expand All @@ -136,8 +137,9 @@
| PAYER_ACCOUNT | | 40 character hex encoded account address without `0x` prefix - with a lot of balance that can be used for costy operations such as token create |
| PRECOMPILE_CONTRACT | | 40 character hex encoded contract address without `0x` prefix for `PrecompileTestContract.sol` |
| RECEIVER_ADDRESS | | 64 character hex encoded account evm address without `0x` prefix - associated account |
| RUN_ESTIMATE_TESTS | true | If set to true, estimate gas tests will be run. |
| RUN_MODIFICATION_TESTS | true | If set to true, modification tests will be run. |
| RUN_COMPLEX_TESTS | true | If set to false, complex function tests will not run. |
| RUN_ESTIMATE_TESTS | true | If set to false, estimate gas tests will not run. |
| RUN_MODIFICATION_TESTS | true | If set to false, modification tests will not run. |
| RUN_WITH_VARIABLES | true | if set to false, tests will be run with data from modificationFunctions.json |
| SERIAL_NUMBER | | 64 character hex encoded nft serial number without `0x` prefix |
| SPENDER_ADDRESS | | 64 character hex encoded account address without `0x` prefix |
Expand Down Expand Up @@ -226,3 +228,35 @@
```

When it completes, k6 will show a similar summary report. However, there will not be a report file generated.

### Web3 Tests details:

#### Complex functions

Test complex scenarios by using a single smart contract function.

1. Fungible Token Lifecycle

Source: src/web3/test/complex-functions/contractCallComplexFunctionsTokenLifecycle.js.

This test covers the lifecycle of a fungible token, including: token creation, association, grantKYC, transfer, Freeze, Unfreeze, Pause, Unpause, Wipe. The test uses `tokenLifecycle(address firstReceiver, address secondReceiver, address treasury)` function from `ComplexFunctions.sol`.

Test Parameters:

- `COMPLEX_FUNCTIONS_CONTRACT_ADDRESS` - contract address for `ComplexFunctions.sol`
- `RECEIVER_ADDRESS` - First account to be used in the test for the transfer. Not associated account
- `SPENDER_ADDRESS` - Second account to be used in the test for the second transfer. Not associated account
- `PAYER_ACCOUNT` - Account to be used as a treasury of the token and for a payer of the transaction. It should have enough balance to pay for the token creation (at least 9.33 Hbars).

2. Non-Fungible Token Lifecycle

Check notice on line 251 in tools/k6/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tools/k6/README.md#L251

Expected: 1; Actual: 2

Check notice on line 251 in tools/k6/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tools/k6/README.md#L251

Expected: 1; Actual: 2; Style: 1/1/1

Source: src/web3/test/complex-functions/contractCallComplexFunctionsNFTLifecycle.js.

This test covers the lifecycle of a Non-fungible token, including: token creation, association, grantKYC, Mint, transfer, Freeze, Unfreeze, Pause, Unpause, Wipe. The test uses `nftLifecycle(address firstReceiver, address secondReceiver, address treasury, bytes[] memory metadata)` function from `ComplexFunctions.sol`.

Check notice on line 255 in tools/k6/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tools/k6/README.md#L255

Expected: 120; Actual: 318

Test Parameters:

- `COMPLEX_FUNCTIONS_CONTRACT_ADDRESS` - contract address for `ComplexFunctions.sol`
- `RECEIVER_ADDRESS` - First account to be used in the test for the transfer. Not associated account
- `SPENDER_ADDRESS` - Second account to be used in the test for the second transfer. Not associated account
- `PAYER_ACCOUNT` - Account to be used as a treasury of the token and for a payer of the transaction. It should have enough balance to pay for the token creation (at least 9.33 Hbars).
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: Apache-2.0

import {PrecompileModificationTestTemplate} from '../commonPrecompileModificationFunctionsTemplate.js';
import {ContractCallTestScenarioBuilder} from '../common.js';

const contract = __ENV.COMPLEX_FUNCTIONS_CONTRACT_ADDRESS;
const firstReceiver = __ENV.RECEIVER_ADDRESS;
const secondReceiver = __ENV.SPENDER_ADDRESS;
const payer = __ENV.PAYER_ACCOUNT;
const treasury = '0'.repeat(24) + payer; // Pad with 24 zeros to convert payer account from 20 to 32 bytes treasury account
const runMode = __ENV.RUN_WITH_VARIABLES;
const metadata =
'0000000000000000000000000000000000000000000000000000000000000080' +
'0000000000000000000000000000000000000000000000000000000000000001' +
'0000000000000000000000000000000000000000000000000000000000000020' +
'0000000000000000000000000000000000000000000000000000000000000001' +
'0200000000000000000000000000000000000000000000000000000000000000';

/*
This test covers the full lifecycle of a Non-Fungible Token
1. Token Creation
2. Associate token
3. GrantTokenKyc
4. Mint token to treasury
5. Transfer token from treasury to account1
6. Freeze / Unfreeze token
7. Transfer token from account1 to account2
8. Wipe amount 10 token from account2
9. Pause / Unpause token
*/
const selector = '0xb046226e'; //nftLifecycle(address firstReceiver,address secondReceiver ,address treasury, bytes[] memory metadata)
const testName = 'contractCallComplexFunctionsNFTLifecycle';

//If RUN_WITH_VARIABLES=true will run tests with __ENV variables
const {options, run} =
runMode === 'false'
? new PrecompileModificationTestTemplate(testName, false)
: new ContractCallTestScenarioBuilder()
.name(testName)
.selector(selector)
.args([firstReceiver, secondReceiver, treasury, metadata])
.from(payer)
.value(933333333) // Value is needed because the first operation in the contract call is token create

Check warning on line 43 in tools/k6/src/web3/test/complex-functions/contractCallComplexFunctionsNFTLifecycle.js

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tools/k6/src/web3/test/complex-functions/contractCallComplexFunctionsNFTLifecycle.js#L43

The numeric literal '933333333' will have at different value at runtime.
.to(contract)
.block('latest')
.build();

export {options, run};
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: Apache-2.0

import {PrecompileModificationTestTemplate} from '../commonPrecompileModificationFunctionsTemplate.js';
import {ContractCallTestScenarioBuilder} from '../common.js';

const contract = __ENV.COMPLEX_FUNCTIONS_CONTRACT_ADDRESS;
const firstReceiver = __ENV.RECEIVER_ADDRESS;
const secondReceiver = __ENV.SPENDER_ADDRESS;
const payer = __ENV.PAYER_ACCOUNT;
const treasury = '0'.repeat(24) + payer; // Pad with 24 zeros to convert payer account from 20 to 32 bytes treasury account
const runMode = __ENV.RUN_WITH_VARIABLES;

/*
This test covers the full lifecycle of a Fungible Token
1. Token Creation
2. Associate token
3. GrantTokenKyc
4. Transfer token from treasury to account1
5. Freeze / Unfreeze token
6. Transfer token from account1 to account2
7. Wipe amount 10 token from account2
8. Pause / Unpause token
*/
const selector = '0x8acb1e70'; //tokenLifecycle(address firstReceiver,address secondReceiver ,address treasury)
const testName = 'contractCallComplexFunctionsTokenLifecycle';

//If RUN_WITH_VARIABLES=true will run tests with __ENV variables
const {options, run} =
runMode === 'false'
? new PrecompileModificationTestTemplate(testName, false)
: new ContractCallTestScenarioBuilder()
.name(testName)
.selector(selector)
.args([firstReceiver, secondReceiver, treasury])
.from(payer)
.value(933333333) // Value is needed because the first operation in the contract call is token create
.to(contract)
.block('latest')
.build();

export {options, run};
9 changes: 9 additions & 0 deletions tools/k6/src/web3/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ import * as contractCallPrecompileMintToken from './modificationTests/contractCa
import * as contractCallPrecompileNestedAssociate from './modificationTests/contractCallPrecompileNestedAssociate.js';
import * as contractCallPrecompileTransferFungibleToken from './modificationTests/contractCallPrecompileTransferFungibleToken.js';
import * as contractCallRedirectApprove from './modificationTests/contractCallRedirectApprove.js';
import * as contractCallComplexFunctionTokenLifecycle from './complex-functions/contractCallComplexFunctionsTokenLifecycle.js';
import * as contractCallComplexFunctionNFTLifecycle from './complex-functions/contractCallComplexFunctionsNFTLifecycle.js';
import * as rampUp from './rampUp.js';

// add test modules here
Expand Down Expand Up @@ -132,6 +134,13 @@ if (__ENV.RUN_MODIFICATION_TESTS !== 'false') {
});
}

if (__ENV.RUN_COMPLEX_TESTS !== 'false') {
Object.assign(tests, {
contractCallComplexFunctionTokenLifecycle,
contractCallComplexFunctionNFTLifecycle,
});
}

const {funcs, options, scenarioDurationGauge, scenarios} = getSequentialTestScenarios(tests, 'WEB3');

export {funcs, options, scenarioDurationGauge, scenarios};
Loading