Skip to content
This repository was archived by the owner on Mar 5, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 5 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
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
92 changes: 88 additions & 4 deletions test/e2e.contract.events.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,90 @@ describe('contract.events [ @E2E ]', function() {
assert.notEqual(events[0].id, events[1].id);
});

it('contract.getPastEvents with filter', 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 instance
.methods
.firesEvent(accounts[0], 3)
.send({from: accounts[0]});

const events2 = await instance.getPastEvents('BasicEvent',{
filter : { val : 2 },
fromBlock: 'earliest',
toBlock: 'latest'
})
assert.equal(events2.length, 1);
assert.equal(events2[0].returnValues.val, 2);

const allEventsFilters = await instance.getPastEvents({
filter : { val : 2 },
fromBlock: 'earliest',
toBlock: 'latest'
})
assert.equal(allEventsFilters.length, 1);
assert.equal(allEventsFilters[0].returnValues.val, 2);

const allEventsFilters2 = await instance.getPastEvents('allEvents',{
filter : { val : 2 },
fromBlock: 'earliest',
toBlock: 'latest'
})
assert.equal(allEventsFilters2.length, 1);
assert.equal(allEventsFilters2[0].returnValues.val, 2);

const event3 = (await instance.getPastEvents('BasicEvent',{
filter : { val : 3 },
fromBlock: 'earliest',
toBlock: 'latest'
}))[0]

assert.equal(event3.returnValues.val, 3);

const events4 = await instance.getPastEvents('BasicEvent',{
filter : { val : [ 2, 3 ] },
fromBlock: 'earliest',
toBlock: 'latest'
});
assert.equal(events4.length, 2);
assert.equal(events4[0].returnValues.val, 2);
assert.equal(events4[1].returnValues.val, 3);

const allEventsFilter3 = await instance.getPastEvents('allEvents',{
filter : { val : [ 2, 3 ] },
fromBlock: 'earliest',
toBlock: 'latest'
});
assert.equal(allEventsFilter3.length, 2);
assert.equal(allEventsFilter3[0].returnValues.val, 2);
assert.equal(allEventsFilter3[1].returnValues.val, 3);


instance.getPastEvents('allEvents',{
filter : { val : [ 2, 3 ] },
fromBlock: 'earliest',
toBlock: 'latest'
}, (err,data)=>{
assert.equal(data.length, 2);
assert.equal(data[0].returnValues.val, 2);
assert.equal(data[1].returnValues.val, 3);
});

instance.getPastEvents('BasicEvent',{
filter : { val : 3},
fromBlock: 'earliest',
toBlock: 'latest'
}, (err,events)=>{
assert.equal(events[0].returnValues.val, 3);
})
});

it('contract.events.<eventName>', function(){
return new Promise(async resolve => {
instance
Expand Down Expand Up @@ -91,7 +175,7 @@ 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 +190,7 @@ 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 +492,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