locklift
Version:
Node JS framework for working with Ever contracts. Inspired by Truffle and Hardhat. Helps you to build, test, run and maintain your smart contracts.
267 lines (266 loc) • 13.1 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractAddress = exports.extractStringAddress = exports.isExistsInArr = exports.getDefaultAllowedCodes = exports.isT = exports.throwErrorInConsole = exports.throwTrace = exports.hexToValue = exports.convertForLogger = exports.convert = exports.extractAccountsFromMsgTree = void 0;
const types_1 = require("./types");
const logger_1 = require("../logger");
const everscale_inpage_provider_1 = require("everscale-inpage-provider");
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const path_1 = __importDefault(require("path"));
const process = __importStar(require("process"));
const chalk_1 = __importDefault(require("chalk"));
const constants_1 = require("./constants");
const fs_1 = __importDefault(require("fs"));
const extractAccountsFromMsgTree = (msgTree) => {
const extractAccounts = (msgTree) => {
const accounts = msgTree.dst && msgTree.dst !== constants_1.CONSOLE_ADDRESS ? [new everscale_inpage_provider_1.Address(msgTree.dst)] : [];
for (const outMsg of msgTree.outMessages) {
accounts.push(...extractAccounts(outMsg));
}
return accounts;
};
return [...new Set(extractAccounts(msgTree))];
};
exports.extractAccountsFromMsgTree = extractAccountsFromMsgTree;
const convert = (number, decimals = 9, precision = 4) => {
return (number / 10 ** decimals).toPrecision(precision);
};
exports.convert = convert;
const convertForLogger = (amount) => new bignumber_js_1.default((0, exports.convert)(amount, 9, 8) || 0);
exports.convertForLogger = convertForLogger;
const hexToValue = (amount) => new bignumber_js_1.default((0, exports.convert)(amount, 9, 9) || 0);
exports.hexToValue = hexToValue;
const normalizeFilePath = (errorPosition) => {
let errFilePath = errorPosition.filename;
// contracts paths look like: "../contracts/ContractName.tsol"
if (errFilePath.startsWith("../contracts/")) {
errFilePath = path_1.default.resolve(process.cwd(), errFilePath.split("../")[1]);
}
// .code paths look like: "ContractName.code"
if (errFilePath.endsWith(".code")) {
errFilePath = path_1.default.resolve(process.cwd(), "build", errFilePath);
}
return errFilePath;
};
const printErrorPositionSnippet = (trace, filename, errLine, offset) => {
const errFile = fs_1.default.readFileSync(filename, "utf8");
const lines = errFile.split("\n");
const lastLineLen = `${errLine + offset}`.length;
const { name, method } = getContractNameAndMethod(trace);
logger_1.logger.printTracingLog("".padStart(lastLineLen - 1, " "), chalk_1.default.blueBright.bold("-->"), chalk_1.default.bold(`${name}.${method} (${path_1.default.basename(filename)}:${errLine})`));
logger_1.logger.printTracingLog("".padStart(lastLineLen, " "), chalk_1.default.blueBright.bold("|"));
const linesToPrint = [];
lines.map((line, i) => {
if (i < errLine - offset - 1 || i >= errLine + offset)
return;
const lineNum = `${i + 1}`.padEnd(lastLineLen, " ");
if (i === errLine - 1) {
linesToPrint.push([chalk_1.default.redBright.bold(`${lineNum} |`), chalk_1.default.redBright(line)]);
}
else {
linesToPrint.push([chalk_1.default.blueBright.bold(`${lineNum} |`), line]);
}
});
const firstNotEmpty = linesToPrint.findIndex(line => line[1].trim() !== "");
const lastNotEmpty = linesToPrint.length - linesToPrint.reverse().findIndex(line => line[1].trim() !== "");
linesToPrint
.reverse()
.slice(firstNotEmpty, lastNotEmpty)
.map(line => {
logger_1.logger.printTracingLog(...line);
});
logger_1.logger.printTracingLog("".padStart(lastLineLen, " "), chalk_1.default.blueBright.bold("|"));
};
const throwTrace = (trace) => {
// const _trace = trace.transactionTrace!.map((trace) => JSON.stringify(trace)).join('\n');
// fs.writeFileSync('log.json', _trace);
logger_1.logger.printTracingLog(chalk_1.default.redBright("-----------------------------------------------------------------"));
// SKIPPED COMPUTE PHASE
if (trace.error?.phase === "compute" && trace.error?.reason) {
let errorDescription = trace.error?.reason;
if (errorDescription === "NoState") {
errorDescription = "NoState. Looks like you tried to call method of contract that doesn't exist";
}
const errorMsg = `!!! Compute phase was skipped with reason: ${errorDescription} !!!`;
logger_1.logger.printError(errorMsg);
throw new Error(errorMsg);
}
let errorDescription = "";
if (trace.error?.phase === "action") {
errorDescription = constants_1.ActionCodeHints[Number(trace.error.code)];
}
if (trace.error?.phase === "compute") {
errorDescription = constants_1.ComputeCodesHints[Number(trace.error.code)];
}
// short common error description
const mainErrorMsg = `!!! Reverted with ${trace.error?.code} error code on ${trace.error?.phase} phase !!!`;
logger_1.logger.printError(mainErrorMsg);
logger_1.logger.printError(errorDescription);
logger_1.logger.printTracingLog(chalk_1.default.redBright("-----------------------------------------------------------------"));
// no trace -> we cant detect line with error
if (trace.transactionTrace === undefined)
throw new Error(mainErrorMsg);
const vmTraces = trace.transactionTrace;
// no debug-map -> we cant detect line with error
if (trace.contract.map === undefined)
throw new Error(mainErrorMsg);
const contract = trace.contract;
const tx = trace.msg.dstTransaction;
let errPosition;
// COMPUTE PHASE ERROR
if (tx.compute.status === "vm" && !tx.compute.success) {
// last vm step is the error position
const lastStep = vmTraces.pop();
errPosition = contract.map[lastStep.cmdCodeCellHash][lastStep.cmdCodeOffset];
if (errPosition === undefined)
throw new Error(mainErrorMsg);
}
// ACTION PHASE ERROR
if (tx.action?.success === false) {
// catch all vm steps, where actions are produced
const actionsSent = vmTraces.filter(t => t.cmdStr === "SENDRAWMSG" || t.cmdStr === "RAWRESERVE" || t.cmdStr === "SETCODE");
let failedAction = tx.action.resultArg;
// too many actions, point to 256th action
if (Number(tx.action.resultCode) === 33)
failedAction = 255;
const failedActionStep = actionsSent[failedAction];
errPosition = contract.map[failedActionStep.cmdCodeCellHash][failedActionStep.cmdCodeOffset];
if (errPosition === undefined)
throw new Error(mainErrorMsg);
}
const errFilePath = normalizeFilePath(errPosition);
const errLineNum = errPosition.line;
const filename = path_1.default.basename(errFilePath);
if (filename.endsWith(".tsol") || filename.endsWith(".sol")) {
printErrorPositionSnippet(trace, errFilePath, errLineNum, 2);
}
throw new Error(mainErrorMsg);
};
exports.throwTrace = throwTrace;
const getContractNameAndMethod = (trace) => {
let name = "undefinedContract";
if (trace.contract) {
name = trace.contract.name;
}
let method = "undefinedMethod";
if (trace.decodedMsg?.method) {
method = trace.decodedMsg.method;
}
else if (trace.type === types_1.TraceType.BOUNCE) {
method = "onBounce";
}
return { name, method };
};
const throwErrorInConsole = (revertedBranch) => {
for (const { totalActions, actionIdx, traceLog } of revertedBranch) {
const bounce = traceLog.msg.bounce;
const { name, method } = getContractNameAndMethod(traceLog);
let paramsStr = "()";
if (traceLog.decodedMsg) {
if (Object.values(traceLog.decodedMsg.params || {}).length === 0) {
paramsStr = "()";
}
else {
paramsStr = "(\n";
for (const [key, value] of Object.entries(traceLog.decodedMsg.params || {})) {
paramsStr += ` ${key}: ${JSON.stringify(value, null, 4)}\n`;
}
paramsStr += ")";
}
}
logger_1.logger.printTracingLog("\t\t⬇\n\t\t⬇");
logger_1.logger.printTracingLog(`\t#${actionIdx + 1} action out of ${totalActions}`);
// green tags
logger_1.logger.printTracingLog(`Addr: \x1b[32m${traceLog.msg.dst}\x1b[0m`);
logger_1.logger.printTracingLog(`MsgId: \x1b[32m${traceLog.msg.hash}\x1b[0m`);
logger_1.logger.printTracingLog("-----------------------------------------------------------------");
if (traceLog.type === types_1.TraceType.BOUNCE) {
logger_1.logger.printTracingLog("-> Bounced msg");
}
if (traceLog.error && traceLog.error.ignored) {
logger_1.logger.printTracingLog(`-> Ignored ${traceLog.error.code} code on ${traceLog.error.phase} phase`);
}
if (!traceLog.contract) {
logger_1.logger.printTracingLog("-> Contract not deployed/Not recognized because build artifacts not provided");
}
// bold tag
logger_1.logger.printTracingLog(`\x1b[1m${name}.${method}\x1b[22m{value: ${(0, exports.convert)(Number(traceLog.msg.value))}, bounce: ${bounce}}${paramsStr}`);
if (traceLog.msg.dstTransaction) {
const tx = traceLog.msg.dstTransaction;
if (tx.storage) {
logger_1.logger.printTracingLog(`Storage fees: ${(0, exports.convert)(Number(tx.storage.storageFeesCollected))}`);
}
if (tx.compute) {
const gasFees = tx.compute.status === "vm" ? tx.compute.gasFees : 0;
logger_1.logger.printTracingLog(`Compute fees: ${(0, exports.convert)(Number(gasFees))}`);
}
if (tx.action) {
logger_1.logger.printTracingLog(`Action fees: ${(0, exports.convert)(Number(tx.action.totalActionFees))}`);
}
logger_1.logger.printTracingLog(chalk_1.default.bold("Total fees:"), `${(0, exports.convert)(Number(tx.totalFees))}`);
if (tx.compute.status === "vm") {
const gasLimit = Number(tx.compute.gasLimit) === 0 ? 1000000 : Number(tx.compute.gasLimit);
const percentage = ((Number(tx.compute.gasUsed) / gasLimit) * 100).toPrecision(2);
logger_1.logger.printTracingLog(chalk_1.default.bold("Gas used:"), `${Number(tx.compute.gasUsed).toLocaleString()}/${gasLimit.toLocaleString()} (${percentage}%)`);
}
}
if (traceLog.error && !traceLog.error.ignored) {
(0, exports.throwTrace)(traceLog);
}
}
};
exports.throwErrorInConsole = throwErrorInConsole;
const isT = (p) => !!p;
exports.isT = isT;
const getDefaultAllowedCodes = () => ({
compute: [],
action: [],
});
exports.getDefaultAllowedCodes = getDefaultAllowedCodes;
const isExistsInArr = (srcArr, isExist) => {
return srcArr.some(item => item === isExist);
};
exports.isExistsInArr = isExistsInArr;
const extractStringAddress = (contract) => typeof contract === "string"
? contract
: contract instanceof everscale_inpage_provider_1.Address
? contract.toString()
: contract.address.toString();
exports.extractStringAddress = extractStringAddress;
const extractAddress = (contract) => new everscale_inpage_provider_1.Address((0, exports.extractStringAddress)(contract));
exports.extractAddress = extractAddress;