Skip to content
This repository was archived by the owner on Mar 5, 2025. It is now read-only.

Commit 3e7f004

Browse files
Event filtering using non/indexed string arguments (#6167)
* Add MultiValueIndexedEventWithStringIndexed event and firesMultiValueIndexedEventWithStringIndexed function to Basic.sol * Refactor event filtering, add support for indexed strings, fix bug for filter using string values * Update packages/web3-eth-contract/src/contract.ts * Update CHANGELOGs * Init encode_event_abi tests
1 parent e2a64f8 commit 3e7f004

File tree

11 files changed

+555
-47
lines changed

11 files changed

+555
-47
lines changed

CHANGELOG.md

+10
Original file line numberDiff line numberDiff line change
@@ -1593,3 +1593,13 @@ Detailed List of changes are mentioned under:
15931593
If there are any bugs, improvements, optimizations or any new feature proposal feel free to create github issue, or post a pull request for contributions.
15941594

15951595
## [Unreleased]
1596+
1597+
### Fixed
1598+
1599+
#### web3-eth-abi
1600+
1601+
- Support for "decoding" indexed string event arguments (returns the keccak256 hash of the string value instead of the actual string value) (#6167)
1602+
1603+
#### web3-eth-contract
1604+
1605+
- Event filtering using non-indexed and indexed string event arguments (#6167)

fixtures/build/Basic.json

+49-1
Large diffs are not rendered by default.

fixtures/build/Basic.ts

+198-27
Large diffs are not rendered by default.

fixtures/contracts/Basic.sol

+5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ contract Basic {
1010
event StringEvent(string str);
1111
event MultiValueEvent(string str, uint256 val, bool flag);
1212
event MultiValueIndexedEvent(string str, uint256 indexed val, bool indexed flag);
13+
event MultiValueIndexedEventWithStringIndexed(string indexed str, uint256 indexed val, bool indexed flag);
1314

1415
constructor(uint256 _val, string memory _stringValue) {
1516
intValue = _val;
@@ -61,4 +62,8 @@ contract Basic {
6162
function firesStringEvent(string memory _str) public {
6263
emit StringEvent(_str);
6364
}
65+
66+
function firesMultiValueIndexedEventWithStringIndexed(string calldata str, uint256 val, bool flag) public {
67+
emit MultiValueIndexedEventWithStringIndexed(str, val, flag);
68+
}
6469
}

packages/web3-eth-abi/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,7 @@ Documentation:
113113
[Migration Guide from 1.x](https://docs.web3js.org/guides/web3_upgrade_guide/x/)
114114

115115
## [Unreleased]
116+
117+
### Fixed
118+
119+
- Support for "decoding" indexed string event arguments (returns the keccak256 hash of the string value instead of the actual string value) (#6167)

packages/web3-eth-abi/src/api/logs_api.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ import { decodeParameter, decodeParametersWith } from './parameters_api.js';
2020

2121
const STATIC_TYPES = ['bool', 'string', 'int', 'uint', 'address', 'fixed', 'ufixed'];
2222

23+
const _decodeParameter = (inputType: string, clonedTopic: string) =>
24+
inputType === 'string' ? clonedTopic : decodeParameter(inputType, clonedTopic);
25+
2326
/**
2427
* Decodes ABI-encoded log data and indexed topic data.
2528
* @param inputs - A {@link AbiParameter} input array. See the [Solidity documentation](https://docs.soliditylang.org/en/develop/types.html) for a list of types.
@@ -90,7 +93,7 @@ export const decodeLog = <ReturnType extends DecodedParams>(
9093

9194
const decodedIndexedInputs = Object.values(indexedInputs).map((input, index) =>
9295
STATIC_TYPES.some(s => input.type.startsWith(s))
93-
? decodeParameter(input.type, clonedTopics[index + offset])
96+
? _decodeParameter(input.type, clonedTopics[index + offset])
9497
: clonedTopics[index + offset],
9598
);
9699

packages/web3-eth-contract/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -273,3 +273,7 @@ Documentation:
273273
[Migration Guide from 1.x](https://docs.web3js.org/guides/web3_upgrade_guide/x/)
274274

275275
## [Unreleased]
276+
277+
### Fixed
278+
279+
- Event filtering using non-indexed and indexed string event arguments (#6167)

packages/web3-eth-contract/src/contract.ts

+30-17
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ import {
7171
Numbers,
7272
Web3ValidationErrorObject,
7373
} from 'web3-types';
74-
import { format, isDataFormat, toChecksumAddress } from 'web3-utils';
74+
import { format, isDataFormat, keccak256, toChecksumAddress } from 'web3-utils';
7575
import {
7676
isNullish,
7777
validator,
@@ -735,22 +735,35 @@ export class Contract<Abi extends ContractAbi>
735735

736736
const filter = options?.filter ?? {};
737737
const filterKeys = Object.keys(filter);
738-
return eventName === 'allEvents' && filterKeys.length > 0
739-
? decodedLogs.filter(log =>
740-
typeof log === 'string'
741-
? true
742-
: filterKeys.every((k: string) =>
743-
Array.isArray(filter[k])
744-
? (filter[k] as Numbers[]).some(
745-
(v: Numbers) =>
746-
String(log.returnValues[k]).toUpperCase() ===
747-
String(v).toUpperCase(),
748-
)
749-
: String(log.returnValues[k]).toUpperCase() ===
750-
String(filter[k]).toUpperCase(),
751-
),
752-
)
753-
: decodedLogs;
738+
739+
if (filterKeys.length > 0) {
740+
return decodedLogs.filter(log => {
741+
if (typeof log === 'string') return true;
742+
743+
return filterKeys.every((key: string) => {
744+
if (Array.isArray(filter[key])) {
745+
return (filter[key] as Numbers[]).some(
746+
(v: Numbers) =>
747+
String(log.returnValues[key]).toUpperCase() ===
748+
String(v).toUpperCase(),
749+
);
750+
}
751+
752+
const inputAbi = abi.inputs?.filter(input => input.name === key)[0];
753+
if (inputAbi?.indexed && inputAbi.type === 'string') {
754+
const hashedIndexedString = keccak256(filter[key] as string);
755+
if (hashedIndexedString === String(log.returnValues[key])) return true;
756+
}
757+
758+
return (
759+
String(log.returnValues[key]).toUpperCase() ===
760+
String(filter[key]).toUpperCase()
761+
);
762+
});
763+
});
764+
}
765+
766+
return decodedLogs;
754767
}
755768

756769
private _parseAndSetAddress(value?: Address, returnFormat: DataFormat = DEFAULT_RETURN_FORMAT) {

packages/web3-eth-contract/src/encoding.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ You should have received a copy of the GNU Lesser General Public License
1515
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
1616
*/
1717

18-
import { format, isNullish } from 'web3-utils';
18+
import { format, isNullish, keccak256 } from 'web3-utils';
1919

2020
import {
2121
AbiConstructorFragment,
@@ -101,6 +101,8 @@ export const encodeEventABI = (
101101
// TODO: deal properly with components
102102
if (Array.isArray(value)) {
103103
opts.topics.push(value.map(v => encodeParameter(input.type, v)));
104+
} else if (input.type === 'string') {
105+
opts.topics.push(keccak256(value as string));
104106
} else {
105107
opts.topics.push(encodeParameter(input.type, value));
106108
}

packages/web3-eth-contract/test/integration/contract_filter_events.test.ts

+43
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@ describe('contract getPastEvent filter', () => {
183183
await contractDeployed.methods
184184
.firesMultiValueIndexedEvent('str3', 3, true)
185185
.send(sendOptions);
186+
await contractDeployed.methods
187+
.firesMultiValueIndexedEventWithStringIndexed('str4', 4, true)
188+
.send(sendOptions);
186189
});
187190

188191
it('should filter one event by address with event name and filter param', async () => {
@@ -238,5 +241,45 @@ describe('contract getPastEvent filter', () => {
238241
expect(event1?.returnValues?.val).toBe(toBigInt(1));
239242
expect(event3?.returnValues?.val).toBe(toBigInt(3));
240243
});
244+
245+
it('should filter events using non-indexed string', async () => {
246+
const res: EventLog[] = (await contractDeployed.getPastEvents(
247+
'MultiValueIndexedEvent',
248+
{
249+
fromBlock: 'earliest',
250+
filter: {
251+
str: 'str2',
252+
},
253+
},
254+
)) as unknown as EventLog[];
255+
expect(res).toHaveLength(1);
256+
257+
const event = res[0];
258+
expect(event).toBeDefined();
259+
expect(event.returnValues.str).toBe('str2');
260+
expect(event.returnValues.val).toBe(BigInt(2));
261+
expect(event.returnValues.flag).toBeFalsy();
262+
});
263+
264+
it('should filter events using indexed string', async () => {
265+
const res: EventLog[] = (await contractDeployed.getPastEvents(
266+
'MultiValueIndexedEventWithStringIndexed',
267+
{
268+
fromBlock: 'earliest',
269+
filter: {
270+
str: 'str4',
271+
},
272+
},
273+
)) as unknown as EventLog[];
274+
expect(res).toHaveLength(1);
275+
276+
const event = res[0];
277+
expect(event).toBeDefined();
278+
expect(event.returnValues.str).toBe(
279+
'0x3f6d5d7b72c0059e2ecac56fd4adeefb2cff23aa41d13170f78ea6bf81e6e0ca',
280+
);
281+
expect(event.returnValues.val).toBe(BigInt(4));
282+
expect(event.returnValues.flag).toBeTruthy();
283+
});
241284
});
242285
});

0 commit comments

Comments
 (0)