Skip to content

Commit a9fc75b

Browse files
authored
feat: support non-zero shard/realm (#3718)
Signed-off-by: Luis Mastrangelo <[email protected]>
1 parent 014f26f commit a9fc75b

File tree

7 files changed

+74
-63
lines changed

7 files changed

+74
-63
lines changed

packages/relay/src/lib/clients/mirrorNodeClient.ts

+17-12
Original file line numberDiff line numberDiff line change
@@ -1398,18 +1398,23 @@ export class MirrorNodeClient {
13981398
: Promise.reject(),
13991399
];
14001400

1401-
// only add long zero evm addresses for tokens as they do not refer to actual contract addresses but rather encoded entity nums
1402-
if (entityIdentifier.startsWith(constants.LONG_ZERO_PREFIX)) {
1403-
promises.push(
1404-
searchableTypes.find((t) => t === constants.TYPE_TOKEN)
1405-
? buildPromise(
1406-
this.getTokenById(`0.0.${parseInt(entityIdentifier, 16)}`, requestDetails, retries).catch(() => {
1407-
return null;
1408-
}),
1409-
)
1410-
: Promise.reject(),
1411-
);
1412-
}
1401+
const toEntityId = (address: string) => {
1402+
address = address.slice(2);
1403+
const shardNum = address.slice(0, 8);
1404+
const realmNum = address.slice(8, 24);
1405+
const accountNum = address.slice(24);
1406+
return `${parseInt(shardNum, 16)}.${parseInt(realmNum, 16)}.${parseInt(accountNum, 16)}`;
1407+
};
1408+
1409+
promises.push(
1410+
searchableTypes.find((t) => t === constants.TYPE_TOKEN)
1411+
? buildPromise(
1412+
this.getTokenById(toEntityId(entityIdentifier), requestDetails, retries).catch(() => {
1413+
return null;
1414+
}),
1415+
)
1416+
: Promise.reject(),
1417+
);
14131418

14141419
// maps the promises with indices of the promises array
14151420
// because there is no such method as Promise.anyWithIndex in js

packages/relay/src/lib/constants.ts

-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ export default {
110110
ETH_FEE_HISTORY_TTL: `${CACHE_TTL.HALF_HOUR}`,
111111
TRANSACTION_ID_REGEX: /\d{1}\.\d{1}\.\d{1,10}\@\d{1,10}\.\d{1,9}/,
112112

113-
LONG_ZERO_PREFIX: '0x000000000000',
114113
CHAIN_IDS: {
115114
mainnet: 0x127,
116115
testnet: 0x128,

packages/relay/src/lib/eth.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,6 @@ export class EthImpl implements Eth {
9494
reward: [],
9595
oldestBlock: EthImpl.zeroHex,
9696
};
97-
static redirectBytecodePrefix = '6080604052348015600f57600080fd5b506000610167905077618dc65e';
98-
static redirectBytecodePostfix =
99-
'600052366000602037600080366018016008845af43d806000803e8160008114605857816000f35b816000fdfea2646970667358221220d8378feed472ba49a0005514ef7087017f707b45fb9bf56bb81bb93ff19a238b64736f6c634300080b0033';
10097
static iHTSAddress = '0x0000000000000000000000000000000000000167';
10198
static invalidEVMInstruction = '0xfe';
10299
static blockHashLength = 66;
@@ -2505,7 +2502,7 @@ export class EthImpl implements Eth {
25052502
}
25062503
}
25072504

2508-
async resolveEvmAddress(
2505+
private async resolveEvmAddress(
25092506
address: string,
25102507
requestDetails: RequestDetails,
25112508
searchableTypes = [constants.TYPE_CONTRACT, constants.TYPE_TOKEN, constants.TYPE_ACCOUNT],
@@ -2763,8 +2760,11 @@ export class EthImpl implements Eth {
27632760
return numberTo0x(gasPriceForTimestamp);
27642761
}
27652762

2766-
private static redirectBytecodeAddressReplace(address: string): string {
2767-
return `${this.redirectBytecodePrefix}${address.slice(2)}${this.redirectBytecodePostfix}`;
2763+
static redirectBytecodeAddressReplace(address: string): string {
2764+
const redirectBytecodePrefix = '6080604052348015600f57600080fd5b506000610167905077618dc65e';
2765+
const redirectBytecodePostfix =
2766+
'600052366000602037600080366018016008845af43d806000803e8160008114605857816000f35b816000fdfea2646970667358221220d8378feed472ba49a0005514ef7087017f707b45fb9bf56bb81bb93ff19a238b64736f6c634300080b0033';
2767+
return `0x${redirectBytecodePrefix}${address.slice(2)}${redirectBytecodePostfix}`;
27682768
}
27692769

27702770
private static prune0x(input: string): string {

packages/relay/tests/lib/eth/eth_getCode.spec.ts

+23-8
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ import { generateEthTestEnv } from './eth-helpers';
2121

2222
use(chaiAsPromised);
2323

24+
function entityIdToEvmAddress(entityId: string): string {
25+
const pad = (num: string, n: number) =>
26+
Number(num)
27+
.toString(16)
28+
.padStart(n * 2, '0');
29+
const [shardNum, realmNum, accountNum] = entityId.split('.');
30+
31+
return `0x${pad(shardNum, 4)}${pad(realmNum, 8)}${pad(accountNum, 8)}`;
32+
}
33+
2434
describe('@ethGetCode using MirrorNode', async function () {
2535
this.timeout(10000);
2636
const { restMock, ethImpl, cacheService } = generateEthTestEnv();
@@ -95,11 +105,8 @@ describe('@ethGetCode using MirrorNode', async function () {
95105
restMock.onGet(`contracts/${HTS_TOKEN_ADDRESS}`).reply(404, JSON.stringify(null));
96106
restMock.onGet(`accounts/${HTS_TOKEN_ADDRESS}?limit=100`).reply(404, JSON.stringify(null));
97107
restMock.onGet(`tokens/0.0.${parseInt(HTS_TOKEN_ADDRESS, 16)}`).reply(200, JSON.stringify(DEFAULT_HTS_TOKEN));
98-
const redirectBytecode = `6080604052348015600f57600080fd5b506000610167905077618dc65e${HTS_TOKEN_ADDRESS.slice(
99-
2,
100-
)}600052366000602037600080366018016008845af43d806000803e8160008114605857816000f35b816000fdfea2646970667358221220d8378feed472ba49a0005514ef7087017f707b45fb9bf56bb81bb93ff19a238b64736f6c634300080b0033`;
101108
const res = await ethImpl.getCode(HTS_TOKEN_ADDRESS, null, requestDetails);
102-
expect(res).to.equal(redirectBytecode);
109+
expect(res).to.be.equal(EthImpl.redirectBytecodeAddressReplace(HTS_TOKEN_ADDRESS));
103110
});
104111

105112
it('should return the static bytecode for address(0x167) call', async () => {
@@ -214,10 +221,7 @@ describe('@ethGetCode using MirrorNode', async function () {
214221
);
215222

216223
const res = await ethImpl.getCode(HTS_TOKEN_ADDRESS, blockNumberAfterCreation, requestDetails);
217-
const expectedRedirectBytecode = `6080604052348015600f57600080fd5b506000610167905077618dc65e${HTS_TOKEN_ADDRESS.slice(
218-
2,
219-
)}600052366000602037600080366018016008845af43d806000803e8160008114605857816000f35b816000fdfea2646970667358221220d8378feed472ba49a0005514ef7087017f707b45fb9bf56bb81bb93ff19a238b64736f6c634300080b0033`;
220-
expect(res).to.equal(expectedRedirectBytecode);
224+
expect(res).to.be.equal(EthImpl.redirectBytecodeAddressReplace(HTS_TOKEN_ADDRESS));
221225
});
222226

223227
it('should throw error for invalid block number', async () => {
@@ -262,5 +266,16 @@ describe('@ethGetCode using MirrorNode', async function () {
262266
const res = await ethImpl.getCode(CONTRACT_ADDRESS_1, 'earliest', requestDetails);
263267
expect(res).to.equal(EthImpl.emptyHex);
264268
});
269+
270+
it('should return redirect bytecode for HTS when accountId has non-zero shard/realm', async function () {
271+
const accountId = '1.2.3';
272+
const addr = entityIdToEvmAddress(accountId);
273+
274+
restMock.onGet(`contracts/${addr}`).reply(404, JSON.stringify(null));
275+
restMock.onGet(`accounts/${addr}?limit=100`).reply(404, JSON.stringify(null));
276+
restMock.onGet(`tokens/${accountId}`).reply(200, JSON.stringify(DEFAULT_HTS_TOKEN));
277+
const res = await ethImpl.getCode(addr, null, requestDetails);
278+
expect(res).to.be.equal(EthImpl.redirectBytecodeAddressReplace(addr));
279+
});
265280
});
266281
});

packages/server/tests/acceptance/erc20.spec.ts

+15-21
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
// SPDX-License-Identifier: Apache-2.0
22

33
// External resources
4-
import { solidity } from 'ethereum-waffle';
4+
import { EthImpl } from '@hashgraph/json-rpc-relay/dist/lib/eth';
55
import chai, { expect } from 'chai';
6-
7-
// Local resources
8-
import { AliasAccount } from '../types/AliasAccount';
9-
import { Utils } from '../helpers/utils';
6+
import { solidity } from 'ethereum-waffle';
107
import { ethers } from 'ethers';
11-
import ERC20MockJson from '../contracts/ERC20Mock.json';
12-
import Assertions from '../helpers/assertions';
13-
import { EthImpl } from '@hashgraph/json-rpc-relay/dist/lib/eth';
148

159
// Constants from local resources
1610
import Constants from '../../../server/tests/helpers/constants';
17-
import ServicesClient from '../clients/servicesClient';
1811
import RelayClient from '../clients/relayClient';
12+
import ServicesClient from '../clients/servicesClient';
13+
import ERC20MockJson from '../contracts/ERC20Mock.json';
14+
import Assertions from '../helpers/assertions';
15+
import { Utils } from '../helpers/utils';
16+
// Local resources
17+
import { AliasAccount } from '../types/AliasAccount';
1918

2019
chai.use(solidity);
2120

@@ -43,10 +42,10 @@ describe('@erc20 Acceptance Tests', async function () {
4342
const symbol = Utils.randomString(5);
4443
const initialSupply = BigInt(10000);
4544

46-
const ERC20 = 'ERC20 Contract';
47-
const HTS = 'HTS token';
48-
49-
const testTitles = [{ testName: ERC20, expectedBytecode: ERC20MockJson.deployedBytecode }, { testName: HTS }];
45+
const testTitles = [
46+
{ testName: 'ERC20 Contract', expectedBytecode: ERC20MockJson.deployedBytecode },
47+
{ testName: 'HTS token' },
48+
];
5049

5150
this.beforeAll(async () => {
5251
requestId = Utils.generateRequestId();
@@ -111,14 +110,9 @@ describe('@erc20 Acceptance Tests', async function () {
111110

112111
it('Relay can execute "eth_getCode" for ERC20 contract with evmAddress', async function () {
113112
const res = await relay.call('eth_getCode', [contract.target, 'latest'], requestId);
114-
const expectedBytecode = `${EthImpl.redirectBytecodePrefix}${contract.target.slice(2)}${
115-
EthImpl.redirectBytecodePostfix
116-
}`;
117-
if (testTitles[i].testName !== HTS) {
118-
expect(res).to.eq(testTitles[i].expectedBytecode);
119-
} else {
120-
expect(res).to.eq(expectedBytecode);
121-
}
113+
expect(res).to.be.equal(
114+
testTitles[i].expectedBytecode ?? EthImpl.redirectBytecodeAddressReplace(contract.target),
115+
);
122116
});
123117

124118
describe('should behave like erc20', function () {

packages/server/tests/acceptance/rpc_batch2.spec.ts

+1-4
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,6 @@ describe('@api-batch-2 RPC Server Acceptance Tests', function () {
730730
let mainContract: ethers.Contract;
731731
let mainContractAddress: string;
732732
let NftHTSTokenContractAddress: string;
733-
let redirectBytecode: string;
734733
let blockBeforeContractCreation: number;
735734
let basicContract: ethers.Contract;
736735
let basicContractAddress: string;
@@ -770,14 +769,12 @@ describe('@api-batch-2 RPC Server Acceptance Tests', function () {
770769
});
771770

772771
it('should execute "eth_getCode" for hts token', async function () {
773-
const tokenAddress = NftHTSTokenContractAddress.slice(2);
774-
redirectBytecode = `6080604052348015600f57600080fd5b506000610167905077618dc65e${tokenAddress}600052366000602037600080366018016008845af43d806000803e8160008114605857816000f35b816000fdfea2646970667358221220d8378feed472ba49a0005514ef7087017f707b45fb9bf56bb81bb93ff19a238b64736f6c634300080b0033`;
775772
const res = await relay.call(
776773
RelayCalls.ETH_ENDPOINTS.ETH_GET_CODE,
777774
[NftHTSTokenContractAddress, 'latest'],
778775
requestId,
779776
);
780-
expect(res).to.equal(redirectBytecode);
777+
expect(res).to.be.equal(EthImpl.redirectBytecodeAddressReplace(NftHTSTokenContractAddress));
781778
});
782779

783780
it('@release should return empty bytecode for HTS token when a block earlier than the token creation is passed', async function () {

packages/server/tests/helpers/utils.ts

+12-11
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
11
// SPDX-License-Identifier: Apache-2.0
22

3-
import { ethers } from 'ethers';
4-
import Assertions from './assertions';
5-
import crypto from 'crypto';
6-
import RelayClient from '../clients/relayClient';
3+
import { ConfigService } from '@hashgraph/json-rpc-config-service/dist/services';
74
import { numberTo0x } from '@hashgraph/json-rpc-relay/dist/formatters';
8-
import RelayCall from '../../tests/helpers/constants';
5+
import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types';
96
import { AccountId, KeyList, PrivateKey } from '@hashgraph/sdk';
10-
import { AliasAccount } from '../types/AliasAccount';
11-
import ServicesClient from '../clients/servicesClient';
7+
import crypto from 'crypto';
8+
import { ethers } from 'ethers';
129
import http from 'http';
10+
import { Context } from 'mocha';
1311
import { GCProfiler, setFlagsFromString, writeHeapSnapshot } from 'v8';
1412
import { runInNewContext } from 'vm';
15-
import { Context } from 'mocha';
13+
14+
import RelayCall from '../../tests/helpers/constants';
1615
import { GitHubClient } from '../clients/githubClient';
1716
import MirrorClient from '../clients/mirrorClient';
17+
import RelayClient from '../clients/relayClient';
18+
import ServicesClient from '../clients/servicesClient';
19+
import { AliasAccount } from '../types/AliasAccount';
1820
import { HeapDifferenceStatistics } from '../types/HeapDifferenceStatistics';
19-
import { ConfigService } from '@hashgraph/json-rpc-config-service/dist/services';
20-
import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types';
21+
import Assertions from './assertions';
2122

2223
export class Utils {
2324
static readonly HEAP_SIZE_DIFF_MEMORY_LEAK_THRESHOLD: number = 4e6; // 4 MB
@@ -100,7 +101,7 @@ export class Utils {
100101
relay: RelayClient,
101102
) => {
102103
const factory = new ethers.ContractFactory(contractJson.abi, contractJson.bytecode, wallet);
103-
let contract = await factory.deploy(...constructorArgs);
104+
const contract = await factory.deploy(...constructorArgs);
104105
await contract.waitForDeployment();
105106

106107
// re-init the contract with the deployed address

0 commit comments

Comments
 (0)