Skip to content

Commit

Permalink
test: add unit tests for setLedgerId and fix expiry.second (#238)
Browse files Browse the repository at this point in the history
Signed-off-by: Luis Mastrangelo <[email protected]>
  • Loading branch information
acuarica authored Feb 13, 2025
1 parent 30e9f8d commit f4950de
Show file tree
Hide file tree
Showing 10 changed files with 77 additions and 42 deletions.
2 changes: 1 addition & 1 deletion contracts/HtsSystemContractJson.sol
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ contract HtsSystemContractJson is HtsSystemContract {
token.tokenSupplyType = keccak256(bytes(vm.parseJsonString(json, ".supply_type"))) == keccak256(bytes("FINITE"));
token.maxSupply = int64(vm.parseInt(vm.parseJsonString(json, ".max_supply")));
token.freezeDefault = vm.parseJsonBool(json, ".freeze_default");
token.expiry.second = abi.decode(vm.parseJson(json, ".expiry_timestamp"), (int64));
token.expiry.second = abi.decode(vm.parseJson(json, ".expiry_timestamp"), (int64)) / 1_000_000_000;

try vm.parseJsonString(json, ".auto_renew_account") returns (string memory autoRenewAccount) {
if (keccak256(bytes(autoRenewAccount)) != keccak256(bytes("null"))) {
Expand Down
4 changes: 3 additions & 1 deletion src/forwarder/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,20 @@ const debug = require('util').debuglog('hedera-forking-rpc');
*
* @param {string} forkUrl the URL to forward calls to.
* @param {string} mirrorNodeUrl used to fetch Hedera entity data.
* @param {number=} chainId the `chainId` of the network it is forwarding to, if any.
* @param {number=} workerPort a TCP port where the JSON-RPC Forwarder will listen. If not provided, a default value will be used.
* @param {string[]=} localAddresses List of local addresses used by the local Network when forking.
* @returns {Promise<Worker & {host: string}>} the running worker together with `host` the forwarder is listening on.
*/
function jsonRPCForwarder(forkUrl, mirrorNodeUrl, workerPort, localAddresses) {
function jsonRPCForwarder(forkUrl, mirrorNodeUrl, chainId, workerPort, localAddresses) {
return new Promise(resolve => {
const scriptPath = path.resolve(__dirname, 'json-rpc-forwarder');
debug(`Creating JSON-RPC Forwarder server from \`${scriptPath}\``);
const worker = new Worker(scriptPath, {
workerData: {
url: forkUrl,
mirrorNodeUrl,
chainId,
workerPort,
localAddresses,
},
Expand Down
4 changes: 2 additions & 2 deletions src/forwarder/json-rpc-forwarder.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ const {
} = require('..');

/** @type {Partial<import('hardhat/types').HardhatNetworkForkingConfig>} */
const { url: forkUrl, mirrorNodeUrl, workerPort, localAddresses = [] } = workerData;
const { url: forkUrl, mirrorNodeUrl, chainId, workerPort, localAddresses = [] } = workerData;

setLedgerId(workerData.chainId);
setLedgerId(chainId);

assert(mirrorNodeUrl !== undefined, 'json-rpc-forwarder: Missing Mirror Node URL');

Expand Down
30 changes: 15 additions & 15 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,21 @@ export const LONG_ZERO_PREFIX: string;
*/
export const localTokens: string[];

/**
* Sets the `ledgerId` used when retrieving fungible and non-fungible tokens.
*
* The `ledgerId` depends on the `chainId` of the remote network it is forking from.
*
* This mapping is as follows:
* - 295: 0x00 (mainnet)
* - 296: 0x01 (testnet)
* - 297: 0x02 (previewnet)
* - 298: 0x03 (local node)
*
* @param chainId Chain id used to determine the corresponding ledger id.
*/
export function setLedgerId(chainId?: number);

/**
* Returns the token proxy contract bytecode for the given `address`.
* Based on the proxy contract defined by [HIP-719](https://hips.hedera.com/hip/hip-719).
Expand Down Expand Up @@ -223,18 +238,3 @@ export function getHtsStorageAt(
blockNumber: number,
mirrorNodeClient: IMirrorNodeClient
): Promise<string | null>;

/**
* Sets the `ledgerId` used when retrieving fungible and non-fungible tokens.
*
* The `ledgerId` depends on the `chainId` of the remote network it is forking from.
*
* This mapping is as follows:
* - 295: 0x00 (mainnet)
* - 296: 0x01 (testnet)
* - 297: 0x02 (previewnet)
* - 298: 0x03 (local node)
*
* @param chainId Chain id used to determine the corresponding ledger id.
*/
export function setLedgerId(chainId: number);
10 changes: 8 additions & 2 deletions src/plugin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,15 @@ extendEnvironment(hre => {
if ('forking' in hre.network.config) {
const forking = hre.network.config.forking;
if (forking.chainId !== undefined) {
const { url, mirrorNodeUrl, workerPort, localAddresses } = forking;
const { url, mirrorNodeUrl, chainId, workerPort, localAddresses } = forking;
assert(mirrorNodeUrl !== undefined);
hre.jsonRPCForwarder = jsonRPCForwarder(url, mirrorNodeUrl, workerPort, localAddresses);
hre.jsonRPCForwarder = jsonRPCForwarder(
url,
mirrorNodeUrl,
chainId,
workerPort,
localAddresses
);
forking.url = `http://127.0.0.1:${forking.workerPort}`;
}
}
Expand Down
16 changes: 8 additions & 8 deletions src/slotmap.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ const {
storageLayout: { storage, types },
} = require('../out/HtsSystemContract.sol/HtsSystemContract.json');

/**
* The `ledgerId` used when retrieving fungible and non-fungible tokens.
*/
let ledgerId = '0x00';

/**
* Sets the `ledgerId` used when retrieving fungible and non-fungible tokens.
* The `ledgerId` depends on the `chainId` of the remote network it is forking from.
*
* @param {number} chainId
* @param {number=} chainId
*/
function setLedgerId(chainId) {
const chainIdToLedgerIdMap = { 295: '0x00', 296: '0x01', 297: '0x02', 298: '0x03' };
ledgerId =
chainIdToLedgerIdMap[/**@type{keyof typeof chainIdToLedgerIdMap}*/ (chainId)] || '0x00';
const chainIdToLedgerId = { 295: '0x00', 296: '0x01', 297: '0x02', 298: '0x03' };
ledgerId = chainIdToLedgerId[/**@type{keyof typeof chainIdToLedgerId}*/ (chainId)] ?? '0x00';
}

/**
Expand Down Expand Up @@ -243,7 +243,7 @@ function slotMapOf(token) {
token['ledger_id'] = ledgerId;
// Every inner `struct` will be flattened by `visit`,
// so it uses the last part of the field path, _i.e._, `.second`.
token['second'] = `${token['expiry_timestamp']}`;
token['second'] = `${BigInt(`${token['expiry_timestamp']}`) / 1_000_000_000n}`;
token['pause_status'] = token['pause_status'] === 'PAUSED';
token['token_keys'] = /**@type {const}*/ ([
['admin_key', 0x1],
Expand Down
6 changes: 3 additions & 3 deletions test/HTS.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ contract HTSTest is Test, TestSetup {
assertEq(tokenInfo.token.tokenKeys[4].key.ECDSA_secp256k1, bytes(""));
assertEq(tokenInfo.token.tokenKeys[4].key.delegatableContractId, address(0));
// Expiry
assertEq(tokenInfo.token.expiry.second, 1706825707000718000);
assertEq(tokenInfo.token.expiry.second, 1706825707);
assertEq(tokenInfo.token.expiry.autoRenewAccount, address(0));
assertEq(tokenInfo.token.expiry.autoRenewPeriod, 0);
assertEq(tokenInfo.totalSupply, 10000000005000000);
Expand Down Expand Up @@ -415,7 +415,7 @@ contract HTSTest is Test, TestSetup {
(int64 expiryStatusCode, IHederaTokenService.Expiry memory expiry)
= IHederaTokenService(HTS_ADDRESS).getTokenExpiryInfo(USDC);
assertEq(expiryStatusCode, HederaResponseCodes.SUCCESS);
assertEq(expiry.second, 1706825707000718000);
assertEq(expiry.second, 1706825707);
assertEq(expiry.autoRenewAccount, address(0));
assertEq(expiry.autoRenewPeriod, 0);
}
Expand Down Expand Up @@ -581,7 +581,7 @@ contract HTSTest is Test, TestSetup {
assertEq(fungibleTokenInfo.tokenInfo.token.tokenKeys[4].key.ECDSA_secp256k1, bytes(""));
assertEq(fungibleTokenInfo.tokenInfo.token.tokenKeys[4].key.delegatableContractId, address(0));
// Expiry
assertEq(fungibleTokenInfo.tokenInfo.token.expiry.second, 1706825707000718000);
assertEq(fungibleTokenInfo.tokenInfo.token.expiry.second, 1706825707);
assertEq(fungibleTokenInfo.tokenInfo.token.expiry.autoRenewAccount, address(0));
assertEq(fungibleTokenInfo.tokenInfo.token.expiry.autoRenewPeriod, 0);
assertEq(fungibleTokenInfo.tokenInfo.totalSupply, 10000000005000000);
Expand Down
2 changes: 1 addition & 1 deletion test/forwarder.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe('::forwarder', function () {

before(async function () {
const forkUrl = /**@type{string}*/ (/**@type{unknown}*/ (undefined));
const { host } = await jsonRPCForwarder(forkUrl, '', undefined, [
const { host } = await jsonRPCForwarder(forkUrl, '', undefined, undefined, [
'0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
]);
provider = new Provider(host);
Expand Down
12 changes: 4 additions & 8 deletions test/hts.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@
* limitations under the License.
*/

/* eslint-disable mocha/no-skipped-tests */
const { inspect } = require('util');

const { strict: assert } = require('assert');
const { expect } = require('chai');
const { Contract, JsonRpcProvider, Wallet } = require('ethers');
Expand Down Expand Up @@ -168,7 +165,7 @@ describe('::e2e', function () {
expect('_status' in token, `Token "${title}" \`${tokenId}\` not found`).to.be.false;
}

const { host: forwarderUrl } = await jsonRPCForwarder(jsonRpcRelayUrl, mirrorNodeUrl);
const { host: forwarderUrl } = await jsonRPCForwarder(jsonRpcRelayUrl, mirrorNodeUrl, 298);
anvilHost = await anvil(forwarderUrl);

// Ensure HTS emulation is reachable
Expand Down Expand Up @@ -239,12 +236,11 @@ describe('::e2e', function () {

// To enable this, we need to change `getTokenInfo` to a `view` function
it("should retrieve token's metadata through `getTokenInfo`", async function () {
const info = await HTS['getTokenInfo'](tokenAddress);
console.log(inspect(info, { depth: null }));
const tokenInfo = await HTS['getTokenInfo'](tokenAddress);
if (self.tokenInfo === undefined) {
self.tokenInfo = info;
self.tokenInfo = tokenInfo;
} else {
expect(info).to.be.deep.equal(self.tokenInfo);
expect(self.tokenInfo).to.be.deep.equal(tokenInfo);
}
});

Expand Down
33 changes: 32 additions & 1 deletion test/slotMapOf.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@
* limitations under the License.
*/

const { strict: assert } = require('assert');
const { expect } = require('chai');

const { slotMapOf, packValues } = require('../src/slotmap');
const { slotMapOf, packValues, setLedgerId } = require('../src/slotmap');
const { toIntHex256 } = require('../src/utils');

const mirrorNode =
Expand Down Expand Up @@ -84,4 +85,34 @@ describe('::slotmap', function () {
});
});
});

describe('setLedgerId', function () {
const getLedgerId = () => {
const map = slotMapOf(require(`../test/data/USDC/getToken.json`));
const [[{ offset, value }]] = [...map._map.values()].filter(
a => a.length === 1 && a[0].path.endsWith('.ledgerId')
);
assert(offset === 0);
assert(typeof value === 'string');
return Buffer.from(value.slice(0, -2), 'hex').toString().replaceAll('\u0000', '');
};

it('should return default ledgerId when no ledgedId has been set', function () {
expect(getLedgerId()).to.be.equal('0x00');
});

[
{ chainId: undefined, ledgerId: '0x00' },
{ chainId: 1, ledgerId: '0x00' },
{ chainId: 295, ledgerId: '0x00' },
{ chainId: 296, ledgerId: '0x01' },
{ chainId: 297, ledgerId: '0x02' },
{ chainId: 298, ledgerId: '0x03' },
].forEach(({ chainId, ledgerId }) => {
it(`should set ledgerId=${ledgerId} when chainId is ${chainId}`, function () {
setLedgerId(chainId);
expect(getLedgerId()).to.be.equal(ledgerId);
});
});
});
});

0 comments on commit f4950de

Please sign in to comment.