Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 0 additions & 30 deletions .github/workflows/soroban-contract.yml

This file was deleted.

109 changes: 109 additions & 0 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
name: Verify SAVE

on:
pull_request:
push:
branches: [main]
workflow_dispatch:
schedule:
- cron: '17 3 * * *'

permissions:
contents: read

jobs:
environment:
name: Environment examples
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- run: npm run env:validate -- --examples --skip-tools

mobile:
name: Mobile lint and types
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- run: npm ci --prefix backend
- run: npm run lint
- run: npm run typecheck
- run: npm run backend:lint

backend:
name: Backend build and tests
runs-on: ubuntu-latest
defaults:
run:
working-directory: backend
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: npm
cache-dependency-path: backend/package-lock.json
- run: npm ci
- run: npm test

admin:
name: Admin lint and build
runs-on: ubuntu-latest
defaults:
run:
working-directory: admin
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: npm
cache-dependency-path: admin/package-lock.json
- run: npm ci
- run: npm run lint
- run: npm run build

contract:
name: Soroban checks and Wasm
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32v1-none
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
with:
workspaces: contract
- run: cargo fmt --manifest-path contract/Cargo.toml --all -- --check
- run: cargo test --manifest-path contract/Cargo.toml --locked
- run: cargo clippy --manifest-path contract/Cargo.toml --all-targets --locked -- -D warnings
- run: cargo build --manifest-path contract/Cargo.toml --package save-savings-vault --target wasm32v1-none --release --locked
- uses: actions/upload-artifact@v4
with:
name: save-savings-vault-wasm
path: contract/target/wasm32v1-none/release/save_savings_vault.wasm
if-no-files-found: error

testnet-health:
name: Deployed Testnet health
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- uses: stellar/stellar-cli@v27.0.0
- run: npm run health:testnet
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ yarn-error.*
# local env files
.env*.local
*.env
!**/.env.example
/backend/.stellar_integrity_signer_secret

# typescript
Expand Down
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22.13.1
70 changes: 53 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,50 @@

# SAVE Finance

[![Verify SAVE](https://github.com/FWDP/save-project/actions/workflows/verify.yml/badge.svg)](https://github.com/FWDP/save-project/actions/workflows/verify.yml)

### Personal finance and goal-based saving, with optional Stellar rails

Track spending. Plan budgets. Build savings on Stellar Testnet without giving up custody of your wallet.

SAVE brings everyday money management, receipt capture, savings goals, and externally signed Stellar transactions into one mobile-first workspace. A NestJS API supports the app, while a Next.js dashboard provides an operational view.

[Explore the repository](https://github.com/FWDP/save-project) · [Stellar architecture](docs/STELLAR_ARCHITECTURE.md) · [Savings vault contract](docs/CONTRACT.md) · [Test evidence](docs/DELIVERABLES_1_2_TEST_REPORT.md)
[Explore the repository](https://github.com/FWDP/save-project) · [Reviewer runbook](docs/REVIEWER_RUNBOOK.md) · [Stellar architecture](docs/STELLAR_ARCHITECTURE.md) · [Verification evidence](docs/DELIVERABLE_4_EVIDENCE.md)

> [!IMPORTANT]
> SAVE's blockchain features are alpha software for **Stellar Testnet only**. The project does not support Mainnet or real-value custody. SAVE never asks for or stores a wallet secret seed; an external wallet must approve every transaction.

---

## 📱 SAVE on Mobile

A tour of SAVE's mobile experience, from everyday money management to non-custodial Stellar Testnet savings. Screenshots show demo financial data from an Android development build.

<table>
<tr>
<th>Financial dashboard</th>
<th>Expense tracking</th>
</tr>
<tr>
<td><img src="screenshots/01-dashboard.png" alt="SAVE financial dashboard with income, expenses, balance, and monthly spending insights" width="360" /></td>
<td><img src="screenshots/02-expenses.png" alt="SAVE expense list with search, date, and category filters" width="360" /></td>
</tr>
<tr>
<th>Budget progress</th>
<th>Savings goals</th>
</tr>
<tr>
<td><img src="screenshots/03-budgets.png" alt="SAVE category budgets with spending progress" width="360" /></td>
<td><img src="screenshots/04-savings.png" alt="SAVE savings goals with Stellar Testnet vault access" width="360" /></td>
</tr>
<tr>
<th colspan="2">Non-custodial Stellar savings</th>
</tr>
<tr>
<td colspan="2" align="center"><img src="screenshots/05-stellar.png" alt="SAVE Stellar Testnet savings screen with a connected Freighter Mobile signer" width="360" /></td>
</tr>
</table>

## 🧩 Why SAVE

Personal finances are often split across expense trackers, spreadsheets, receipt folders, bank apps, and crypto wallets. That fragmentation makes it difficult to connect daily spending decisions with longer-term savings goals.
Expand Down Expand Up @@ -93,7 +124,7 @@ The browser or mobile client cannot declare a transaction successful. SAVE recon

| Contract | Address | Explorer |
| --- | --- | --- |
| SAVE Savings Vault | `CALFEOYNTNJYB5HTUPYHHFHMNNYLYEBOCV43Z73J54G3CQNSH5VCNP7H` | [View on Stellar Expert](https://stellar.expert/explorer/testnet/contract/CALFEOYNTNJYB5HTUPYHHFHMNNYLYEBOCV43Z73J54G3CQNSH5VCNP7H) |
| SAVE Savings Vault | `CDYPVKFWSPHKGDHZ77M2T2TZPCS3LVXDFJDH5PERL5HTUNVFYSTB7AG3` | [View on Stellar Expert](https://stellar.expert/explorer/testnet/contract/CDYPVKFWSPHKGDHZ77M2T2TZPCS3LVXDFJDH5PERL5HTUNVFYSTB7AG3) |
| Native XLM SAC | `CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC` | [View on Stellar Expert](https://stellar.expert/explorer/testnet/contract/CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC) |

These are public Testnet identifiers, not credentials. Deployments may be replaced as SAVE evolves; update the backend environment and contract documentation together after any redeployment.
Expand All @@ -113,10 +144,10 @@ These are public Testnet identifiers, not credentials. Deployments may be replac

### Prerequisites

- Node.js LTS and npm.
- Node.js 22.13+ and npm 10+ (`.nvmrc` pins the reviewer version).
- Docker with Docker Compose.
- Android Studio or Xcode for a native development build.
- Rust and the Stellar CLI for contract development.
- Rust/Cargo stable 1.90+ and Stellar CLI 27.x for contract development.
- A funded Stellar Testnet account and Freighter Mobile for the complete signing flow.
- A public WalletConnect project ID.

Expand All @@ -125,22 +156,29 @@ These are public Testnet identifiers, not credentials. Deployments may be replac
From the repository root:

```bash
npm install
npm --prefix backend install
npm --prefix admin install
npm ci
npm --prefix backend ci
npm --prefix admin ci
```

### 2. Configure the environments

```bash
cp .env.example .env
cp backend/.env.example backend/.env
cp admin/.env.example admin/.env.local
```

Set `EXPO_PUBLIC_API_URL` to a backend URL reachable by the phone, such as `http://192.168.1.25:3000`. Add your public `EXPO_PUBLIC_WALLETCONNECT_PROJECT_ID`; values prefixed with `EXPO_PUBLIC_` are bundled into the client and must never contain secrets.

Review the local MongoDB, Redis, MinIO, JWT, Stellar Testnet, callback, and contract settings in `backend/.env` before starting the API. The admin app can use `admin/.env.local` for its own API configuration.

Validate the configuration and installed tool versions before continuing:

```bash
npm run env:validate
```

### 3. Start local services

```bash
Expand Down Expand Up @@ -181,29 +219,25 @@ In the app, open **More → Stellar Testnet → Connect Freighter Mobile**. Use

## 🧪 Test and Validate

Run the application checks from the repository root:
Run the complete local verification gate from the repository root:

```bash
npm run lint
npx tsc --noEmit
npm --prefix backend test
npm run admin:build
npm run verify
```

Run the Soroban contract suite and build its Wasm artifact:
Verify the live Testnet services, deployed contract, and exact Wasm hash:

```bash
npm run contract:test
npm run contract:build
npm run health:testnet
```

The contract artifact is written to `contract/target/wasm32v1-none/release/save_savings_vault.wasm`. See the [test report](docs/DELIVERABLES_1_2_TEST_REPORT.md) for repeatable runtime checks and existing Testnet evidence.
After starting the API and admin app, add `--services` to verify their connectivity. The contract artifact is written to `contract/target/wasm32v1-none/release/save_savings_vault.wasm`. See the [reviewer runbook](docs/REVIEWER_RUNBOOK.md) for the clean-checkout process and the [Deliverable 4 evidence package](docs/DELIVERABLE_4_EVIDENCE.md) for expected outputs.

## 🚢 Deploy the Savings Vault

Contract deployment is an explicit operator action. Use a funded Stellar CLI identity—never put its secret key in a command, environment file, application, or repository.

Read the [contract deployment guide](docs/CONTRACT.md) before deploying. It documents the build artifact, current Wasm hash, deployment command, configuration update, and pre-Mainnet security requirements.
Read the [contract deployment guide](docs/CONTRACT.md) before deploying. It documents the build artifact, immutable Native XLM SAC constructor allowlist, current Wasm hash, deployment command, and configuration update.

> [!CAUTION]
> A working Testnet deployment is not evidence of Mainnet readiness. Independent review, stronger invariant testing, recovery procedures, key custody, asset allowlists, operational controls, and legal review are required before real-value use.
Expand All @@ -230,6 +264,8 @@ Read the [contract deployment guide](docs/CONTRACT.md) before deploying. It docu
- [Stellar architecture](docs/STELLAR_ARCHITECTURE.md) — network decisions, trust boundaries, reconciliation, and non-goals.
- [Savings vault contract](docs/CONTRACT.md) — interface, events, deployment, and security review.
- [Deliverables 1–2 test report](docs/DELIVERABLES_1_2_TEST_REPORT.md) — acceptance evidence and repeatable verification.
- [Deliverable 4 evidence](docs/DELIVERABLE_4_EVIDENCE.md) — CI matrix, deployed contract proof, health checks, and negative-path coverage.
- [Independent reviewer runbook](docs/REVIEWER_RUNBOOK.md) — clean-checkout setup and end-to-end verification.
- [Combined Stellar implementation](docs/COMBINED_SOROBAN_STELLAR_IMPLEMENTATION.md) — implementation context across the app, API, and contract.

## ⚠️ Alpha Boundaries
Expand Down
4 changes: 4 additions & 0 deletions admin/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Server-side backend URL used by the admin health endpoint.
SAVE_API_URL=http://localhost:3000
# URL where the reviewer runs the admin app.
SAVE_ADMIN_URL=http://localhost:3001
1 change: 1 addition & 0 deletions admin/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ yarn-error.log*

# env files (can opt-in for committing if needed)
.env*
!.env.example

# vercel
.vercel
Expand Down
6 changes: 5 additions & 1 deletion admin/next.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
/* config options here */
experimental: {
// The JavaScript compiler API keeps clean-checkout verification reliable
// in restricted runners while preserving full TypeScript build checks.
useTypeScriptCli: false,
},
};

export default nextConfig;
30 changes: 30 additions & 0 deletions admin/src/app/api/health/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const BACKEND_URL = (process.env.SAVE_API_URL ?? 'http://localhost:3000').replace(/\/+$/, '');

export async function GET() {
try {
const response = await fetch(`${BACKEND_URL}/health`, {
cache: 'no-store',
signal: AbortSignal.timeout(10_000),
});
const backend = await response.json();
return Response.json(
{
status: response.ok ? 'ok' : 'degraded',
service: 'save-admin',
backendUrl: BACKEND_URL,
backend,
},
{ status: response.ok ? 200 : 503 },
);
} catch (error) {
return Response.json(
{
status: 'degraded',
service: 'save-admin',
backendUrl: BACKEND_URL,
error: error instanceof Error ? error.message : 'backend health request failed',
},
{ status: 503 },
);
}
}
4 changes: 3 additions & 1 deletion backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ STELLAR_NETWORK_PASSPHRASE=Test SDF Network ; September 2015
STELLAR_HORIZON_URL=https://horizon-testnet.stellar.org
STELLAR_RPC_URL=https://soroban-testnet.stellar.org
STELLAR_FRIENDBOT_URL=https://friendbot.stellar.org
STELLAR_VAULT_CONTRACT_ID=CALFEOYNTNJYB5HTUPYHHFHMNNYLYEBOCV43Z73J54G3CQNSH5VCNP7H
STELLAR_VAULT_CONTRACT_ID=CDYPVKFWSPHKGDHZ77M2T2TZPCS3LVXDFJDH5PERL5HTUNVFYSTB7AG3
STELLAR_XLM_SAC_ID=CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC
# SHA-256 of the Wasm installed for STELLAR_VAULT_CONTRACT_ID.
STELLAR_VAULT_WASM_HASH=0977e310e0f296e8811774ee74800367053879f809cc6ebbb49dca0816b8587f
# Maximum assembled Soroban fee in stroops (1000000000 = 100 XLM).
STELLAR_MAX_SOROBAN_FEE=1000000000
STELLAR_EVENT_POLL_MS=15000
Expand Down
3 changes: 2 additions & 1 deletion backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { TransactionsModule } from './transactions/transactions.module';
import { UsersModule } from './users/users.module';
import { SavingsModule } from './savings/savings.module';
import { StellarModule } from './stellar/stellar.module';
import { HealthController } from './health.controller';

@Module({
imports: [
Expand All @@ -33,7 +34,7 @@ import { StellarModule } from './stellar/stellar.module';
SavingsModule,
StellarModule,
],
controllers: [],
controllers: [HealthController],
providers: [],
})
export class AppModule {}
Loading
Loading