diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..9acec66e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,206 @@ +name: "CI" + +concurrency: + cancel-in-progress: true + group: ${{github.workflow}}-${{github.ref}} + +env: + MAINNET_RPC_URL: ${{ secrets.MAINNET_RPC_URL }} + API_KEY_INFURA: ${{ secrets.API_KEY_INFURA }} + +on: + workflow_dispatch: + pull_request: + push: + branches: + - "main" + +jobs: + lint: + runs-on: "ubuntu-latest" + steps: + - name: "Check out the repo" + uses: "actions/checkout@v3" + + - name: "Install Foundry" + uses: "foundry-rs/foundry-toolchain@v1" + + - name: "Install Node.js" + uses: "actions/setup-node@v3" + with: + cache: "yarn" + node-version: "lts/*" + + - name: "Install Yarn" + run: "npm install -g yarn" + + - name: "Install the Node.js dependencies" + run: "yarn install" + + - name: "Lint the contracts" + run: "yarn lint" + + - name: "Add lint summary" + run: | + echo "## Lint result" >> $GITHUB_STEP_SUMMARY + echo "✅ Passed" >> $GITHUB_STEP_SUMMARY + + build: + runs-on: "ubuntu-latest" + steps: + - name: "Check out the repo" + uses: "actions/checkout@v3" + + - name: "Install Foundry" + uses: "foundry-rs/foundry-toolchain@v1" + + - name: "Install Node.js" + uses: "actions/setup-node@v3" + with: + cache: "yarn" + node-version: "lts/*" + + - name: "Install Yarn" + run: "npm install -g yarn" + + - name: "Install the Node.js dependencies" + run: "yarn install" + + - name: "Show the Foundry config" + run: "forge config" + + - name: "Build the contracts" + run: "forge build" + + - name: "Cache the build and the node modules so that they can be re-used by the other jobs" + uses: "actions/cache/save@v3" + with: + key: "build-and-modules-${{ github.sha }}" + path: | + cache + node_modules + out + + - name: "Add build summary" + run: | + echo "## Build result" >> $GITHUB_STEP_SUMMARY + echo "✅ Passed" >> $GITHUB_STEP_SUMMARY + + test-integration: + needs: ["lint", "build"] + env: + FOUNDRY_FUZZ_RUNS: "5000" + runs-on: "ubuntu-latest" + steps: + - name: "Check out the repo" + uses: "actions/checkout@v3" + + - name: "Install Foundry" + uses: "foundry-rs/foundry-toolchain@v1" + + - name: "Restore the cached build and the node modules" + uses: "actions/cache/restore@v3" + with: + fail-on-cache-miss: true + key: "build-and-modules-${{ github.sha }}" + path: | + cache + node_modules + out + + - name: "Run the integration tests" + run: "forge test --match-path \"test/integration/**/*.sol\"" + + - name: "Add test summary" + run: | + echo "## Integration tests result" >> $GITHUB_STEP_SUMMARY + echo "✅ Passed" >> $GITHUB_STEP_SUMMARY + + test-utils: + needs: ["lint", "build"] + runs-on: "ubuntu-latest" + steps: + - name: "Check out the repo" + uses: "actions/checkout@v3" + + - name: "Install Foundry" + uses: "foundry-rs/foundry-toolchain@v1" + + - name: "Restore the cached build and the node modules" + uses: "actions/cache/restore@v3" + with: + fail-on-cache-miss: true + key: "build-and-modules-${{ github.sha }}" + path: | + cache + node_modules + out + + - name: "Run the utils tests" + run: "forge test --match-path \"test/utils/**/*.sol\"" + + - name: "Add test summary" + run: | + echo "## Utils tests result" >> $GITHUB_STEP_SUMMARY + echo "✅ Passed" >> $GITHUB_STEP_SUMMARY + + test-invariant: + needs: ["lint", "build"] + runs-on: "ubuntu-latest" + steps: + - name: "Check out the repo" + uses: "actions/checkout@v3" + + - name: "Install Foundry" + uses: "foundry-rs/foundry-toolchain@v1" + + - name: "Restore the cached build and the node modules" + uses: "actions/cache/restore@v3" + with: + fail-on-cache-miss: true + key: "build-and-modules-${{ github.sha }}" + path: | + cache + node_modules + out + + - name: "Run the invariant tests" + run: "forge test --match-path \"test/invariant/**/*.sol\"" + + - name: "Add test summary" + run: | + echo "## Invariant tests result" >> $GITHUB_STEP_SUMMARY + echo "✅ Passed" >> $GITHUB_STEP_SUMMARY + + coverage: + needs: ["lint", "build"] + runs-on: "ubuntu-latest" + steps: + - name: "Check out the repo" + uses: "actions/checkout@v3" + + - name: "Install Foundry" + uses: "foundry-rs/foundry-toolchain@v1" + + - name: "Restore the cached build and the node modules" + uses: "actions/cache/restore@v3" + with: + fail-on-cache-miss: true + key: "build-and-modules-${{ github.sha }}" + path: | + cache + node_modules + out + + - name: "Generate the coverage report using the unit and the integration tests" + run: "forge coverage --match-path \"test/{unit,integration}/**/*.sol\" --report lcov" + + - name: "Upload coverage report to Codecov" + uses: "codecov/codecov-action@v3" + with: + files: "./lcov.info" + + - name: "Add coverage summary" + run: | + echo "## Coverage result" >> $GITHUB_STEP_SUMMARY + echo "✅ Uploaded to Codecov" >> $GITHUB_STEP_SUMMARY diff --git a/.solhint.json b/.solhint.json index 0c7b93df..fb66994e 100644 --- a/.solhint.json +++ b/.solhint.json @@ -1,11 +1,13 @@ { "extends": "solhint:recommended", "rules": { + "avoid-low-level-calls": "off", "code-complexity": ["error", 8], "compiler-version": ["error", ">=0.8.22"], + "contract-name-camelcase": "off", "func-name-mixedcase": "off", "func-visibility": ["error", { "ignoreConstructors": true }], - "max-line-length": ["error", 120], + "max-line-length": ["error", 123], "named-parameters-mapping": "warn", "no-console": "off", "not-rely-on-time": "off", diff --git a/script/Base.s.sol b/script/Base.s.sol index bd4f6934..b382184d 100644 --- a/script/Base.s.sol +++ b/script/Base.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22; import { Script } from "@forge-std/src/Script.sol"; diff --git a/test/Base.t.sol b/test/Base.t.sol index 35448c25..66f34a57 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -6,7 +6,6 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; import { SablierV2OpenEnded } from "src/SablierV2OpenEnded.sol"; -import { OpenEnded } from "src/types/DataTypes.sol"; import { ERC20Mock } from "./mocks/ERC20Mock.sol"; import { ERC20MissingReturn } from "./mocks/ERC20MissingReturn.sol"; diff --git a/test/integration/Integration.t.sol b/test/integration/Integration.t.sol index 3d13c357..a65f4e2a 100644 --- a/test/integration/Integration.t.sol +++ b/test/integration/Integration.t.sol @@ -47,7 +47,7 @@ abstract contract Integration_Test is Base_Test { assertEq(returnData, abi.encodeWithSelector(Errors.DelegateCall.selector), "delegatecall return data"); } - uint256 nullStreamId = 420; + uint256 internal nullStreamId = 420; function _test_RevertGiven_Null() internal { vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2OpenEnded_Null.selector, nullStreamId)); diff --git a/test/integration/adjust-rate-per-second/adjustRatePerSecond.t.sol b/test/integration/adjust-rate-per-second/adjustRatePerSecond.t.sol index a5a67946..561428f4 100644 --- a/test/integration/adjust-rate-per-second/adjustRatePerSecond.t.sol +++ b/test/integration/adjust-rate-per-second/adjustRatePerSecond.t.sol @@ -5,7 +5,6 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierV2OpenEnded } from "src/interfaces/ISablierV2OpenEnded.sol"; import { Errors } from "src/libraries/Errors.sol"; -import { OpenEnded } from "src/types/DataTypes.sol"; import { Integration_Test } from "../Integration.t.sol"; diff --git a/test/integration/cancel/cancel.t.sol b/test/integration/cancel/cancel.t.sol index 1f6f911d..3457e0f3 100644 --- a/test/integration/cancel/cancel.t.sol +++ b/test/integration/cancel/cancel.t.sol @@ -5,7 +5,6 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierV2OpenEnded } from "src/interfaces/ISablierV2OpenEnded.sol"; import { Errors } from "src/libraries/Errors.sol"; -import { OpenEnded } from "src/types/DataTypes.sol"; import { Integration_Test } from "../Integration.t.sol"; diff --git a/test/integration/deposit/deposit.t.sol b/test/integration/deposit/deposit.t.sol index e92ff504..8aca0fee 100644 --- a/test/integration/deposit/deposit.t.sol +++ b/test/integration/deposit/deposit.t.sol @@ -5,7 +5,6 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierV2OpenEnded } from "src/interfaces/ISablierV2OpenEnded.sol"; import { Errors } from "src/libraries/Errors.sol"; -import { OpenEnded } from "src/types/DataTypes.sol"; import { Integration_Test } from "../Integration.t.sol"; diff --git a/test/integration/refund-from-stream/refundFromStream.t.sol b/test/integration/refund-from-stream/refundFromStream.t.sol index 27e4facf..5cbba4fd 100644 --- a/test/integration/refund-from-stream/refundFromStream.t.sol +++ b/test/integration/refund-from-stream/refundFromStream.t.sol @@ -5,7 +5,6 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierV2OpenEnded } from "src/interfaces/ISablierV2OpenEnded.sol"; import { Errors } from "src/libraries/Errors.sol"; -import { OpenEnded } from "src/types/DataTypes.sol"; import { Integration_Test } from "../Integration.t.sol"; diff --git a/test/integration/restart-stream/restartStream.t.sol b/test/integration/restart-stream/restartStream.t.sol index b328786e..1a52f100 100644 --- a/test/integration/restart-stream/restartStream.t.sol +++ b/test/integration/restart-stream/restartStream.t.sol @@ -3,7 +3,6 @@ pragma solidity >=0.8.22; import { ISablierV2OpenEnded } from "src/interfaces/ISablierV2OpenEnded.sol"; import { Errors } from "src/libraries/Errors.sol"; -import { OpenEnded } from "src/types/DataTypes.sol"; import { Integration_Test } from "../Integration.t.sol"; diff --git a/test/integration/withdraw/withdraw.t.sol b/test/integration/withdraw/withdraw.t.sol index 75d0ba54..d3aae1e3 100644 --- a/test/integration/withdraw/withdraw.t.sol +++ b/test/integration/withdraw/withdraw.t.sol @@ -5,7 +5,6 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierV2OpenEnded } from "src/interfaces/ISablierV2OpenEnded.sol"; import { Errors } from "src/libraries/Errors.sol"; -import { OpenEnded } from "src/types/DataTypes.sol"; import { Integration_Test } from "../Integration.t.sol"; diff --git a/test/invariant/handlers/OpenEndedCreateHandler.sol b/test/invariant/handlers/OpenEndedCreateHandler.sol index a4dcab45..14526bca 100644 --- a/test/invariant/handlers/OpenEndedCreateHandler.sol +++ b/test/invariant/handlers/OpenEndedCreateHandler.sol @@ -4,7 +4,6 @@ pragma solidity >=0.8.22 <0.9.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierV2OpenEnded } from "src/interfaces/ISablierV2OpenEnded.sol"; -import { OpenEnded } from "src/types/DataTypes.sol"; import { OpenEndedStore } from "../stores/OpenEndedStore.sol"; import { TimestampStore } from "../stores/TimestampStore.sol"; diff --git a/test/invariant/handlers/OpenEndedHandler.sol b/test/invariant/handlers/OpenEndedHandler.sol index b3099751..3b6de616 100644 --- a/test/invariant/handlers/OpenEndedHandler.sol +++ b/test/invariant/handlers/OpenEndedHandler.sol @@ -4,7 +4,6 @@ pragma solidity >=0.8.22 <0.9.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierV2OpenEnded } from "src/interfaces/ISablierV2OpenEnded.sol"; -import { OpenEnded } from "src/types/DataTypes.sol"; import { OpenEndedStore } from "../stores/OpenEndedStore.sol"; import { TimestampStore } from "../stores/TimestampStore.sol";