Skip to content

fix: add insurance fund mechanism for protocol failures #400

fix: add insurance fund mechanism for protocol failures

fix: add insurance fund mechanism for protocol failures #400

Workflow file for this run

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.