diff --git a/remix-analyzer/src/solidity-analyzer/modules/etherTransferInLoop.js b/remix-analyzer/src/solidity-analyzer/modules/etherTransferInLoop.js new file mode 100644 index 0000000000..75330db333 --- /dev/null +++ b/remix-analyzer/src/solidity-analyzer/modules/etherTransferInLoop.js @@ -0,0 +1,31 @@ +var name = 'Ether transfer in a loop: ' +var desc = 'Avoid transferring Ether to multiple addresses in a loop' +var categories = require('./categories') +var common = require('./staticAnalysisCommon') + +function etherTransferInLoop () { + this.relevantNodes = [] +} + +etherTransferInLoop.prototype.visit = function (node) { + if (common.isLoop(node) && common.isTransfer(node)) { + this.relevantNodes.push(node) + } +} + +etherTransferInLoop.prototype.report = function (compilationResults) { + return this.relevantNodes.map((node) => { + return { + warning: 'Ether payout should not be done in a loop: Due to the block gas limit, transactions can only consume a certain amount of gas. The number of iterations in a loop can grow beyond the block gas limit which can cause the complete contract to be stalled at a certain point. If required then make sure that number of iterations are low and you trust each address involved.', + location: node.src, + more: 'https://solidity.readthedocs.io/en/latest/security-considerations.html#gas-limit-and-loops' + } + }) +} + +module.exports = { + name: name, + description: desc, + category: categories.GAS, + Module: etherTransferInLoop +} diff --git a/remix-analyzer/src/solidity-analyzer/modules/staticAnalysisCommon.js b/remix-analyzer/src/solidity-analyzer/modules/staticAnalysisCommon.js index 4243ea0258..55bb227888 100644 --- a/remix-analyzer/src/solidity-analyzer/modules/staticAnalysisCommon.js +++ b/remix-analyzer/src/solidity-analyzer/modules/staticAnalysisCommon.js @@ -947,6 +947,17 @@ function isBytesLengthCheck (node) { return isMemberAccess(node, exactMatch(util.escapeRegExp(basicTypes.UINT)), undefined, util.escapeRegExp('bytes *'), 'length') } +/** + * True if it is a loop + * @node {ASTNode} some AstNode + * @return {bool} + */ +function isLoop (node) { + return nodeType(node, exactMatch(nodeTypes.FORSTATEMENT)) || + nodeType(node, exactMatch(nodeTypes.WHILESTATEMENT)) || + nodeType(node, exactMatch(nodeTypes.DOWHILESTATEMENT)) +} + /** * True if it is a 'for' loop * @node {ASTNode} some AstNode @@ -1117,6 +1128,7 @@ module.exports = { isIntDivision: isIntDivision, isStringToBytesConversion: isStringToBytesConversion, isBytesLengthCheck: isBytesLengthCheck, + isLoop: isLoop, isForLoop: isForLoop, // #################### Trivial Node Identification