@r3l/app
Version:
355 lines (286 loc) • 11.9 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getRevertReason = getRevertReason;
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var _providers = require("@ethersproject/providers");
var _strings = require("@ethersproject/strings");
var _constants = require("@ethersproject/constants");
// This file is a modified version of the following library:
// https://github.com/authereum/eth-revert-reason
// implemented changes:
// - add _tx parameter so we can call a tx without a txHash
// - handle Ganache errors
/**
* Get the revert reason from just a transaction hash
* @param {string} txHash - Hash of an Ethereum transaction
* @param {object} tx - Ethereum transaction object
* @param {string} network - Ethereum network name
* @param {number || string} blockNumber - A block number or 'latest' to make the call from
* @param {*} customProvider - Custom provider (Only ethers and web3 providers are supported at thsitime)
*/
function getRevertReason(_x, _x2) {
return _getRevertReason.apply(this, arguments);
}
function _getRevertReason() {
_getRevertReason = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(txHash, _tx) {
var network,
blockNumber,
customProvider,
provider,
tx,
code,
_args = arguments;
return _regenerator.default.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
network = _args.length > 2 && _args[2] !== undefined ? _args[2] : 'mainnet';
blockNumber = _args.length > 3 && _args[3] !== undefined ? _args[3] : undefined;
customProvider = _args.length > 4 && _args[4] !== undefined ? _args[4] : undefined;
// Normalize the input params
network = network.toLowerCase();
blockNumber = blockNumber || 'latest'; // Perform validation before defining a provider
if (!txHash) {
_context.next = 8;
break;
}
_context.next = 8;
return validateInputPreProvider(txHash, network);
case 8:
// If a web3 provider is passed in, wrap it in an ethers provider
// A standard web3 provider will have `.version`, while an ethers will not
if (customProvider && customProvider.version) {
customProvider = new _providers.Web3Provider(customProvider.currentProvider);
}
provider = customProvider || (0, _providers.getDefaultProvider)(network); // Perform validation after defining a provider
_context.next = 12;
return validateInputPostProvider(txHash, network, blockNumber, provider);
case 12:
_context.prev = 12;
_context.t0 = _tx;
if (_context.t0) {
_context.next = 18;
break;
}
_context.next = 17;
return provider.getTransaction(txHash);
case 17:
_context.t0 = _context.sent;
case 18:
tx = _context.t0;
_context.next = 21;
return getCode(tx, network, blockNumber, provider);
case 21:
code = _context.sent;
return _context.abrupt("return", decodeMessage(code, network));
case 25:
_context.prev = 25;
_context.t1 = _context["catch"](12);
throw new Error('Unable to decode revert reason');
case 28:
case "end":
return _context.stop();
}
}
}, _callee, null, [[12, 25]]);
}));
return _getRevertReason.apply(this, arguments);
}
function validateInputPreProvider(_x3, _x4) {
return _validateInputPreProvider.apply(this, arguments);
}
function _validateInputPreProvider() {
_validateInputPreProvider = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(txHash, network) {
var networks;
return _regenerator.default.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
if (!(!/^0x([A-Fa-f0-9]{64})$/.test(txHash) || txHash.substring(0, 2) !== '0x')) {
_context2.next = 2;
break;
}
throw new Error('Invalid transaction hash');
case 2:
// Only accept valid networks
networks = ['mainnet', 'kovan', 'goerli', 'ropsten', 'rinkeby'];
if (networks.includes(network)) {
_context2.next = 5;
break;
}
throw new Error('Not a valid network');
case 5:
case "end":
return _context2.stop();
}
}
}, _callee2);
}));
return _validateInputPreProvider.apply(this, arguments);
}
function validateInputPostProvider(_x5, _x6, _x7, _x8) {
return _validateInputPostProvider.apply(this, arguments);
}
function _validateInputPostProvider() {
_validateInputPostProvider = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3(txHash, network, blockNumber, provider) {
var tx, currentBlockNumber, errCode, infuraErrCode;
return _regenerator.default.wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
if (!(network === 'kovan')) {
_context3.next = 11;
break;
}
_context3.prev = 1;
_context3.next = 4;
return provider.getTransaction(txHash);
case 4:
tx = _context3.sent;
getCode(tx, network, blockNumber, provider);
_context3.next = 11;
break;
case 8:
_context3.prev = 8;
_context3.t0 = _context3["catch"](1);
throw new Error('Please use a provider that exposes the Parity trace methods to decode the revert reason');
case 11:
if (!(blockNumber !== 'latest')) {
_context3.next = 30;
break;
}
_context3.next = 14;
return provider.getBlockNumber();
case 14:
currentBlockNumber = _context3.sent;
blockNumber = Number(blockNumber); // The block cannot be in the future
if (!(blockNumber >= currentBlockNumber)) {
_context3.next = 18;
break;
}
throw new Error('You cannot use a blocknumber that has not yet happened');
case 18:
if (!(blockNumber < currentBlockNumber - 128)) {
_context3.next = 30;
break;
}
_context3.prev = 19;
_context3.next = 22;
return provider.getBalance(_constants.AddressZero, blockNumber);
case 22:
_context3.next = 30;
break;
case 24:
_context3.prev = 24;
_context3.t1 = _context3["catch"](19);
errCode = JSON.parse(_context3.t1.responseText).error.code; // NOTE: This error code is specific to Infura. Alchemy offers an Archive node by default, so an Alchemy node will never throw here.
infuraErrCode = -32002;
if (!(errCode === infuraErrCode)) {
_context3.next = 30;
break;
}
throw new Error('You cannot use a blocknumber that is older than 128 blocks. Please use a provider that uses a full archival node.');
case 30:
case "end":
return _context3.stop();
}
}
}, _callee3, null, [[1, 8], [19, 24]]);
}));
return _validateInputPostProvider.apply(this, arguments);
}
function decodeMessage(code, network) {
// NOTE: `code` may end with 0's which will return a text string with empty whitespace characters
// This will truncate all 0s and set up the hex string as expected
// NOTE: Parity (Kovan) returns in a different format than other clients
var codeString;
var fnSelectorByteLength = 4;
var dataOffsetByteLength = 32;
var strLengthByteLength = 32;
var strLengthStartPos = 2 + (fnSelectorByteLength + dataOffsetByteLength) * 2;
var strDataStartPos = 2 + (fnSelectorByteLength + dataOffsetByteLength + strLengthByteLength) * 2;
if (network === 'kovan') {
var strLengthHex = code.slice(strLengthStartPos).slice(0, strLengthByteLength * 2);
var strLengthInt = parseInt("0x".concat(strLengthHex), 16);
var strDataEndPos = strDataStartPos + strLengthInt * 2;
if (codeString === '0x') return '';
codeString = "0x".concat(code.slice(strDataStartPos, strDataEndPos));
} else {
codeString = "0x".concat(code.substr(138)).replace(/0+$/, '');
} // If the codeString is an odd number of characters, add a trailing 0
if (codeString.length % 2 === 1) {
codeString += '0';
}
return (0, _strings.toUtf8String)(codeString);
}
function getCode(_x9, _x10, _x11, _x12) {
return _getCode.apply(this, arguments);
}
function _getCode() {
_getCode = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4(tx, network, blockNumber, provider) {
var data;
return _regenerator.default.wrap(function _callee4$(_context4) {
while (1) {
switch (_context4.prev = _context4.next) {
case 0:
if (!(network === 'unknown' || network === 'local')) {
_context4.next = 11;
break;
}
_context4.prev = 1;
_context4.next = 4;
return provider.call(tx, blockNumber);
case 4:
return _context4.abrupt("return", _context4.sent);
case 7:
_context4.prev = 7;
_context4.t0 = _context4["catch"](1);
data = _context4.t0.data[Object.keys(_context4.t0.data)[0]];
return _context4.abrupt("return", data.return);
case 11:
if (!(network === 'kovan')) {
_context4.next = 21;
break;
}
_context4.prev = 12;
_context4.next = 15;
return provider.call(tx, blockNumber);
case 15:
return _context4.abrupt("return", _context4.sent);
case 18:
_context4.prev = 18;
_context4.t1 = _context4["catch"](12);
return _context4.abrupt("return", JSON.parse(_context4.t1.responseText).error.data.substr(9));
case 21:
if (!(network === 'polygon')) {
_context4.next = 33;
break;
}
_context4.prev = 22;
_context4.next = 25;
return provider.call(tx, blockNumber);
case 25:
return _context4.abrupt("return", _context4.sent);
case 28:
_context4.prev = 28;
_context4.t2 = _context4["catch"](22);
return _context4.abrupt("return", _context4.t2.error ? _context4.t2.error.message.replace('code err execution reverted: ', '') : _context4.t2.message.replace('code err execution reverted: ', ''));
case 31:
_context4.next = 34;
break;
case 33:
return _context4.abrupt("return", provider.call(tx, blockNumber));
case 34:
case "end":
return _context4.stop();
}
}
}, _callee4, null, [[1, 7], [12, 18], [22, 28]]);
}));
return _getCode.apply(this, arguments);
}
//# sourceMappingURL=eth-revert-reason.js.map