feat: harden family wallet initialization invariants #951
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: Soroban Smart Contracts CI | |
| on: | |
| push: | |
| branches: ["main"] | |
| pull_request: | |
| branches: ["main"] | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| env: | |
| CARGO_TERM_COLOR: always | |
| RUST_BACKTRACE: 1 | |
| jobs: | |
| build: | |
| name: Build and Test | |
| runs-on: macos-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: wasm32-unknown-unknown | |
| components: rustfmt, clippy | |
| - name: Cache Cargo dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/bin/ | |
| ~/.cargo/registry/index/ | |
| ~/.cargo/registry/cache/ | |
| ~/.cargo/git/db/ | |
| target/ | |
| key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo- | |
| - name: Install Soroban CLI | |
| run: | | |
| if ! command -v soroban &> /dev/null; then | |
| echo "Attempting to install via Homebrew..." | |
| if brew list stellar-cli &>/dev/null || brew install stellar-cli 2>/dev/null; then | |
| echo "Installed via Homebrew" | |
| else | |
| echo "Homebrew install failed, installing via cargo..." | |
| cargo install --locked --version 21.0.0 soroban-cli | |
| fi | |
| fi | |
| # Try both commands in case Homebrew installs it as 'stellar' | |
| if command -v soroban &> /dev/null; then | |
| soroban --version | |
| elif command -v stellar &> /dev/null; then | |
| stellar --version | |
| # Create alias for consistency | |
| ln -s $(which stellar) /usr/local/bin/soroban || true | |
| else | |
| echo "Error: Neither soroban nor stellar CLI found" | |
| exit 1 | |
| fi | |
| - name: Verify Rust installation | |
| run: | | |
| rustc --version | |
| cargo --version | |
| rustup target list --installed | grep wasm32-unknown-unknown || rustup target add wasm32-unknown-unknown | |
| - name: Build workspace (WASM) | |
| run: | | |
| cargo build --release --target wasm32-unknown-unknown --workspace \ | |
| --exclude remitwise-cli \ | |
| --exclude scenarios \ | |
| --exclude integration_tests \ | |
| --exclude testutils \ | |
| --verbose | |
| continue-on-error: false | |
| - name: Build Soroban contracts | |
| run: | | |
| for contract in remittance_split savings_goals bill_payments insurance; do | |
| echo "Building contract: $contract" | |
| # Build from workspace root to ensure WASM files go to target/wasm32-unknown-unknown/release/ | |
| cargo build --release --target wasm32-unknown-unknown --package $contract --verbose | |
| done | |
| continue-on-error: false | |
| - name: Verify WASM files exist | |
| run: | | |
| for contract in remittance_split savings_goals bill_payments insurance; do | |
| wasm_file="target/wasm32-unknown-unknown/release/${contract}.wasm" | |
| if [ ! -f "$wasm_file" ]; then | |
| echo "Error: WASM file not found: $wasm_file" | |
| exit 1 | |
| fi | |
| echo "✓ Found: $wasm_file ($(du -h $wasm_file | cut -f1))" | |
| done | |
| - name: Run Cargo tests | |
| run: | | |
| cargo test -p orchestrator --verbose | |
| continue-on-error: false | |
| - name: Run Clippy | |
| run: | | |
| cargo clippy -p orchestrator --all-targets -- -D warnings | |
| continue-on-error: false | |
| - name: Check formatting | |
| run: | | |
| cargo fmt --all -- --check | |
| continue-on-error: false | |
| - name: Upload WASM artifacts | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: wasm-contracts | |
| path: | | |
| target/wasm32-unknown-unknown/release/*.wasm | |
| retention-days: 7 | |
| compression-level: 9 | |
| - name: Upload build logs | |
| uses: actions/upload-artifact@v4 | |
| if: failure() | |
| with: | |
| name: build-logs | |
| path: | | |
| target/**/*.log | |
| retention-days: 3 | |
| security: | |
| name: Security Checks | |
| runs-on: macos-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache Cargo dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/bin/ | |
| ~/.cargo/registry/index/ | |
| ~/.cargo/registry/cache/ | |
| ~/.cargo/git/db/ | |
| target/ | |
| key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo- | |
| - name: Install cargo-audit | |
| run: | | |
| if ! command -v cargo-audit &> /dev/null; then | |
| cargo install cargo-audit --locked | |
| fi | |
| - name: Run cargo audit | |
| run: | | |
| cargo audit --deny warnings | |
| continue-on-error: true | |
| gas-benchmarks: | |
| name: Gas Benchmarks | |
| runs-on: macos-latest | |
| timeout-minutes: 15 | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: wasm32-unknown-unknown | |
| - name: Cache Cargo dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/bin/ | |
| ~/.cargo/registry/index/ | |
| ~/.cargo/registry/cache/ | |
| ~/.cargo/git/db/ | |
| target/ | |
| key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo- | |
| - name: Install jq | |
| run: | | |
| if ! command -v jq &> /dev/null; then | |
| brew install jq | |
| fi | |
| - name: Run gas benchmarks | |
| run: | | |
| ./scripts/run_gas_benchmarks.sh | |
| continue-on-error: false | |
| - name: Check for gas regressions | |
| run: | | |
| if [ -f benchmarks/baseline.json ]; then | |
| echo "Comparing against baseline..." | |
| ./scripts/compare_gas_results.sh benchmarks/baseline.json gas_results.json | |
| else | |
| echo "⚠️ No baseline found. Skipping regression check." | |
| echo "Run './scripts/update_baseline.sh' locally to create initial baseline." | |
| fi | |
| continue-on-error: false | |
| - name: Upload gas benchmark results | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: gas-benchmarks | |
| path: gas_results.json | |
| retention-days: 30 | |
| - name: Comment PR with gas results | |
| if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const fs = require('fs'); | |
| if (!fs.existsSync('gas_results.json')) { | |
| console.log('No gas results found'); | |
| return; | |
| } | |
| const results = JSON.parse(fs.readFileSync('gas_results.json', 'utf8')); | |
| let comment = '## ⛽ Gas Benchmark Results\n\n'; | |
| comment += '| Contract | Method | Scenario | CPU | Memory |\n'; | |
| comment += '|----------|--------|----------|-----|--------|\n'; | |
| results.forEach(r => { | |
| comment += `| ${r.contract} | ${r.method} | ${r.scenario} | ${r.cpu.toLocaleString()} | ${r.mem.toLocaleString()} |\n`; | |
| }); | |
| comment += '\n*Results are in instruction/byte counts. Lower is better.*\n'; | |
| // Check if baseline exists and add comparison | |
| if (fs.existsSync('benchmarks/baseline.json')) { | |
| const baseline = JSON.parse(fs.readFileSync('benchmarks/baseline.json', 'utf8')); | |
| comment += '\n### Comparison vs Baseline\n\n'; | |
| results.forEach(curr => { | |
| const base = baseline.find(b => | |
| b.contract === curr.contract && | |
| b.method === curr.method && | |
| b.scenario === curr.scenario | |
| ); | |
| if (base && base.cpu > 0) { | |
| const cpuChange = ((curr.cpu - base.cpu) / base.cpu * 100).toFixed(1); | |
| const memChange = ((curr.mem - base.mem) / base.mem * 100).toFixed(1); | |
| const cpuIcon = cpuChange > 10 ? '⚠️' : cpuChange < -5 ? '✨' : '✅'; | |
| const memIcon = memChange > 10 ? '⚠️' : memChange < -5 ? '✨' : '✅'; | |
| comment += `- **${curr.contract}::${curr.method}**: CPU ${cpuIcon} ${cpuChange > 0 ? '+' : ''}${cpuChange}%, Memory ${memIcon} ${memChange > 0 ? '+' : ''}${memChange}%\n`; | |
| } | |
| }); | |
| } | |
| github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: comment | |
| }); |