Skip to content

Commit

Permalink
Filter option doesn't work in getPastEvents (#6015)
Browse files Browse the repository at this point in the history
* add filter and tests

* add topic filter and tests

* add filter for all events and tests

* add changelog

* fix tests

* fix events

* fix tests

* fix tests

* revert spaces
  • Loading branch information
avkos committed Apr 20, 2023
1 parent 48958ee commit 195cd3c
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 43 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -672,3 +672,6 @@ Released with 1.0.0-beta.37 code base.

- `transaction.type` is now formatted to a hex string before being send to provider (#5979)
- When sending a transaction, if `transaction.type === '0x1' && transaction.accessList === undefined`, then `transaction.accessList` is set to `[]` (#5979)

### Added
- Added support for `getPastEvents` method to filter `allEvents` and specific event (#6015)
77 changes: 49 additions & 28 deletions packages/web3-eth-contract/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ var Contract = function Contract(jsonInterface, address, options) {
_this.options.blockHeaderTimeout = val;
},
enumerable: true
});
});
Object.defineProperty(this, 'defaultAccount', {
get: function () {
return defaultAccount;
Expand Down Expand Up @@ -415,46 +415,44 @@ Contract.prototype._encodeEventABI = function (event, options) {
result[f] = formatters.inputBlockNumberFormatter(options[f]);
});

// use given topics
if(Array.isArray(options.topics)) {
result.topics = options.topics;

// create topics based on filter
let topics = []
if (options.topics && Array.isArray(options.topics)) {
topics = [...options.topics];
} else {

result.topics = [];

topics = [];
// add event signature
if (event && !event.anonymous && event.name !== 'ALLEVENTS') {
result.topics.push(event.signature);
topics.push(
event.signature || abi.encodeEventSignature(utils.jsonInterfaceMethodToString(event)),
);
}

// add event topics (indexed arguments)
if (event.name !== 'ALLEVENTS') {
var indexedTopics = event.inputs.filter(function (i) {
return i.indexed === true;
}).map(function (i) {
var value = filter[i.name];
if (!value) {
return null;
if (event.name !== 'ALLEVENTS' && event.inputs) {
for (const input of event.inputs) {
if (!input.indexed) {
continue;
}

// TODO: https://github.com/ethereum/web3.js/issues/344
// TODO: deal properly with components
const value = filter[input.name];
if (!value) {
// eslint-disable-next-line no-null/no-null
topics.push(null);
continue;
}

if (Array.isArray(value)) {
return value.map(function (v) {
return abi.encodeParameter(i.type, v);
});
topics.push(value.map(v => abi.encodeParameter(input.type, v)));
} else {
topics.push(abi.encodeParameter(input.type, value));
}
return abi.encodeParameter(i.type, value);
});

result.topics = result.topics.concat(indexedTopics);
}
}
}

if(!result.topics.length)
delete result.topics;
if(topics.length) {
result.topics = topics
}

if(this.options.address) {
Expand Down Expand Up @@ -682,6 +680,7 @@ Contract.prototype._generateEventOptions = function() {
return {
params: this._encodeEventABI(event, options),
event: event,
filter: options.filter || {},
callback: callback
};
};
Expand Down Expand Up @@ -784,6 +783,24 @@ Contract.prototype._on = function(){
return subscription;
};

const filterAllEventsResults = (subOptions, data) => {
if (subOptions.event && subOptions.event.name === 'ALLEVENTS' && Array.isArray(data)) {
const filter = subOptions.filter || {};
const filterKeys = Object.keys(filter);
return filterKeys.length > 0
? data.filter(log => typeof log === 'string' ? true : filterKeys.every((k) => Array.isArray(filter[k]) ? (filter[k]).some(
(v) =>
String(log.returnValues[k]).toUpperCase() ===
String(v).toUpperCase(),
) : (
String(log.returnValues[k]).toUpperCase() ===
String(filter[k]).toUpperCase()
)),
)
: data;
}
return data;
};
/**
* Get past events from contracts
*
Expand All @@ -808,7 +825,11 @@ Contract.prototype.getPastEvents = function(){

getPastLogs = null;

return call(subOptions.params, subOptions.callback);
return call(subOptions.params, (err, data)=>{
if(typeof subOptions.callback === 'function'){
subOptions.callback(err, filterAllEventsResults(subOptions, data))
}
}).then(filterAllEventsResults.bind(this, subOptions));
};


Expand Down
127 changes: 112 additions & 15 deletions test/e2e.contract.events.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ var Parent = require('./sources/Parent');
var utils = require('./helpers/test.utils');
var Web3 = utils.getWeb3();

const prepareEvents = async (instance,address) => {
await instance
.methods
.firesEvent(address, 1)
.send({from: address});
await instance
.methods
.firesEvent(address, 2)
.send({from: address});
await instance
.methods
.firesEvent(address, 3)
.send({from: address});
}
describe('contract.events [ @E2E ]', function() {
// `getPastEvents` not working with Geth instamine over websockets.
if (process.env.GETH_INSTAMINE) return;
Expand Down Expand Up @@ -33,27 +47,112 @@ describe('contract.events [ @E2E ]', function() {
});

it('contract.getPastEvents', async function(){
await instance
.methods
.firesEvent(accounts[0], 1)
.send({from: accounts[0]});

await instance
.methods
.firesEvent(accounts[0], 2)
.send({from: accounts[0]});

await prepareEvents(instance, accounts[0]);
const events = await instance.getPastEvents({
fromBlock: 0,
toBlock: 'latest'
});

assert.equal(events.length, 2);
assert.equal(events.length, 3);
assert.equal(events[0].event, 'BasicEvent');
assert.equal(events[1].event, 'BasicEvent');
assert.equal(events[2].event, 'BasicEvent');
assert.notEqual(events[0].id, events[1].id);
});

it('contract.getPastEvents filter by val', async function() {
await prepareEvents(instance, accounts[0]);
const events = await instance.getPastEvents('BasicEvent', {
filter: { val: 2 },
fromBlock: 'earliest',
toBlock: 'latest',
});
assert.equal(events.length, 1);
assert.equal(events[0].returnValues.val, 2);
});

it('contract.getPastEvents without specify event name: filter by val', async function() {
await prepareEvents(instance, accounts[0]);
const events = await instance.getPastEvents({
filter: { val: 2 },
fromBlock: 'earliest',
toBlock: 'latest',
});
assert.equal(events.length, 1);
assert.equal(events[0].returnValues.val, 2);
});

it('contract.getPastEvents all events: filter by val', async function() {
await prepareEvents(instance, accounts[0]);
const events = await instance.getPastEvents('allEvents', {
filter: { val: 2 },
fromBlock: 'earliest',
toBlock: 'latest',
});
assert.equal(events.length, 1);
assert.equal(events[0].returnValues.val, 2);
});

it('contract.getPastEvents filter by val different value', async function() {
await prepareEvents(instance, accounts[0]);
const events = await instance.getPastEvents('BasicEvent', {
filter: { val: 3 },
fromBlock: 'earliest',
toBlock: 'latest',
});
assert.equal(events.length, 1);
assert.equal(events[0].returnValues.val, 3);
});

it('contract.getPastEvents filter by array', async function() {
await prepareEvents(instance, accounts[0]);
const events = await instance.getPastEvents('BasicEvent', {
filter: { val: [2, 3] },
fromBlock: 'earliest',
toBlock: 'latest',
});
assert.equal(events.length, 2);
assert.equal(events[0].returnValues.val, 2);
assert.equal(events[1].returnValues.val, 3);
});

it('contract.getPastEvents allEvents: filter by array', async function() {
await prepareEvents(instance, accounts[0]);
const events = await instance.getPastEvents('allEvents', {
filter: { val: [2, 3] },
fromBlock: 'earliest',
toBlock: 'latest',
});
assert.equal(events.length, 2);
assert.equal(events[0].returnValues.val, 2);
assert.equal(events[1].returnValues.val, 3);
});

it('contract.getPastEvents allEvents: filter by array using callback', async function() {
await prepareEvents(instance, accounts[0]);
instance.getPastEvents('allEvents', {
filter: { val: [2, 3] },
fromBlock: 'earliest',
toBlock: 'latest',
}, (err, events) => {
assert.equal(events.length, 2);
assert.equal(events[0].returnValues.val, 2);
assert.equal(events[1].returnValues.val, 3);
});
});

it('contract.getPastEvents filter by val using callback', async function() {
await prepareEvents(instance, accounts[0]);
instance.getPastEvents('BasicEvent', {
filter: { val: 3 },
fromBlock: 'earliest',
toBlock: 'latest',
}, (err, events) => {
assert.equal(events.length, 1);
assert.equal(events[0].returnValues.val, 3);
});
});

it('contract.events.<eventName>', function(){
return new Promise(async resolve => {
instance
Expand Down Expand Up @@ -91,7 +190,6 @@ describe('contract.events [ @E2E ]', function() {
this.removeAllListeners();
resolve();
});

assert.equal(message, 'Invalid option: toBlock. Use getPastEvents for specific range.');
console.warn = originalWarn

Expand All @@ -106,7 +204,6 @@ describe('contract.events [ @E2E ]', function() {
const originalWarn = console.warn
let message
console.warn = function(str) { message = str }

return new Promise(async (resolve, reject) => {
instance
.events
Expand Down Expand Up @@ -408,9 +505,9 @@ describe('contract.events [ @E2E ]', function() {
assert.equal(events[0].returnValues.str, msg)
});

// Malformed utf-8 sequence in the following two tests comes from
// Malformed utf-8 sequence in the following two tests comes from
// https://www.w3.org/2001/06/utf-8-wrong/UTF-8-test.html
// Section: 3.1.8
// Section: 3.1.8
it('when an invalid utf-8 string is passed in JS as param to emit', async function(){
const msg = '�������';

Expand Down

0 comments on commit 195cd3c

Please sign in to comment.