Skip to content
This repository was archived by the owner on Mar 5, 2025. It is now read-only.

Commit 48958ee

Browse files
Alexnicos99
Alex
andauthored
Nicos99/revert call (#6009)
* improvement of 'web3-core-method.buildCall.sendTxCallback(err, result)' to manage revert embedded error details got from some providers like MetaMask (related to issue #4454) * continuation of previous work on 'web3-core-method.buildCall.sendTxCallback(err, result)' to handle Revert in all cases: use `originalError` sub-object if provided * add fixed #4454 in the CHANGELOG * add unit test to cover my improvement of 'web3-core-method.buildCall.sendTxCallback(err, result)' to better manage revert call reason from some providers like MetaMask (related to issue #4454) * removal of unnecessary debug logs --------- Co-authored-by: Nicolas COURTILLER <[email protected]>
1 parent 6ce085b commit 48958ee

File tree

4 files changed

+114
-2
lines changed

4 files changed

+114
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,8 @@ Released with 1.0.0-beta.37 code base.
666666
### Fixed
667667

668668
- Improved the error propagation in `web3-providers-http` package to effectively propagate useful error infomation about failed HTTP connections (#5955)
669-
- Fix error: "n.data.substring is not a function", that is raised when there is a revert and `web.eth.handleRevert = true` (#6000)
669+
- Fixed "Uncaught TypeError" calling a contract function that revert using MetaMask (#4454) and related "n.data.substring is not a function", that is raised when there is a revert and `web.eth.handleRevert = true` (#6000)
670+
670671
### Changed
671672

672673
- `transaction.type` is now formatted to a hex string before being send to provider (#5979)

packages/web3-core-method/src/index.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,15 @@ Method.prototype.buildCall = function () {
653653
if (!err && method.isRevertReasonString(result)){
654654
reasonData = result.substring(10);
655655
} else if (err && err.data){
656-
reasonData = (err.data.data || err.data).substring(10);
656+
// workaround embedded error details got from some providers like MetaMask
657+
if (typeof err.data === 'object') {
658+
// Ganache has no `originalError` sub-object unlike others
659+
var originalError = err.data.originalError ?? err.data;
660+
reasonData = originalError.data.substring(10);
661+
}
662+
else {
663+
reasonData = err.data.substring(10);
664+
}
657665
}
658666

659667
if (reasonData){

test/eth.call.revert.js

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
var chai = require('chai');
2+
var assert = chai.assert;
3+
var FakeIpcProvider = require('./helpers/FakeIpcProvider');
4+
var Web3 = require('../packages/web3');
5+
6+
describe('call revert', function () {
7+
var provider;
8+
var web3;
9+
10+
beforeEach(function () {
11+
provider = new FakeIpcProvider();
12+
web3 = new Web3(provider);
13+
web3.eth.handleRevert = true;
14+
});
15+
16+
it('Errors with revert reason string through MetaMask', async function() {
17+
provider.injectRawError({
18+
"code": -32603,
19+
"message": "execution reverted: DeadlineExpired",
20+
"data": {
21+
"originalError": {
22+
"code": 3,
23+
"data": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000f446561646c696e65457870697265640000000000000000000000000000000000",
24+
"message": "execution reverted: DeadlineExpired"
25+
}
26+
}
27+
});
28+
provider.injectValidation(function (payload) {
29+
assert.equal(payload.jsonrpc, '2.0');
30+
assert.equal(payload.method, 'eth_call');
31+
assert.deepEqual(
32+
payload.params,
33+
[{
34+
to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
35+
data: '0x23455654',
36+
gas: '0xb',
37+
gasPrice: '0xb'
38+
}, 'latest']
39+
);
40+
});
41+
42+
var options = {
43+
to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
44+
data: '0x23455654',
45+
gas: 11,
46+
gasPrice: 11
47+
};
48+
49+
try {
50+
await web3.eth.call(options, 'latest');
51+
assert.fail('call should have failed!');
52+
} catch (error) {
53+
assert.equal(error.reason, 'DeadlineExpired');
54+
}
55+
});
56+
57+
it('Errors with revert reason string from Ganache through MetaMask', async function() {
58+
provider.injectRawError({
59+
"code": -32603,
60+
"message": "Internal JSON-RPC error.",
61+
"data": {
62+
"message": "VM Exception while processing transaction: revert ImproperState",
63+
"stack": "CallError: VM Exception while processing transaction: revert ImproperState\n at Blockchain.simulateTransaction (C:\\Users\\nicos\\AppData\\Roaming\\npm\\node_modules\\ganache\\dist\\node\\1.js:2:82786)",
64+
"code": -32000,
65+
"name": "CallError",
66+
"data": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d496d70726f706572537461746500000000000000000000000000000000000000"
67+
}
68+
});
69+
provider.injectValidation(function (payload) {
70+
assert.equal(payload.jsonrpc, '2.0');
71+
assert.equal(payload.method, 'eth_call');
72+
assert.deepEqual(
73+
payload.params,
74+
[{
75+
to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
76+
data: '0x23455654',
77+
gas: '0xb',
78+
gasPrice: '0xb'
79+
}, 'latest']
80+
);
81+
});
82+
83+
var options = {
84+
to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
85+
data: '0x23455654',
86+
gas: 11,
87+
gasPrice: 11
88+
};
89+
90+
try {
91+
await web3.eth.call(options, 'latest');
92+
assert.fail('call should have failed!');
93+
} catch (error) {
94+
assert.equal(error.reason, 'ImproperState');
95+
}
96+
});
97+
98+
});

test/helpers/FakeIpcProvider.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@ FakeIpcProvider.prototype.injectError = function (error) {
142142
this.error.push(errorStub);
143143
};
144144

145+
// to simulate strange behavior of MetaMask
146+
FakeIpcProvider.prototype.injectRawError = function (error) {
147+
this.error.push(error);
148+
};
149+
145150
FakeIpcProvider.prototype.injectValidation = function (callback) {
146151
this.validation.push(callback);
147152
};

0 commit comments

Comments
 (0)