Skip to content

Commit c1d635a

Browse files
chore: improve test coverage for sdkClient (#3946)
Signed-off-by: Konstantina Blazhukova <konstantina.blajukova@gmail.com>
1 parent 4a98371 commit c1d635a

File tree

4 files changed

+2147
-2126
lines changed

4 files changed

+2147
-2126
lines changed

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

Lines changed: 12 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@
22

33
import { ConfigService } from '@hashgraph/json-rpc-config-service/dist/services';
44
import {
5-
AccountId,
65
Client,
7-
ContractCallQuery,
8-
ContractFunctionResult,
9-
ContractId,
106
EthereumTransaction,
117
EthereumTransactionData,
128
ExchangeRate,
@@ -129,7 +125,7 @@ export class SDKClient {
129125
* @returns {Promise<{ txResponse: TransactionResponse; fileId: FileId | null }>}
130126
* @throws {SDKClientError} Throws an error if no file ID is created or if the preemptive fee check fails.
131127
*/
132-
async submitEthereumTransaction(
128+
public async submitEthereumTransaction(
133129
transactionBuffer: Uint8Array,
134130
callerName: string,
135131
requestDetails: RequestDetails,
@@ -187,137 +183,6 @@ export class SDKClient {
187183
};
188184
}
189185

190-
/**
191-
* Submits a contract call query to a smart contract on the Hedera network.
192-
* @param {string} to - The address of the contract to call, in either Solidity or EVM format.
193-
* @param {string} data - The encoded function parameters for the contract call, in hexadecimal format.
194-
* @param {number} gas - The amount of gas to use for the contract call.
195-
* @param {string} from - The address of the sender in EVM format.
196-
* @param {string} callerName - The name of the caller for logging purposes.
197-
* @param {RequestDetails} requestDetails - The request details for logging and tracking.
198-
* @returns {Promise<ContractFunctionResult>} The result of the contract function call.
199-
* @throws {SDKClientError} Throws an SDK client error if the contract call query fails.
200-
*/
201-
async submitContractCallQuery(
202-
to: string,
203-
data: string,
204-
gas: number,
205-
from: string,
206-
callerName: string,
207-
requestDetails: RequestDetails,
208-
): Promise<ContractFunctionResult> {
209-
const contract = SDKClient.prune0x(to);
210-
const contractId = contract.startsWith('00000000000')
211-
? ContractId.fromSolidityAddress(contract)
212-
: ContractId.fromEvmAddress(0, 0, contract);
213-
214-
const contractCallQuery = new ContractCallQuery().setContractId(contractId).setGas(gas);
215-
216-
// data is optional and can be omitted in which case fallback function will be employed
217-
if (data) {
218-
contractCallQuery.setFunctionParameters(Buffer.from(SDKClient.prune0x(data), 'hex'));
219-
}
220-
221-
if (from) {
222-
contractCallQuery.setSenderAccountId(AccountId.fromEvmAddress(0, 0, from));
223-
}
224-
225-
if (this.clientMain.operatorAccountId !== null) {
226-
contractCallQuery.setPaymentTransactionId(TransactionId.generate(this.clientMain.operatorAccountId));
227-
}
228-
229-
return this.executeQuery(contractCallQuery, this.clientMain, callerName, to, requestDetails, from);
230-
}
231-
232-
/**
233-
* Submits a contract call query with retries in case of timeout errors.
234-
* @param {string} to - The address of the contract to call.
235-
* @param {string} data - The data to send with the contract call.
236-
* @param {number} gas - The amount of gas to use for the contract call.
237-
* @param {string} from - The address from which the contract call is made.
238-
* @param {string} callerName - The name of the caller for logging purposes.
239-
* @param {RequestDetails} requestDetails - The request details for logging and tracking.
240-
* @returns {Promise<ContractFunctionResult>} The result of the contract function call.
241-
* @throws {JsonRpcError} Throws an error if the error is a JSON-RPC error.
242-
* @throws {SDKClientError} Throws an SDK client error if the error is not a timeout error or if the retries are exhausted.
243-
*/
244-
async submitContractCallQueryWithRetry(
245-
to: string,
246-
data: string,
247-
gas: number,
248-
from: string,
249-
callerName: string,
250-
requestDetails: RequestDetails,
251-
): Promise<ContractFunctionResult> {
252-
let retries = 0;
253-
let resp;
254-
while (ConfigService.get('CONTRACT_QUERY_TIMEOUT_RETRIES') > retries) {
255-
try {
256-
resp = await this.submitContractCallQuery(to, data, gas, from, callerName, requestDetails);
257-
return resp;
258-
} catch (e: any) {
259-
const sdkClientError = new SDKClientError(e, e.message);
260-
if (sdkClientError.isTimeoutExceeded()) {
261-
const delay = retries * 1000;
262-
if (this.logger.isLevelEnabled('trace')) {
263-
this.logger.trace(
264-
`${requestDetails.formattedRequestId} Contract call query failed with status ${sdkClientError.message}. Retrying again after ${delay} ms ...`,
265-
);
266-
}
267-
retries++;
268-
await new Promise((r) => setTimeout(r, delay));
269-
continue;
270-
}
271-
if (e instanceof JsonRpcError) {
272-
throw e;
273-
}
274-
throw sdkClientError;
275-
}
276-
}
277-
return resp;
278-
}
279-
280-
/**
281-
* Increases the query cost and retries the query execution if the initial attempt fails due to insufficient transaction fees.
282-
* @param {Query<any>} query - The query to be executed.
283-
* @param {Hbar} baseCost - The base cost of the query.
284-
* @param {Client} client - The client to use for executing the query.
285-
* @param {number} maxRetries - The maximum number of retries allowed.
286-
* @param {number} currentRetry - The current retry attempt number.
287-
* @param {RequestDetails} requestDetails - The request details for logging and tracking.
288-
* @returns {Promise<{resp: any, cost: Hbar}>} The response of the query execution and the cost used.
289-
* @throws Will throw an error if the maximum number of retries is exceeded or if the error is not due to insufficient transaction fees.
290-
*/
291-
async increaseCostAndRetryExecution(
292-
query: Query<any>,
293-
baseCost: Hbar,
294-
client: Client,
295-
maxRetries: number,
296-
currentRetry: number,
297-
requestDetails: RequestDetails,
298-
): Promise<{ resp: any; cost: Hbar }> {
299-
const baseMultiplier = constants.QUERY_COST_INCREMENTATION_STEP;
300-
const multiplier = Math.pow(baseMultiplier, currentRetry);
301-
302-
const cost = Hbar.fromTinybars(baseCost._valueInTinybar.multipliedBy(multiplier).toFixed(0));
303-
304-
try {
305-
const resp = await query.setQueryPayment(cost).execute(client);
306-
return { resp, cost };
307-
} catch (e: any) {
308-
const sdkClientError = new SDKClientError(e, e.message);
309-
if (maxRetries > currentRetry && sdkClientError.isInsufficientTxFee()) {
310-
const newRetry = currentRetry + 1;
311-
this.logger.info(
312-
`${requestDetails.formattedRequestId} Retrying query execution with increased cost, retry number: ${newRetry}`,
313-
);
314-
return await this.increaseCostAndRetryExecution(query, baseCost, client, maxRetries, newRetry, requestDetails);
315-
}
316-
317-
throw e;
318-
}
319-
}
320-
321186
/**
322187
* Executes a Hedera query and handles potential errors.
323188
* @param {Query<T>} query - The Hedera query to execute.
@@ -330,11 +195,10 @@ export class SDKClient {
330195
* @throws {Error} Throws an error if the query fails or if rate limits are exceeded.
331196
* @template T - The type of the query response.
332197
*/
333-
async executeQuery<T>(
198+
private async executeQuery<T>(
334199
query: Query<T>,
335200
client: Client,
336201
callerName: string,
337-
interactingEntity: string,
338202
requestDetails: RequestDetails,
339203
originalCallerAddress?: string,
340204
): Promise<T> {
@@ -347,20 +211,11 @@ export class SDKClient {
347211
this.logger.info(`${requestIdPrefix} Execute ${queryConstructorName} query.`);
348212

349213
try {
350-
if (query.paymentTransactionId) {
351-
const baseCost = await query.getCost(this.clientMain);
352-
const res = await this.increaseCostAndRetryExecution(query, baseCost, client, 3, 0, requestDetails);
353-
queryResponse = res.resp;
354-
queryCost = res.cost.toTinybars().toNumber();
355-
} else {
356-
queryResponse = await query.execute(client);
357-
queryCost = query._queryPayment?.toTinybars().toNumber();
358-
}
359-
214+
queryResponse = await query.execute(client);
215+
queryCost = query._queryPayment?.toTinybars().toNumber();
360216
status = Status.Success.toString();
361-
362217
this.logger.info(
363-
`${requestIdPrefix} Successfully execute ${queryConstructorName} query: paymentTransactionId=${query.paymentTransactionId}, callerName=${callerName}, cost=${queryCost} tinybars`,
218+
`${requestIdPrefix} Successfully execute ${queryConstructorName} query: callerName=${callerName}, cost=${queryCost} tinybars`,
364219
);
365220
return queryResponse;
366221
} catch (e: any) {
@@ -369,16 +224,13 @@ export class SDKClient {
369224
queryCost = query._queryPayment?.toTinybars().toNumber();
370225
status = sdkClientError.status.toString();
371226

372-
if (e instanceof PrecheckStatusError && e.contractFunctionResult?.errorMessage) {
373-
throw predefined.CONTRACT_REVERT(e.contractFunctionResult.errorMessage);
374-
}
375227
if (sdkClientError.isGrpcTimeout()) {
376228
throw predefined.REQUEST_TIMEOUT;
377229
}
378230

379231
if (this.logger.isLevelEnabled('debug')) {
380232
this.logger.debug(
381-
`${requestIdPrefix} Fail to execute ${queryConstructorName} query: paymentTransactionId=${query.paymentTransactionId}, callerName=${callerName}, status=${sdkClientError.status}(${sdkClientError.status._code}), cost=${queryCost} tinybars`,
233+
`${requestIdPrefix} Fail to execute ${queryConstructorName} callerName=${callerName}, status=${sdkClientError.status}(${sdkClientError.status._code}), cost=${queryCost} tinybars`,
382234
);
383235
}
384236

@@ -412,7 +264,7 @@ export class SDKClient {
412264
* @returns {Promise<TransactionResponse>} - A promise that resolves to the transaction response.
413265
* @throws {SDKClientError} - Throws if an error occurs during transaction execution.
414266
*/
415-
async executeTransaction(
267+
private async executeTransaction(
416268
transaction: Transaction,
417269
callerName: string,
418270
interactingEntity: string,
@@ -509,7 +361,7 @@ export class SDKClient {
509361
* @returns {Promise<void>} - A promise that resolves when the batch execution is complete.
510362
* @throws {SDKClientError} - Throws if an error occurs during batch transaction execution.
511363
*/
512-
async executeAllTransaction(
364+
private async executeAllTransaction(
513365
transaction: FileAppendTransaction,
514366
callerName: string,
515367
interactingEntity: string,
@@ -579,7 +431,7 @@ export class SDKClient {
579431
* @returns {Promise<FileId | null>} A promise that resolves to the created file ID or null if the creation failed.
580432
* @throws Will throw an error if the created file is empty or if any transaction fails during execution.
581433
*/
582-
async createFile(
434+
private async createFile(
583435
callData: Uint8Array,
584436
client: Client,
585437
requestDetails: RequestDetails,
@@ -646,7 +498,6 @@ export class SDKClient {
646498
new FileInfoQuery().setFileId(fileId),
647499
this.clientMain,
648500
callerName,
649-
interactingEntity,
650501
requestDetails,
651502
originalCallerAddress,
652503
);
@@ -676,7 +527,7 @@ export class SDKClient {
676527
* @returns {Promise<void>} - A promise that resolves when the operation is complete.
677528
* @throws {any} - Throws an error if the file deletion fails.
678529
*/
679-
async deleteFile(
530+
public async deleteFile(
680531
fileId: FileId,
681532
requestDetails: RequestDetails,
682533
callerName: string,
@@ -702,7 +553,6 @@ export class SDKClient {
702553
new FileInfoQuery().setFileId(fileId),
703554
this.clientMain,
704555
callerName,
705-
interactingEntity,
706556
requestDetails,
707557
originalCallerAddress,
708558
);
@@ -779,7 +629,7 @@ export class SDKClient {
779629
* @param {string} accountId - The ID of the account for which the transfer sum is to be calculated.
780630
* @returns {number} The total sum of transfer amounts for the specified account, in tinybars.
781631
*/
782-
public getTransferAmountSumForAccount(transactionRecord: TransactionRecord, accountId: string): number {
632+
private getTransferAmountSumForAccount(transactionRecord: TransactionRecord, accountId: string): number {
783633
return transactionRecord.transfers
784634
.filter((transfer) => transfer.accountId.toString() === accountId && transfer.amount.isNegative())
785635
.reduce((acc, transfer) => {
@@ -793,19 +643,9 @@ export class SDKClient {
793643
* @param {number} exchangeRate - The exchange rate in cents used to convert the transaction query cost.
794644
* @returns {number} - The transaction record query cost in tinybars.
795645
*/
796-
public calculateTxRecordChargeAmount(exchangeRate: ExchangeRate): number {
646+
private calculateTxRecordChargeAmount(exchangeRate: ExchangeRate): number {
797647
const exchangeRateInCents = exchangeRate.exchangeRateInCents;
798648
const hbarToTinybar = Hbar.from(1, HbarUnit.Hbar).toTinybars().toNumber();
799649
return Math.round((constants.NETWORK_FEES_IN_CENTS.TRANSACTION_GET_RECORD / exchangeRateInCents) * hbarToTinybar);
800650
}
801-
802-
/**
803-
* Removes the '0x' prefix from a string if it exists.
804-
* @private
805-
* @param {string} input - The input string to be pruned.
806-
* @returns {string} The input string without the '0x' prefix.
807-
*/
808-
private static prune0x(input: string): string {
809-
return input.startsWith('0x') ? input.substring(2) : input;
810-
}
811651
}

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,6 @@ describe('@ethCall Eth Call spec', async function () {
373373
await mockContractCall({ ...callData, block: 'latest' }, false, 400, mockData.contractReverted, requestDetails);
374374
sinon.reset();
375375
const result = await contractService.call(callData, 'latest', requestDetails);
376-
sinon.assert.notCalled(sdkClientStub.submitContractCallQueryWithRetry);
377376
expect(result).to.not.be.null;
378377
expect((result as JsonRpcError).code).to.eq(3);
379378
expect((result as JsonRpcError).message).to.contain(mockData.contractReverted._status.messages[0].message);

0 commit comments

Comments
 (0)