Skip to content

Commit

Permalink
Cherry pick commits for v0.13.1 (#753)
Browse files Browse the repository at this point in the history
* Check `to` parameter in `eth_call` method (#744)

Checks if to parameter is passed in eth_call method, before checking it's length.

Signed-off-by: georgi-l95 <[email protected]>

* eth_getBalance workaround (#717)

* feat: add workaround logic

Signed-off-by: Ivo Yankov <[email protected]>

* fix: unit tests

Signed-off-by: Ivo Yankov <[email protected]>

* chore: code cleanup

Signed-off-by: Ivo Yankov <[email protected]>

* test: add acceptance test

Signed-off-by: Ivo Yankov <[email protected]>

* chore: fix unit tests

Signed-off-by: Ivo Yankov <[email protected]>

* refactor: remove duplicated pagination util

Signed-off-by: Ivo Yankov <[email protected]>

* fix: add safeguard against double link prefix

Signed-off-by: Ivo Yankov <[email protected]>

* chore: fix acceptancetest api tests structure

Signed-off-by: Ivo Yankov <[email protected]>

* nit: code cleanup

Signed-off-by: Ivo Yankov <[email protected]>

* fix: calculate balance at block end

Signed-off-by: Ivo Yankov <[email protected]>

* fix: broken unit tests

Signed-off-by: Ivo Yankov <[email protected]>

* test: add explicit unit texts for balance calculation

Signed-off-by: Ivo Yankov <[email protected]>

* fix: tweak unit tests

Signed-off-by: Ivo Yankov <[email protected]>

* fix: failing acceptance test

Signed-off-by: Ivo Yankov <[email protected]>

* fix: failing acceptance test

Signed-off-by: Ivo Yankov <[email protected]>

* fix: failing tests

Signed-off-by: Ivo Yankov <[email protected]>

Signed-off-by: Ivo Yankov <[email protected]>

Signed-off-by: georgi-l95 <[email protected]>
Signed-off-by: Ivo Yankov <[email protected]>
Co-authored-by: georgi-l95 <[email protected]>
Co-authored-by: Ivo Yankov <[email protected]>
  • Loading branch information
3 people authored Dec 6, 2022
1 parent 8686666 commit d9fc303
Show file tree
Hide file tree
Showing 8 changed files with 1,030 additions and 742 deletions.
92 changes: 57 additions & 35 deletions packages/relay/src/lib/clients/mirrorNodeClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ export class MirrorNodeClient {
private static GET_NETWORK_EXCHANGERATE_ENDPOINT = 'network/exchangerate';
private static GET_NETWORK_FEES_ENDPOINT = 'network/fees';
private static GET_TOKENS_ENDPOINT = 'tokens';
private static GET_TRANSACTIONS_ENDPOINT = 'transactions';

private static CONTRACT_RESULT_LOGS_PROPERTY = 'logs';

private static ORDER = {
ASC: 'asc',
Expand Down Expand Up @@ -196,6 +199,23 @@ export class MirrorNodeClient {
throw new MirrorNodeClientError(error.message, effectiveStatusCode);
}

async getPaginatedResults(url: string, pathLabel: string, resultProperty: string, allowedErrorStatuses?: number[], requestId?: string, results = [], page = 1) {
const result = await this.request(url, pathLabel, allowedErrorStatuses, requestId);

if (result && result[resultProperty]) {
results = results.concat(result[resultProperty]);
}

if (result && result.links?.next && page < constants.MAX_MIRROR_NODE_PAGINATION) {
page++;
const next = result.links.next.replace(constants.NEXT_LINK_PREFIX, "");
return this.getPaginatedResults(next, pathLabel, resultProperty, allowedErrorStatuses, requestId, results, page);
}
else {
return results;
}
}

public async getAccountLatestTransactionByAddress(idOrAliasOrEvmAddress: string, requestId?: string): Promise<object> {
return this.request(`${MirrorNodeClient.GET_ACCOUNTS_ENDPOINT}${idOrAliasOrEvmAddress}?order=desc&limit=1`,
MirrorNodeClient.GET_ACCOUNTS_ENDPOINT,
Expand All @@ -210,7 +230,23 @@ export class MirrorNodeClient {
requestId);
}

public async getBalanceAtTimestamp(accountId: string, timestamp: string, requestId?: string) {
public async getTransactionsForAccount(accountId: string, timestampFrom: string, timestampTo: string, requestId?: string) {
const queryParamObject = {};
this.setQueryParam(queryParamObject, 'account.id', accountId);
this.setQueryParam(queryParamObject, 'timestamp', `gte:${timestampFrom}`);
this.setQueryParam(queryParamObject, 'timestamp', `lt:${timestampTo}`);
const queryParams = this.getQueryParams(queryParamObject);

return this.getPaginatedResults(
`${MirrorNodeClient.GET_TRANSACTIONS_ENDPOINT}${queryParams}`,
MirrorNodeClient.GET_TRANSACTIONS_ENDPOINT,
'transactions',
[400, 404],
requestId
);
}

public async getBalanceAtTimestamp(accountId: string, timestamp?: string, requestId?: string) {
const queryParamObject = {};
this.setQueryParam(queryParamObject, 'account.id', accountId);
this.setQueryParam(queryParamObject, 'timestamp', timestamp);
Expand Down Expand Up @@ -316,10 +352,14 @@ export class MirrorNodeClient {
limitOrderParams?: ILimitOrderParams,
requestId?: string) {
const queryParams = this.prepareLogsParams(contractLogsResultsParams, limitOrderParams);
return this.request(`${MirrorNodeClient.GET_CONTRACT_RESULT_LOGS_ENDPOINT}${queryParams}`,

return this.getPaginatedResults(
`${MirrorNodeClient.GET_CONTRACT_RESULT_LOGS_ENDPOINT}${queryParams}`,
MirrorNodeClient.GET_CONTRACT_RESULT_LOGS_ENDPOINT,
MirrorNodeClient.CONTRACT_RESULT_LOGS_PROPERTY,
[400, 404],
requestId);
requestId
);
}

public async getContractResultsLogsByAddress(
Expand All @@ -333,41 +373,16 @@ export class MirrorNodeClient {
MirrorNodeClient.ADDRESS_PLACEHOLDER,
address
);
return this.request(`${apiEndpoint}${queryParams}`,

return this.getPaginatedResults(
`${apiEndpoint}${queryParams}`,
MirrorNodeClient.GET_CONTRACT_RESULT_LOGS_BY_ADDRESS_ENDPOINT,
MirrorNodeClient.CONTRACT_RESULT_LOGS_PROPERTY,
[400, 404],
requestId);
requestId
);
}

public async getContractResultsLogsByNextLink(
link: string,
requestId?: string
) {
const nextLink = link.replace(constants.NEXT_LINK_PREFIX, '');
return this.request(`${nextLink}`,
MirrorNodeClient.GET_CONTRACT_RESULT_LOGS_ENDPOINT,
[400, 404],
requestId);
}

public async pageAllResults(
result: any,
requestId?: string
) {
let unproccesedLogs = result.logs;
if (result.links && result.links.next) {
let nextLink = result.links.next;
while (nextLink) {
let nextResult = await this.getContractResultsLogsByNextLink(nextLink, requestId);
if (!nextResult || !nextResult.logs) {
break;
}
unproccesedLogs = unproccesedLogs.concat(nextResult.logs);
nextLink = nextResult.links.next;
}
}
return unproccesedLogs;
}

public async getLatestBlock(requestId?: string) {
return this.getBlocks(undefined, undefined, this.getLimitOrderQueryParam(1, MirrorNodeClient.ORDER.DESC), requestId);
Expand Down Expand Up @@ -461,7 +476,14 @@ export class MirrorNodeClient {

setQueryParam(queryParamObject, key, value) {
if (key && value !== undefined) {
queryParamObject[key] = value;
if (!queryParamObject[key]) {
queryParamObject[key] = value;
}

// Allow for duplicating params
else {
queryParamObject[key] += `&${key}=${value}`;
}
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/relay/src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export default {
TX_DATA_ZERO_COST: 4,
REQUEST_ID_STRING: `Request ID: `,
BALANCES_UPDATE_INTERVAL: 900, // 15 minutes
MAX_MIRROR_NODE_PAGINATION: 20,
MIRROR_NODE_QUERY_LIMIT: 100,
NEXT_LINK_PREFIX: '/api/v1/'
};
5 changes: 0 additions & 5 deletions packages/relay/src/lib/errors/JsonRpcError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,6 @@ export const predefined = {
code: -32606,
message: 'HBAR Rate limit exceeded'
}),
'UNKNOWN_HISTORICAL_BALANCE': new JsonRpcError({
name: 'Unavailable balance',
code: -32007,
message: 'Historical balance data is available only after 15 minutes.'
}),
'CONTRACT_REVERT': (errorMessage?: string) => new JsonRpcError({
name: 'Contract revert executed',
code: -32008,
Expand Down
46 changes: 33 additions & 13 deletions packages/relay/src/lib/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -570,10 +570,33 @@ export class EthImpl implements Eth {
const latestTimestamp = Number(latestBlock.timestamp.from.split('.')[0]);
const blockTimestamp = Number(block.timestamp.from.split('.')[0]);
const timeDiff = latestTimestamp - blockTimestamp;

// The block is from the last 15 minutes, therefore the historical balance hasn't been imported in the Mirror Node yet
if (timeDiff < constants.BALANCES_UPDATE_INTERVAL) {
throw predefined.UNKNOWN_HISTORICAL_BALANCE;
let currentBalance = 0;
let currentTimestamp;
let balanceFromTxs = 0;
if (mirrorAccount.balance) {
currentBalance = mirrorAccount.balance.balance;
currentTimestamp = mirrorAccount.balance.timestamp;
}

let transactionsInTimeWindow = await this.mirrorNodeClient.getTransactionsForAccount(
mirrorAccount.account,
block.timestamp.to,
currentTimestamp,
requestId
);

for(const tx of transactionsInTimeWindow) {
for (const transfer of tx.transfers) {
if (transfer.account === mirrorAccount.account && !transfer.is_approval) {
balanceFromTxs += transfer.amount;
}
}
}

balanceFound = true;
weibars = (currentBalance - balanceFromTxs) * constants.TINYBAR_TO_WEIBAR_COEF;
}

// The block is NOT from the last 15 minutes, use /balances rest API
Expand Down Expand Up @@ -894,10 +917,9 @@ export class EthImpl implements Eth {
const requestIdPrefix = formatRequestIdMessage(requestId);
this.logger.trace(`${requestIdPrefix} call(hash=${JSON.stringify(call)}, blockParam=${blockParam})`, call, blockParam);
// The "to" address must always be 42 chars.
if (call.to.length != 42) {
throw new Error(requestIdPrefix+
" Invalid Contract Address: '" + call.to + "'. Expected length of 42 chars but was" + call.to.length
);
if (!call.to || call.to.length != 42) {
const callToExist = call.to && call.to.length ? ` Expected length of 42 chars but was ${call.to.length}.` : '';
throw new Error(`${requestIdPrefix}Invalid Contract Address: '${call.to}'.${callToExist}`);
}

try {
Expand Down Expand Up @@ -1348,22 +1370,20 @@ export class EthImpl implements Eth {
}
}

let result;
let results;
if (address) {
result = await this.mirrorNodeClient.getContractResultsLogsByAddress(address, params, undefined, requestId);
results = await this.mirrorNodeClient.getContractResultsLogsByAddress(address, params, undefined, requestId);
}
else {
result = await this.mirrorNodeClient.getContractResultsLogs(params, undefined, requestId);
results = await this.mirrorNodeClient.getContractResultsLogs(params, undefined, requestId);
}

if (!result || !result.logs) {
if (!results) {
return [];
}

const unproccesedLogs = await this.mirrorNodeClient.pageAllResults(result, requestId);

const logs: Log[] = [];
for(const log of unproccesedLogs) {
for(const log of results) {
logs.push(
new Log({
address: await this.getLogEvmAddress(log.address, requestId) || log.address,
Expand Down
40 changes: 40 additions & 0 deletions packages/relay/tests/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ const mockData = {
},

tokenId: '0.0.13312',
tokenLongZero: '0x0000000000000000000000000000000000003400',
token: {
"admin_key": {
"_type": "ProtobufEncoded",
Expand Down Expand Up @@ -503,4 +504,43 @@ export const defaultDetailedContractResultByHash = {
"nonce": 1
};

export const buildCryptoTransferTransaction = (from, to, amount, args: any = {}) => {
return {
"bytes": null,
"charged_tx_fee": 2116872,
"consensus_timestamp": args.timestamp || "1669207658.365113311",
"entity_id": null,
"max_fee": "100000000",
"memo_base64": "UmVsYXkgdGVzdCB0b2tlbiB0cmFuc2Zlcg==",
"name": "CRYPTOTRANSFER",
"node": "0.0.8",
"nonce": 0,
"parent_consensus_timestamp": null,
"result": "SUCCESS",
"scheduled": false,
"token_transfers": [],
"transaction_hash": args.transactionHash || "OpCU4upAgJEBv2bjaoIurl4UYI4tuNA44ChtlKj+l0g0EvKbBpVI7lmnzeswVibQ",
"transaction_id": args.transactionId || "0.0.28527683-1669207645-620109637",
"transfers": [
{
"account": "0.0.8",
"amount": 99202,
"is_approval": false
},
{
"account": from,
"amount": -1 * amount,
"is_approval": false
},
{
"account": to,
"amount": amount,
"is_approval": false
}
],
"valid_duration_seconds": "120",
"valid_start_timestamp": "1669207645.620109637"
}
};

export const defaultErrorMessage = '0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d53657420746f2072657665727400000000000000000000000000000000000000';
Loading

0 comments on commit d9fc303

Please sign in to comment.