Skip to content

Commit

Permalink
Merge pull request #313 from DistributedCollective/functionSignatures
Browse files Browse the repository at this point in the history
Bash script functionSignatures.sh
  • Loading branch information
ororopickpocket authored Sep 20, 2021
2 parents 4b533ea + 7897c5a commit fb776a3
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 5 deletions.
23 changes: 18 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@
}
},
"dependencies": {
"keccak": "^3.0.1",
"keccak256": "^1.0.3",
"phantomjs-prebuilt": "^2.1.16",
"yarn": "^1.22.10"
}
Expand Down
10 changes: 10 additions & 0 deletions scripts/functionSignatures.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash

# This script extracts all function signatures from contracts on a repo.
# It's meant to run on Linux from the repo base folder like this:
# cd Sovryn-smart-contracts/
# ./scripts/functionSignatures.sh

find . -type f -name '*.sol' ! -path '*node_modules/*' -print0 | xargs -0 sed -z "s/\/\/[^\n]*\n//g" | awk '/function/,/{/' | awk '/function/,/;/' | sed "s/[[:space:]]+function/function/" | sed -z "s/\n[\t ]*//g" | sed -z "s/{/\n/g" | sed -z "s/;/\n/g" | egrep --text "^function " | egrep --text " (public|external) " | sed -E "s/(address|u*int[0-9]+|bytes[0-9]*|bool|string)(\[[0-9]*\])* [^,\)]+/\1/g" | sed "s/function //" | sed -r "s/ (public|external).*$//" | sed -r "s/\s+//g"


187 changes: 187 additions & 0 deletions scripts/getSelectors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
// This is a nodejs script to compute the selectors of Solidity events or functions
// (four bytes of the Keccak-256 or SHA-3 hash of the signature of the function)
// Knowledge: In Solidiy selector = bytes4(keccak256(signature))
// Install: npm i keccak256
// Run: node ./scripts/getSelectors.js ./contracts functions > functionSignatures.txt
// Run: node ./scripts/getSelectors.js ./contracts events > eventTopics.txt

// TODO: Pending to fix 3 issues:
// 1.- On function signatures, interfaces should be typed as addresses.
// 2.- On function signatures, structs should be exploded into their components, around brackets ()
// 3.- According to the list swapExternal(address,address,address,address,uint256,uint256,bytes) has selector: 0x11058a8a
// According to vscode: e321b540 => swapExternal(address,address,address,address,uint256,uint256,uint256,bytes)
// It has an additional parameter. This case should be debugged.

// Get the path where contracts are located
var args = process.argv.slice(2);
let path = String(args[0]);

// Get the type of items to search on contracts
let searchType = String(args[1]);

const keccak256 = require("keccak256");

// Get keccak256 in 0x string format for a given content
var keccak256_0x = function (content) {
return "0x" + keccak256(content).toString("hex");
};

const fs = require("fs");

// Parse a contract searching for function and event declarations
var parseContract = function (fileContent) {
fileContent = fileContent
.replace(/\/\/[^\n]*\n/g, "\n") // remove comments like //
.replace(/\/\*[\s\S]*?\*\//g, ""); // remove comments like /* */

var signatureList = {};
if (searchType == "functions") {
searchRegExp = new RegExp(/function [^\)]*\)/g); // focus on function declarations
} else if (searchType == "events") {
searchRegExp = new RegExp(/event [^\)]*\)/g); // focus on event declarations
} else {
console.log("Error: Unknown searchType", searchType);
process.exit(1);
}

while (null != (f = searchRegExp.exec(fileContent))) {
// For every function or event found
signature = f[0]
.replace(/(function|event) /g, "") // remove "function " or "event " on every match
.replace(/([\(,])\s+/g, "$1") // remove whitespaces and newlines inmediatly after ( or ,
.replace(/\s+\)/g, ")") // remove whitespaces and newlines inmediatly before )
.replace(/\s.*?([,\)])/g, "$1") // remove var names and extra modifiers
.replace(/^(u?int[0-9]*|address|bool|string|bytes(32|4)*)/g, "address") // every unknown type found is considered to be an address
;
if (!!signature) {
signatureList[signature] = keccak256_0x(signature);
}
}

return signatureList;
};

// Parse a contract searching for interface declarations
var parseInterfacesFromContract = function (fileContent) {
fileContent = fileContent
.replace(/\/\/[^\n]*\n/g, "\n") // remove comments like //
.replace(/\/\*[\s\S]*?\*\//g, ""); // remove comments like /* */

var interfaceList = [];
searchRegExp = new RegExp(/interface [^\}]*\}/g); // focus on interface declarations

// Get all interfaces from repo
while (null != (f = searchRegExp.exec(fileContent))) {
// For every interface found
interfaceName = f[0]
.replace(/[\n\r\t\s]+/g, " ") // remove newlines and tabs
.replace(/^interface ([^\s]+).*$/g, "$1") // leave only interface name
;
if (!!interfaceName) {
interfaceList.push(interfaceName);
}
}

return interfaceList;
};

// Parse a contract searching for struct declarations
var parseStructsFromContract = function (fileContent) {
fileContent = fileContent
.replace(/\/\/[^\n]*\n/g, "\n") // remove comments like //
.replace(/\/\*[\s\S]*?\*\//g, ""); // remove comments like /* */

var structList = {};
searchRegExp = new RegExp(/struct [^\}]*\}/g); // focus on struct declarations

// Get all structs from repo
while (null != (f = searchRegExp.exec(fileContent))) {
// For every struct found
structName = f[0]
.replace(/[\n\r\t\s]+/g, " ") // remove newlines and tabs
.replace(/^struct ([^\s]+).*$/g, "$1") // leave only struct name
;
structDeclaration = f[0]
.replace(/[\n\r\t\s]+/g, " ") // remove newlines and tabs
.replace(/^struct [^\s]+ \{(.*)\}/g, "$1") // leave only struct declaration
;
if (!!structName) {
structList[structName] = structDeclaration;
}
}

return structList;
};

// Loop through fileList and extract all interfaces from repo
var getAllInterfaces = function (fileList) {
var interfaces = [];
for (let file of fileList) {
// console.log("\nFile: ", file);
let content = fs.readFileSync(file, { encoding: "utf8" });
var interfacesToAdd = parseInterfacesFromContract(content);
interfaces.push(...interfacesToAdd);
}

return interfaces;
};

// Loop through fileList and extract all structs from repo
var getAllStructs = function (fileList) {
var structs = {};
for (let file of fileList) {
// console.log("\nFile: ", file);
let content = fs.readFileSync(file, { encoding: "utf8" });
var structsToAdd = parseStructsFromContract(content);
for (var key in structsToAdd) {
structs[key] = structsToAdd[key];
}
}

return structs;
};

// Loop through fileList and call parser on each one
var parseContractList = function (fileList) {
var contractSignatures = {};
for (let file of fileList) {
// console.log("\nFile: ", file);
let content = fs.readFileSync(file, { encoding: "utf8" });
contractSignatures[file] = parseContract(content);
}

return contractSignatures;
};

// Open files recursively
const glob = require("glob");

var getDirectories = function (src, ext, callback) {
glob(src + "/**/*" + ext, callback);
};

getDirectories(path, ".sol", function (err, res) {
if (err) {
console.log("Error", err);
} else {
/*
interfaces = getAllInterfaces(res);
console.log("Interfaces found: ", interfaces);
process.exit(1);
structs = getAllStructs(res);
console.log("Structs found: ", structs);
process.exit(1);
*/
contractSignatures = parseContractList(res);
// console.log("contractSignatures: ", contractSignatures);
// Loop through results and apply tabulated format to copy/past on Google Docs
for (const [contract, functions] of Object.entries(contractSignatures)) {
console.log(contract);
for (const [signature, selector] of Object.entries(functions)) {
console.log("\t" + signature + "\t" + selector.slice(0, 10) + "\t" + selector);
}
}
}
});
27 changes: 27 additions & 0 deletions scripts/keccak256.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// This is a nodejs script to compute the selectors of Solidity events or functions
// (four bytes of the Keccak-256 or SHA-3 hash of the signature of the function)
// Install: npm i keccak256

var myArgs = process.argv.slice(2);
// console.log('arg: ', String(myArgs[0]).split(/\r?\n/));

let signatures = String(myArgs[0])
.replace(/;/g, "") // remove final ;
.split(/\r?\n/) // split lines, every line is a signature
.sort(function (a, b) {
// sorted alphabetically
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
})
.filter((item, i, ar) => ar.indexOf(item) === i); // get unique
// console.log('signatures: ', signatures);

const keccak256 = require("keccak256");
for (let s of signatures) {
if (s) console.log(s + "\t" + "0x" + keccak256(s).toString("hex"));
}

0 comments on commit fb776a3

Please sign in to comment.