Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

774 request add fragment support for filtereventlogs #878

Merged
merged 14 commits into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions docs/contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,7 @@ Here is an example of how to delegate a contract call:
// Transferring 10000 tokens to another address with a delegated transaction
const transferResult = await contract.transact.transfer(
'0x9e7911de289c3c856ce7f421034f66b6cde49c39',
10000,
{
delegatorPrivateKey: delegatorAccount.privateKey
}
10000
);

// Wait for the transfer transaction to complete and obtain its receipt
Expand Down
5 changes: 1 addition & 4 deletions docs/examples/contracts/contract-delegation-ERC20.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,7 @@ const contract = await setupERC20Contract();
// Transferring 10000 tokens to another address with a delegated transaction
const transferResult = await contract.transact.transfer(
'0x9e7911de289c3c856ce7f421034f66b6cde49c39',
10000,
{
delegatorPrivateKey: delegatorAccount.privateKey
}
10000
);

// Wait for the transfer transaction to complete and obtain its receipt
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/thor-client/logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const thorClient = ThorClient.fromUrl(_testnetUrl);

// 2 - Filter event logs based on the provided criteria. (EXAMPLE 1)

const eventLogs = await thorClient.logs.filterEventLogs({
const eventLogs = await thorClient.logs.filterRawEventLogs({
// Specify the range of blocks to search for events
range: {
unit: 'block',
Expand Down
2 changes: 1 addition & 1 deletion docs/thor-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ const thorClient = ThorClient.fromUrl(_testnetUrl);

// 2 - Filter event logs based on the provided criteria. (EXAMPLE 1)

const eventLogs = await thorClient.logs.filterEventLogs({
const eventLogs = await thorClient.logs.filterRawEventLogs({
// Specify the range of blocks to search for events
range: {
unit: 'block',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const ethGetLogs = async (
});

// Call thor client to get logs
const logs: EventLogs[] = await thorClient.logs.filterEventLogs({
const logs: EventLogs[] = await thorClient.logs.filterRawEventLogs({
range: {
unit: 'block',
from:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,17 @@ class ContractsModule {
await signer.getAddress()
);

console.log(options);

// Build a transaction for calling the contract function
const txBody = await this.thor.transactions.buildTransactionBody(
[clause],
gasResult.totalGas,
options
);

console.log('txBody:', txBody);

// Sign the transaction
const result = await this._signContractTransaction(signer, txBody);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {
type EventCriteria,
type EventLogs,
type FilterEventLogsOptions,
type Range,
type PaginationOptions,
type EventDisplayOrder
import type {
Range,
PaginationOptions,
EventDisplayOrder,
FilterCriteria,
FilterEventLogsOptions,
EventLogs
} from '../../logs';
import { type Contract } from './contract';

Expand All @@ -20,15 +20,15 @@ class ContractFilter {
/**
* A set of criteria used to filter events.
*/
public criteriaSet: EventCriteria[];
public criteriaSet: FilterCriteria[];

/**
* Constructs an instance of the `ContractFilter` class.
*
* @param contract - The smart contract instance to apply the filter on.
* @param criteriaSet - A set of criteria used to filter events.
*/
constructor(contract: Contract, criteriaSet: EventCriteria[]) {
constructor(contract: Contract, criteriaSet: FilterCriteria[]) {
this.contract = contract;
this.criteriaSet = criteriaSet;
}
Expand Down
127 changes: 63 additions & 64 deletions packages/network/src/thor-client/contracts/model/contract-proxy.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import {
type ContractFunctionClause,
type ContractFunctionCriteria,
type ContractFunctionFilter,
type ContractFunctionRead,
type ContractFunctionTransact,
type TransactionValue
} from './types';
import {
type SendTransactionResult,
type SignTransactionOptions
} from '../../transactions';
import { type SendTransactionResult } from '../../transactions';
import { type Contract } from './contract';
import { buildError, ERROR_CODES } from '@vechain/sdk-errors';
import {
Expand All @@ -19,6 +17,7 @@ import {
import { type ContractCallResult } from '../types';
import { ContractFilter } from './contract-filter';
import { type VechainSigner } from '../../../signer';
import { type FilterCriteria } from '../../logs';

/**
* Creates a Proxy object for reading contract state, allowing for the dynamic invocation of contract read operations.
Expand Down Expand Up @@ -76,21 +75,11 @@ function getTransactProxy(contract: Contract): ContractFunctionTransact {
// check if the transaction value is provided as an argument
const transactionValue = getTransactionValue(args);

const signTransactionOptions = getSignTransactionOptions(args);

// if present remove the transaction value argument from the list of arguments
if (transactionValue !== undefined) {
args = args.filter((arg) => !isTransactionValue(arg));
}

// if present remove the sign transaction options argument from the list of arguments
if (signTransactionOptions !== undefined) {
args = args.filter((arg) => !isSignTransactionOptions(arg));
transactionOptions.signTransactionOptions =
signTransactionOptions;
transactionOptions.isDelegated = true;
}

return await contract.thor.contracts.executeTransaction(
contract.getSigner() as VechainSigner,
contract.address,
Expand Down Expand Up @@ -119,31 +108,9 @@ function getFilterProxy(contract: Contract): ContractFunctionFilter {
get: (_target, prop) => {
// Otherwise, assume that the function is a contract method
return (...args: unknown[]): ContractFilter => {
// Create the vechain sdk event fragment starting from the contract ABI event fragment
const eventFragment = new fragment.Event(
contract.getEventFragment(prop)
);

// Create a map of encoded filter topics for the event
const topics = new Map<number, string | undefined>(
eventFragment
.encodeFilterTopics(args)
.map((topic, index) => [index, topic])
);

// Create the criteria set for the contract filter
const criteriaSet = [
{
address: contract.address,
topic0: topics.get(0) as string, // the first topic is always defined since it's the event signature
topic1: topics.has(1) ? topics.get(1) : undefined,
topic2: topics.has(2) ? topics.get(2) : undefined,
topic3: topics.has(3) ? topics.get(3) : undefined,
topic4: topics.has(4) ? topics.get(4) : undefined
}
];
const criteriaSet = buildCriteria(contract, prop, args);

return new ContractFilter(contract, criteriaSet);
return new ContractFilter(contract, [criteriaSet]);
};
}
});
Expand All @@ -157,8 +124,6 @@ function getFilterProxy(contract: Contract): ContractFunctionFilter {
function getClauseProxy(contract: Contract): ContractFunctionClause {
return new Proxy(contract.clause, {
get: (_target, prop) => {
// Otherwise, assume that the function is a contract method

return (...args: unknown[]): TransactionClause => {
// get the transaction options for the contract
const transactionOptions =
Expand All @@ -184,6 +149,57 @@ function getClauseProxy(contract: Contract): ContractFunctionClause {
});
}

/**
* Create a proxy object for building event criteria for the event filtering.
* @param contract - The contract instance to create the criteria proxy for.
* @returns A Proxy that intercepts calls to build event criteria, automatically handling the invocation with the configured options.
*/
function getCriteriaProxy(contract: Contract): ContractFunctionCriteria {
return new Proxy(contract.criteria, {
get: (_target, prop) => {
return (...args: unknown[]): FilterCriteria => {
return buildCriteria(contract, prop, args);
};
}
});
}

/**
* Builds the filter criteria for the contract filter.
* @param contract - The contract instance to create the criteria for.
* @param prop - The property name of the contract event.
* @param args - The arguments to filter the event.
* @returns The event criteria for the contract filter.
*/
function buildCriteria(
contract: Contract,
prop: string | symbol,
args: unknown[]
): FilterCriteria {
// Create the vechain sdk event fragment starting from the contract ABI event fragment
const eventFragment = new fragment.Event(contract.getEventFragment(prop));

// Create a map of encoded filter topics for the event
const topics = new Map<number, string | undefined>(
eventFragment
.encodeFilterTopics(args)
.map((topic, index) => [index, topic])
);

// Create the criteria set for the contract filter
return {
criteria: {
address: contract.address,
topic0: topics.get(0) as string, // the first topic is always defined since it's the event signature
topic1: topics.has(1) ? topics.get(1) : undefined,
topic2: topics.has(2) ? topics.get(2) : undefined,
topic3: topics.has(3) ? topics.get(3) : undefined,
topic4: topics.has(4) ? topics.get(4) : undefined
},
eventFragment: eventFragment.fragment
};
}

/**
* Extracts the transaction value from the list of arguments, if present.
* @param args - The list of arguments to search for the transaction value.
Expand All @@ -195,18 +211,6 @@ function getTransactionValue(args: unknown[]): TransactionValue | undefined {
| undefined;
}

/**
* Extracts the sign transaction options from the list of arguments, if present.
* @param args - The list of arguments to search for the sign transaction options.
*/
function getSignTransactionOptions(
args: unknown[]
): SignTransactionOptions | undefined {
return args.find((arg) => isSignTransactionOptions(arg)) as
| SignTransactionOptions
| undefined;
}

/**
* Type guard function to check if an object is a TransactionValue.
* @param obj - The object to check.
Expand All @@ -216,15 +220,10 @@ function isTransactionValue(obj: unknown): obj is TransactionValue {
return (obj as TransactionValue).value !== undefined;
}

/**
* Type guard function to check if an object is a SignTransactionOptions.
* @param obj - The object to check.
*/
function isSignTransactionOptions(obj: unknown): obj is SignTransactionOptions {
return (
(obj as SignTransactionOptions).delegatorPrivateKey !== undefined ||
(obj as SignTransactionOptions).delegatorUrl !== undefined
);
}

export { getReadProxy, getTransactProxy, getFilterProxy, getClauseProxy };
export {
getReadProxy,
getTransactProxy,
getFilterProxy,
getClauseProxy,
getCriteriaProxy
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import type { ContractCallOptions, ContractTransactionOptions } from '../types';
import { buildError, ERROR_CODES } from '@vechain/sdk-errors';
import {
type ContractFunctionClause,
type ContractFunctionCriteria,
type ContractFunctionFilter,
type ContractFunctionRead,
type ContractFunctionTransact
} from './types';
import {
getClauseProxy,
getCriteriaProxy,
getFilterProxy,
getReadProxy,
getTransactProxy
Expand All @@ -37,6 +39,7 @@ class Contract {
public transact: ContractFunctionTransact = {};
public filters: ContractFunctionFilter = {};
public clause: ContractFunctionClause = {};
public criteria: ContractFunctionCriteria = {};

private contractCallOptions: ContractCallOptions = {};
private contractTransactionOptions: ContractTransactionOptions = {};
Expand Down Expand Up @@ -65,6 +68,7 @@ class Contract {
this.transact = getTransactProxy(this);
this.filters = getFilterProxy(this);
this.clause = getClauseProxy(this);
this.criteria = getCriteriaProxy(this);
}

/**
Expand Down
12 changes: 12 additions & 0 deletions packages/network/src/thor-client/contracts/model/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { ContractCallResult } from '../types';
import type { SendTransactionResult } from '../../transactions';
import { type ContractFilter } from './contract-filter';
import { type TransactionClause } from '@vechain/sdk-core';
import { type FilterCriteria } from '../../logs';

/**
* Represents a generic contract function type that accepts an arbitrary number of arguments
Expand Down Expand Up @@ -79,6 +80,16 @@ type ContractFunctionClause = Record<
ContractFunctionSync<TransactionClause>
>;

/**
* Defines a mapping of contract function names to their corresponding filter criteria contract functions.
* Each function in this record is expected to return a value of type `FilterCriteria`, which represents
* the criteria used to filter events emitted by the contract.
*/
type ContractFunctionCriteria = Record<
string,
ContractFunctionSync<FilterCriteria>
>;

/**
* Represents the amount of VET to transfer in a transaction.
*/
Expand All @@ -93,5 +104,6 @@ export type {
ContractFunctionTransact,
ContractFunctionFilter,
ContractFunctionClause,
ContractFunctionCriteria,
TransactionValue
};
Loading