Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds revert instruction handling #3248

Merged
merged 25 commits into from
Dec 6, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
30cf8af
revert reason string handling added
nivida Nov 27, 2019
d74843e
method.isRevertReasonString simplified
nivida Nov 28, 2019
182783e
method.getRevertReason funcDoc added and return values improved
nivida Nov 28, 2019
854e579
types extended with RevertInstructionError in core-helpers module
nivida Nov 28, 2019
8dcc9d7
web3-eth types updated
nivida Nov 28, 2019
7c246cb
error handling in Method object of the web3-core-method module fixed
nivida Nov 28, 2019
3c2b6b2
Method.isRevertReasonString updated
nivida Nov 28, 2019
523c811
Merge branch '1.x' into feature/revert-reason-string
nivida Dec 2, 2019
b63fca3
console.log added to test case to check the solely remotely failing t…
nivida Dec 2, 2019
04156ae
handleRevert module option added to eth and eth-contract. Duplicated …
nivida Dec 2, 2019
357b59e
Basic contract updated, handleRevert option handling fixed, and e2e.m…
nivida Dec 2, 2019
5f67c48
revert instruction test cases added to e2e.method.call and assertions…
nivida Dec 2, 2019
2561b81
eth.handleRevert test added
nivida Dec 2, 2019
62a8214
Inline comments added to the Method object because of the code comple…
nivida Dec 2, 2019
4e5f4c8
web3-eth and web3-eth-contract documentation extended with 'handleRev…
nivida Dec 2, 2019
842e2b8
CHANGELOG.md updated
nivida Dec 2, 2019
9db572c
Contract options extended to be able to configure singles contracts w…
nivida Dec 2, 2019
ec5f803
Merge branch '1.x' into feature/revert-reason-string
nivida Dec 4, 2019
d76c6eb
'eth_sendRawTransaction' does get ignored for the revert handling, in…
nivida Dec 4, 2019
fbcde45
Merge branch 'feature/revert-reason-string' of github.com:ethereum/we…
nivida Dec 4, 2019
cce490a
TransactionRevertInstructionError added and related types updated
nivida Dec 4, 2019
eba77c2
logic checked if the transaction does get signed locally on call of '…
nivida Dec 4, 2019
0623f2c
contract options handling improved, contract test extended, and relat…
nivida Dec 4, 2019
7fb7b0c
web3-eth.rst updated
nivida Dec 4, 2019
b0b7f3a
hint about the 'reason' and 'signature' property of the returnd error…
nivida Dec 6, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/web3-core-helpers/src/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,12 @@ module.exports = {
},
ConnectionTimeout: function (ms){
return new Error('CONNECTION TIMEOUT: timeout of ' + ms + ' ms achived');
},
RevertInstructionError: function(reason, signature) {
var error = new Error('Your request got reverted with the following reason string: ' + reason);
nivida marked this conversation as resolved.
Show resolved Hide resolved
error.reason = reason;
error.signature = signature;

return error;
}
};
6 changes: 6 additions & 0 deletions packages/web3-core-helpers/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export class errors {
static InvalidProvider(): Error;
static InvalidResponse(result: Error): Error;
static ConnectionTimeout(ms: string): Error;
static RevertInstructionError(reason: string, signature: string): RevertInstructionError
}

export class WebsocketProviderBase {
Expand Down Expand Up @@ -182,3 +183,8 @@ export interface JsonRpcResponse {
result?: any;
error?: string;
}

export interface RevertInstructionError extends Error {
reason: string;
signature: string;
}
3 changes: 3 additions & 0 deletions packages/web3-core-helpers/types/tests/errors-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ errors.InvalidResponse(new Error('hey'));

// $ExpectType Error
errors.ConnectionTimeout('timeout');

// $ExpectType RevertInstructionError
errors.RevertInstructionError('reason', 'signature');
120 changes: 106 additions & 14 deletions packages/web3-core-method/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ var Method = function Method(options) {
this.outputFormatter = options.outputFormatter;
this.transformPayload = options.transformPayload;
this.extraFormatters = options.extraFormatters;
this.abiCoder = options.abiCoder; // Will be used to encode the revert reason string

this.requestManager = options.requestManager;

Expand Down Expand Up @@ -391,7 +392,7 @@ Method.prototype._confirmTransaction = function(defer, result, payload) {
return receipt;
})
// CHECK for normal tx check for receipt only
.then(function(receipt) {
.then(async function(receipt) {
if (!isContractDeployment && !promiseResolved) {
if (!receipt.outOfGas &&
(!gasProvided || gasProvided !== receipt.gasUsed) &&
Expand All @@ -408,13 +409,39 @@ Method.prototype._confirmTransaction = function(defer, result, payload) {
receiptJSON = JSON.stringify(receipt, null, 2);

if (receipt.status === false || receipt.status === '0x0') {
utils._fireError(
new Error('Transaction has been reverted by the EVM:\n' + receiptJSON),
defer.eventEmitter,
defer.reject,
null,
receipt
);
try {
var revertMessage = await method.getRevertReason(
payload.params[0],
receipt.blockNumber
);

if (revertMessage) {
utils._fireError(
errors.RevertInstructionError(revertMessage.reason, revertMessage.signature),
defer.eventEmitter,
defer.reject,
payload.callback,
receipt
);
} else {
utils._fireError(
new Error('Transaction has been reverted by the EVM:\n' + receiptJSON),
defer.eventEmitter,
defer.reject,
null,
receipt
);
}
} catch (error) {
utils._fireError(
new Error('Transaction has been reverted by the EVM:\n' + receiptJSON),
defer.eventEmitter,
defer.reject,
null,
receipt
);
}

} else {
utils._fireError(
new Error('Transaction ran out of gas. Please provide more gas:\n' + receiptJSON),
Expand Down Expand Up @@ -526,16 +553,34 @@ var getWallet = function(from, accounts) {

Method.prototype.buildCall = function() {
var method = this,
isSendTx = (method.call === 'eth_sendTransaction' || method.call === 'eth_sendRawTransaction'); // || method.call === 'personal_sendTransaction'
isSendTx = (method.call === 'eth_sendTransaction' || method.call === 'eth_sendRawTransaction'), // || method.call === 'personal_sendTransaction'
isCall = (method.call === 'eth_call');

// actual send function
var send = function() {
var defer = promiEvent(!isSendTx),
payload = method.toPayload(Array.prototype.slice.call(arguments));


// CALLBACK function
var sendTxCallback = function(err, result) {
if (isCall && (method.isRevertReasonString(result) && method.abiCoder)) {
var reason = method.abiCoder.decodeParameter('string', '0x' + result.substring(10));
nivida marked this conversation as resolved.
Show resolved Hide resolved
var signature = 'Error(String)';

utils._fireError(
errors.RevertInstructionError(reason, signature),
defer.eventEmitter,
defer.reject,
payload.callback,
{
reason: reason,
signature: signature
}
);

return;
}

try {
result = method.formatOutput(result);
} catch (e) {
Expand All @@ -560,10 +605,8 @@ Method.prototype.buildCall = function() {

// return PROMISE
if (!isSendTx) {

if (!err) {
defer.resolve(result);

}

// return PROMIEVENT
Expand Down Expand Up @@ -620,8 +663,7 @@ Method.prototype.buildCall = function() {
if (_.isFunction(defer.eventEmitter.listeners) && defer.eventEmitter.listeners('error').length) {
defer.eventEmitter.emit('error', err);
defer.eventEmitter.removeAllListeners();
defer.eventEmitter.catch(function() {
});
defer.eventEmitter.catch(function() {});
}
defer.reject(err);
});
Expand Down Expand Up @@ -683,6 +725,56 @@ Method.prototype.buildCall = function() {
return send;
};

/**
* Returns the revert reason string if existing or otherwise false.
*
* @method getRevertReason
*
* @param {Object} txOptions
* @param {Number} blockNumber
*
* @returns {Promise<Boolean|String>}
*/
Method.prototype.getRevertReason = function(txOptions, blockNumber) {
var self = this;

return new Promise(function(resolve, reject) {
(new Method({
name: 'call',
call: 'eth_call',
params: 2,
abiCoder: self.abiCoder
}))
.createFunction(self.requestManager)(txOptions, utils.numberToHex(blockNumber))
.then(function() {
resolve(false);
})
.catch(function(error) {
if (error.reason) {
resolve({
reason: error.reason,
signature: error.signature
});
} else {
reject(error);
}
});
});
};

/**
* Checks if the given hex string is a revert message from the EVM
*
* @method isRevertReasonString
*
* @param {String} data - Hex string prefixed with 0x
*
* @returns {Boolean}
*/
Method.prototype.isRevertReasonString = function (data) {
return ((data.length - 2) / 2) % 32 === 4 && data.substring(0, 10) === '0x08c379a0';
};

/**
* Should be called to create the pure JSONRPC request which can be used in a batch request
*
Expand Down
1 change: 1 addition & 0 deletions packages/web3-core-method/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ export interface Method {
extraFormatters?: any;
defaultBlock?: string;
defaultAccount?: string | null;
abiCoder?: any
}
6 changes: 4 additions & 2 deletions packages/web3-eth-contract/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,8 @@ Contract.prototype._executeMethod = function _executeMethod(){
requestManager: _this._parent._requestManager,
accounts: ethAccounts, // is eth.accounts (necessary for wallet signing)
defaultAccount: _this._parent.defaultAccount,
defaultBlock: _this._parent.defaultBlock
defaultBlock: _this._parent.defaultBlock,
abiCoder: abi
})).createFunction();

return call(args.options, args.defaultBlock, args.callback);
Expand Down Expand Up @@ -903,7 +904,8 @@ Contract.prototype._executeMethod = function _executeMethod(){
defaultCommon: _this._parent.defaultCommon,
defaultChain: _this._parent.defaultChain,
defaultHardfork: _this._parent.defaultHardfork,
extraFormatters: extraFormatters
extraFormatters: extraFormatters,
abiCoder: abi
})).createFunction();

return sendTransaction(args.options, args.callback);
Expand Down
9 changes: 6 additions & 3 deletions packages/web3-eth/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,8 @@ var Eth = function Eth() {
name: 'sendSignedTransaction',
call: 'eth_sendRawTransaction',
params: 1,
inputFormatter: [null]
inputFormatter: [null],
abiCoder: abi
}),
new Method({
name: 'signTransaction',
Expand All @@ -435,7 +436,8 @@ var Eth = function Eth() {
name: 'sendTransaction',
call: 'eth_sendTransaction',
params: 1,
inputFormatter: [formatter.inputTransactionFormatter]
inputFormatter: [formatter.inputTransactionFormatter],
abiCoder: abi
}),
new Method({
name: 'sign',
Expand All @@ -451,7 +453,8 @@ var Eth = function Eth() {
name: 'call',
call: 'eth_call',
params: 2,
inputFormatter: [formatter.inputCallFormatter, formatter.inputDefaultBlockNumberFormatter]
inputFormatter: [formatter.inputCallFormatter, formatter.inputDefaultBlockNumberFormatter],
abiCoder: abi
}),
new Method({
name: 'estimateGas',
Expand Down
13 changes: 7 additions & 6 deletions packages/web3-eth/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
LogsOptions,
PastLogsOptions
} from 'web3-core';
import {RevertInstructionError} from 'web3-core-helpers';
import {Subscription} from 'web3-core-subscriptions';
import {AbiCoder} from 'web3-eth-abi';
import {Accounts} from 'web3-eth-accounts';
Expand Down Expand Up @@ -294,12 +295,12 @@ export class Eth {
sendTransaction(
transactionConfig: TransactionConfig,
callback?: (error: Error, hash: string) => void
): PromiEvent<TransactionReceipt>;
): PromiEvent<TransactionReceipt | RevertInstructionError>;

sendSignedTransaction(
signedTransactionData: string,
callback?: (error: Error, hash: string) => void
): PromiEvent<TransactionReceipt>;
): PromiEvent<TransactionReceipt | RevertInstructionError>;

sign(
dataToSign: string,
Expand Down Expand Up @@ -327,20 +328,20 @@ export class Eth {
) => void
): Promise<RLPEncodedTransaction>;

call(transactionConfig: TransactionConfig): Promise<string>;
call(transactionConfig: TransactionConfig): Promise<string | RevertInstructionError>;
call(
transactionConfig: TransactionConfig,
defaultBlock?: BlockNumber
): Promise<string>;
): Promise<string | RevertInstructionError>;
call(
transactionConfig: TransactionConfig,
callback?: (error: Error, data: string) => void
): Promise<string>;
): Promise<string | RevertInstructionError>;
call(
transactionConfig: TransactionConfig,
defaultBlock: BlockNumber,
callback: (error: Error, data: string) => void
): Promise<string>;
): Promise<string | RevertInstructionError>;

estimateGas(
transactionConfig: TransactionConfig,
Expand Down
Loading