diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7cdf212e012..38e5c54cd22 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,6 +29,7 @@ jobs: env: FORCE_COLOR: 1 ENABLE_GAS_REPORT: true + - run: npm run test:inheritance - name: Print gas report run: cat gas-report.txt diff --git a/contracts/mocks/ERC1155ReceiverMock.sol b/contracts/mocks/ERC1155ReceiverMock.sol index 1d9def93dea..6443a56c7ae 100644 --- a/contracts/mocks/ERC1155ReceiverMock.sol +++ b/contracts/mocks/ERC1155ReceiverMock.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.0; import "../token/ERC1155/IERC1155Receiver.sol"; import "../utils/introspection/ERC165.sol"; -contract ERC1155ReceiverMock is IERC1155Receiver, ERC165 { +contract ERC1155ReceiverMock is ERC165, IERC1155Receiver { bytes4 private _recRetval; bool private _recReverts; bytes4 private _batRetval; diff --git a/package-lock.json b/package-lock.json index 96182c370e0..c6c81288e24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "eth-sig-util": "^3.0.0", "ethereumjs-util": "^7.0.7", "ethereumjs-wallet": "^1.0.1", + "graphlib": "^2.1.8", "hardhat": "^2.0.6", "hardhat-gas-reporter": "^1.0.4", "keccak256": "^1.0.2", @@ -38,6 +39,7 @@ "prettier-plugin-solidity": "^1.0.0-beta.13", "rimraf": "^3.0.2", "solhint": "^3.3.6", + "solidity-ast": "^0.4.25", "solidity-coverage": "^0.7.11", "solidity-docgen": "^0.5.3", "web3": "^1.3.0", @@ -7713,7 +7715,104 @@ "bundleDependencies": [ "source-map-support", "yargs", - "ethereumjs-util" + "ethereumjs-util", + "@types/bn.js", + "@types/node", + "@types/pbkdf2", + "@types/secp256k1", + "ansi-regex", + "ansi-styles", + "base-x", + "blakejs", + "bn.js", + "brorand", + "browserify-aes", + "bs58", + "bs58check", + "buffer-from", + "buffer-xor", + "camelcase", + "cipher-base", + "cliui", + "color-convert", + "color-name", + "create-hash", + "create-hmac", + "cross-spawn", + "decamelize", + "elliptic", + "emoji-regex", + "end-of-stream", + "ethereum-cryptography", + "ethjs-util", + "evp_bytestokey", + "execa", + "find-up", + "get-caller-file", + "get-stream", + "hash-base", + "hash.js", + "hmac-drbg", + "inherits", + "invert-kv", + "is-fullwidth-code-point", + "is-hex-prefixed", + "is-stream", + "isexe", + "keccak", + "lcid", + "locate-path", + "map-age-cleaner", + "md5.js", + "mem", + "mimic-fn", + "minimalistic-assert", + "minimalistic-crypto-utils", + "nice-try", + "node-addon-api", + "node-gyp-build", + "npm-run-path", + "once", + "os-locale", + "p-defer", + "p-finally", + "p-is-promise", + "p-limit", + "p-locate", + "p-try", + "path-exists", + "path-key", + "pbkdf2", + "pump", + "randombytes", + "readable-stream", + "require-directory", + "require-main-filename", + "ripemd160", + "rlp", + "safe-buffer", + "scrypt-js", + "secp256k1", + "semver", + "set-blocking", + "setimmediate", + "sha.js", + "shebang-command", + "shebang-regex", + "signal-exit", + "source-map", + "string_decoder", + "string-width", + "strip-ansi", + "strip-eof", + "strip-hex-prefix", + "util-deprecate", + "which", + "which-module", + "wrap-ansi", + "wrappy", + "y18n", + "yargs-parser" ], "dev": true, "dependencies": { @@ -9040,6 +9139,15 @@ "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", "dev": true }, + "node_modules/graphlib": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz", + "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", + "dev": true, + "dependencies": { + "lodash": "^4.17.15" + } + }, "node_modules/growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", @@ -15965,6 +16073,12 @@ "node": ">=4" } }, + "node_modules/solidity-ast": { + "version": "0.4.25", + "resolved": "https://registry.npmjs.org/solidity-ast/-/solidity-ast-0.4.25.tgz", + "integrity": "sha512-8IpweS/vgHEpKvY4l0sfr3EsHk+JFIzRWqq/0JefRjzP/Wyi2xZYfx8aHlJ9kcEn2M2RCQK9PexuZ+scaa83OQ==", + "dev": true + }, "node_modules/solidity-comments-extractor": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz", @@ -20678,7 +20792,6 @@ "integrity": "sha512-5vwpq6kbvwkQwKqAoOU3L72GZ3Ta8RRrewKj9OJRolx28KLJJ8Dg9Rf7obRwt5jQA9bkYd8gqzMTrI7H3xLfaw==", "dev": true, "requires": { - "@oclif/config": "^1.15.1", "@oclif/errors": "^1.3.3", "@oclif/parser": "^3.8.3", "@oclif/plugin-help": "^3", @@ -27034,6 +27147,15 @@ "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", "dev": true }, + "graphlib": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz", + "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", + "dev": true, + "requires": { + "lodash": "^4.17.15" + } + }, "growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", @@ -32535,6 +32657,12 @@ } } }, + "solidity-ast": { + "version": "0.4.25", + "resolved": "https://registry.npmjs.org/solidity-ast/-/solidity-ast-0.4.25.tgz", + "integrity": "sha512-8IpweS/vgHEpKvY4l0sfr3EsHk+JFIzRWqq/0JefRjzP/Wyi2xZYfx8aHlJ9kcEn2M2RCQK9PexuZ+scaa83OQ==", + "dev": true + }, "solidity-comments-extractor": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz", diff --git a/package.json b/package.json index 40fa5696306..384e14327a4 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "release": "scripts/release/release.sh", "version": "scripts/release/version.sh", "test": "hardhat test", + "test:inheritance": "node scripts/inheritanceOrdering artifacts/build-info/*", "gas-report": "env ENABLE_GAS_REPORT=true npm run test" }, "repository": { @@ -64,6 +65,7 @@ "eth-sig-util": "^3.0.0", "ethereumjs-util": "^7.0.7", "ethereumjs-wallet": "^1.0.1", + "graphlib": "^2.1.8", "hardhat": "^2.0.6", "hardhat-gas-reporter": "^1.0.4", "keccak256": "^1.0.2", @@ -75,6 +77,7 @@ "prettier-plugin-solidity": "^1.0.0-beta.13", "rimraf": "^3.0.2", "solhint": "^3.3.6", + "solidity-ast": "^0.4.25", "solidity-coverage": "^0.7.11", "solidity-docgen": "^0.5.3", "web3": "^1.3.0", diff --git a/scripts/inheritanceOrdering.js b/scripts/inheritanceOrdering.js new file mode 100644 index 00000000000..dd965989b16 --- /dev/null +++ b/scripts/inheritanceOrdering.js @@ -0,0 +1,38 @@ +const path = require('path'); +const graphlib = require('graphlib'); +const { findAll } = require('solidity-ast/utils'); +const { _: artifacts } = require('yargs').argv; + +for (const artifact of artifacts) { + const { output: solcOutput } = require(path.resolve(__dirname, '..', artifact)); + + const graph = new graphlib.Graph({ directed: true }); + const names = {}; + const linearized = []; + + for (const source in solcOutput.contracts) { + for (const contractDef of findAll('ContractDefinition', solcOutput.sources[source].ast)) { + names[contractDef.id] = contractDef.name; + linearized.push(contractDef.linearizedBaseContracts); + + contractDef.linearizedBaseContracts.forEach((c1, i, contracts) => contracts.slice(i + 1).forEach(c2 => { + graph.setEdge(c1, c2); + })); + } + } + + graphlib.alg.findCycles(graph).forEach(([ c1, c2 ]) => { + console.log(`Conflict between ${names[c1]} and ${names[c2]} detected in the following dependency chains:`); + linearized + .filter(chain => chain.includes(parseInt(c1)) && chain.includes(parseInt(c2))) + .forEach(chain => { + const comp = chain.indexOf(c1) < chain.indexOf(c2) ? '>' : '<'; + console.log(`- ${names[c1]} ${comp} ${names[c2]}: ${chain.reverse().map(id => names[id]).join(', ')}`); + }); + process.exitCode = 1; + }); +} + +if (!process.exitCode) { + console.log('Contract ordering is consistent.'); +}