UNPKG

37.3 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 params: [address, typedData],
391 });
392 }
393 catch (err) {
394 lastErr = err;
395 // If there are no more methods to try or the error says something other
396 // than the method not existing, throw.
397 if (!/(not handled|does not exist|not supported)/.test(err.message)) {
398 throw err;
399 }
400 }
401 }
402 throw lastErr;
403 });
404 }
405 /**
406 * Fetches the latest block number
407 * @returns Block number
408 */
409 getBlockNumberAsync() {
410 return __awaiter(this, void 0, void 0, function* () {
411 const blockNumberHex = yield this.sendRawPayloadAsync({
412 method: 'eth_blockNumber',
413 params: [],
414 });
415 const blockNumber = utils_2.utils.convertHexToNumberOrNull(blockNumberHex);
416 return blockNumber;
417 });
418 }
419 /**
420 * Fetches the nonce for an account (transaction count for EOAs).
421 * @param address Address of account.
422 * @param defaultBlock Block height at which to make the call. Defaults to `latest`
423 * @returns Account nonce.
424 */
425 getAccountNonceAsync(address, defaultBlock) {
426 return __awaiter(this, void 0, void 0, function* () {
427 assert_1.assert.isETHAddressHex('address', address);
428 if (defaultBlock !== undefined) {
429 Web3Wrapper._assertBlockParam(defaultBlock);
430 }
431 const marshalledDefaultBlock = marshaller_1.marshaller.marshalBlockParam(defaultBlock);
432 const encodedAddress = marshaller_1.marshaller.marshalAddress(address);
433 const nonceHex = yield this.sendRawPayloadAsync({
434 method: 'eth_getTransactionCount',
435 params: [encodedAddress, marshalledDefaultBlock],
436 });
437 assert_1.assert.isHexString('nonce', nonceHex);
438 // tslint:disable-next-line:custom-no-magic-numbers
439 return parseInt(nonceHex.substr(2), 16);
440 });
441 }
442 /**
443 * Fetch a specific Ethereum block without transaction data
444 * @param blockParam The block you wish to fetch (blockHash, blockNumber or blockLiteral)
445 * @returns The requested block without transaction data, or undefined if block was not found
446 * (e.g the node isn't fully synced, there was a block re-org and the requested block was uncles, etc...)
447 */
448 getBlockIfExistsAsync(blockParam) {
449 return __awaiter(this, void 0, void 0, function* () {
450 Web3Wrapper._assertBlockParamOrString(blockParam);
451 const encodedBlockParam = marshaller_1.marshaller.marshalBlockParam(blockParam);
452 const method = utils_2.utils.isHexStrict(blockParam) ? 'eth_getBlockByHash' : 'eth_getBlockByNumber';
453 const shouldIncludeTransactionData = false;
454 const blockWithoutTransactionDataWithHexValuesOrNull = yield this.sendRawPayloadAsync({
455 method,
456 params: [encodedBlockParam, shouldIncludeTransactionData],
457 });
458 let blockWithoutTransactionDataIfExists;
459 if (blockWithoutTransactionDataWithHexValuesOrNull !== null) {
460 blockWithoutTransactionDataIfExists = marshaller_1.marshaller.unmarshalIntoBlockWithoutTransactionData(blockWithoutTransactionDataWithHexValuesOrNull);
461 }
462 return blockWithoutTransactionDataIfExists;
463 });
464 }
465 /**
466 * Fetch a specific Ethereum block with transaction data
467 * @param blockParam The block you wish to fetch (blockHash, blockNumber or blockLiteral)
468 * @returns The requested block with transaction data
469 */
470 getBlockWithTransactionDataAsync(blockParam) {
471 return __awaiter(this, void 0, void 0, function* () {
472 Web3Wrapper._assertBlockParamOrString(blockParam);
473 let encodedBlockParam = blockParam;
474 if (_.isNumber(blockParam)) {
475 encodedBlockParam = utils_2.utils.numberToHex(blockParam);
476 }
477 const method = utils_2.utils.isHexStrict(blockParam) ? 'eth_getBlockByHash' : 'eth_getBlockByNumber';
478 const shouldIncludeTransactionData = true;
479 const blockWithTransactionDataWithHexValues = yield this.sendRawPayloadAsync({
480 method,
481 params: [encodedBlockParam, shouldIncludeTransactionData],
482 });
483 const blockWithoutTransactionData = marshaller_1.marshaller.unmarshalIntoBlockWithTransactionData(blockWithTransactionDataWithHexValues);
484 return blockWithoutTransactionData;
485 });
486 }
487 /**
488 * Fetch a block's timestamp
489 * @param blockParam The block you wish to fetch (blockHash, blockNumber or blockLiteral)
490 * @returns The block's timestamp
491 */
492 getBlockTimestampAsync(blockParam) {
493 return __awaiter(this, void 0, void 0, function* () {
494 Web3Wrapper._assertBlockParamOrString(blockParam);
495 const blockIfExists = yield this.getBlockIfExistsAsync(blockParam);
496 if (blockIfExists === undefined) {
497 throw new Error(`Failed to fetch block with blockParam: ${JSON.stringify(blockParam)}`);
498 }
499 return blockIfExists.timestamp;
500 });
501 }
502 /**
503 * Retrieve the user addresses available through the backing provider
504 * @returns Available user addresses
505 */
506 getAvailableAddressesAsync() {
507 return __awaiter(this, void 0, void 0, function* () {
508 const addresses = yield this.sendRawPayloadAsync({
509 method: 'eth_accounts',
510 params: [],
511 });
512 const normalizedAddresses = _.map(addresses, address => address.toLowerCase());
513 return normalizedAddresses;
514 });
515 }
516 /**
517 * Take a snapshot of the blockchain state on a TestRPC/Ganache local node
518 * @returns The snapshot id. This can be used to revert to this snapshot
519 */
520 takeSnapshotAsync() {
521 return __awaiter(this, void 0, void 0, function* () {
522 const snapshotId = Number(yield this.sendRawPayloadAsync({ method: 'evm_snapshot', params: [] }));
523 return snapshotId;
524 });
525 }
526 /**
527 * Revert the blockchain state to a previous snapshot state on TestRPC/Ganache local node
528 * @param snapshotId snapshot id to revert to
529 * @returns Whether the revert was successful
530 */
531 revertSnapshotAsync(snapshotId) {
532 return __awaiter(this, void 0, void 0, function* () {
533 assert_1.assert.isNumber('snapshotId', snapshotId);
534 const didRevert = yield this.sendRawPayloadAsync({ method: 'evm_revert', params: [snapshotId] });
535 return didRevert;
536 });
537 }
538 /**
539 * Mine a block on a TestRPC/Ganache local node
540 */
541 mineBlockAsync() {
542 return __awaiter(this, void 0, void 0, function* () {
543 yield this.sendRawPayloadAsync({ method: 'evm_mine', params: [] });
544 });
545 }
546 /**
547 * Increase the next blocks timestamp on TestRPC/Ganache or Geth local node.
548 * Will throw if provider is neither TestRPC/Ganache or Geth.
549 * @param timeDelta Amount of time to add in seconds
550 */
551 increaseTimeAsync(timeDelta) {
552 return __awaiter(this, void 0, void 0, function* () {
553 assert_1.assert.isNumber('timeDelta', timeDelta);
554 // Detect Geth vs. Ganache and use appropriate endpoint.
555 const version = yield this.getNodeVersionAsync();
556 if (_.includes(version, uniqueVersionIds.geth)) {
557 return this.sendRawPayloadAsync({ method: 'debug_increaseTime', params: [timeDelta] });
558 }
559 else if (_.includes(version, uniqueVersionIds.ganache)) {
560 return this.sendRawPayloadAsync({ method: 'evm_increaseTime', params: [timeDelta] });
561 }
562 else {
563 throw new Error(`Unknown client version: ${version}`);
564 }
565 });
566 }
567 /**
568 * Retrieve smart contract logs for a given filter
569 * @param filter Parameters by which to filter which logs to retrieve
570 * @returns The corresponding log entries
571 */
572 getLogsAsync(filter) {
573 return __awaiter(this, void 0, void 0, function* () {
574 if (filter.blockHash !== undefined && (filter.fromBlock !== undefined || filter.toBlock !== undefined)) {
575 throw new Error(`Cannot specify 'blockHash' as well as 'fromBlock'/'toBlock' in the filter supplied to 'getLogsAsync'`);
576 }
577 let fromBlock = filter.fromBlock;
578 if (_.isNumber(fromBlock)) {
579 fromBlock = utils_2.utils.numberToHex(fromBlock);
580 }
581 let toBlock = filter.toBlock;
582 if (_.isNumber(toBlock)) {
583 toBlock = utils_2.utils.numberToHex(toBlock);
584 }
585 const serializedFilter = Object.assign(Object.assign({}, filter), { fromBlock,
586 toBlock });
587 const payload = {
588 method: 'eth_getLogs',
589 params: [serializedFilter],
590 };
591 const rawLogs = yield this.sendRawPayloadAsync(payload);
592 const formattedLogs = _.map(rawLogs, marshaller_1.marshaller.unmarshalLog.bind(marshaller_1.marshaller));
593 return formattedLogs;
594 });
595 }
596 /**
597 * Calculate the estimated gas cost for a given transaction
598 * @param txData Transaction data
599 * @returns Estimated gas cost
600 */
601 estimateGasAsync(txData) {
602 return __awaiter(this, void 0, void 0, function* () {
603 assert_1.assert.doesConformToSchema('txData', txData, json_schemas_1.schemas.txDataSchema);
604 const txDataHex = marshaller_1.marshaller.marshalTxData(txData);
605 const gasHex = yield this.sendRawPayloadAsync({ method: 'eth_estimateGas', params: [txDataHex] });
606 const gas = utils_2.utils.convertHexToNumber(gasHex);
607 return gas;
608 });
609 }
610 /**
611 * Generate an access list for an ethereum call and also compute the gas used.
612 * @param callData Call data
613 * @param defaultBlock Block height at which to make the call. Defaults to 'latest'.
614 * @returns The access list and gas used.
615 */
616 createAccessListAsync(callData, defaultBlock) {
617 return __awaiter(this, void 0, void 0, function* () {
618 assert_1.assert.doesConformToSchema('callData', callData, json_schemas_1.schemas.callDataSchema, [
619 json_schemas_1.schemas.addressSchema,
620 json_schemas_1.schemas.numberSchema,
621 json_schemas_1.schemas.jsNumber,
622 ]);
623 const rawResult = yield this.sendRawPayloadAsync({
624 method: 'eth_createAccessList',
625 params: [marshaller_1.marshaller.marshalCallData(callData), marshaller_1.marshaller.marshalBlockParam(defaultBlock)],
626 });
627 if (rawResult.error) {
628 throw new Error(rawResult.error);
629 }
630 return {
631 accessList: rawResult.accessList.reduce((o, v) => {
632 o[v.address] = o[v.address] || [];
633 o[v.address].push(...(v.storageKeys || []));
634 return o;
635 },
636 // tslint:disable-next-line: no-object-literal-type-assertion
637 {}),
638 // tslint:disable-next-line: custom-no-magic-numbers
639 gasUsed: parseInt(rawResult.gasUsed.slice(2), 16),
640 };
641 });
642 }
643 /**
644 * Call a smart contract method at a given block height
645 * @param callData Call data
646 * @param defaultBlock Block height at which to make the call. Defaults to `latest`
647 * @returns The raw call result
648 */
649 callAsync(callData, defaultBlock) {
650 return __awaiter(this, void 0, void 0, function* () {
651 assert_1.assert.doesConformToSchema('callData', callData, json_schemas_1.schemas.callDataSchema);
652 if (defaultBlock !== undefined) {
653 Web3Wrapper._assertBlockParam(defaultBlock);
654 }
655 const marshalledDefaultBlock = marshaller_1.marshaller.marshalBlockParam(defaultBlock);
656 const callDataHex = marshaller_1.marshaller.marshalCallData(callData);
657 const overrides = marshaller_1.marshaller.marshalCallOverrides(callData.overrides || {});
658 const rawCallResult = yield this.sendRawPayloadAsync({
659 method: 'eth_call',
660 params: [callDataHex, marshalledDefaultBlock, ...(Object.keys(overrides).length === 0 ? [] : [overrides])],
661 });
662 return rawCallResult;
663 });
664 }
665 /**
666 * Send a transaction
667 * @param txData Transaction data
668 * @returns Transaction hash
669 */
670 sendTransactionAsync(txData) {
671 return __awaiter(this, void 0, void 0, function* () {
672 assert_1.assert.doesConformToSchema('txData', txData, json_schemas_1.schemas.txDataSchema);
673 const txDataHex = marshaller_1.marshaller.marshalTxData(txData);
674 const txHash = yield this.sendRawPayloadAsync({ method: 'eth_sendTransaction', params: [txDataHex] });
675 return txHash;
676 });
677 }
678 /**
679 * Waits for a transaction to be mined and returns the transaction receipt.
680 * Note that just because a transaction was mined does not mean it was
681 * successful. You need to check the status code of the transaction receipt
682 * to find out if it was successful, or use the helper method
683 * awaitTransactionSuccessAsync.
684 * @param txHash Transaction hash
685 * @param pollingIntervalMs How often (in ms) should we check if the transaction is mined.
686 * @param timeoutMs How long (in ms) to poll for transaction mined until aborting.
687 * @return Transaction receipt with decoded log args.
688 */
689 awaitTransactionMinedAsync(txHash, pollingIntervalMs = 1000, timeoutMs) {
690 return __awaiter(this, void 0, void 0, function* () {
691 assert_1.assert.isHexString('txHash', txHash);
692 assert_1.assert.isNumber('pollingIntervalMs', pollingIntervalMs);
693 if (timeoutMs !== undefined) {
694 assert_1.assert.isNumber('timeoutMs', timeoutMs);
695 }
696 // Immediately check if the transaction has already been mined.
697 let transactionReceipt = yield this.getTransactionReceiptIfExistsAsync(txHash);
698 if (transactionReceipt !== undefined) {
699 const logsWithDecodedArgs = _.map(transactionReceipt.logs, this.abiDecoder.tryToDecodeLogOrNoop.bind(this.abiDecoder));
700 const transactionReceiptWithDecodedLogArgs = Object.assign(Object.assign({}, transactionReceipt), { logs: logsWithDecodedArgs });
701 return transactionReceiptWithDecodedLogArgs;
702 }
703 // Otherwise, check again every pollingIntervalMs.
704 let wasTimeoutExceeded = false;
705 if (timeoutMs) {
706 setTimeout(() => (wasTimeoutExceeded = true), timeoutMs);
707 }
708 const txReceiptPromise = new Promise((resolve, reject) => {
709 const intervalId = utils_1.intervalUtils.setAsyncExcludingInterval(() => __awaiter(this, void 0, void 0, function* () {
710 if (wasTimeoutExceeded) {
711 utils_1.intervalUtils.clearAsyncExcludingInterval(intervalId);
712 return reject(types_1.Web3WrapperErrors.TransactionMiningTimeout);
713 }
714 transactionReceipt = yield this.getTransactionReceiptIfExistsAsync(txHash);
715 if (transactionReceipt !== undefined) {
716 utils_1.intervalUtils.clearAsyncExcludingInterval(intervalId);
717 const logsWithDecodedArgs = _.map(transactionReceipt.logs, this.abiDecoder.tryToDecodeLogOrNoop.bind(this.abiDecoder));
718 const transactionReceiptWithDecodedLogArgs = Object.assign(Object.assign({}, transactionReceipt), { logs: logsWithDecodedArgs });
719 resolve(transactionReceiptWithDecodedLogArgs);
720 }
721 }), pollingIntervalMs, (err) => {
722 utils_1.intervalUtils.clearAsyncExcludingInterval(intervalId);
723 reject(err);
724 });
725 });
726 const txReceipt = yield txReceiptPromise;
727 return txReceipt;
728 });
729 }
730 /**
731 * Waits for a transaction to be mined and returns the transaction receipt.
732 * Unlike awaitTransactionMinedAsync, it will throw if the receipt has a
733 * status that is not equal to 1. A status of 0 or null indicates that the
734 * transaction was mined, but failed. See:
735 * https://github.com/ethereum/wiki/wiki/JavaScript-API#web3ethgettransactionreceipt
736 * @param txHash Transaction hash
737 * @param pollingIntervalMs How often (in ms) should we check if the transaction is mined.
738 * @param timeoutMs How long (in ms) to poll for transaction mined until aborting.
739 * @return Transaction receipt with decoded log args.
740 */
741 awaitTransactionSuccessAsync(txHash, pollingIntervalMs = 1000, timeoutMs) {
742 return __awaiter(this, void 0, void 0, function* () {
743 const receipt = yield this.awaitTransactionMinedAsync(txHash, pollingIntervalMs, timeoutMs);
744 if (receipt.status !== 1) {
745 throw new Error(`Transaction failed: ${txHash}`);
746 }
747 return receipt;
748 });
749 }
750 /**
751 * Calls the 'debug_setHead' JSON RPC method, which sets the current head of
752 * the local chain by block number. Note, this is a destructive action and
753 * may severely damage your chain. Use with extreme caution. As of now, this
754 * is only supported by Geth. It sill throw if the 'debug_setHead' method is
755 * not supported.
756 * @param blockNumber The block number to reset to.
757 */
758 setHeadAsync(blockNumber) {
759 return __awaiter(this, void 0, void 0, function* () {
760 assert_1.assert.isNumber('blockNumber', blockNumber);
761 yield this.sendRawPayloadAsync({ method: 'debug_setHead', params: [utils_2.utils.numberToHex(blockNumber)] });
762 });
763 }
764 /**
765 * Sends a raw Ethereum JSON RPC payload and returns the response's `result` key
766 * @param payload A partial JSON RPC payload. No need to include version, id, params (if none needed)
767 * @return The contents nested under the result key of the response body
768 */
769 sendRawPayloadAsync(payload) {
770 return __awaiter(this, void 0, void 0, function* () {
771 if (!payload.method) {
772 throw new Error(`Must supply method in JSONRPCRequestPayload, tried: [${payload}]`);
773 }
774 // tslint:disable:no-object-literal-type-assertion
775 const payloadWithDefaults = Object.assign({ id: this._jsonRpcRequestId++, params: [], jsonrpc: '2.0' }, payload);
776 // tslint:enable:no-object-literal-type-assertion
777 const sendAsync = utils_1.promisify(this._provider.sendAsync.bind(this._provider));
778 const response = yield sendAsync(payloadWithDefaults); // will throw if it fails
779 if (!response) {
780 throw new Error(`No response`);
781 }
782 const errorMessage = response.error ? response.error.message || response.error : undefined;
783 if (errorMessage) {
784 throw new Error(errorMessage);
785 }
786 if (response.result === undefined) {
787 throw new Error(`JSON RPC response has no result`);
788 }
789 return response.result;
790 });
791 }
792 /**
793 * Returns either NodeType.Geth or NodeType.Ganache depending on the type of
794 * the backing Ethereum node. Throws for any other type of node.
795 */
796 getNodeTypeAsync() {
797 return __awaiter(this, void 0, void 0, function* () {
798 const version = yield this.getNodeVersionAsync();
799 if (_.includes(version, uniqueVersionIds.geth)) {
800 return types_1.NodeType.Geth;
801 }
802 else if (_.includes(version, uniqueVersionIds.ganache)) {
803 return types_1.NodeType.Ganache;
804 }
805 else {
806 throw new Error(`Unknown client version: ${version}`);
807 }
808 });
809 }
810} // tslint:disable-line:max-file-line-count
811exports.Web3Wrapper = Web3Wrapper;
812//# sourceMappingURL=web3_wrapper.js.map
\No newline at end of file