fix: add insurance fund mechanism for protocol failures #400
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: ["main", "develop", "feat/**"] | |
| pull_request: | |
| branches: ["main", "develop"] | |
| workflow_dispatch: | |
| schedule: | |
| - cron: "0 3 * * 0" | |
| permissions: | |
| contents: write | |
| env: | |
| # Maximum allowed size in bytes for the optimised contract WASM. | |
| # Raise this in the same PR as any deliberate feature addition that grows | |
| # the binary, and document the reason in the PR description. | |
| WASM_SIZE_LIMIT_BYTES: 1500000 | |
| jobs: | |
| ci-matrix: | |
| name: CI Matrix | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - name: "Format Check" | |
| step: "fmt" | |
| working_directory: "neurowealth-vault" | |
| - name: "Clippy Lint" | |
| step: "clippy" | |
| working_directory: "neurowealth-vault" | |
| - name: "Tests" | |
| step: "test" | |
| working_directory: "neurowealth-vault" | |
| - name: "Documentation Check" | |
| step: "doc" | |
| working_directory: "neurowealth-vault" | |
| - name: "Vault Client Typecheck" | |
| step: "vault-client-typecheck" | |
| working_directory: "packages/vault-client" | |
| - name: "Vault UI Accessibility & Notifications" | |
| step: "vault-ui-test" | |
| working_directory: "packages/vault-ui" | |
| - name: "Frontend Lint" | |
| step: "frontend-lint" | |
| working_directory: "frontend" | |
| - name: "Build WASM" | |
| step: "build-wasm" | |
| working_directory: "neurowealth-vault" | |
| - name: "No Bare Panic Check" | |
| step: "no-bare-panic" | |
| working_directory: "." | |
| - name: "Stale-State Audit Check" | |
| step: "stale-state-audit" | |
| working_directory: "." | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Read Stellar CLI version | |
| id: cli-version | |
| run: | | |
| VERSION=$(cat .stellar-version | tr -d '[:space:]') | |
| echo "version=${VERSION}" >> "$GITHUB_OUTPUT" | |
| - name: Install Rust toolchain with components | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: rustfmt, clippy | |
| targets: wasm32-unknown-unknown | |
| - name: Cache Rust dependencies | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: neurowealth-vault | |
| - name: Cache cargo registry and tooling | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/bin/ | |
| ~/.cargo/registry/index/ | |
| ~/.cargo/registry/cache/ | |
| ~/.cargo/git/db/ | |
| ~/.local/bin/ | |
| key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo- | |
| - name: Install Stellar CLI | |
| if: matrix.step == 'build-wasm' | |
| run: | | |
| VERSION="${{ steps.cli-version.outputs.version }}" | |
| mkdir -p "$HOME/.local/bin" | |
| curl -sSL "https://github.com/stellar/stellar-cli/releases/download/v${VERSION}/stellar-cli-${VERSION}-x86_64-unknown-linux-gnu.tar.gz" | tar -xz | |
| mv stellar "$HOME/.local/bin/stellar" | |
| echo "$HOME/.local/bin" >> "$GITHUB_PATH" | |
| stellar --version | |
| - name: Install wasm-opt | |
| if: matrix.step == 'build-wasm' | |
| run: cargo install wasm-opt --locked | |
| - name: Run ${{ matrix.name }} | |
| working-directory: ${{ matrix.working_directory }} | |
| shell: bash | |
| env: | |
| CARGO_TARGET_DIR: ${{ runner.temp }}/neurowealth-target | |
| run: | | |
| set -euo pipefail | |
| case "${{ matrix.step }}" in | |
| fmt) | |
| cargo fmt --all -- --check | |
| ;; | |
| clippy) | |
| cargo clippy --all-targets --all-features -- -D warnings -D clippy::all -D clippy::pedantic | |
| ;; | |
| test) | |
| cargo test --verbose | |
| ;; | |
| doc) | |
| RUSTDOCFLAGS="-D warnings" cargo doc --no-deps | |
| ;; | |
| vault-client-typecheck) | |
| npm ci | |
| npm run typecheck | |
| npm run test | |
| ;; | |
| vault-ui-test) | |
| npm install | |
| npm run test | |
| ;; | |
| frontend-lint) | |
| npm ci | |
| npm run lint | |
| ;; | |
| build-wasm) | |
| RUSTFLAGS="-C target-cpu=mvp" cargo build \ | |
| -p neurowealth-vault \ | |
| --target wasm32-unknown-unknown \ | |
| --release | |
| WASM_DIR="${CARGO_TARGET_DIR:-target}/wasm32-unknown-unknown/release" | |
| WASM_FILE="$WASM_DIR/neurowealth_vault.wasm" | |
| wasm-opt --strip-target-features \ | |
| "$WASM_FILE" \ | |
| -o "$WASM_FILE" | |
| WASM_SIZE=$(stat -c%s "$WASM_FILE") | |
| LIMIT="${WASM_SIZE_LIMIT_BYTES:-1500000}" | |
| BASE_BRANCH="${GITHUB_BASE_REF:-${GITHUB_REF_NAME}}" | |
| BASELINE_FILE=".github/wasm-size-history.json" | |
| BASELINE_SOURCE_FILE="$BASELINE_FILE" | |
| BASELINE_SIZE="" | |
| DELTA_BYTES="" | |
| DELTA_DISPLAY="n/a" | |
| if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then | |
| git fetch origin "$BASE_BRANCH" --depth=1 >/dev/null 2>&1 || true | |
| if git show "origin/$BASE_BRANCH:$BASELINE_SOURCE_FILE" > "$RUNNER_TEMP/wasm-size-baseline.json" 2>/dev/null; then | |
| BASELINE_FILE="$RUNNER_TEMP/wasm-size-baseline.json" | |
| else | |
| BASELINE_FILE="$BASELINE_SOURCE_FILE" | |
| fi | |
| fi | |
| if [ -f "$BASELINE_FILE" ]; then | |
| BASELINE_SIZE=$(python3 - "$BASELINE_FILE" "$BASE_BRANCH" -c "import json, sys; path, branch = sys.argv[1], sys.argv[2]; data = json.load(open(path)) if open(path) else {}; entry = data.get(branch, {}).get('latest'); print(entry.get('size_bytes', '') if entry else '')") | |
| fi | |
| if [ -n "$BASELINE_SIZE" ]; then | |
| DELTA_BYTES=$((WASM_SIZE - BASELINE_SIZE)) | |
| if [ "$DELTA_BYTES" -gt 0 ]; then | |
| DELTA_DISPLAY="+$DELTA_BYTES bytes" | |
| elif [ "$DELTA_BYTES" -lt 0 ]; then | |
| DELTA_DISPLAY="$DELTA_BYTES bytes" | |
| else | |
| DELTA_DISPLAY="0 bytes" | |
| fi | |
| fi | |
| { | |
| echo "## WASM Size Report" | |
| echo "" | |
| echo "| Metric | Value |" | |
| echo "|--------|-------|" | |
| echo "| Optimised size | ${WASM_SIZE} bytes |" | |
| echo "| Limit | ${LIMIT} bytes |" | |
| if [ -n "$BASELINE_SIZE" ]; then | |
| echo "| Baseline (${BASE_BRANCH}) | ${BASELINE_SIZE} bytes |" | |
| echo "| Delta vs ${BASE_BRANCH} | ${DELTA_DISPLAY} |" | |
| else | |
| echo "| Baseline (${BASE_BRANCH}) | n/a |" | |
| echo "| Delta vs ${BASE_BRANCH} | n/a |" | |
| fi | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| if [ "$WASM_SIZE" -gt "$LIMIT" ]; then | |
| echo "| Status | :x: EXCEEDED |" >> "$GITHUB_STEP_SUMMARY" | |
| echo "::error::Optimised WASM is ${WASM_SIZE} bytes, which exceeds the ${LIMIT}-byte limit. See docs/WASM_SIZE.md for reduction tips." | |
| exit 1 | |
| else | |
| echo "| Status | :white_check_mark: OK |" >> "$GITHUB_STEP_SUMMARY" | |
| echo "Optimised WASM is ${WASM_SIZE} bytes — within the ${LIMIT}-byte limit." | |
| fi | |
| if [ "$GITHUB_EVENT_NAME" = "push" ] && { [ "$GITHUB_REF_NAME" = "main" ] || [ "$GITHUB_REF_NAME" = "develop" ]; }; then | |
| python3 scripts/update_wasm_baseline.py "$WASM_SIZE" "$GITHUB_SHA" "$GITHUB_REF_NAME" "$BASELINE_FILE" | |
| fi | |
| # Validate storage layout compatibility gate | |
| mkdir -p scripts/e2e-artifacts | |
| python3 scripts/check-storage-layout.py \ | |
| "$WASM_FILE" \ | |
| "$WASM_FILE" \ | |
| --migration-doc docs/UPGRADE_MIGRATION.md \ | |
| --output scripts/e2e-artifacts/storage_layout_diff.txt | |
| # Export the WASM path for the artifact upload step. | |
| echo "WASM_FILE=$WASM_FILE" >> "$GITHUB_ENV" | |
| ;; | |
| no-bare-panic) | |
| bash scripts/check-no-bare-panic.sh \ | |
| neurowealth-vault/contracts/vault/src | |
| ;; | |
| stale-state-audit) | |
| bash scripts/check-stale-state-audit.sh \ | |
| neurowealth-vault/contracts/vault/src/lib.rs | |
| ;; | |
| *) | |
| echo "Unknown step: ${{ matrix.step }}" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| - name: Upload optimised WASM artifact | |
| if: matrix.step == 'build-wasm' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: neurowealth-vault-wasm | |
| path: ${{ env.WASM_FILE }} | |
| retention-days: 14 | |
| kani-share-math: | |
| name: Kani Share-Accounting Proofs | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Run Kani proofs | |
| uses: model-checking/kani-github-action@v1.1 | |
| with: | |
| args: --manifest-path neurowealth-vault/Cargo.toml -p share-math | |
| cargo-deny: | |
| name: Dependency Audit (cargo-deny) | |
| runs-on: ubuntu-latest | |
| # The workflow-level weekly cron (Sunday 03:00 UTC) re-runs this job | |
| # against a fresh RustSec advisory database, so advisories published | |
| # against pinned dependencies surface without waiting for a PR. | |
| permissions: | |
| contents: read | |
| issues: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Run cargo-deny | |
| uses: EmbarkStudios/cargo-deny-action@v2 | |
| with: | |
| command: check | |
| # The vault workspace manifest drives the dependency graph. | |
| manifest-path: ./neurowealth-vault/Cargo.toml | |
| # Policy lives in deny.toml at the repo root. | |
| command-arguments: "--config deny.toml" | |
| # Respect the [graph] settings in deny.toml instead of forcing | |
| # --all-features (the action's default). | |
| arguments: "" | |
| - name: Open or update advisory tracking issue | |
| # Only scheduled runs manage the tracking issue: a per-PR failure is | |
| # already visible on the PR itself, while a scheduled failure has no | |
| # PR to surface it and would otherwise go unnoticed. | |
| if: failure() && github.event_name == 'schedule' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const title = 'Scheduled cargo-deny audit is failing (dependency advisory drift)'; | |
| const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; | |
| const body = [ | |
| 'The weekly scheduled `cargo deny check` run failed, meaning a new', | |
| 'RustSec advisory (or license/ban/source violation) now applies to the', | |
| 'pinned dependency graph even though no PR has touched it.', | |
| '', | |
| `- Failing run: ${runUrl}`, | |
| '- Policy: `deny.toml` (workspace: `neurowealth-vault/Cargo.toml`)', | |
| '', | |
| 'Triage: inspect the run log for the advisory ID, then either upgrade', | |
| 'the affected dependency or record a justified exception in', | |
| '`deny.toml`. Close this issue once a scheduled run passes again.', | |
| ].join('\n'); | |
| const { data: issues } = await github.rest.issues.listForRepo({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| state: 'open', | |
| per_page: 100, | |
| }); | |
| const existing = issues.find((issue) => issue.title === title); | |
| if (existing) { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: existing.number, | |
| body: `Still failing as of the latest scheduled run: ${runUrl}`, | |
| }); | |
| } else { | |
| await github.rest.issues.create({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| title, | |
| body, | |
| }); | |
| } | |
| reproducible-build: | |
| name: Reproducible Build Check | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| # Two clean uncached builds are expensive, so this runs where an | |
| # attestation matters — weekly, on demand, and on pushes to main — | |
| # rather than on every PR. | |
| if: | | |
| github.event_name == 'schedule' || | |
| github.event_name == 'workflow_dispatch' || | |
| (github.event_name == 'push' && github.ref == 'refs/heads/main') | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: wasm32-unknown-unknown | |
| - name: Install wasm-opt | |
| run: cargo install wasm-opt --locked | |
| # No build caches on purpose: a reproducibility attestation must come | |
| # from two genuinely clean builds. | |
| - name: Dual build + hash comparison | |
| run: bash scripts/check-reproducible-build.sh | |
| readme-check: | |
| name: README Validation | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Validate README | |
| run: bash scripts/check-readme.sh . | |
| security-audit: | |
| name: Security Documentation Audit | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Verify access control table accuracy (spec) | |
| run: bash scripts/check-access-control.sh | |
| - name: Verify auth matrix against source (lib.rs) | |
| run: python3 scripts/check-auth-matrix.py | |
| - name: Verify DataKey::Balance deprecation | |
| run: bash scripts/check-balance-deprecation.sh | |
| - name: Verify DataKey docs match lib.rs | |
| run: bash scripts/check-data-key-docs.sh | |
| changes: | |
| name: Detect changes | |
| runs-on: ubuntu-latest | |
| outputs: | |
| contract: ${{ steps.filter.outputs.contract }} | |
| vault_src: ${{ steps.filter.outputs.vault_src }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Check for contract changes | |
| uses: dorny/paths-filter@v3 | |
| id: filter | |
| with: | |
| filters: | | |
| contract: | |
| - 'neurowealth-vault/**' | |
| vault_src: | |
| - 'neurowealth-vault/contracts/vault/src/**' | |
| fuzz: | |
| name: Fuzz ${{ matrix.target }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| needs: [changes] | |
| if: | | |
| github.event_name == 'schedule' || | |
| (github.event_name == 'pull_request' && needs.changes.outputs.vault_src == 'true') | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: | |
| - deposit_withdraw_sequence | |
| - rebalance_transitions | |
| - rounding_boundaries | |
| - share_accounting_invariants | |
| - agent_update_timelock | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust nightly | |
| uses: dtolnay/rust-toolchain@nightly | |
| - name: Cache Rust dependencies | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: neurowealth-vault | |
| - name: Install cargo-fuzz | |
| run: cargo install cargo-fuzz --locked | |
| - name: Run ${{ matrix.target }} fuzz target (PR – short bounds) | |
| if: github.event_name == 'pull_request' | |
| working-directory: neurowealth-vault | |
| run: cargo fuzz run ${{ matrix.target }} -- -runs=1000 -max_total_time=120 | |
| - name: Run ${{ matrix.target }} fuzz target (scheduled – full bounds) | |
| if: github.event_name == 'schedule' | |
| working-directory: neurowealth-vault | |
| run: cargo fuzz run ${{ matrix.target }} -- -runs=5000 -max_total_time=300 | |
| e2e-devnet: | |
| name: E2E Devnet Validation | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| needs: [ci-matrix, changes] | |
| # Trigger e2e-devnet on manual dispatch, weekly schedule, or PRs with 'run-e2e' label or contract modifications | |
| if: | | |
| github.event_name == 'workflow_dispatch' || | |
| github.event_name == 'schedule' || | |
| (github.event_name == 'pull_request' && ( | |
| contains(github.event.pull_request.labels.*.name, 'run-e2e') || | |
| needs.changes.outputs.contract == 'true' | |
| )) | |
| # Required secrets: | |
| # - SOROBAN_SECRET_KEY: Funded Stellar account secret key used by E2E script | |
| # to deploy and interact with the contracts on testnet. | |
| env: | |
| SOROBAN_RPC_URL: https://soroban-testnet.stellar.org | |
| SOROBAN_NETWORK_PASSPHRASE: "Test SDF Network ; September 2015" | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Read Stellar CLI version | |
| id: cli-version | |
| run: | | |
| VERSION=$(cat .stellar-version | tr -d '[:space:]') | |
| echo "version=${VERSION}" >> "$GITHUB_OUTPUT" | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: wasm32-unknown-unknown | |
| - name: Cache Rust dependencies | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: neurowealth-vault | |
| - name: Install Stellar CLI | |
| run: | | |
| VERSION="${{ steps.cli-version.outputs.version }}" | |
| if [[ -x "$HOME/.local/bin/stellar" ]]; then | |
| echo "$HOME/.local/bin" >> "$GITHUB_PATH" | |
| stellar --version | |
| exit 0 | |
| fi | |
| mkdir -p "$HOME/.local/bin" | |
| curl -sSL "https://github.com/stellar/stellar-cli/releases/download/v${VERSION}/stellar-cli-${VERSION}-x86_64-unknown-linux-gnu.tar.gz" | tar -xz | |
| mv stellar "$HOME/.local/bin/stellar" | |
| echo "$HOME/.local/bin" >> "$GITHUB_PATH" | |
| stellar --version | |
| - name: Install wasm-opt | |
| run: cargo install wasm-opt --locked | |
| - name: Build contract WASM (MVP-compatible) | |
| working-directory: neurowealth-vault | |
| env: | |
| CARGO_TARGET_DIR: ${{ runner.temp }}/neurowealth-target | |
| run: | | |
| RUSTFLAGS="-C target-cpu=mvp" cargo build \ | |
| -p neurowealth-vault \ | |
| --target wasm32-unknown-unknown \ | |
| --release | |
| WASM_DIR="${CARGO_TARGET_DIR:-target}/wasm32-unknown-unknown/release" | |
| WASM_FILE="$WASM_DIR/neurowealth_vault.wasm" | |
| wasm-opt --strip-target-features \ | |
| "$WASM_FILE" \ | |
| -o "$WASM_FILE" | |
| - name: Make scripts executable | |
| run: chmod +x scripts/*.sh scripts/*.py | |
| - name: Run E2E devnet validation | |
| id: e2e | |
| run: | | |
| echo "::group::E2E Output" | |
| ./scripts/e2e-devnet.sh 2>&1 | tee scripts/e2e-artifacts/full_output.log | |
| echo "::endgroup::" | |
| - name: Upload E2E artifacts | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: e2e-devnet-artifacts | |
| path: scripts/e2e-artifacts/ | |
| retention-days: 14 | |
| - name: Cleanup testnet state | |
| if: always() | |
| run: ./scripts/e2e-restore.sh --keep-artifacts | |
| - name: Report failure details | |
| if: failure() | |
| run: | | |
| echo "## E2E Devnet Validation Failed" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [[ -f scripts/e2e-artifacts/summary.txt ]]; then | |
| echo "### Scenario Results" >> $GITHUB_STEP_SUMMARY | |
| echo '```' >> $GITHUB_STEP_SUMMARY | |
| cat scripts/e2e-artifacts/summary.txt >> $GITHUB_STEP_SUMMARY | |
| echo '```' >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [[ -f scripts/e2e-artifacts/contract_id.txt ]]; then | |
| CID=$(cat scripts/e2e-artifacts/contract_id.txt) | |
| echo "### References" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Contract ID:** \`$CID\`" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Explorer:** [View on StellarExpert](https://stellar.expert/explorer/testnet/contract/$CID)" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Check the uploaded **e2e-devnet-artifacts** for full logs and transaction details." >> $GITHUB_STEP_SUMMARY | |
| - name: Report success details | |
| if: success() | |
| run: | | |
| echo "## E2E Devnet Validation Passed" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [[ -f scripts/e2e-artifacts/summary.txt ]]; then | |
| echo '```' >> $GITHUB_STEP_SUMMARY | |
| cat scripts/e2e-artifacts/summary.txt >> $GITHUB_STEP_SUMMARY | |
| echo '```' >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [[ -f scripts/e2e-artifacts/contract_id.txt ]]; then | |
| CID=$(cat scripts/e2e-artifacts/contract_id.txt) | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Contract ID:** \`$CID\`" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| arithmetic-guard: | |
| name: Arithmetic overflow guard | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust stable | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: clippy | |
| - name: Cache cargo registry & build artefacts | |
| uses: Swatinem/rust-cache@v2 | |
| # Layer (a): Clippy forbids bare integer arithmetic operators. | |
| # -D turns the lint into a hard error; the build fails if any violation | |
| # is found. This runs on ALL features so test helpers are also covered. | |
| - name: Clippy — forbid integer_arithmetic | |
| working-directory: neurowealth-vault/contracts/vault | |
| run: | | |
| cargo clippy \ | |
| --all-features \ | |
| -- \ | |
| -D clippy::integer_arithmetic | |
| # Layer (b): Shell grep sweep for patterns Clippy can miss. | |
| # Checks: | |
| # - No source-level #[allow(clippy::integer_arithmetic)] suppression | |
| # - No .checked_*().unwrap() (must use .expect("msg")) | |
| # - No silent 'as i128 / as u64 …' truncating casts in contract source | |
| - name: Shell arithmetic sweep | |
| working-directory: neurowealth-vault/contracts/vault | |
| run: bash scripts/check_arithmetic.sh | |
| # Summary is written to the GitHub Actions step summary by the script. |