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

Add events to receipt object #6410

Merged
Show file tree
Hide file tree
Changes from 3 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
26 changes: 0 additions & 26 deletions packages/web3-eth-contract/src/constants.ts

This file was deleted.

29 changes: 20 additions & 9 deletions packages/web3-eth-contract/src/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ import {
NewHeadsSubscription,
sendTransaction,
SendTransactionEvents,
ALL_EVENTS,
ALL_EVENTS_ABI,
decodeEventABI,
} from 'web3-eth';
import {
encodeEventSignature,
Expand Down Expand Up @@ -76,6 +79,9 @@ import {
DEFAULT_RETURN_FORMAT,
Numbers,
Web3ValidationErrorObject,
EventLog,
ContractAbiWithSignature,
ContractOptions,
} from 'web3-types';
import { format, isDataFormat, keccak256, toChecksumAddress } from 'web3-utils';
import {
Expand All @@ -85,14 +91,10 @@ import {
ValidationSchemaInput,
Web3ValidatorError,
} from 'web3-validator';
import { ALL_EVENTS, ALL_EVENTS_ABI } from './constants.js';
import { decodeEventABI, decodeMethodReturn, encodeEventABI, encodeMethodABI } from './encoding.js';
import { decodeMethodReturn, encodeEventABI, encodeMethodABI } from './encoding.js';
import { LogsSubscription } from './log_subscription.js';
import {
ContractAbiWithSignature,
ContractEventOptions,
ContractOptions,
EventLog,
NonPayableMethodObject,
NonPayableTxOptions,
PayableMethodObject,
Expand Down Expand Up @@ -683,24 +685,29 @@ export class Contract<Abi extends ContractAbi>
returnFormat?: ReturnFormat,
): Promise<(string | EventLog)[]>;
public async getPastEvents<ReturnFormat extends DataFormat = typeof DEFAULT_RETURN_FORMAT>(
eventName: keyof ContractEvents<Abi> | 'allEvents',
eventName: keyof ContractEvents<Abi> | 'allEvents' | 'ALLEVENTS',
returnFormat?: ReturnFormat,
): Promise<(string | EventLog)[]>;
public async getPastEvents<ReturnFormat extends DataFormat = typeof DEFAULT_RETURN_FORMAT>(
filter: Omit<Filter, 'address'>,
returnFormat?: ReturnFormat,
): Promise<(string | EventLog)[]>;
public async getPastEvents<ReturnFormat extends DataFormat = typeof DEFAULT_RETURN_FORMAT>(
eventName: keyof ContractEvents<Abi> | 'allEvents',
eventName: keyof ContractEvents<Abi> | 'allEvents' | 'ALLEVENTS',
filter: Omit<Filter, 'address'>,
returnFormat?: ReturnFormat,
): Promise<(string | EventLog)[]>;
public async getPastEvents<ReturnFormat extends DataFormat = typeof DEFAULT_RETURN_FORMAT>(
param1?: keyof ContractEvents<Abi> | 'allEvents' | Omit<Filter, 'address'> | ReturnFormat,
param1?:
| keyof ContractEvents<Abi>
| 'allEvents'
| 'ALLEVENTS'
| Omit<Filter, 'address'>
| ReturnFormat,
param2?: Omit<Filter, 'address'> | ReturnFormat,
param3?: ReturnFormat,
): Promise<(string | EventLog)[]> {
const eventName = typeof param1 === 'string' ? param1 : 'allEvents';
const eventName = typeof param1 === 'string' ? param1 : ALL_EVENTS;

const options =
// eslint-disable-next-line no-nested-ternary
Expand All @@ -727,11 +734,13 @@ export class Contract<Abi extends ContractAbi>
if (!abi) {
throw new Web3ContractError(`Event ${eventName} not found.`);
}

const { fromBlock, toBlock, topics, address } = encodeEventABI(
this.options,
abi,
options ?? {},
);

const logs = await getLogs(this, { fromBlock, toBlock, topics, address }, returnFormat);
const decodedLogs = logs.map(log =>
typeof log === 'string'
Expand Down Expand Up @@ -1052,6 +1061,7 @@ export class Contract<Abi extends ContractAbi>
const transactionToSend = sendTransaction(this, tx, DEFAULT_RETURN_FORMAT, {
// TODO Should make this configurable by the user
checkRevertBeforeSending: false,
contractAbi: this._jsonInterface,
});

// eslint-disable-next-line no-void
Expand Down Expand Up @@ -1096,6 +1106,7 @@ export class Contract<Abi extends ContractAbi>
newContract.options.address = receipt.contractAddress;
return newContract;
},
contractAbi: this._jsonInterface,
// TODO Should make this configurable by the user
checkRevertBeforeSending: false,
});
Expand Down
76 changes: 5 additions & 71 deletions packages/web3-eth-contract/src/encoding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,15 @@ import {
AbiConstructorFragment,
AbiEventFragment,
AbiFunctionFragment,
LogsInput,
Filter,
HexString,
Topic,
FMT_NUMBER,
FMT_BYTES,
DataFormat,
DEFAULT_RETURN_FORMAT,
ContractOptions,
} from 'web3-types';

import {
decodeLog,
decodeParameters,
encodeEventSignature,
encodeFunctionSignature,
Expand All @@ -42,13 +39,10 @@ import {
jsonInterfaceMethodToString,
} from 'web3-eth-abi';

import { blockSchema, logSchema } from 'web3-eth';
import { blockSchema, decodeEventABI as _decodeEventABI, ALL_EVENTS } from 'web3-eth';

import { Web3ContractError } from 'web3-errors';

// eslint-disable-next-line import/no-cycle
import { ContractOptions, ContractAbiWithSignature, EventLog } from './types.js';

type Writeable<T> = { -readonly [P in keyof T]: T[P] };
export const encodeEventABI = (
{ address }: ContractOptions,
Expand Down Expand Up @@ -77,14 +71,14 @@ export const encodeEventABI = (
} else {
opts.topics = [];
// add event signature
if (event && !event.anonymous && event.name !== 'ALLEVENTS') {
if (event && !event.anonymous && ![ALL_EVENTS, 'allEvents'].includes(event.name)) {
opts.topics.push(
event.signature ?? encodeEventSignature(jsonInterfaceMethodToString(event)),
);
}

// add event topics (indexed arguments)
if (event.name !== 'ALLEVENTS' && event.inputs) {
if (![ALL_EVENTS, 'allEvents'].includes(event.name) && event.inputs) {
for (const input of event.inputs) {
if (!input.indexed) {
continue;
Expand Down Expand Up @@ -119,67 +113,7 @@ export const encodeEventABI = (
return opts;
};

export const decodeEventABI = (
event: AbiEventFragment & { signature: string },
data: LogsInput,
jsonInterface: ContractAbiWithSignature,
returnFormat: DataFormat = DEFAULT_RETURN_FORMAT,
): EventLog => {
let modifiedEvent = { ...event };

const result = format(logSchema, data, returnFormat);

// if allEvents get the right event
if (modifiedEvent.name === 'ALLEVENTS') {
const matchedEvent = jsonInterface.find(j => j.signature === data.topics[0]);
if (matchedEvent) {
modifiedEvent = matchedEvent as AbiEventFragment & { signature: string };
} else {
modifiedEvent = { anonymous: true } as unknown as AbiEventFragment & {
signature: string;
};
}
}

// create empty inputs if none are present (e.g. anonymous events on allEvents)
modifiedEvent.inputs = modifiedEvent.inputs ?? event.inputs ?? [];

// Handle case where an event signature shadows the current ABI with non-identical
// arg indexing. If # of topics doesn't match, event is anon.
if (!modifiedEvent.anonymous) {
let indexedInputs = 0;
(modifiedEvent.inputs ?? []).forEach(input => {
if (input.indexed) {
indexedInputs += 1;
}
});

if (indexedInputs > 0 && data?.topics && data?.topics.length !== indexedInputs + 1) {
// checks if event is anonymous
modifiedEvent = {
...modifiedEvent,
anonymous: true,
inputs: [],
};
}
}

const argTopics = modifiedEvent.anonymous ? data.topics : (data.topics ?? []).slice(1);
return {
...result,
returnValues: decodeLog([...(modifiedEvent.inputs ?? [])], data.data, argTopics),
event: modifiedEvent.name,
signature:
modifiedEvent.anonymous || !data.topics || data.topics.length === 0 || !data.topics[0]
? undefined
: data.topics[0],

raw: {
data: data.data,
topics: data.topics,
},
};
};
export const decodeEventABI = _decodeEventABI;

export const encodeMethodABI = (
abi: AbiFunctionFragment | AbiConstructorFragment,
Expand Down
15 changes: 10 additions & 5 deletions packages/web3-eth-contract/src/log_subscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,17 @@ You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/

import { AbiEventFragment, LogsInput, HexString, Topic, DataFormat } from 'web3-types';
import {
AbiEventFragment,
LogsInput,
HexString,
Topic,
DataFormat,
EventLog,
ContractAbiWithSignature,
} from 'web3-types';
import { Web3RequestManager, Web3Subscription, Web3SubscriptionManager } from 'web3-core';
// eslint-disable-next-line import/no-cycle
import { decodeEventABI } from './encoding.js';
// eslint-disable-next-line import/no-cycle
import { EventLog, ContractAbiWithSignature } from './types.js';
import { decodeEventABI } from 'web3-eth';

/**
* LogSubscription to be used to subscribe to events logs.
Expand Down
94 changes: 0 additions & 94 deletions packages/web3-eth-contract/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,37 +29,13 @@ import {
DataFormat,
DEFAULT_RETURN_FORMAT,
FormatType,
AbiFragment,
Address,
Bytes,
ContractAbi,
HexString32Bytes,
Uint,
} from 'web3-types';
// eslint-disable-next-line import/no-cycle
import { LogsSubscription } from './log_subscription.js';

export type NonPayableTxOptions = NonPayableCallOptions;
export type PayableTxOptions = PayableCallOptions;

export type ContractAbiWithSignature = ReadonlyArray<AbiFragment & { signature: HexString }>;

export interface EventLog {
readonly event: string;
readonly id?: string;
readonly logIndex?: bigint | number | string;
readonly transactionIndex?: bigint | number | string;
readonly transactionHash?: HexString32Bytes;
readonly blockHash?: HexString32Bytes;
readonly blockNumber?: bigint | number | string;
readonly address: string;
readonly topics: HexString[];
readonly data: HexString;
readonly raw?: { data: string; topics: unknown[] };
readonly returnValues: Record<string, unknown>;
readonly signature?: HexString;
}

export interface ContractEventOptions {
/**
* Let you filter events by indexed parameters, e.g. `{filter: {myNumber: [12,13]}}` means all events where `myNumber` is `12` or `13`.
Expand All @@ -75,76 +51,6 @@ export interface ContractEventOptions {
topics?: string[];
}

export interface ContractOptions {
avkos marked this conversation as resolved.
Show resolved Hide resolved
/**
* The maximum gas provided for a transaction (gas limit).
*/
readonly gas?: Uint;
/**
* The gas price in wei to use for transactions.
*/
readonly gasPrice?: Uint;
/**
* The address transactions should be made from.
*/
readonly from?: Address;
/**
* The byte code of the contract. Used when the contract gets {@link Contract.deploy | deployed}
*/
readonly input?: Bytes;
/**
* The {@doclink glossary/json_interface | json interface} object derived from the [ABI](https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI) of this contract.
*
* Re-setting this will regenerate the methods and events of the contract instance.
*
* ```ts
* myContract.options.jsonInterface;
* > [{
* "type":"function",
* "name":"foo",
* "inputs": [{"name":"a","type":"uint256"}],
* "outputs": [{"name":"b","type":"address"}],
* "signature": "0x...",
* },{
* "type":"event",
* "name":"Event",
* "inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}],
* "signature": "0x...",
* }]
*
* // Set a new ABI interface
* // Note: the "signature" of every function and event's ABI is not needed to be provided when assigning.
* // It will be calculated and set automatically inside the setter.
* myContract.options.jsonInterface = [...];
* ```
*/
get jsonInterface(): ContractAbiWithSignature;
set jsonInterface(value: ContractAbi);

/**
* The address used for this contract instance. All transactions generated by web3.js from this contract will contain this address as the `to`.
*
* The address will be stored in lowercase.
*
* ```ts
* myContract.options.address;
* > '0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae'
*
* // set a new address
* myContract.options.address = '0x1234FFDD...';
* ```
*/
address?: Address; // All transactions generated by web3.js from this contract will contain this address as the "to".
/**
* The max priority fee per gas to use for transactions.
*/
maxPriorityFeePerGas?: Uint;
/**
* The max fee per gas to use for transactions.
*/
maxFeePerGas?: Uint;
}

export interface NonPayableMethodObject<Inputs = unknown[], Outputs = unknown[]> {
arguments: Inputs;
/**
Expand Down
3 changes: 2 additions & 1 deletion packages/web3-eth-contract/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ import {
NonPayableCallOptions,
PayableCallOptions,
ContractInitOptions,
ContractOptions,
} from 'web3-types';
import { isNullish, mergeDeep, toHex } from 'web3-utils';
import { encodeMethodABI } from './encoding.js';
import { ContractOptions, Web3ContractContext } from './types.js';
import { Web3ContractContext } from './types.js';
avkos marked this conversation as resolved.
Show resolved Hide resolved

export const getSendTxParams = ({
abi,
Expand Down
Loading