Skip to content

Commit

Permalink
Event filtering using non/indexed string arguments (#6167)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
spacesailor24 authored Jun 9, 2023
1 parent e2a64f8 commit 3e7f004
Show file tree
Hide file tree
Showing 11 changed files with 555 additions and 47 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1593,3 +1593,13 @@ Detailed List of changes are mentioned under:
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.

## [Unreleased]

### Fixed

#### web3-eth-abi

- Support for "decoding" indexed string event arguments (returns the keccak256 hash of the string value instead of the actual string value) (#6167)

#### web3-eth-contract

- Event filtering using non-indexed and indexed string event arguments (#6167)
50 changes: 49 additions & 1 deletion fixtures/build/Basic.json

Large diffs are not rendered by default.

225 changes: 198 additions & 27 deletions fixtures/build/Basic.ts

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions fixtures/contracts/Basic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ contract Basic {
event StringEvent(string str);
event MultiValueEvent(string str, uint256 val, bool flag);
event MultiValueIndexedEvent(string str, uint256 indexed val, bool indexed flag);
event MultiValueIndexedEventWithStringIndexed(string indexed str, uint256 indexed val, bool indexed flag);

constructor(uint256 _val, string memory _stringValue) {
intValue = _val;
Expand Down Expand Up @@ -61,4 +62,8 @@ contract Basic {
function firesStringEvent(string memory _str) public {
emit StringEvent(_str);
}

function firesMultiValueIndexedEventWithStringIndexed(string calldata str, uint256 val, bool flag) public {
emit MultiValueIndexedEventWithStringIndexed(str, val, flag);
}
}
4 changes: 4 additions & 0 deletions packages/web3-eth-abi/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,7 @@ Documentation:
[Migration Guide from 1.x](https://docs.web3js.org/guides/web3_upgrade_guide/x/)

## [Unreleased]

### Fixed

- Support for "decoding" indexed string event arguments (returns the keccak256 hash of the string value instead of the actual string value) (#6167)
5 changes: 4 additions & 1 deletion packages/web3-eth-abi/src/api/logs_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import { decodeParameter, decodeParametersWith } from './parameters_api.js';

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

const _decodeParameter = (inputType: string, clonedTopic: string) =>
inputType === 'string' ? clonedTopic : decodeParameter(inputType, clonedTopic);

/**
* Decodes ABI-encoded log data and indexed topic data.
* @param inputs - A {@link AbiParameter} input array. See the [Solidity documentation](https://docs.soliditylang.org/en/develop/types.html) for a list of types.
Expand Down Expand Up @@ -90,7 +93,7 @@ export const decodeLog = <ReturnType extends DecodedParams>(

const decodedIndexedInputs = Object.values(indexedInputs).map((input, index) =>
STATIC_TYPES.some(s => input.type.startsWith(s))
? decodeParameter(input.type, clonedTopics[index + offset])
? _decodeParameter(input.type, clonedTopics[index + offset])
: clonedTopics[index + offset],
);

Expand Down
4 changes: 4 additions & 0 deletions packages/web3-eth-contract/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,3 +273,7 @@ Documentation:
[Migration Guide from 1.x](https://docs.web3js.org/guides/web3_upgrade_guide/x/)

## [Unreleased]

### Fixed

- Event filtering using non-indexed and indexed string event arguments (#6167)
47 changes: 30 additions & 17 deletions packages/web3-eth-contract/src/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ import {
Numbers,
Web3ValidationErrorObject,
} from 'web3-types';
import { format, isDataFormat, toChecksumAddress } from 'web3-utils';
import { format, isDataFormat, keccak256, toChecksumAddress } from 'web3-utils';
import {
isNullish,
validator,
Expand Down Expand Up @@ -735,22 +735,35 @@ export class Contract<Abi extends ContractAbi>

const filter = options?.filter ?? {};
const filterKeys = Object.keys(filter);
return eventName === 'allEvents' && filterKeys.length > 0
? decodedLogs.filter(log =>
typeof log === 'string'
? true
: filterKeys.every((k: string) =>
Array.isArray(filter[k])
? (filter[k] as Numbers[]).some(
(v: Numbers) =>
String(log.returnValues[k]).toUpperCase() ===
String(v).toUpperCase(),
)
: String(log.returnValues[k]).toUpperCase() ===
String(filter[k]).toUpperCase(),
),
)
: decodedLogs;

if (filterKeys.length > 0) {
return decodedLogs.filter(log => {
if (typeof log === 'string') return true;

return filterKeys.every((key: string) => {
if (Array.isArray(filter[key])) {
return (filter[key] as Numbers[]).some(
(v: Numbers) =>
String(log.returnValues[key]).toUpperCase() ===
String(v).toUpperCase(),
);
}

const inputAbi = abi.inputs?.filter(input => input.name === key)[0];
if (inputAbi?.indexed && inputAbi.type === 'string') {
const hashedIndexedString = keccak256(filter[key] as string);
if (hashedIndexedString === String(log.returnValues[key])) return true;
}

return (
String(log.returnValues[key]).toUpperCase() ===
String(filter[key]).toUpperCase()
);
});
});
}

return decodedLogs;
}

private _parseAndSetAddress(value?: Address, returnFormat: DataFormat = DEFAULT_RETURN_FORMAT) {
Expand Down
4 changes: 3 additions & 1 deletion packages/web3-eth-contract/src/encoding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ 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 { format, isNullish } from 'web3-utils';
import { format, isNullish, keccak256 } from 'web3-utils';

import {
AbiConstructorFragment,
Expand Down Expand Up @@ -101,6 +101,8 @@ export const encodeEventABI = (
// TODO: deal properly with components
if (Array.isArray(value)) {
opts.topics.push(value.map(v => encodeParameter(input.type, v)));
} else if (input.type === 'string') {
opts.topics.push(keccak256(value as string));
} else {
opts.topics.push(encodeParameter(input.type, value));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@ describe('contract getPastEvent filter', () => {
await contractDeployed.methods
.firesMultiValueIndexedEvent('str3', 3, true)
.send(sendOptions);
await contractDeployed.methods
.firesMultiValueIndexedEventWithStringIndexed('str4', 4, true)
.send(sendOptions);
});

it('should filter one event by address with event name and filter param', async () => {
Expand Down Expand Up @@ -238,5 +241,45 @@ describe('contract getPastEvent filter', () => {
expect(event1?.returnValues?.val).toBe(toBigInt(1));
expect(event3?.returnValues?.val).toBe(toBigInt(3));
});

it('should filter events using non-indexed string', async () => {
const res: EventLog[] = (await contractDeployed.getPastEvents(
'MultiValueIndexedEvent',
{
fromBlock: 'earliest',
filter: {
str: 'str2',
},
},
)) as unknown as EventLog[];
expect(res).toHaveLength(1);

const event = res[0];
expect(event).toBeDefined();
expect(event.returnValues.str).toBe('str2');
expect(event.returnValues.val).toBe(BigInt(2));
expect(event.returnValues.flag).toBeFalsy();
});

it('should filter events using indexed string', async () => {
const res: EventLog[] = (await contractDeployed.getPastEvents(
'MultiValueIndexedEventWithStringIndexed',
{
fromBlock: 'earliest',
filter: {
str: 'str4',
},
},
)) as unknown as EventLog[];
expect(res).toHaveLength(1);

const event = res[0];
expect(event).toBeDefined();
expect(event.returnValues.str).toBe(
'0x3f6d5d7b72c0059e2ecac56fd4adeefb2cff23aa41d13170f78ea6bf81e6e0ca',
);
expect(event.returnValues.val).toBe(BigInt(4));
expect(event.returnValues.flag).toBeTruthy();
});
});
});
Loading

0 comments on commit 3e7f004

Please sign in to comment.