@nomiclabs/truffle-contract
Version:
A better contract abstraction for Ethereum (Buidler's fork)
136 lines (117 loc) • 3.97 kB
JavaScript
const debug = require("debug")("contract:promievent");
const DebugUtils = require("@truffle/debug-utils");
const Web3PromiEvent = require("web3-core-promievent");
function PromiEvent(justPromise, bugger = undefined, isDeploy = false) {
const { resolve, reject, eventEmitter } = new Web3PromiEvent(justPromise);
const originalStackTrace = new Error().stack;
function rejectHijacker(e) {
debug("hijacking!");
debug("hash: %s", this.txHash);
let getSolidityStackTrace;
if (bugger && this.txHash) {
debug("debugging time!");
getSolidityStackTrace = async () => {
try {
await bugger.load(this.txHash);
await bugger.runToEnd();
const report = bugger.stacktrace();
await bugger.unload();
return DebugUtils.formatStacktrace(report, 4); //indent 4 to match node's stacktraces
} catch (_) {
//ignore errors
return undefined;
}
};
} else {
getSolidityStackTrace = async () => undefined;
}
getSolidityStackTrace().then(solidityStackTrace => {
debug("e.stack: %s", e.stack);
debug("originalStackTrace: %s", originalStackTrace);
debug("solidityStackTrace: %s", solidityStackTrace);
const initialLinesRegexp = isDeploy
? /^.*\n.*\n.*\n.*/ //first 4 lines (note . does not include \n)
: /^.*\n.*\n.*/; //first 3 lines
//we replace not just the first line but also the next 2 as they contain
//useless stuff users shouldn't see; in case of deployments there's one
//additional to remove
try {
let stackTrace = originalStackTrace.replace(
initialLinesRegexp,
e.stack.split("\n")[0]
);
if (solidityStackTrace) {
//let's split the solidity stack trace into first line & rest
let [
_,
solidityFirstLine,
solidityRemaining
] = solidityStackTrace.match(/^(.*?)\r?\n((.|\r|\n)*)$/);
stackTrace = stackTrace.replace(
/^.*/, //note that . does not include \n
solidityRemaining //note: this does not end in \n, so no modification needed
);
e.hijackedMessage = e.message;
e.message = solidityFirstLine;
}
e.hijackedStack = e.stack;
e.stack = stackTrace;
} catch (_) {
//again, ignore errors
//(not sure how this can happen here but I'll leave this block here)
}
reject(e);
});
}
this.resolve = resolve;
this.reject = modifyRejectHijacker(rejectHijacker, reject).bind(this);
this.eventEmitter = eventEmitter;
if (bugger) {
this.debug = true;
}
}
PromiEvent.resolve = Web3PromiEvent.resolve;
PromiEvent.prototype.setTransactionHash = function (txHash) {
debug("setting!");
debug("hash: %s", txHash);
this.txHash = txHash;
};
module.exports = PromiEvent;
// The added functionality is implemented below, to make merging
// future changes easier.
const solidityErrorsModule = loadSolidityErrorsModule();
function modifyRejectHijacker(rejectHijacker, reject) {
if (solidityErrorsModule === undefined) {
return rejectHijacker;
}
const currentRawStackTrace = solidityErrorsModule.getCurrentStack();
return function modifiedRejectHijacker(e) {
if (e.stackTrace !== undefined) {
const error = solidityErrorsModule.encodeSolidityStackTrace(
e.message,
e.stackTrace,
currentRawStackTrace.slice(3)
);
reject(error);
return;
}
rejectHijacker.call(this, e);
};
}
function loadSolidityErrorsModule() {
return (
tryLoadingModule(
"hardhat/internal/hardhat-network/stack-traces/solidity-errors"
) ||
tryLoadingModule(
"@nomiclabs/buidler/internal/buidler-evm/stack-traces/solidity-errors"
)
);
}
function tryLoadingModule(moduleName) {
try {
return require(moduleName);
} catch (e) {
// Do nothing
}
}