Skip to content

Commit

Permalink
eth_getLogs method (#537)
Browse files Browse the repository at this point in the history
* fix: convert log return type to array and fix comments on types

* fix: rename helper parameter

* feat: implement eth_getLogs.ts

* fix: simplest approach is the best

* feat: tests

* fix: missing fixtures

* fix: changed name

* feat: edge cases

* fix: typo

* fix: better explanation

* fix: avoid code repetitions
  • Loading branch information
rodolfopietro97 authored Feb 6, 2024
1 parent 716bb75 commit 009d431
Show file tree
Hide file tree
Showing 12 changed files with 610 additions and 45 deletions.
8 changes: 4 additions & 4 deletions packages/network/src/thor-client/logs/logs-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class LogsModule {
*/
public async filterEventLogs(
filterOptions: FilterEventLogsOptions
): Promise<EventLogs> {
): Promise<EventLogs[]> {
return (await this.thor.httpClient.http(
'POST',
thorest.logs.post.EVENT_LOGS(),
Expand All @@ -35,7 +35,7 @@ class LogsModule {
body: filterOptions,
headers: {}
}
)) as EventLogs;
)) as EventLogs[];
}

/**
Expand All @@ -46,7 +46,7 @@ class LogsModule {
*/
public async filterTransferLogs(
filterOptions: FilterTransferLogsOptions
): Promise<TransferLogs> {
): Promise<TransferLogs[]> {
return (await this.thor.httpClient.http(
'POST',
thorest.logs.post.TRANSFER_LOGS(),
Expand All @@ -55,7 +55,7 @@ class LogsModule {
body: filterOptions,
headers: {}
}
)) as TransferLogs;
)) as TransferLogs[];
}
}

Expand Down
23 changes: 18 additions & 5 deletions packages/network/src/thor-client/logs/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,20 @@ interface Metadata {
* TransferCriteria interface for filtering transfer logs.
*/
interface TransferCriteria {
txOrigin?: string; // Transaction origin filter for transfer criteria.
sender?: string; // Sender's address filter.
recipient?: string; // Recipient's address filter.
/**
* Transaction origin filter for transfer criteria.
*/

txOrigin?: string;
/**
* Sender's address filter.
*/

sender?: string;
/**
* Recipient's address filter.
*/
recipient?: string;
}

/**
Expand Down Expand Up @@ -190,7 +201,7 @@ interface Transfer {
*/
interface EventLogs extends Event {
/**
* // Event logs with associated metadata
* Event logs with associated metadata
*/
meta: Metadata;
}
Expand All @@ -213,5 +224,7 @@ export type {
EventLogs,
FilterTransferLogsOptions,
Transfer,
TransferLogs
TransferLogs,
EventCriteria,
Range
};
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ enum RPC_METHODS {
eth_estimateGas = 'eth_estimateGas', // TEMPORARY COMMENT - TO IMPLEMENT
eth_call = 'eth_call', // TEMPORARY COMMENT - TO IMPLEMENT
eth_sendRawTransaction = 'eth_sendRawTransaction',
eth_getLogs = 'eth_getLogs', // TEMPORARY COMMENT - TO IMPLEMENT
eth_getLogs = 'eth_getLogs',
eth_getBlockByHash = 'eth_getBlockByHash',
eth_getBlockByNumber = 'eth_getBlockByNumber',
eth_accounts = 'eth_accounts',
Expand Down
123 changes: 123 additions & 0 deletions packages/provider/src/utils/formatter/logs/formatter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import {
type EventCriteria,
type EventLogs
} from '@vechain/vechain-sdk-network';
import { type LogsRPC } from './types';
import { vechain_sdk_core_ethers } from '@vechain/vechain-sdk-core';

/**
* Output formatter for Event logs.
* It converts the Event logs into the RPC standard.
*
* @param eventLogs - The Event logs to be formatted.
*/
const formatToLogsRPC = (eventLogs: EventLogs[]): LogsRPC[] => {
// Final RPC event logs formatted
return eventLogs.map((eventLog: EventLogs) => {
return {
transactionHash: eventLog.meta.txID,
blockHash: eventLog.meta.blockID,
blockNumber: vechain_sdk_core_ethers.toQuantity(
eventLog.meta.blockNumber
),
address: eventLog.address,
data: eventLog.data,
topics: eventLog.topics,

// Always false for now
removed: false,

// @NOTE: These two fields are not implemented yet. This for performance reasons.
//
/**
* @NOTE: These two fields are not implemented yet.
* This for performance reasons.
* We can implement them later if needed.
*
* To have these two fields, we need to query a block for each entry into the logs.
* After from the block, we can get the transaction index and the log index.
* This is a performance issue because we have to query a block for each entry into the logs.
*/
logIndex: '0x0',
transactionIndex: '0x0'
} satisfies LogsRPC;
});
};

/**
* Convert the criteria topics into an array of topics.
*
* This because the criteria topics are not an array of topics in vechain,
* but they are directly enumerated (topic0, topic1, topic2, topic3, topic4).
*
* RPC standard requires an array of topics instead.
*
* @param criteriaTopicsArray - The criteria topics array.
* @param address - The address to filter.
*/
const _scatterArrayTopic = (
criteriaTopicsArray: string[],
address?: string
): EventCriteria => {
return {
address,
topic0: criteriaTopicsArray[0] ?? undefined,
topic1: criteriaTopicsArray[1] ?? undefined,
topic2: criteriaTopicsArray[2] ?? undefined,
topic3: criteriaTopicsArray[3] ?? undefined,
topic4: criteriaTopicsArray[4] ?? undefined
};
};

/**
* Get the criteria set for the input.
*
* Basically with vechain swagger we have:
*
* {
* address = string | undefined;
* topic1: string | undefined;
* ...
* topic4: string | undefined;
* }
*
* With RPC we can have an array of address:
*
* {
* **address = string | string[] | undefined;**
* topic1: string | undefined;
* ...
* topic4: string | undefined;
* }.
*
* To have a complete research space, we can filter by address and topics, and only by address.
*
* @param criteria - The criteria input.
*/
const getCriteriaSetForInput = (criteria: {
address?: string | string[];
topics?: string[];
}): EventCriteria[] => {
// String to an array of addresses and topics
let criteriaAddress: string[] = [];
let criteriaTopics: string[] = [];

// Convert in any case to an array of addresses
if (criteria.address !== undefined)
criteriaAddress =
typeof criteria.address === 'string'
? [criteria.address]
: criteria.address;

// Convert in any case to an array of topics
if (criteria.topics !== undefined) criteriaTopics = criteria.topics;

// Filtering considering the address and topics. For each address, we have to consider the topics
return criteriaAddress.length > 0
? criteriaAddress.map((addr: string) => {
return _scatterArrayTopic(criteriaTopics, addr);
})
: [_scatterArrayTopic(criteriaTopics)];
};

export { formatToLogsRPC, getCriteriaSetForInput };
2 changes: 2 additions & 0 deletions packages/provider/src/utils/formatter/logs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './formatter';
export * from './types.d';
51 changes: 51 additions & 0 deletions packages/provider/src/utils/formatter/logs/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* Return type of logs according to the Ethereum RPC standard.
*/
interface LogsRPC {
/**
* (boolean) true when the log was removed, due to a chain reorganization. false if it's a valid log.
*/
removed: boolean;

/**
* Hexadecimal of the log index position in the block. Null when it is a pending log.
*/
logIndex: string;

/**
* Hexadecimal of the transaction index position from which the log created. Null when it is a pending log.
*/
transactionIndex: string;

/**
* 32 bytes. Hash of the transactions from which this log was created. Null when it is a pending log.
*/
transactionHash: string;

/**
* 32 bytes. Hash of the block where this log was in. Null when it is a pending log.
*/
blockHash: string;

/**
* Block number where this log was in. Null when it is a pending log.
*/
blockNumber: string;

/**
* 20 bytes. Address from which this log originated.
*/
address: string;

/**
* Contains one or more 32-bytes non-indexed arguments of the log.
*/
data: string;

/**
* An array of 0 to 4 indexed log arguments, each 32 bytes. In solidity the first topic is the hash of the signature of the event (e.g. Deposit(address,bytes32,uint256)), except when you declared the event with the anonymous specifier.
*/
topics: string[];
}

export { type LogsRPC };
6 changes: 3 additions & 3 deletions packages/provider/src/utils/helpers/transaction-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,18 @@ const getTransactionIndexIntoBlock = (
* Get the number of logs ahead of a transaction into a block.
*
* @param blockExpanded - The block to search in.
* @param hash - The hash of the transaction to search for.
* @param transactionId - The hash of the transaction to search for.
* @param chainId - The chain ID of the network.
*/
const getNumberOfLogsAheadOfTransactionIntoBlockExpanded = (
blockExpanded: BlockDetail,
hash: string,
transactionId: string,
chainId: string
): number => {
// Get transaction index into the block
const transactionIndex = getTransactionIndexIntoBlock(
blocksFormatter.formatToRPCStandard(blockExpanded, chainId),
hash
transactionId
);

// Count the number of logs in the txs whose number is lower than txId
Expand Down
Loading

0 comments on commit 009d431

Please sign in to comment.