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

Filter option doesn't work in getPastEvents #6015

Merged
merged 10 commits into from
Apr 20, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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) => {
spacesailor24 marked this conversation as resolved.
Show resolved Hide resolved
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