From cb6d18035fa2a4be4445340f0d57fd4e7f4abd8c Mon Sep 17 00:00:00 2001 From: Richard Moore Date: Wed, 1 Aug 2018 17:02:27 -0400 Subject: [PATCH] Added error code detection for sendTransaction. --- src.ts/providers/etherscan-provider.ts | 17 ++++++++++-- src.ts/providers/json-rpc-provider.ts | 37 +++++++++++++++++++++++--- src.ts/providers/provider.ts | 6 ++--- src.ts/utils/errors.ts | 15 ++++++++--- 4 files changed, 63 insertions(+), 12 deletions(-) diff --git a/src.ts/providers/etherscan-provider.ts b/src.ts/providers/etherscan-provider.ts index 2c564e59..6fb7d6e9 100644 --- a/src.ts/providers/etherscan-provider.ts +++ b/src.ts/providers/etherscan-provider.ts @@ -151,8 +151,21 @@ export class EtherscanProvider extends Provider{ case 'sendTransaction': url += '/api?module=proxy&action=eth_sendRawTransaction&hex=' + params.signedTransaction; url += apiKey; - return fetchJson(url, null, getJsonResult); - + return fetchJson(url, null, getJsonResult).catch((error) => { + // "Insufficient funds. The account you tried to send transaction from does not have enough funds. Required 21464000000000 and got: 0" + if (error.responseText.toLowerCase().indexOf('insufficient funds') >= 0) { + errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, { }); + } + // "Transaction with the same hash was already imported." + if (error.responseText.indexOf('same hash was already imported') >= 0) { + errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, { }); + } + // "Transaction gas price is too low. There is another transaction with same nonce in the queue. Try increasing the gas price or incrementing the nonce." + if (error.responseText.indexOf('another transaction with same nonce') >= 0) { + errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, { }); + } + throw error; + }); case 'getBlock': if (params.blockTag) { diff --git a/src.ts/providers/json-rpc-provider.ts b/src.ts/providers/json-rpc-provider.ts index 4436d194..d31b22a8 100644 --- a/src.ts/providers/json-rpc-provider.ts +++ b/src.ts/providers/json-rpc-provider.ts @@ -111,8 +111,7 @@ export class JsonRpcSigner extends Signer { } return resolveProperties(tx).then((tx) => { - tx = JsonRpcProvider.hexlifyTransaction(tx); - return this.provider.send('eth_sendTransaction', [ tx ]).then((hash) => { + return this.provider.send('eth_sendTransaction', [ JsonRpcProvider.hexlifyTransaction(tx) ]).then((hash) => { return poll(() => { return this.provider.getTransaction(hash).then((tx: TransactionResponse) => { if (tx === null) { return undefined; } @@ -122,6 +121,24 @@ export class JsonRpcSigner extends Signer { (error).transactionHash = hash; throw error; }); + }, (error) => { + // See: JsonRpcProvider.sendTransaction (@TODO: Expose a ._throwError??) + if (error.responseText.indexOf('insufficient funds') >= 0) { + errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, { + transaction: tx + }); + } + if (error.responseText.indexOf('nonce too low') >= 0) { + errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, { + transaction: tx + }); + } + if (error.responseText.indexOf('replacement transaction underpriced') >= 0) { + errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, { + transaction: tx + }); + } + throw error; }); }); } @@ -235,7 +252,21 @@ export class JsonRpcProvider extends Provider { return this.send('eth_getStorageAt', [ getLowerCase(params.address), params.position, params.blockTag ]); case 'sendTransaction': - return this.send('eth_sendRawTransaction', [ params.signedTransaction ]); + return this.send('eth_sendRawTransaction', [ params.signedTransaction ]).catch((error) => { + // "insufficient funds for gas * price + value" + if (error.responseText.indexOf('insufficient funds') > 0) { + errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, { }); + } + // "nonce too low" + if (error.responseText.indexOf('nonce too low') > 0) { + errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, { }); + } + // "replacement transaction underpriced" + if (error.responseText.indexOf('replacement transaction underpriced') > 0) { + errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, { }); + } + throw error; + }); case 'getBlock': if (params.blockTag) { diff --git a/src.ts/providers/provider.ts b/src.ts/providers/provider.ts index 29c68c9d..e415d5b4 100644 --- a/src.ts/providers/provider.ts +++ b/src.ts/providers/provider.ts @@ -836,9 +836,9 @@ export class Provider extends AbstractProvider { return this.perform('sendTransaction', params).then((hash) => { return this._wrapTransaction(parseTransaction(signedTransaction), hash); }, function (error) { - let tx = parseTransaction(signedTransaction); - if (tx.hash) { - (error).transactionHash = tx.hash; + error.transaction = parseTransaction(signedTransaction); + if (error.transaction.hash) { + (error).transactionHash = error.transaction.hash; } throw error; }); diff --git a/src.ts/utils/errors.ts b/src.ts/utils/errors.ts index 420b68d5..11759736 100644 --- a/src.ts/utils/errors.ts +++ b/src.ts/utils/errors.ts @@ -20,10 +20,6 @@ export const MISSING_NEW = 'MISSING_NEW'; // - reason: The reason (only for EIP848 "Error(string)") export const CALL_EXCEPTION = 'CALL_EXCEPTION'; -// Response from a server was invalid -// - response: The body of the response -//'BAD_RESPONSE', - // Invalid argument (e.g. value is incompatible with type) to a function: // - arg: The argument name that was invalid // - value: The value of the argument @@ -44,6 +40,17 @@ export const UNEXPECTED_ARGUMENT = 'UNEXPECTED_ARGUMENT'; // - fault: the reason this faulted export const NUMERIC_FAULT = 'NUMERIC_FAULT'; +// Insufficien funds (< value + gasLimit * gasPrice) +// - transaction: the transaction attempted +export const INSUFFICIENT_FUNDS = 'INSUFFICIENT_FUNDS'; + +// Nonce has already been used +// - transaction: the transaction attempted +export const NONCE_EXPIRED = 'NONCE_EXPIRED'; + +// The replacement fee for the transaction is too low +// - transaction: the transaction attempted +export const REPLACEMENT_UNDERPRICED = 'REPLACEMENT_UNDERPRICED'; // Unsupported operation // - operation