UNPKG

37.5 kBJavaScriptView Raw
1"use strict";
2var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3 function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4 return new (P || (P = Promise))(function (resolve, reject) {
5 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7 function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8 step((generator = generator.apply(thisArg, _arguments || [])).next());
9 });
10};
11Object.defineProperty(exports, "__esModule", { value: true });
12exports.Web3Wrapper = void 0;
13const assert_1 = require("@0x/assert");
14const json_schemas_1 = require("@0x/json-schemas");
15const utils_1 = require("@0x/utils");
16const ethereum_types_1 = require("ethereum-types");
17const _ = require("lodash");
18const marshaller_1 = require("./marshaller");
19const types_1 = require("./types");
20const utils_2 = require("./utils");
21const BASE_TEN = 10;
22// These are unique identifiers contained in the response of the
23// web3_clientVersion call.
24const uniqueVersionIds = {
25 geth: 'Geth',
26 ganache: 'EthereumJS TestRPC',
27};
28/**
29 * An alternative to the Web3.js library that provides a consistent, clean, promise-based interface.
30 */
31class Web3Wrapper {
32 /**
33 * Instantiates a new Web3Wrapper.
34 * @param provider The Web3 provider instance you would like the Web3Wrapper to use for interacting with
35 * the backing Ethereum node.
36 * @param callAndTxnDefaults Override Call and Txn Data defaults sent with RPC requests to the backing Ethereum node.
37 * @return An instance of the Web3Wrapper class.
38 */
39 constructor(supportedProvider, callAndTxnDefaults = {}) {
40 /**
41 * Flag to check if this instance is of type Web3Wrapper
42 */
43 this.isZeroExWeb3Wrapper = true;
44 this.abiDecoder = new utils_1.AbiDecoder([]);
45 this._supportedProvider = supportedProvider;
46 this._provider = utils_1.providerUtils.standardizeOrThrow(supportedProvider);
47 this._callAndTxnDefaults = callAndTxnDefaults;
48 this._jsonRpcRequestId = 1;
49 }
50 /**
51 * Check if an address is a valid Ethereum address
52 * @param address Address to check
53 * @returns Whether the address is a valid Ethereum address
54 */
55 static isAddress(address) {
56 return utils_1.addressUtils.isAddress(address);
57 }
58 /**
59 * A unit amount is defined as the amount of a token above the specified decimal places (integer part).
60 * E.g: If a currency has 18 decimal places, 1e18 or one quintillion of the currency is equivalent
61 * to 1 unit.
62 * @param amount The amount in baseUnits that you would like converted to units.
63 * @param decimals The number of decimal places the unit amount has.
64 * @return The amount in units.
65 */
66 static toUnitAmount(amount, decimals) {
67 assert_1.assert.isValidBaseUnitAmount('amount', amount);
68 assert_1.assert.isNumber('decimals', decimals);
69 const aUnit = new utils_1.BigNumber(BASE_TEN).pow(decimals);
70 const unit = amount.div(aUnit);
71 return unit;
72 }
73 /**
74 * A baseUnit is defined as the smallest denomination of a token. An amount expressed in baseUnits
75 * is the amount expressed in the smallest denomination.
76 * E.g: 1 unit of a token with 18 decimal places is expressed in baseUnits as 1000000000000000000
77 * @param amount The amount of units that you would like converted to baseUnits.
78 * @param decimals The number of decimal places the unit amount has.
79 * @return The amount in baseUnits.
80 */
81 static toBaseUnitAmount(amount, decimals) {
82 assert_1.assert.isNumber('decimals', decimals);
83 const unit = new utils_1.BigNumber(BASE_TEN).pow(decimals);
84 const baseUnitAmount = unit.times(amount);
85 const hasDecimals = baseUnitAmount.decimalPlaces() !== 0;
86 if (hasDecimals) {
87 throw new Error(`Invalid unit amount: ${amount.toString(BASE_TEN)} - Too many decimal places`);
88 }
89 return baseUnitAmount;
90 }
91 /**
92 * Convert an Ether amount from ETH to Wei
93 * @param ethAmount Amount of Ether to convert to wei
94 * @returns Amount in wei
95 */
96 static toWei(ethAmount) {
97 assert_1.assert.isBigNumber('ethAmount', ethAmount);
98 const ETH_DECIMALS = 18;
99 const balanceWei = Web3Wrapper.toBaseUnitAmount(ethAmount, ETH_DECIMALS);
100 return balanceWei;
101 }
102 static _assertBlockParam(blockParam) {
103 if (_.isNumber(blockParam)) {
104 return;
105 }
106 else if (_.isString(blockParam)) {
107 assert_1.assert.doesBelongToStringEnum('blockParam', blockParam, ethereum_types_1.BlockParamLiteral);
108 }
109 }
110 static _assertBlockParamOrString(blockParam) {
111 try {
112 Web3Wrapper._assertBlockParam(blockParam);
113 }
114 catch (err) {
115 try {
116 assert_1.assert.isHexString('blockParam', blockParam);
117 return;
118 }
119 catch (err) {
120 throw new Error(`Expected blockParam to be of type "string | BlockParam", encountered ${blockParam}`);
121 }
122 }
123 }
124 static _normalizeTxReceiptStatus(status) {
125 // Transaction status might have four values
126 // undefined - Testrpc and other old clients
127 // null - New clients on old transactions
128 // number - Parity
129 // hex - Geth
130 if (_.isString(status)) {
131 return utils_2.utils.convertHexToNumber(status);
132 }
133 else if (status === undefined) {
134 return null;
135 }
136 else {
137 return status;
138 }
139 }
140 /**
141 * Get the contract defaults set to the Web3Wrapper instance
142 * @return CallAndTxnData defaults (e.g gas, gasPrice, nonce, etc...)
143 */
144 getContractDefaults() {
145 return this._callAndTxnDefaults;
146 }
147 /**
148 * Retrieve the Web3 provider
149 * @return Web3 provider instance
150 */
151 getProvider() {
152 return this._supportedProvider;
153 }
154 /**
155 * Update the used Web3 provider
156 * @param provider The new Web3 provider to be set
157 */
158 setProvider(supportedProvider) {
159 const provider = utils_1.providerUtils.standardizeOrThrow(supportedProvider);
160 this._provider = provider;
161 }
162 /**
163 * Check whether an address is available through the backing provider. This can be
164 * useful if you want to know whether a user can sign messages or transactions from
165 * a given Ethereum address.
166 * @param senderAddress Address to check availability for
167 * @returns Whether the address is available through the provider.
168 */
169 isSenderAddressAvailableAsync(senderAddress) {
170 return __awaiter(this, void 0, void 0, function* () {
171 assert_1.assert.isETHAddressHex('senderAddress', senderAddress);
172 const addresses = yield this.getAvailableAddressesAsync();
173 const normalizedAddress = senderAddress.toLowerCase();
174 return _.includes(addresses, normalizedAddress);
175 });
176 }
177 /**
178 * Fetch the backing Ethereum node's version string (e.g `MetaMask/v4.2.0`)
179 * @returns Ethereum node's version string
180 */
181 getNodeVersionAsync() {
182 return __awaiter(this, void 0, void 0, function* () {
183 const nodeVersion = yield this.sendRawPayloadAsync({ method: 'web3_clientVersion' });
184 return nodeVersion;
185 });
186 }
187 /**
188 * Fetches the networkId of the backing Ethereum node
189 * @returns The network id
190 */
191 getNetworkIdAsync() {
192 return __awaiter(this, void 0, void 0, function* () {
193 const networkIdStr = yield this.sendRawPayloadAsync({ method: 'net_version' });
194 const networkId = _.parseInt(networkIdStr);
195 return networkId;
196 });
197 }
198 /**
199 * Fetches the chainId of the backing Ethereum node
200 * @returns The chain id
201 */
202 getChainIdAsync() {
203 return __awaiter(this, void 0, void 0, function* () {
204 const chainIdStr = yield this.sendRawPayloadAsync({ method: 'eth_chainId' });
205 const chainId = _.parseInt(chainIdStr);
206 return chainId;
207 });
208 }
209 /**
210 * Fetch the current gas price.
211 * For post-London hardfork chains, this will be baseFeePerGas + maxPriorityFeePerGas
212 */
213 getGasPriceAsync() {
214 return __awaiter(this, void 0, void 0, function* () {
215 const gasPriceStr = yield this.sendRawPayloadAsync({ method: 'eth_gasPrice' });
216 return new utils_1.BigNumber(gasPriceStr);
217 });
218 }
219 /**
220 * Fetch the base fee per gas for the pending block.
221 */
222 getBaseFeePerGasAsync() {
223 return __awaiter(this, void 0, void 0, function* () {
224 const rawBlock = yield this.sendRawPayloadAsync({
225 method: 'eth_getBlockByNumber',
226 params: ['pending', false],
227 });
228 const { baseFeePerGas } = rawBlock;
229 return new utils_1.BigNumber(baseFeePerGas || 0);
230 });
231 }
232 /**
233 * Fetch the current max piority fee per gas. This is the suggested miner tip
234 * to get mined in the current block.
235 */
236 getMaxPriorityFeePerGasAsync() {
237 return __awaiter(this, void 0, void 0, function* () {
238 const feeStr = yield this.sendRawPayloadAsync({ method: 'eth_maxPriorityFeePerGas' });
239 return new utils_1.BigNumber(feeStr);
240 });
241 }
242 /**
243 * Retrieves the transaction receipt for a given transaction hash if found
244 * @param txHash Transaction hash
245 * @returns The transaction receipt, including it's status (0: failed, 1: succeeded). Returns undefined if transaction not found.
246 */
247 getTransactionReceiptIfExistsAsync(txHash) {
248 return __awaiter(this, void 0, void 0, function* () {
249 assert_1.assert.isHexString('txHash', txHash);
250 const transactionReceiptRpc = yield this.sendRawPayloadAsync({
251 method: 'eth_getTransactionReceipt',
252 params: [txHash],
253 });
254 // HACK Parity can return a pending transaction receipt. We check for a non null
255 // block number before continuing with returning a fully realised receipt.
256 // ref: https://github.com/paritytech/parity-ethereum/issues/1180
257 if (transactionReceiptRpc !== null && transactionReceiptRpc.blockNumber !== null) {
258 transactionReceiptRpc.status = Web3Wrapper._normalizeTxReceiptStatus(transactionReceiptRpc.status);
259 const transactionReceipt = marshaller_1.marshaller.unmarshalTransactionReceipt(transactionReceiptRpc);
260 return transactionReceipt;
261 }
262 else {
263 return undefined;
264 }
265 });
266 }
267 /**
268 * Retrieves the transaction data for a given transaction
269 * @param txHash Transaction hash
270 * @returns The raw transaction data
271 */
272 getTransactionByHashAsync(txHash) {
273 return __awaiter(this, void 0, void 0, function* () {
274 assert_1.assert.isHexString('txHash', txHash);
275 const transactionRpc = yield this.sendRawPayloadAsync({
276 method: 'eth_getTransactionByHash',
277 params: [txHash],
278 });
279 const transaction = marshaller_1.marshaller.unmarshalTransaction(transactionRpc);
280 return transaction;
281 });
282 }
283 /**
284 * Retrieves an accounts Ether balance in wei
285 * @param owner Account whose balance you wish to check
286 * @param defaultBlock The block depth at which to fetch the balance (default=latest)
287 * @returns Balance in wei
288 */
289 getBalanceInWeiAsync(owner, defaultBlock) {
290 return __awaiter(this, void 0, void 0, function* () {
291 assert_1.assert.isETHAddressHex('owner', owner);
292 if (defaultBlock !== undefined) {
293 Web3Wrapper._assertBlockParam(defaultBlock);
294 }
295 const marshalledDefaultBlock = marshaller_1.marshaller.marshalBlockParam(defaultBlock);
296 const encodedOwner = marshaller_1.marshaller.marshalAddress(owner);
297 const balanceInWei = yield this.sendRawPayloadAsync({
298 method: 'eth_getBalance',
299 params: [encodedOwner, marshalledDefaultBlock],
300 });
301 // Rewrap in a new BigNumber
302 return new utils_1.BigNumber(balanceInWei);
303 });
304 }
305 /**
306 * Check if a contract exists at a given address
307 * @param address Address to which to check
308 * @returns Whether or not contract code was found at the supplied address
309 */
310 doesContractExistAtAddressAsync(address) {
311 return __awaiter(this, void 0, void 0, function* () {
312 assert_1.assert.isETHAddressHex('address', address);
313 const code = yield this.getContractCodeAsync(address);
314 // Regex matches 0x0, 0x00, 0x in order to accommodate poorly implemented clients
315 const isCodeEmpty = /^0x0{0,40}$/i.test(code);
316 return !isCodeEmpty;
317 });
318 }
319 /**
320 * Gets the contract code by address
321 * @param address Address of the contract
322 * @param defaultBlock Block height at which to make the call. Defaults to `latest`
323 * @return Code of the contract
324 */
325 getContractCodeAsync(address, defaultBlock) {
326 return __awaiter(this, void 0, void 0, function* () {
327 assert_1.assert.isETHAddressHex('address', address);
328 if (defaultBlock !== undefined) {
329 Web3Wrapper._assertBlockParam(defaultBlock);
330 }
331 const marshalledDefaultBlock = marshaller_1.marshaller.marshalBlockParam(defaultBlock);
332 const encodedAddress = marshaller_1.marshaller.marshalAddress(address);
333 const code = yield this.sendRawPayloadAsync({
334 method: 'eth_getCode',
335 params: [encodedAddress, marshalledDefaultBlock],
336 });
337 return code;
338 });
339 }
340 /**
341 * Gets the debug trace of a transaction
342 * @param txHash Hash of the transactuon to get a trace for
343 * @param traceParams Config object allowing you to specify if you need memory/storage/stack traces.
344 * @return Transaction trace
345 */
346 getTransactionTraceAsync(txHash, traceParams) {
347 return __awaiter(this, void 0, void 0, function* () {
348 assert_1.assert.isHexString('txHash', txHash);
349 const trace = yield this.sendRawPayloadAsync({
350 method: 'debug_traceTransaction',
351 params: [txHash, traceParams],
352 });
353 return trace;
354 });
355 }
356 /**
357 * Sign a message with a specific address's private key (`eth_sign`)
358 * @param address Address of signer
359 * @param message Message to sign
360 * @returns Signature string (might be VRS or RSV depending on the Signer)
361 */
362 signMessageAsync(address, message) {
363 return __awaiter(this, void 0, void 0, function* () {
364 assert_1.assert.isETHAddressHex('address', address);
365 assert_1.assert.isString('message', message); // TODO: Should this be stricter? Hex string?
366 const signData = yield this.sendRawPayloadAsync({
367 method: 'eth_sign',
368 params: [address, message],
369 });
370 return signData;
371 });
372 }
373 /**
374 * Sign an EIP712 typed data message with a specific address's private key (`eth_signTypedData`)
375 * @param address Address of signer
376 * @param typedData Typed data message to sign
377 * @returns Signature string (as RSV)
378 */
379 signTypedDataAsync(address, typedData) {
380 return __awaiter(this, void 0, void 0, function* () {
381 assert_1.assert.isETHAddressHex('address', address);
382 assert_1.assert.doesConformToSchema('typedData', typedData, json_schemas_1.schemas.eip712TypedDataSchema);
383 // Try decreasing versions of `eth_signTypedData` until it works.
384 const methodsToTry = ['eth_signTypedData_v4', 'eth_signTypedData_v3', 'eth_signTypedData'];
385 let lastErr;
386 for (const method of methodsToTry) {
387 try {
388 return yield this.sendRawPayloadAsync({
389 method,
390 // `eth_signTypedData` expects an object, whereas the others expect
391 // a JSON string.
392 params: [address, method === 'eth_signTypedData' ? typedData : JSON.stringify(typedData)],
393 });
394 }
395 catch (err) {
396 lastErr = err;
397 // If there are no more methods to try or the error says something other
398 // than the method not existing, throw.
399 if (!/(not handled|does not exist|not supported)/.test(err.message)) {
400 throw err;
401 }
402 }
403 }
404 throw lastErr;
405 });
406 }
407 /**
408 * Fetches the latest block number
409 * @returns Block number
410 */
411 getBlockNumberAsync() {
412 return __awaiter(this, void 0, void 0, function* () {
413 const blockNumberHex = yield this.sendRawPayloadAsync({
414 method: 'eth_blockNumber',
415 params: [],
416 });
417 const blockNumber = utils_2.utils.convertHexToNumberOrNull(blockNumberHex);
418 return blockNumber;
419 });
420 }
421 /**
422 * Fetches the nonce for an account (transaction count for EOAs).
423 * @param address Address of account.
424 * @param defaultBlock Block height at which to make the call. Defaults to `latest`
425 * @returns Account nonce.
426 */
427 getAccountNonceAsync(address, defaultBlock) {
428 return __awaiter(this, void 0, void 0, function* () {
429 assert_1.assert.isETHAddressHex('address', address);
430 if (defaultBlock !== undefined) {
431 Web3Wrapper._assertBlockParam(defaultBlock);
432 }
433 const marshalledDefaultBlock = marshaller_1.marshaller.marshalBlockParam(defaultBlock);
434 const encodedAddress = marshaller_1.marshaller.marshalAddress(address);
435 const nonceHex = yield this.sendRawPayloadAsync({
436 method: 'eth_getTransactionCount',
437 params: [encodedAddress, marshalledDefaultBlock],
438 });
439 assert_1.assert.isHexString('nonce', nonceHex);
440 // tslint:disable-next-line:custom-no-magic-numbers
441 return parseInt(nonceHex.substr(2), 16);
442 });
443 }
444 /**
445 * Fetch a specific Ethereum block without transaction data
446 * @param blockParam The block you wish to fetch (blockHash, blockNumber or blockLiteral)
447 * @returns The requested block without transaction data, or undefined if block was not found
448 * (e.g the node isn't fully synced, there was a block re-org and the requested block was uncles, etc...)
449 */
450 getBlockIfExistsAsync(blockParam) {
451 return __awaiter(this, void 0, void 0, function* () {
452 Web3Wrapper._assertBlockParamOrString(blockParam);
453 const encodedBlockParam = marshaller_1.marshaller.marshalBlockParam(blockParam);
454 const method = utils_2.utils.isHexStrict(blockParam) ? 'eth_getBlockByHash' : 'eth_getBlockByNumber';
455 const shouldIncludeTransactionData = false;
456 const blockWithoutTransactionDataWithHexValuesOrNull = yield this.sendRawPayloadAsync({
457 method,
458 params: [encodedBlockParam, shouldIncludeTransactionData],
459 });
460 let blockWithoutTransactionDataIfExists;
461 if (blockWithoutTransactionDataWithHexValuesOrNull !== null) {
462 blockWithoutTransactionDataIfExists = marshaller_1.marshaller.unmarshalIntoBlockWithoutTransactionData(blockWithoutTransactionDataWithHexValuesOrNull);
463 }
464 return blockWithoutTransactionDataIfExists;
465 });
466 }
467 /**
468 * Fetch a specific Ethereum block with transaction data
469 * @param blockParam The block you wish to fetch (blockHash, blockNumber or blockLiteral)
470 * @returns The requested block with transaction data
471 */
472 getBlockWithTransactionDataAsync(blockParam) {
473 return __awaiter(this, void 0, void 0, function* () {
474 Web3Wrapper._assertBlockParamOrString(blockParam);
475 let encodedBlockParam = blockParam;
476 if (_.isNumber(blockParam)) {
477 encodedBlockParam = utils_2.utils.numberToHex(blockParam);
478 }
479 const method = utils_2.utils.isHexStrict(blockParam) ? 'eth_getBlockByHash' : 'eth_getBlockByNumber';
480 const shouldIncludeTransactionData = true;
481 const blockWithTransactionDataWithHexValues = yield this.sendRawPayloadAsync({
482 method,
483 params: [encodedBlockParam, shouldIncludeTransactionData],
484 });
485 const blockWithoutTransactionData = marshaller_1.marshaller.unmarshalIntoBlockWithTransactionData(blockWithTransactionDataWithHexValues);
486 return blockWithoutTransactionData;
487 });
488 }
489 /**
490 * Fetch a block's timestamp
491 * @param blockParam The block you wish to fetch (blockHash, blockNumber or blockLiteral)
492 * @returns The block's timestamp
493 */
494 getBlockTimestampAsync(blockParam) {
495 return __awaiter(this, void 0, void 0, function* () {
496 Web3Wrapper._assertBlockParamOrString(blockParam);
497 const blockIfExists = yield this.getBlockIfExistsAsync(blockParam);
498 if (blockIfExists === undefined) {
499 throw new Error(`Failed to fetch block with blockParam: ${JSON.stringify(blockParam)}`);
500 }
501 return blockIfExists.timestamp;
502 });
503 }
504 /**
505 * Retrieve the user addresses available through the backing provider
506 * @returns Available user addresses
507 */
508 getAvailableAddressesAsync() {
509 return __awaiter(this, void 0, void 0, function* () {
510 const addresses = yield this.sendRawPayloadAsync({
511 method: 'eth_accounts',
512 params: [],
513 });
514 const normalizedAddresses = _.map(addresses, address => address.toLowerCase());
515 return normalizedAddresses;
516 });
517 }
518 /**
519 * Take a snapshot of the blockchain state on a TestRPC/Ganache local node
520 * @returns The snapshot id. This can be used to revert to this snapshot
521 */
522 takeSnapshotAsync() {
523 return __awaiter(this, void 0, void 0, function* () {
524 const snapshotId = Number(yield this.sendRawPayloadAsync({ method: 'evm_snapshot', params: [] }));
525 return snapshotId;
526 });
527 }
528 /**
529 * Revert the blockchain state to a previous snapshot state on TestRPC/Ganache local node
530 * @param snapshotId snapshot id to revert to
531 * @returns Whether the revert was successful
532 */
533 revertSnapshotAsync(snapshotId) {
534 return __awaiter(this, void 0, void 0, function* () {
535 assert_1.assert.isNumber('snapshotId', snapshotId);
536 const didRevert = yield this.sendRawPayloadAsync({ method: 'evm_revert', params: [snapshotId] });
537 return didRevert;
538 });
539 }
540 /**
541 * Mine a block on a TestRPC/Ganache local node
542 */
543 mineBlockAsync() {
544 return __awaiter(this, void 0, void 0, function* () {
545 yield this.sendRawPayloadAsync({ method: 'evm_mine', params: [] });
546 });
547 }
548 /**
549 * Increase the next blocks timestamp on TestRPC/Ganache or Geth local node.
550 * Will throw if provider is neither TestRPC/Ganache or Geth.
551 * @param timeDelta Amount of time to add in seconds
552 */
553 increaseTimeAsync(timeDelta) {
554 return __awaiter(this, void 0, void 0, function* () {
555 assert_1.assert.isNumber('timeDelta', timeDelta);
556 // Detect Geth vs. Ganache and use appropriate endpoint.
557 const version = yield this.getNodeVersionAsync();
558 if (_.includes(version, uniqueVersionIds.geth)) {
559 return this.sendRawPayloadAsync({ method: 'debug_increaseTime', params: [timeDelta] });
560 }
561 else if (_.includes(version, uniqueVersionIds.ganache)) {
562 return this.sendRawPayloadAsync({ method: 'evm_increaseTime', params: [timeDelta] });
563 }
564 else {
565 throw new Error(`Unknown client version: ${version}`);
566 }
567 });
568 }
569 /**
570 * Retrieve smart contract logs for a given filter
571 * @param filter Parameters by which to filter which logs to retrieve
572 * @returns The corresponding log entries
573 */
574 getLogsAsync(filter) {
575 return __awaiter(this, void 0, void 0, function* () {
576 if (filter.blockHash !== undefined && (filter.fromBlock !== undefined || filter.toBlock !== undefined)) {
577 throw new Error(`Cannot specify 'blockHash' as well as 'fromBlock'/'toBlock' in the filter supplied to 'getLogsAsync'`);
578 }
579 let fromBlock = filter.fromBlock;
580 if (_.isNumber(fromBlock)) {
581 fromBlock = utils_2.utils.numberToHex(fromBlock);
582 }
583 let toBlock = filter.toBlock;
584 if (_.isNumber(toBlock)) {
585 toBlock = utils_2.utils.numberToHex(toBlock);
586 }
587 const serializedFilter = Object.assign(Object.assign({}, filter), { fromBlock,
588 toBlock });
589 const payload = {
590 method: 'eth_getLogs',
591 params: [serializedFilter],
592 };
593 const rawLogs = yield this.sendRawPayloadAsync(payload);
594 const formattedLogs = _.map(rawLogs, marshaller_1.marshaller.unmarshalLog.bind(marshaller_1.marshaller));
595 return formattedLogs;
596 });
597 }
598 /**
599 * Calculate the estimated gas cost for a given transaction
600 * @param txData Transaction data
601 * @returns Estimated gas cost
602 */
603 estimateGasAsync(txData) {
604 return __awaiter(this, void 0, void 0, function* () {
605 assert_1.assert.doesConformToSchema('txData', txData, json_schemas_1.schemas.txDataSchema);
606 const txDataHex = marshaller_1.marshaller.marshalTxData(txData);
607 const gasHex = yield this.sendRawPayloadAsync({ method: 'eth_estimateGas', params: [txDataHex] });
608 const gas = utils_2.utils.convertHexToNumber(gasHex);
609 return gas;
610 });
611 }
612 /**
613 * Generate an access list for an ethereum call and also compute the gas used.
614 * @param callData Call data
615 * @param defaultBlock Block height at which to make the call. Defaults to 'latest'.
616 * @returns The access list and gas used.
617 */
618 createAccessListAsync(callData, defaultBlock) {
619 return __awaiter(this, void 0, void 0, function* () {
620 assert_1.assert.doesConformToSchema('callData', callData, json_schemas_1.schemas.callDataSchema, [
621 json_schemas_1.schemas.addressSchema,
622 json_schemas_1.schemas.numberSchema,
623 json_schemas_1.schemas.jsNumber,
624 ]);
625 const rawResult = yield this.sendRawPayloadAsync({
626 method: 'eth_createAccessList',
627 params: [marshaller_1.marshaller.marshalCallData(callData), marshaller_1.marshaller.marshalBlockParam(defaultBlock)],
628 });
629 if (rawResult.error) {
630 throw new Error(rawResult.error);
631 }
632 return {
633 accessList: rawResult.accessList.reduce((o, v) => {
634 o[v.address] = o[v.address] || [];
635 o[v.address].push(...(v.storageKeys || []));
636 return o;
637 },
638 // tslint:disable-next-line: no-object-literal-type-assertion
639 {}),
640 // tslint:disable-next-line: custom-no-magic-numbers
641 gasUsed: parseInt(rawResult.gasUsed.slice(2), 16),
642 };
643 });
644 }
645 /**
646 * Call a smart contract method at a given block height
647 * @param callData Call data
648 * @param defaultBlock Block height at which to make the call. Defaults to `latest`
649 * @returns The raw call result
650 */
651 callAsync(callData, defaultBlock) {
652 return __awaiter(this, void 0, void 0, function* () {
653 assert_1.assert.doesConformToSchema('callData', callData, json_schemas_1.schemas.callDataSchema);
654 if (defaultBlock !== undefined) {
655 Web3Wrapper._assertBlockParam(defaultBlock);
656 }
657 const marshalledDefaultBlock = marshaller_1.marshaller.marshalBlockParam(defaultBlock);
658 const callDataHex = marshaller_1.marshaller.marshalCallData(callData);
659 const overrides = marshaller_1.marshaller.marshalCallOverrides(callData.overrides || {});
660 const rawCallResult = yield this.sendRawPayloadAsync({
661 method: 'eth_call',
662 params: [callDataHex, marshalledDefaultBlock, ...(Object.keys(overrides).length === 0 ? [] : [overrides])],
663 });
664 return rawCallResult;
665 });
666 }
667 /**
668 * Send a transaction
669 * @param txData Transaction data
670 * @returns Transaction hash
671 */
672 sendTransactionAsync(txData) {
673 return __awaiter(this, void 0, void 0, function* () {
674 assert_1.assert.doesConformToSchema('txData', txData, json_schemas_1.schemas.txDataSchema);
675 const txDataHex = marshaller_1.marshaller.marshalTxData(txData);
676 const txHash = yield this.sendRawPayloadAsync({ method: 'eth_sendTransaction', params: [txDataHex] });
677 return txHash;
678 });
679 }
680 /**
681 * Waits for a transaction to be mined and returns the transaction receipt.
682 * Note that just because a transaction was mined does not mean it was
683 * successful. You need to check the status code of the transaction receipt
684 * to find out if it was successful, or use the helper method
685 * awaitTransactionSuccessAsync.
686 * @param txHash Transaction hash
687 * @param pollingIntervalMs How often (in ms) should we check if the transaction is mined.
688 * @param timeoutMs How long (in ms) to poll for transaction mined until aborting.
689 * @return Transaction receipt with decoded log args.
690 */
691 awaitTransactionMinedAsync(txHash, pollingIntervalMs = 1000, timeoutMs) {
692 return __awaiter(this, void 0, void 0, function* () {
693 assert_1.assert.isHexString('txHash', txHash);
694 assert_1.assert.isNumber('pollingIntervalMs', pollingIntervalMs);
695 if (timeoutMs !== undefined) {
696 assert_1.assert.isNumber('timeoutMs', timeoutMs);
697 }
698 // Immediately check if the transaction has already been mined.
699 let transactionReceipt = yield this.getTransactionReceiptIfExistsAsync(txHash);
700 if (transactionReceipt !== undefined) {
701 const logsWithDecodedArgs = _.map(transactionReceipt.logs, this.abiDecoder.tryToDecodeLogOrNoop.bind(this.abiDecoder));
702 const transactionReceiptWithDecodedLogArgs = Object.assign(Object.assign({}, transactionReceipt), { logs: logsWithDecodedArgs });
703 return transactionReceiptWithDecodedLogArgs;
704 }
705 // Otherwise, check again every pollingIntervalMs.
706 let wasTimeoutExceeded = false;
707 if (timeoutMs) {
708 setTimeout(() => (wasTimeoutExceeded = true), timeoutMs);
709 }
710 const txReceiptPromise = new Promise((resolve, reject) => {
711 const intervalId = utils_1.intervalUtils.setAsyncExcludingInterval(() => __awaiter(this, void 0, void 0, function* () {
712 if (wasTimeoutExceeded) {
713 utils_1.intervalUtils.clearAsyncExcludingInterval(intervalId);
714 return reject(types_1.Web3WrapperErrors.TransactionMiningTimeout);
715 }
716 transactionReceipt = yield this.getTransactionReceiptIfExistsAsync(txHash);
717 if (transactionReceipt !== undefined) {
718 utils_1.intervalUtils.clearAsyncExcludingInterval(intervalId);
719 const logsWithDecodedArgs = _.map(transactionReceipt.logs, this.abiDecoder.tryToDecodeLogOrNoop.bind(this.abiDecoder));
720 const transactionReceiptWithDecodedLogArgs = Object.assign(Object.assign({}, transactionReceipt), { logs: logsWithDecodedArgs });
721 resolve(transactionReceiptWithDecodedLogArgs);
722 }
723 }), pollingIntervalMs, (err) => {
724 utils_1.intervalUtils.clearAsyncExcludingInterval(intervalId);
725 reject(err);
726 });
727 });
728 const txReceipt = yield txReceiptPromise;
729 return txReceipt;
730 });
731 }
732 /**
733 * Waits for a transaction to be mined and returns the transaction receipt.
734 * Unlike awaitTransactionMinedAsync, it will throw if the receipt has a
735 * status that is not equal to 1. A status of 0 or null indicates that the
736 * transaction was mined, but failed. See:
737 * https://github.com/ethereum/wiki/wiki/JavaScript-API#web3ethgettransactionreceipt
738 * @param txHash Transaction hash
739 * @param pollingIntervalMs How often (in ms) should we check if the transaction is mined.
740 * @param timeoutMs How long (in ms) to poll for transaction mined until aborting.
741 * @return Transaction receipt with decoded log args.
742 */
743 awaitTransactionSuccessAsync(txHash, pollingIntervalMs = 1000, timeoutMs) {
744 return __awaiter(this, void 0, void 0, function* () {
745 const receipt = yield this.awaitTransactionMinedAsync(txHash, pollingIntervalMs, timeoutMs);
746 if (receipt.status !== 1) {
747 throw new Error(`Transaction failed: ${txHash}`);
748 }
749 return receipt;
750 });
751 }
752 /**
753 * Calls the 'debug_setHead' JSON RPC method, which sets the current head of
754 * the local chain by block number. Note, this is a destructive action and
755 * may severely damage your chain. Use with extreme caution. As of now, this
756 * is only supported by Geth. It sill throw if the 'debug_setHead' method is
757 * not supported.
758 * @param blockNumber The block number to reset to.
759 */
760 setHeadAsync(blockNumber) {
761 return __awaiter(this, void 0, void 0, function* () {
762 assert_1.assert.isNumber('blockNumber', blockNumber);
763 yield this.sendRawPayloadAsync({ method: 'debug_setHead', params: [utils_2.utils.numberToHex(blockNumber)] });
764 });
765 }
766 /**
767 * Sends a raw Ethereum JSON RPC payload and returns the response's `result` key
768 * @param payload A partial JSON RPC payload. No need to include version, id, params (if none needed)
769 * @return The contents nested under the result key of the response body
770 */
771 sendRawPayloadAsync(payload) {
772 return __awaiter(this, void 0, void 0, function* () {
773 if (!payload.method) {
774 throw new Error(`Must supply method in JSONRPCRequestPayload, tried: [${payload}]`);
775 }
776 // tslint:disable:no-object-literal-type-assertion
777 const payloadWithDefaults = Object.assign({ id: this._jsonRpcRequestId++, params: [], jsonrpc: '2.0' }, payload);
778 // tslint:enable:no-object-literal-type-assertion
779 const sendAsync = utils_1.promisify(this._provider.sendAsync.bind(this._provider));
780 const response = yield sendAsync(payloadWithDefaults); // will throw if it fails
781 if (!response) {
782 throw new Error(`No response`);
783 }
784 const errorMessage = response.error ? response.error.message || response.error : undefined;
785 if (errorMessage) {
786 throw new Error(errorMessage);
787 }
788 if (response.result === undefined) {
789 throw new Error(`JSON RPC response has no result`);
790 }
791 return response.result;
792 });
793 }
794 /**
795 * Returns either NodeType.Geth or NodeType.Ganache depending on the type of
796 * the backing Ethereum node. Throws for any other type of node.
797 */
798 getNodeTypeAsync() {
799 return __awaiter(this, void 0, void 0, function* () {
800 const version = yield this.getNodeVersionAsync();
801 if (_.includes(version, uniqueVersionIds.geth)) {
802 return types_1.NodeType.Geth;
803 }
804 else if (_.includes(version, uniqueVersionIds.ganache)) {
805 return types_1.NodeType.Ganache;
806 }
807 else {
808 throw new Error(`Unknown client version: ${version}`);
809 }
810 });
811 }
812} // tslint:disable-line:max-file-line-count
813exports.Web3Wrapper = Web3Wrapper;
814//# sourceMappingURL=web3_wrapper.js.map
\No newline at end of file