mirror of https://github.com/status-im/web3.js.git
231 lines
6.6 KiB
JavaScript
231 lines
6.6 KiB
JavaScript
/*
|
|
This file is part of web3.js.
|
|
|
|
web3.js is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
web3.js is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
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/>.
|
|
*/
|
|
/** @file filter.js
|
|
* @authors:
|
|
* Jeffrey Wilcke <jeff@ethdev.com>
|
|
* Marek Kotewicz <marek@ethdev.com>
|
|
* Marian Oancea <marian@ethdev.com>
|
|
* Fabian Vogelsteller <fabian@ethdev.com>
|
|
* Gav Wood <g@ethdev.com>
|
|
* @date 2014
|
|
*/
|
|
|
|
var formatters = require('./formatters');
|
|
var utils = require('../utils/utils');
|
|
|
|
/**
|
|
* Converts a given topic to a hex string, but also allows null values.
|
|
*
|
|
* @param {Mixed} value
|
|
* @return {String}
|
|
*/
|
|
var toTopic = function(value){
|
|
|
|
if(value === null || typeof value === 'undefined')
|
|
return null;
|
|
|
|
value = String(value);
|
|
|
|
if(value.indexOf('0x') === 0)
|
|
return value;
|
|
else
|
|
return utils.fromUtf8(value);
|
|
};
|
|
|
|
/// This method should be called on options object, to verify deprecated properties && lazy load dynamic ones
|
|
/// @param should be string or object
|
|
/// @returns options string or object
|
|
var getOptions = function (options) {
|
|
|
|
if (utils.isString(options)) {
|
|
return options;
|
|
}
|
|
|
|
options = options || {};
|
|
|
|
// make sure topics, get converted to hex
|
|
options.topics = options.topics || [];
|
|
options.topics = options.topics.map(function(topic){
|
|
return (utils.isArray(topic)) ? topic.map(toTopic) : toTopic(topic);
|
|
});
|
|
|
|
// lazy load
|
|
return {
|
|
topics: options.topics,
|
|
to: options.to,
|
|
address: options.address,
|
|
fromBlock: formatters.inputBlockNumberFormatter(options.fromBlock),
|
|
toBlock: formatters.inputBlockNumberFormatter(options.toBlock)
|
|
};
|
|
};
|
|
|
|
/**
|
|
Adds the callback and sets up the methods, to iterate over the results.
|
|
|
|
@method getLogsAtStart
|
|
@param {Object} self
|
|
@param {funciton}
|
|
*/
|
|
var getLogsAtStart = function(self, callback){
|
|
// call getFilterLogs for the first watch callback start
|
|
if (!utils.isString(self.options)) {
|
|
self.get(function (err, messages) {
|
|
// don't send all the responses to all the watches again... just to self one
|
|
if (err) {
|
|
callback(err);
|
|
}
|
|
|
|
if(utils.isArray(messages)) {
|
|
messages.forEach(function (message) {
|
|
callback(null, message);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
Adds the callback and sets up the methods, to iterate over the results.
|
|
|
|
@method pollFilter
|
|
@param {Object} self
|
|
*/
|
|
var pollFilter = function(self) {
|
|
|
|
var onMessage = function (error, messages) {
|
|
if (error) {
|
|
return self.callbacks.forEach(function (callback) {
|
|
callback(error);
|
|
});
|
|
}
|
|
|
|
if(utils.isArray(messages)) {
|
|
messages.forEach(function (message) {
|
|
message = self.formatter ? self.formatter(message) : message;
|
|
self.callbacks.forEach(function (callback) {
|
|
callback(null, message);
|
|
});
|
|
});
|
|
}
|
|
};
|
|
|
|
self.requestManager.startPolling({
|
|
method: self.implementation.poll.call,
|
|
params: [self.filterId],
|
|
}, self.filterId, onMessage, self.stopWatching.bind(self));
|
|
|
|
};
|
|
|
|
var Filter = function (web3, options, methods, formatter, callback) {
|
|
var self = this;
|
|
var implementation = {};
|
|
methods.forEach(function (method) {
|
|
method.setRequestManager(web3._requestManager);
|
|
method.attachToObject(implementation);
|
|
});
|
|
this.requestManager = web3._requestManager;
|
|
this.options = getOptions(options);
|
|
this.implementation = implementation;
|
|
this.filterId = null;
|
|
this.callbacks = [];
|
|
this.getLogsCallbacks = [];
|
|
this.pollFilters = [];
|
|
this.formatter = formatter;
|
|
this.implementation.newFilter(this.options, function(error, id){
|
|
if(error) {
|
|
self.callbacks.forEach(function(cb){
|
|
cb(error);
|
|
});
|
|
} else {
|
|
self.filterId = id;
|
|
|
|
// check if there are get pending callbacks as a consequence
|
|
// of calling get() with filterId unassigned.
|
|
self.getLogsCallbacks.forEach(function (cb){
|
|
self.get(cb);
|
|
});
|
|
self.getLogsCallbacks = [];
|
|
|
|
// get filter logs for the already existing watch calls
|
|
self.callbacks.forEach(function(cb){
|
|
getLogsAtStart(self, cb);
|
|
});
|
|
if(self.callbacks.length > 0)
|
|
pollFilter(self);
|
|
|
|
// start to watch immediately
|
|
if(callback) {
|
|
return self.watch(callback);
|
|
}
|
|
}
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
Filter.prototype.watch = function (callback) {
|
|
this.callbacks.push(callback);
|
|
|
|
if(this.filterId) {
|
|
getLogsAtStart(this, callback);
|
|
pollFilter(this);
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
Filter.prototype.stopWatching = function () {
|
|
this.requestManager.stopPolling(this.filterId);
|
|
// remove filter async
|
|
this.implementation.uninstallFilter(this.filterId, function(){});
|
|
this.callbacks = [];
|
|
};
|
|
|
|
Filter.prototype.get = function (callback) {
|
|
var self = this;
|
|
if (utils.isFunction(callback)) {
|
|
if (this.filterId === null) {
|
|
// If filterId is not set yet, call it back
|
|
// when newFilter() assigns it.
|
|
this.getLogsCallbacks.push(callback);
|
|
} else {
|
|
this.implementation.getLogs(this.filterId, function(err, res){
|
|
if (err) {
|
|
callback(err);
|
|
} else {
|
|
callback(null, res.map(function (log) {
|
|
return self.formatter ? self.formatter(log) : log;
|
|
}));
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
if (this.filterId === null) {
|
|
throw new Error('Filter ID Error: filter().get() can\'t be chained synchronous, please provide a callback for the get() method.');
|
|
}
|
|
var logs = this.implementation.getLogs(this.filterId);
|
|
return logs.map(function (log) {
|
|
return self.formatter ? self.formatter(log) : log;
|
|
});
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
module.exports = Filter;
|
|
|