Skip to content

Commit 5983a39

Browse files
ebadiereNana-EC
andauthored
Add resolve type consideration for tokens (#1158) (#1184)
Handle valid evm contract address considerations as part of entity resolution - Add path to MirrorNodeClient retry log - Conditionally add `this.getTokenById()` to `resolveEntityType()` only if address is of long zero type - Move requestId to add of `resolveEntityType()` parameters - Added test for to verify token is only called for long zero address --------- Signed-off-by: Nana Essilfie-Conduah <[email protected]> Signed-off-by: ebadiere <[email protected]> Co-authored-by: Nana Essilfie-Conduah <[email protected]>
1 parent 139b893 commit 5983a39

File tree

5 files changed

+49
-10
lines changed

5 files changed

+49
-10
lines changed

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

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ export class MirrorNodeClient {
179179
const requestId = request ? request.split('\n')[3].substring(11,47) : '';
180180
const requestIdPrefix = formatRequestIdMessage(requestId);
181181
const delay = isDevMode ? mirrorNodeRetryDelayDevMode || 200 : mirrorNodeRetryDelay * retryCount;
182-
this.logger.trace(`${requestIdPrefix} Retry delay ${delay} ms`);
182+
this.logger.trace(`${requestIdPrefix} Retry delay ${delay} ms on '${error?.request?.path}'`);
183183
return delay;
184184
},
185185
retryCondition: (error) => {
@@ -650,10 +650,17 @@ export class MirrorNodeClient {
650650
}
651651
}
652652

653+
/**
654+
* Get the contract results for a given address
655+
* @param entityIdentifier the address of the contract
656+
* @param requestId the request id
657+
* @param searchableTypes the types to search for
658+
* @returns entity object or null if not found
659+
*/
653660
public async resolveEntityType(
654661
entityIdentifier: string,
655-
requestId?: string,
656-
searchableTypes: any[] = [constants.TYPE_CONTRACT, constants.TYPE_ACCOUNT, constants.TYPE_TOKEN]
662+
searchableTypes: any[] = [constants.TYPE_CONTRACT, constants.TYPE_ACCOUNT, constants.TYPE_TOKEN],
663+
requestId?: string
657664
) {
658665
const cachedLabel = `resolveEntityType.${entityIdentifier}`;
659666
const cachedResponse: { type: string, entity: any } | undefined = this.cache.get(cachedLabel);
@@ -682,8 +689,13 @@ export class MirrorNodeClient {
682689
try {
683690
const promises = [
684691
searchableTypes.find(t => t === constants.TYPE_ACCOUNT) ? buildPromise(this.getAccount(entityIdentifier, requestId)) : Promise.reject(),
685-
searchableTypes.find(t => t === constants.TYPE_TOKEN) ? buildPromise(this.getTokenById(`0.0.${parseInt(entityIdentifier, 16)}`, requestId)) : Promise.reject()
686692
];
693+
694+
// only add long zero evm addresses for tokens as they do not refer to actual contract addresses but rather encoded entity nums
695+
if (entityIdentifier.startsWith(constants.LONG_ZERO_PREFIX)) {
696+
promises.push(searchableTypes.find(t => t === constants.TYPE_TOKEN) ? buildPromise(this.getTokenById(`0.0.${parseInt(entityIdentifier, 16)}`, requestId)) : Promise.reject());
697+
}
698+
687699
// maps the promises with indices of the promises array
688700
// because there is no such method as Promise.anyWithIndex in js
689701
// the index is needed afterward for detecting the resolved promise type (contract, account, or token)

packages/relay/src/lib/constants.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,7 @@ export default {
7070
NEXT_LINK_PREFIX: '/api/v1/',
7171
QUERY_COST_INCREMENTATION_STEP: 1.1,
7272

73-
TRANSACTION_ID_REGEX: /\d{1}\.\d{1}\.\d{1,10}\@\d{1,10}\.\d{1,9}/
73+
TRANSACTION_ID_REGEX: /\d{1}\.\d{1}\.\d{1,10}\@\d{1,10}\.\d{1,9}/,
74+
75+
LONG_ZERO_PREFIX: '0x000000000000',
7476
};

packages/relay/src/lib/eth.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -777,7 +777,7 @@ export class EthImpl implements Eth {
777777
}
778778

779779
try {
780-
const result = await this.mirrorNodeClient.resolveEntityType(address, requestId, [constants.TYPE_CONTRACT, constants.TYPE_TOKEN]);
780+
const result = await this.mirrorNodeClient.resolveEntityType(address, [constants.TYPE_CONTRACT, constants.TYPE_TOKEN], requestId);
781781
if (result) {
782782
if (result?.type === constants.TYPE_TOKEN) {
783783
this.logger.trace(`${requestIdPrefix} Token redirect case, return redirectBytecode`);
@@ -944,7 +944,7 @@ export class EthImpl implements Eth {
944944

945945
// check consensus node as back up
946946
try {
947-
const result = await this.mirrorNodeClient.resolveEntityType(address, requestId, [constants.TYPE_ACCOUNT, constants.TYPE_CONTRACT]);
947+
const result = await this.mirrorNodeClient.resolveEntityType(address, [constants.TYPE_ACCOUNT, constants.TYPE_CONTRACT], requestId);
948948
if (result?.type === constants.TYPE_ACCOUNT) {
949949
const accountInfo = await this.sdkClient.getAccountInfo(result?.entity.account, EthImpl.ethGetTransactionCount, requestId);
950950
return EthImpl.numberTo0x(Number(accountInfo.ethereumNonce));
@@ -1159,13 +1159,13 @@ export class EthImpl implements Eth {
11591159

11601160
// If "From" is distinct from blank, we check is a valid account
11611161
if(call.from) {
1162-
const fromEntityType = await this.mirrorNodeClient.resolveEntityType(call.from, requestId, [constants.TYPE_ACCOUNT]);
1162+
const fromEntityType = await this.mirrorNodeClient.resolveEntityType(call.from, [constants.TYPE_ACCOUNT], requestId);
11631163
if (fromEntityType?.type !== constants.TYPE_ACCOUNT) {
11641164
throw predefined.NON_EXISTING_ACCOUNT(call.from);
11651165
}
11661166
}
11671167
// Check "To" is a valid Contract or HTS Address
1168-
const toEntityType = await this.mirrorNodeClient.resolveEntityType(call.to, requestId, [constants.TYPE_TOKEN, constants.TYPE_CONTRACT]);
1168+
const toEntityType = await this.mirrorNodeClient.resolveEntityType(call.to, [constants.TYPE_TOKEN, constants.TYPE_CONTRACT], requestId);
11691169
if(!(toEntityType?.type === constants.TYPE_CONTRACT || toEntityType?.type === constants.TYPE_TOKEN)) {
11701170
throw predefined.NON_EXISTING_CONTRACT(call.to);
11711171
}

packages/relay/tests/lib/mirrorNodeClient.spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,31 @@ describe('MirrorNodeClient', async function () {
696696
const entityType = await mirrorNodeInstance.resolveEntityType(notFoundAddress);
697697
expect(entityType).to.be.null;
698698
});
699+
700+
it('calls mirror node tokens API when token is long zero type', async() => {
701+
mock.onGet(`contracts/${mockData.tokenId}`).reply(404, mockData.notFound);
702+
mock.onGet(`tokens/${mockData.tokenId}`).reply(200, mockData.token);
703+
704+
const entityType = await mirrorNodeInstance.resolveEntityType(mockData.tokenLongZero, [constants.TYPE_CONTRACT, constants.TYPE_TOKEN]);
705+
expect(entityType).to.exist;
706+
expect(entityType).to.have.property('type');
707+
expect(entityType).to.have.property('entity');
708+
expect(entityType.type).to.eq('token');
709+
expect(entityType.entity.token_id).to.eq(mockData.tokenId);
710+
});
711+
712+
it('does not call mirror node tokens API when token is not long zero type', async() => {
713+
mock.onGet(`contracts/${mockData.contractEvmAddress}`).reply(200, mockData.contract);
714+
mock.onGet(`tokens/${mockData.tokenId}`).reply(404, mockData.notFound);
715+
716+
const entityType = await mirrorNodeInstance.resolveEntityType(mockData.contractEvmAddress, [constants.TYPE_CONTRACT, constants.TYPE_TOKEN]);
717+
expect(entityType).to.exist;
718+
expect(entityType).to.have.property('type');
719+
expect(entityType).to.have.property('entity');
720+
expect(entityType.type).to.eq('contract');
721+
expect(entityType.entity).to.have.property('contract_id');
722+
expect(entityType.entity.contract_id).to.eq(mockData.contract.contract_id);
723+
});
699724
});
700725

701726
describe('getTransactionById', async() => {

packages/ws-server/src/webSocketServer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ function getMultipleAddressesEnabled() {
7575
}
7676

7777
async function validateIsContractAddress(address, requestId) {
78-
const isContract = await mirrorNodeClient.resolveEntityType(address, requestId, [constants.TYPE_CONTRACT]);
78+
const isContract = await mirrorNodeClient.resolveEntityType(address, [constants.TYPE_CONTRACT], requestId);
7979
if (!isContract) {
8080
throw new JsonRpcError(predefined.INVALID_PARAMETER(`filters.address`, `${address} is not a valid contract type or does not exists`), requestId);
8181
}

0 commit comments

Comments
 (0)