Home

Awesome

<img width="200" alt="get in touch with Consensys Diligence" src="https://user-images.githubusercontent.com/2865694/56826101-91dcf380-685b-11e9-937c-af49c2510aa0.png"><br/> <sup> [ šŸŒ šŸ“© šŸ”„ ] </sup><br/><br/>

šŸ§  SolGrep - A scriptable semantic grep utility for solidity

So you have a set of smart contracts and want to find all contracts that have a public method named withdrawEth but lexical grep yields a lot of false-positives? Here's where solgrep can help! šŸ™Œ

šŸ’¾ npm install -g solgrep

Solgrep recursively finds smart contracts in a target directory, parses the source units to understand the language semantics, and makes this information available to a powerful javascript-based filter function. This way, you can:

Probably the most common way to use this tool is to run it with the --find=<js-filter-statement> option, where js-filter-statement is a javascript one-liner that tells the engine what you are interested in. You either provide a statement that returns boolean ("find mode") or return information you want to extract ("extract mode").

solgrep2

āš ļø Make sure to only allow trusted inputs to --find=<js-filter-statement> as this argument is being evaluated as javascript!

āš ļø Check out the proper order for arguments when using --find, --rule. See Usage.

Examples

Run the default rules and display some stats?

ā‡’  solgrep <folder> 

Run a specific or multiple built-in rules (solgrep -l to list available rules)? šŸ‘‰

ā‡’  solgrep <folder> --rule=IsInitializable --rule=Stats

You want to find all source-units with a contract that has a function named withdrawEth? šŸ‘‰

ā‡’  solgrep <folder> --find="function.name=='withdrawEth'" 

Do the same thing but case-insensitive? šŸ‘‰

ā‡’  solgrep <folder> --find="function.name.toLowerCase()=='withdraweth'" 

Find all functions that call selfdestruct? šŸ‘‰

ā‡’  solgrep <folder> --find="function.callsTo('selfdestruct')"

Exctract all function names from all contracts? šŸ‘‰

ā‡’  solgrep <folder> --find="function.name" 

Get a list of all external functions? šŸ‘‰

ā‡’  solgrep <folder> --find="function.visibility.includes('external')"  

Find ERC777 contracts? šŸ‘‰

ā‡’  solgrep <folder> --find="contract.name=='ERC777'" 

Extract all Contract names? šŸ‘‰

ā‡’  solgrep <folder> --find="contract.name"

Extract all Interface names? šŸ‘‰

ā‡’  solgrep <folder> --find="contract.name && contract.kind=='interface'"

Match against something in the AST? šŸ‘‰

ā‡’  solgrep <folder> --find="Object.keys(function.modifiers).includes('nonReentrant')"

Lexial match a functions source code? šŸ‘‰

ā‡’  solgrep <folder> --find="function.getSource().includes('hi')"

Find duplicate contracts? (AST exact and fuzzy matching) šŸ‘‰

ā‡’  solgrep <folder> --rule DupeFinder

Use option --output=<output.json> to write all results to a file.

Built-In Keywords for --find

Available Methos

The following methods are available:

Special Functions

Special contract functions can be references as:

Built-In Rules

SHARING IS CARING - submit your rules!

Usage


ā‡’  solgrep --help
Usage: solgrep <folder|...> [options]

Options:
  -r, --rule        Enable rules                           [array] [default: []]
  -l, --list-rules  List available rules              [boolean] [default: false]
  -f, --find        Find/Extract information using custom pattern
                                                           [array] [default: []]
  -o, --output      Write "results" as JSON to output file path.        [string]
  -h, --help        Show help                                          [boolean]
  -v, --version     Show version number                                [boolean]

āš ļø when using multi-options (--find, --rule) make sure to use this format:

ā‡’  solgrep <folder|...> [options]

or

ā‡’  solgrep [options] -- <folder|...> 

or else additional options might be interpreted as additional --find options!

Library

const solgrep = require('solgrep');

let sg = new SolGrep('::memory::', rules, callbacks);
sg.analyzeDir("/path/to/smart/contracts").then(() => {
    console.log("   ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ Results")
    console.log(sg.results)
    console.log("   ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€")
    sgrep.close();
})


Demo

Here's an example that illustrates how to extract all function names from all sourceUnits and contracts.

ā‡’  solgrep ../d0  --find='function.name' 
šŸ§  SolGrep v0.0.3 ready!

  Enabled Modules:
    āœ”ļø GenericGrep          _function.name

[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ] 100% | šŸ•™ ETA: 0s | 5/5 | šŸŒ² 389 | šŸ”„ 0 | šŸ—‚ļø  ../d0

   ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€
{
  '../d0/D0788Af7a613b81F437a51b96594A6387c7329b1_PendleLiquidityMiningBaseV2Multi.sol': [
    {
      rule: 'GenericGrep',
      tag: 'match-function: PendleLiquidityMiningBaseV2Multi.__constructor__',
      info: '__constructor__',
      loc: [Object]
    },
    {
      rule: 'GenericGrep',
      tag: 'match-function: PendleLiquidityMiningBaseV2Multi.setUpEmergencyMode',
      info: 'setUpEmergencyMode',
      loc: [Object]
    },
...

Run default Rulesets

If you provide no configuration options it will take the default built-in grep rules and crunch numbers using the stats module.

ā‡’  solgrep ../d6 
šŸ§  SolGrep v0.0.1 starting ...


  šŸ“ ../d6
 ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ 100% | ETA: 0s | 5/5

   ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ Results
{
  '../d6/d60a598998ed27a7C54315F2675908B628E434B1_LiquidityPool.sol': [
    {
      rule: 'IsInitializable',
      tag: 'INITIALIZEABLE',
      info: 'initialize - public initialize function; likely proxy'
    },
    {
      rule: 'IsInitializable',
      tag: 'INITIALIZEABLE',
      info: 'initialize - public initialize function; likely proxy'
    }
  ],
  '../d6/d64E77C7C6A1dcC7e302F8fe31A22745e223c39c_MyStrategy.sol': [
    {
      rule: 'IsInitializable',
      tag: 'INITIALIZEABLE',
      info: 'initialize - public initialize function; likely proxy'
    }
  ],
  undefined: [ { rule: 'Stats', tag: 'STATS', info: [Object] } ]
}
   ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€
{
  sourceUnits: 4,
  contracts: {
    total: 13,
    names: {
      ERC20: 1,
      StrategySushiEthMimLp: 1,
      LiquidityPool: 1,
      Getter: 1,
      Governance: 1,
      LibraryEvents: 1,
      Perpetual: 1,
      Storage: 1,
      ArbiBoneman: 1,
      ERC721: 1,
      MyStrategy: 1,
      PausableUpgradeable: 1,
      SettAccessControl: 1
    }
  },
  interfaces: {
    total: 39,
    names: {
      IERC20: 1,
      IJar: 1,
      IStakingRewards: 1,
      IStakingRewardsFactory: 1,
      IMasterchef: 1,
      UniswapRouterV2: 1,
      IUniswapV2Pair: 1,
      IUniswapV2Factory: 1,
      IRewarder: 1,
      IMiniChefV2: 1,
      ILiquidityPool: 1,
      IOracle: 1,
      IAccessControl: 1,
      IGovernor: 1,
      IPoolCreatorFull: 1,
      ISymbolService: 1,
      IPoolCreator: 1,
      ITracer: 1,
      IVersionControl: 1,
      IVariables: 1,
      IKeeperWhitelist: 1,
      IProxyAdmin: 1,
      IDecimals: 1,
      ILiquidityPoolGetter: 1,
      ILiquidityPoolGovernance: 1,
      IPerpetual: 1,
      IERC721Enumerable: 1,
      IERC721: 1,
      IERC721Receiver: 1,
      IERC721Metadata: 1,
      IERC165: 1,
      ICurveGauge: 1,
      ICurveStableSwapREN: 1,
      IUniswapRouterV2: 1,
      IStrategy: 1,
      IController: 2,
      IERC20Upgradeable: 2
    }
  },
  libraries: {
    total: 31,
    names: {
      SafeMath: 1,
      SafeERC20: 1,
      BoringERC20: 1,
      EnumerableSetUpgradeable: 1,
      SafeCastUpgradeable: 1,
      AMMModule: 1,
      LiquidityPoolModule: 1,
      PerpetualModule: 1,
      SignedSafeMathUpgradeable: 1,
      Constant: 1,
      Math: 1,
      SafeMathExt: 1,
      Utils: 1,
      MarginAccountModule: 1,
      EnumerableSet: 1,
      OrderData: 1,
      CollateralModule: 1,
      TradeModule: 1,
      OrderModule: 1,
      Signature: 1,
      ECDSAUpgradeable: 1,
      Strings: 1,
      MathUpgradeable: 1,
      Address: 2,
      SafeMathUpgradeable: 2,
      AddressUpgradeable: 2,
      SafeERC20Upgradeable: 2
    }
  },
  abstract: {
    total: 13,
    names: {
      StrategyBase: 1,
      StrategySushiFarmBase: 1,
      ReentrancyGuardUpgradeable: 1,
      ERC721Enumerable: 1,
      Ownable: 1,
      ERC165: 1,
      BaseStrategy: 1,
      Context: 2,
      ContextUpgradeable: 2,
      Initializable: 2
    }
  }
}
TOTAL FILES: 5
ERRORS: 1
   ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€



cheers šŸ™Œ 
    @tintinweb 
    ConsenSys Diligence @ https://consensys.net/diligence/
    https://github.com/tintinweb/solgrep/