const SolExplore = require('sol-explore'); const SolidityParser = require('solidity-parser-sc'); /** * Splices enclosing brackets into `contract` around `expression`; * @param {String} contract solidity code * @param {Object} node AST node to bracket * @return {String} contract */ function blockWrap(contract, expression) { return contract.slice(0, expression.start) + '{' + contract.slice(expression.start, expression.end) + '}' + contract.slice(expression.end); } /** * Locates unbracketed singleton statements attached to if, else, for and while statements * and brackets them. Instrumenter needs to inject events at these locations and having * them pre-bracketed simplifies the process. Each time a modification is made the contract * is passed back to the parser and re-walked because all the starts and ends get shifted. * * Also removes pure and view modifiers. * * @param {String} contract solidity code * @return {String} contract */ module.exports.run = function r(contract) { let keepRunning = true; while (keepRunning) { try { const ast = SolidityParser.parse(contract); keepRunning = false; SolExplore.traverse(ast, { enter(node, parent) { // eslint-disable-line no-loop-func // If consequents if (node.type === 'IfStatement' && node.consequent.type !== 'BlockStatement') { contract = blockWrap(contract, node.consequent); keepRunning = true; this.stopTraversal(); // If alternates } else if ( node.type === 'IfStatement' && node.alternate && node.alternate.type !== 'BlockStatement') { contract = blockWrap(contract, node.alternate); keepRunning = true; this.stopTraversal(); // Loops } else if ( (node.type === 'ForStatement' || node.type === 'WhileStatement') && node.body.type !== 'BlockStatement') { contract = blockWrap(contract, node.body); keepRunning = true; this.stopTraversal(); } else if (node.type === 'FunctionDeclaration' && node.modifiers) { // We want to remove constant / pure / view from functions for (let i = 0; i < node.modifiers.length; i++) { if (['pure', 'constant', 'view'].indexOf(node.modifiers[i].name) > -1) { contract = contract.slice(0, node.modifiers[i].start) + contract.slice(node.modifiers[i].end); keepRunning = true; this.stopTraversal(); } } } }, }); } catch (err) { contract = err; keepRunning = false; } } return contract; }; /** * Traverses contract AST and returns names of all methods with constant, view or pure modifiers * @param {String} contract * @return {Array} `string` function names */ module.exports.findPureMethods = function collectABIData(contract){ let pureFunctionNames = []; let parentContractNames = []; try { const ast = SolidityParser.parse(contract); keepRunning = false; SolExplore.traverse(ast, { enter(node, parent) { // eslint-disable-line no-loop-func if (node.type === 'FunctionDeclaration' && node.modifiers) { for (let i = 0; i < node.modifiers.length; i++) { if (['pure', 'constant', 'view'].indexOf(node.modifiers[i].name) > -1) { pureFunctionNames.push(node.name); } } } else if (node.type === 'ContractStatement' || node.type === 'LibraryStatement'){ node.is.forEach( item => parentContractNames.push(item.name)); } }, }); } catch (err) {}; return { pureFunctionNames: pureFunctionNames, parentContractNames: parentContractNames } }