-
-
Notifications
You must be signed in to change notification settings - Fork 771
/
hooked-web3-provider.js
213 lines (172 loc) · 9.08 KB
/
hooked-web3-provider.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
"use strict";
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var factory = function factory(web3) {
var HookedWeb3Provider = (function (_web3$providers$HttpProvider) {
_inherits(HookedWeb3Provider, _web3$providers$HttpProvider);
function HookedWeb3Provider(_ref) {
var host = _ref.host;
var transaction_signer = _ref.transaction_signer;
_classCallCheck(this, HookedWeb3Provider);
_get(Object.getPrototypeOf(HookedWeb3Provider.prototype), "constructor", this).call(this, host);
this.transaction_signer = transaction_signer;
// Cache of the most up to date transaction counts (nonces) for each address
// encountered by the web3 provider that's managed by the transaction signer.
this.global_nonces = {};
}
// We can't support *all* synchronous methods because we have to call out to
// a transaction signer. So removing the ability to serve any.
_createClass(HookedWeb3Provider, [{
key: "send",
value: function send(payload, callback) {
var _this = this;
var requests = payload;
if (!(requests instanceof Array)) {
requests = [requests];
}
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = requests[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var request = _step.value;
if (request.method == "eth_sendTransaction") {
throw new Error("HookedWeb3Provider does not support synchronous transactions. Please provide a callback.");
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator["return"]) {
_iterator["return"]();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
var finishedWithRewrite = function finishedWithRewrite() {
return _get(Object.getPrototypeOf(HookedWeb3Provider.prototype), "send", _this).call(_this, payload, callback);
};
return this.rewritePayloads(0, requests, {}, finishedWithRewrite);
}
// Catch the requests at the sendAsync level, rewriting all sendTransaction
// methods to sendRawTransaction, calling out to the transaction_signer to
// get the data for sendRawTransaction.
}, {
key: "sendAsync",
value: function sendAsync(payload, callback) {
var _this2 = this;
var finishedWithRewrite = function finishedWithRewrite() {
_get(Object.getPrototypeOf(HookedWeb3Provider.prototype), "sendAsync", _this2).call(_this2, payload, callback);
};
var requests = payload;
if (!(payload instanceof Array)) {
requests = [payload];
}
this.rewritePayloads(0, requests, {}, finishedWithRewrite);
}
// Rewrite all eth_sendTransaction payloads in the requests array.
// This takes care of batch requests, and updates the nonces accordingly.
}, {
key: "rewritePayloads",
value: function rewritePayloads(index, requests, session_nonces, finished) {
var _this3 = this;
if (index >= requests.length) {
return finished();
}
var payload = requests[index];
// Function to remove code duplication for going to the next payload
var next = function next(err) {
if (err != null) {
return finished(err);
}
return _this3.rewritePayloads(index + 1, requests, session_nonces, finished);
};
// If this isn't a transaction we can modify, ignore it.
if (payload.method != "eth_sendTransaction") {
return next();
}
var tx_params = payload.params[0];
var sender = tx_params.from;
this.transaction_signer.hasAddress(sender, function (err, has_address) {
if (err != null || has_address == false) {
return next(err);
}
// Get the nonce, requesting from web3 if we haven't already requested it in this session.
// Remember: "session_nonces" is the nonces we know about for this batch of rewriting (this "session").
// Having this cache makes it so we only need to call getTransactionCount once per batch.
// "global_nonces" is nonces across the life of this provider.
var getNonce = function getNonce(done) {
// If a nonce is specified in our nonce list, use that nonce.
var nonce = session_nonces[sender];
if (nonce != null) {
done(null, nonce);
} else {
// Include pending transactions, so the nonce is set accordingly.
// Note: "pending" doesn't seem to take effect for some Ethereum clients (geth),
// hence the need for global_nonces.
// We call directly to our own sendAsync method, because the web3 provider
// is not guaranteed to be set.
_this3.sendAsync({
jsonrpc: '2.0',
method: 'eth_getTransactionCount',
params: [sender, "pending"],
id: new Date().getTime()
}, function (err, result) {
if (err != null) {
done(err);
} else {
var new_nonce = result.result;
done(null, web3.toDecimal(new_nonce));
}
});
}
};
// Get the nonce, requesting from web3 if we need to.
// We then store the nonce and update it so we don't have to
// to request from web3 again.
getNonce(function (err, nonce) {
if (err != null) {
return finished(err);
}
// Set the expected nonce, and update our caches of nonces.
// Note that if our session nonce is lower than what we have cached
// across all transactions (and not just this batch) use our cached
// version instead, even if
var final_nonce = Math.max(nonce, _this3.global_nonces[sender] || 0);
// Update the transaction parameters.
tx_params.nonce = web3.toHex(final_nonce);
// Update caches.
session_nonces[sender] = final_nonce + 1;
_this3.global_nonces[sender] = final_nonce + 1;
// If our transaction signer does represent the address,
// sign the transaction ourself and rewrite the payload.
_this3.transaction_signer.signTransaction(tx_params, function (err, raw_tx) {
if (err != null) {
return next(err);
}
payload.method = "eth_sendRawTransaction";
payload.params = [raw_tx];
return next();
});
});
});
}
}]);
return HookedWeb3Provider;
})(web3.providers.HttpProvider);
return HookedWeb3Provider;
};
if (typeof module !== 'undefined') {
module.exports = factory(require("web3"));
} else {
if(typeof web3 != 'undefined'){
window.HookedWeb3Provider = factory(web3);
}
}