solidity-doppelganger

A tool to check if a contract is similar (exact/fuzzy AST based comparison) to a set of known/common contracts like openzeppelin's

Usage no npm install needed!

<script type="module">
  import solidityDoppelganger from 'https://cdn.skypack.dev/solidity-doppelganger';
</script>

README

get in touch with Consensys Diligence
[ 🌐 📩 🔥 ]

Solidity Doppelgaenger

🌐 npm install solidity-doppelganger

A tool to check if a contract is similar to a set of known/common contracts stored in a DB. E.g. quickly check if a contract SafeMath was copied from a reputable source without having to manually check it.

Allows for strict/exact and fuzzy AST based Doppelgänger detection.

Example

Setup

// Import
const { SolidityDoppelganger, HASH_MODES } = require('./SolidityDoppelganger');

Input is Solidity SourceCode

// Import: 
const fs = require("fs");

fs.readFile(fpath, (err, data) => {
    if (err) {
        console.error(`ERROR processing file '${fpath}': ${err}`);
        return;
    }
    let selectedModes = Array(...new Set(options.modes.split(',').filter(m => HASH_MODES.includes(m))));
    let dupeFinder = new SolidityDoppelganger({ modes: selectedModes });

    /**
     *  Optionally hash inputs and print them
     * */
    dupeFinder.hashSourceCode(data.toString('utf-8'))
        .then(results =>
            results.forEach(r =>
                r.then(x => {
                    // Result Object
                    let result = {
                        name: x.name,
                        hash: x.hash,
                        options: x.options,
                        path: `${fpath}`
                    };
                    console.log(JSON.stringify(result));
                })
            )
        );

    /**
     * Compare sourceCode with database
     * */
    dupeFinder.compareSourceCode(data.toString('utf-8'), fpath)
        .then(results => {
            Object.keys(results.results).forEach(contractName => {
                // results per contract and path
                let resultobj = results.results[contractName];
                console.log(`**** MATCH ****: ${resultobj.target.path} :: ${resultobj.target.name}`);
                for (let m of resultobj.matches) {
                    console.log(`    :: ${m.name} :: ${m.path}  (mode=${m.options.mode})`);
                }
            });
        });

 });

Input is Contract AST

/*
    print codehashes
*/
if (options.print) {
    dupeFinder.hashSourceCode(data.toString('utf-8'))
        .then(results => {
            results.forEach(x => {
                let result = {
                    name: x.name,
                    hash: x.hash,
                    options: x.options,
                    path: `${options.basePath}${fpath}`
                };
                console.log(JSON.stringify(result));
            });
        });
}
/*
    find similar contracts
*/
if (options.compare) {
    dupeFinder.compareSourceCode(data.toString('utf-8'), fpath)
        .then(results => {
            Object.keys(results.results).forEach(contractName => {
                let resultobj = results.results[contractName];
                console.log(`**** MATCH ****: ${resultobj.target.path} :: ${resultobj.target.name}`);
                for (let m of resultobj.matches) {
                    console.log(`    :: ${m.name} :: ${m.path}  (mode=${m.options.mode})`);
                }
            });
        });
}