From b9e0ffcb338eb39943ad4772a46cea5a7734f069 Mon Sep 17 00:00:00 2001 From: Francisco de Borja Aranda Castillejo Date: Mon, 9 Dec 2024 14:44:49 +0100 Subject: [PATCH] ci: slither and soldeer (#310) ## Summary by CodeRabbit ## Release Notes - **New Features** - Introduced a GitHub Actions workflow for automated analysis of Solidity smart contracts using the Slither tool. - Added a new configuration file for Slither analysis to manage detector exclusions and filter paths. - **Bug Fixes** - Updated import paths to reference specific versions of libraries, ensuring compatibility and stability. - **Chores** - Added entries to `.gitignore` to exclude unnecessary files and directories. - Removed obsolete remapping directives and subproject references to streamline the codebase. - Enhanced the testing process in the GitHub Actions workflow for improved contract testing. - Updated deployment scripts to ensure dependencies are up to date before deployment. --- .github/workflows/solidity-slither.yml | 51 ++++++++++++++++++++++++++ .github/workflows/test.yml | 3 +- contracts/.gitignore | 5 +++ contracts/foundry.toml | 14 +++++-- contracts/lib/forge-std | 1 - contracts/lib/openzeppelin-contracts | 1 - contracts/remappings.txt | 3 +- contracts/script/Deployer.s.sol | 2 +- contracts/slither.config.json | 4 ++ contracts/soldeer.lock | 13 +++++++ contracts/src/Nodes.sol | 14 +++---- contracts/test/GroupMessage.t.sol | 2 +- contracts/test/IdentityUpdates.t.sol | 2 +- contracts/test/Nodes.sol | 4 +- dev/contracts/deploy-local | 3 ++ 15 files changed, 102 insertions(+), 20 deletions(-) create mode 100644 .github/workflows/solidity-slither.yml delete mode 160000 contracts/lib/forge-std delete mode 160000 contracts/lib/openzeppelin-contracts create mode 100644 contracts/slither.config.json create mode 100644 contracts/soldeer.lock diff --git a/.github/workflows/solidity-slither.yml b/.github/workflows/solidity-slither.yml new file mode 100644 index 00000000..53e27c82 --- /dev/null +++ b/.github/workflows/solidity-slither.yml @@ -0,0 +1,51 @@ +name: Contracts - Slither + +on: + push: + branches: + - main + paths: + - "contracts/src/**" + pull_request: + paths: + - "contracts/src/**" + +defaults: + run: + working-directory: ./contracts + +jobs: + slither: + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: "21.1.0" + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Install dependencies + run: forge soldeer update + + - name: Build contracts + run: forge build + + - name: Install Slither + run: pip3 install slither-analyzer + + - name: Run Slither + run: slither . --sarif output.sarif + + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: contracts/output.sarif diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0a9168f1..a5ebaaef 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -50,13 +50,12 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 - with: - version: "nightly-ac81a53d1d5823919ffbadd3c65f081927aa11f2" - name: Run Forge build working-directory: contracts run: | forge --version + forge soldeer update forge build --sizes - name: Run Forge tests diff --git a/contracts/.gitignore b/contracts/.gitignore index 85198aaa..ebe566f2 100644 --- a/contracts/.gitignore +++ b/contracts/.gitignore @@ -12,3 +12,8 @@ docs/ # Dotenv file .env + +# Soldeer +/dependencies + +.vscode/ diff --git a/contracts/foundry.toml b/contracts/foundry.toml index 59addb91..66cb3155 100644 --- a/contracts/foundry.toml +++ b/contracts/foundry.toml @@ -1,8 +1,16 @@ +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options [profile.default] +auto_detect_solc = true src = "src" out = "out" -libs = ["lib"] +libs = ["dependencies"] +gas_reports = ["*"] +optimizer = true +optimizer_runs = 10_000 -# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options +[soldeer] +recursive_deps = true -solc = "0.8.28" +[dependencies] +forge-std = "1.9.4" +"@openzeppelin-contracts" = "5.1.0" diff --git a/contracts/lib/forge-std b/contracts/lib/forge-std deleted file mode 160000 index 035de35f..00000000 --- a/contracts/lib/forge-std +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 035de35f5e366c8d6ed142aec4ccb57fe2dd87d4 diff --git a/contracts/lib/openzeppelin-contracts b/contracts/lib/openzeppelin-contracts deleted file mode 160000 index 8b591bae..00000000 --- a/contracts/lib/openzeppelin-contracts +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8b591baef460523e5ca1c53712c464bcc1a1c467 diff --git a/contracts/remappings.txt b/contracts/remappings.txt index eede3c19..8b4a64d6 100644 --- a/contracts/remappings.txt +++ b/contracts/remappings.txt @@ -1 +1,2 @@ -@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ \ No newline at end of file +@openzeppelin-contracts-5.1.0/=dependencies/@openzeppelin-contracts-5.1.0/ +forge-std-1.9.4/=dependencies/forge-std-1.9.4/ diff --git a/contracts/script/Deployer.s.sol b/contracts/script/Deployer.s.sol index 2742eeb0..e16949d3 100644 --- a/contracts/script/Deployer.s.sol +++ b/contracts/script/Deployer.s.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.28; -import {Script, console} from "forge-std/Script.sol"; +import {Script, console} from "forge-std-1.9.4/src/Script.sol"; import "../src/Nodes.sol"; contract Deployer is Script { diff --git a/contracts/slither.config.json b/contracts/slither.config.json new file mode 100644 index 00000000..1df96896 --- /dev/null +++ b/contracts/slither.config.json @@ -0,0 +1,4 @@ +{ + "detectors_to_exclude": "", + "filter_paths": "dependencies" +} diff --git a/contracts/soldeer.lock b/contracts/soldeer.lock new file mode 100644 index 00000000..ecca7c99 --- /dev/null +++ b/contracts/soldeer.lock @@ -0,0 +1,13 @@ +[[dependencies]] +name = "@openzeppelin-contracts" +version = "5.1.0" +url = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts/5_1_0_19-10-2024_10:28:52_contracts.zip" +checksum = "fd3d1ea561cb27897008aee18ada6e85f248eb161c86e4435272fc2b5777574f" +integrity = "cb6cf6e878f2943b2291d5636a9d72ac51d43d8135896ceb6cf88d36c386f212" + +[[dependencies]] +name = "forge-std" +version = "1.9.4" +url = "https://soldeer-revisions.s3.amazonaws.com/forge-std/1_9_4_25-10-2024_14:36:59_forge-std-1.9.zip" +checksum = "b5be24beb5e4dab5e42221b2ad1288b64c826bee5ee71b6159ba93ffe86f14d4" +integrity = "3874463846ab995a6a9a88412913cacec6144f7605daa1af57c2d8bf3f210b13" diff --git a/contracts/src/Nodes.sol b/contracts/src/Nodes.sol index 68894b73..b7d2d0fb 100644 --- a/contracts/src/Nodes.sol +++ b/contracts/src/Nodes.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.28; -import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; -import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin-contracts-5.1.0/token/ERC721/ERC721.sol"; +import "@openzeppelin-contracts-5.1.0/access/Ownable.sol"; /** * A NFT contract for XMTP Node Operators. @@ -14,7 +14,7 @@ import "@openzeppelin/contracts/access/Ownable.sol"; contract Nodes is ERC721, Ownable { constructor() ERC721("XMTP Node Operator", "XMTP") Ownable(msg.sender) {} - uint32 private _nodeIncrement = 100; + uint32 private constant NODE_INCREMENT = 100; // uint32 counter so that we cannot create more than max IDs // The ERC721 standard expects the tokenID to be uint256 for standard methods unfortunately uint32 private _nodeCounter = 0; @@ -46,7 +46,7 @@ contract Nodes is ERC721, Ownable { { // the first node starts with 100 _nodeCounter++; - uint32 nodeId = _nodeCounter * _nodeIncrement; + uint32 nodeId = _nodeCounter * NODE_INCREMENT; _mint(to, nodeId); _nodes[nodeId] = Node(signingKeyPub, httpAddress, true); _emitNodeUpdate(nodeId); @@ -93,7 +93,7 @@ contract Nodes is ERC721, Ownable { // First, count the number of healthy nodes for (uint256 i = 0; i < _nodeCounter; i++) { - uint256 nodeId = _nodeIncrement * (i + 1); + uint256 nodeId = NODE_INCREMENT * (i + 1); if (_nodeExists(nodeId) && _nodes[nodeId].isHealthy) { healthyCount++; } @@ -105,7 +105,7 @@ contract Nodes is ERC721, Ownable { // Populate the array with healthy nodes for (uint32 i = 0; i < _nodeCounter; i++) { - uint32 nodeId = _nodeIncrement * (i + 1); + uint32 nodeId = NODE_INCREMENT * (i + 1); if (_nodeExists(nodeId) && _nodes[nodeId].isHealthy) { healthyNodesList[currentIndex] = NodeWithId({nodeId: nodeId, node: _nodes[nodeId]}); currentIndex++; @@ -122,7 +122,7 @@ contract Nodes is ERC721, Ownable { NodeWithId[] memory allNodesList = new NodeWithId[](_nodeCounter); for (uint32 i = 0; i < _nodeCounter; i++) { - uint32 nodeId = _nodeIncrement * (i + 1); + uint32 nodeId = NODE_INCREMENT * (i + 1); if (_nodeExists(nodeId)) { allNodesList[i] = NodeWithId({nodeId: nodeId, node: _nodes[nodeId]}); } diff --git a/contracts/test/GroupMessage.t.sol b/contracts/test/GroupMessage.t.sol index 6c1eb52b..e1f761cd 100644 --- a/contracts/test/GroupMessage.t.sol +++ b/contracts/test/GroupMessage.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.28; -import {Test, console} from "forge-std/Test.sol"; +import {Test, console} from "forge-std-1.9.4/src/Test.sol"; import {GroupMessages} from "../src/GroupMessages.sol"; contract GroupMessagesTest is Test { diff --git a/contracts/test/IdentityUpdates.t.sol b/contracts/test/IdentityUpdates.t.sol index fb413a93..a4d17fa8 100644 --- a/contracts/test/IdentityUpdates.t.sol +++ b/contracts/test/IdentityUpdates.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.28; -import {Test, console} from "forge-std/Test.sol"; +import {Test, console} from "forge-std-1.9.4/src/Test.sol"; import {IdentityUpdates} from "../src/IdentityUpdates.sol"; contract IdentityUpdatesTest is Test { diff --git a/contracts/test/Nodes.sol b/contracts/test/Nodes.sol index 0d5f1fcb..df4b21a5 100644 --- a/contracts/test/Nodes.sol +++ b/contracts/test/Nodes.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.28; -import {Test, console} from "forge-std/Test.sol"; +import {Test, console} from "forge-std-1.9.4/src/Test.sol"; +import {Ownable} from "@openzeppelin-contracts-5.1.0/access/Ownable.sol"; import {Nodes} from "../src/Nodes.sol"; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; contract NodesTest is Test { Nodes public nodes; diff --git a/dev/contracts/deploy-local b/dev/contracts/deploy-local index feae3942..daa455fb 100755 --- a/dev/contracts/deploy-local +++ b/dev/contracts/deploy-local @@ -10,6 +10,9 @@ mkdir -p ./build cd ./contracts +# Update depencencies +forge soldeer update + # Deploy a contract and save the output (which includes the contract address) to a JSON file to be used in tests function deploy_contract() { forge create --broadcast --legacy --json --rpc-url $DOCKER_RPC_URL --private-key $PRIVATE_KEY "$1:$2" > ../build/$2.json