Skip to content

About

No description, website, or topics provided.

Resources

Contributing

Security policy

Accessibility

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

schoolfees — app

SchoolFees

The contract now has a verified synthetic testnet demonstration. The hosted browser prototype is public. Signed browser business flows remain unverified. No real pilot or production readiness is claimed.

A small web app for paying school fees on Stellar testnet. A school records a fee obligation against an opaque reference, a payer settles it in any number of installments, and anyone can read the record. The contract never holds money: payments move straight from the payer's balance to the school's.

Status: hosted testnet prototype, synthetic contract demo, no pilot or audit. The landing page opens the school or payer workspace. Wallet signing is explicit; signed browser business flows still need verification.

Part of the schoolfees project, which is three repositories: schoolfees-contracts (the Rust contract), schoolfees-app (this one) and schoolfees-docs (the book).

What the app does

Page Who What it does
Landing / workspace anyone Explains the flow; Pay a fee, Create a fee and Open workspace lead to the relevant task
Create a fee school Records a fee: token, opaque reference, total, due date
View a fee anyone Looks a fee up by id and shows everything stored, plus its status
Pay payer Pays part or all of a fee, straight to the school
School actions school Refunds a payer from the school's own balance; closes a settled fee

Contract functions are called exactly as named in src/lib.rs in schoolfees-contracts: create_fee, pay, close_fee, refund, get_fee, status. No other contract function exists in v0.

Rules the app enforces

  • A visible "TESTNET - no real money" banner on every screen, including the configuration-error screen.
  • The app refuses to operate on any network other than testnet: the network comes from .env, the passphrase is pinned in src/lib/network.ts, and the wallet's own network is re-read before every write.
  • Wallets sign; the app never asks for, receives, stores or logs a secret key or seed phrase. It only ever sees a public address and a signed XDR.
  • Every action that produces a transaction shows the transaction hash and an explorer link.
  • No analytics, trackers, injected third-party scripts or backend. The app calls the configured RPC, loads wallet modules locally and uses local picker icons. The selected wallet provider may make its own requests during connection. No browser network capture has verified all wallet traffic.
  • The reference field accepts only a 32-byte opaque value (64 hex characters) and says in plain words that names, phone numbers, emails and student or member ids must never be entered.

Getting started

Requires Node.js 24 (what CI and the maintainer's machine use).

cp .env.example .env      # then fill in the values
npm install
npm run dev

.env is git-ignored and must never be committed. Every value the app reads is documented in .env.example, and it is read in exactly one place, src/config.ts.

Checks

npm run lint        # oxlint
npm run typecheck   # tsc -b (strict)
npm test            # pure logic, hook, component and page regressions
npm run build       # tsc -b && vite build

All four run in CI (.github/workflows/web.yml).

Structure

├── src/
│   ├── config.ts          # the only place .env is read
│   ├── lib/               # pure logic + unit tests
│   │   ├── network.ts     #   config resolution, testnet-only refusal
│   │   ├── amount.ts      #   amount parsing and formatting
│   │   ├── datetime.ts    #   Unix seconds <-> local input
│   │   ├── reference.ts   #   opaque reference validation + the warning
│   │   ├── validation.ts  #   address, contract id and fee id checks
│   │   ├── contractErrors.ts  # error code -> ERRORS.md wording
│   │   ├── fee.ts         #   Fee record helpers and status rules
│   │   ├── scval.ts       #   JS <-> xdr.ScVal conversions
│   │   ├── contract.ts    #   the Soroban client (build/simulate/submit)
│   │   ├── wallet.ts      #   Stellar Wallets Kit adapter
│   │   └── explorer.ts    #   explorer links
│   ├── hooks/             # useWallet, useAction
│   ├── components/        # UI only
│   └── pages/             # one per flow
├── docs/
│   ├── decisions/         # 0001: why Vite + React, not Scaffold Stellar
│   ├── issue-drafts/      # everything not built, as drafts
│   └── contract-errors.md # vendored copy of ERRORS.md, used by the tests
└── scripts/deploy-testnet.sh   # written, never run by an agent

Errors

Error wording is never written in this repo. src/lib/contractErrors.ts maps the contract's numeric error codes to the "user-facing message" column of ERRORS.md in schoolfees-contracts, and src/lib/contractErrors.test.ts fails if a variant in the vendored copy of that table has no mapped message — in either direction. If ERRORS.md changes, re-copy docs/contract-errors.md in the same commit.

What is proven vs assumed

Read this before trusting the app with anything.

Verified locally on 2026-10-07:

  • 26 test files, 168 tests passed, including pure validation and SDK ScVal conversion, mocked page flows, automated axe checks, synchronous duplicate guards and abandoned wallet/action request regressions.
  • Lint and strict TypeScript checks passed. Current build evidence is in docs/TESTING.md. These are local working-tree checks; this update has not been pushed or verified on GitHub CI.

Assumed — never exercised:

  • A synthetic contract demo is deployed, but signed browser flows remain unverified. RPC calls in src/lib/contract.ts is unproven.
  • Signed browser business flows are unverified. Connection screenshots do not prove signing, payment or network refusal.
  • No signed browser transaction has been verified from this app, so the submit/poll path, the archived-record refusal, and the "extract the code from a host error string" assumption (Error(Contract, #N)) are untested against a real network.
  • The contract's status() return value is read defensively (symbol, single-element vec, or object) because unit-variant enums were not verified against a deployed contract; an unrecognised value shows as Unknown rather than a guess.
  • Accessibility is built in and machine-checked, not audited: semantic landmarks, labels, focus styles and aria-describedby are used throughout, and an axe-core check now runs over every rendered component and page — but axe is a floor, not an audit: no screen-reader or keyboard audit has been done, and colour contrast is verified against the design tokens rather than rendered styles.

The published CLI demonstration verifies the contract, not the browser flow. Treat the chain-facing code as unverified.

Contract deployment

A narrow maintainer-authorized synthetic testnet contract demonstration exists. A real school pilot remains separately gated; see the linked deployment record. scripts/deploy-testnet.sh refuses to run without PILOT_CONFIRMED=yes. It was written, not run: no agent should run it.

Decisions and roadmap

Notes

  • The wallet adapter now imports eight Stellar modules lazily instead of the broad default module bundle. The installed dependency tree still has 19 audit findings (13 low, 6 moderate); see docs/RESOURCES.md.
  • Amounts are whole numbers in the token's smallest unit; the app does not convert decimal places yet (draft 03).
  • Browsing the app does not keep a fee record alive. Reads are simulations (simulateTransaction), so the TTL extension the contract performs while serving a read is never persisted; only submitted transactions extend TTL. A fee nobody writes to can still archive and show the archived-record refusal. Decided 2026-10-02 as option A in draft 18.

License

MIT — see LICENSE.

October 9, 2026 presentation update

The default screen is now a public landing page. Pay a fee opens the payer task, Create a fee opens the school form, and Open workspace starts with lookup. The workspace retains all existing contract operations and wallet/network refusals; the brand button and Home task return to the landing page.

Local lint, typecheck, 168 tests and production build passed. Offline browser layout/accessibility checks passed 29 scenes; details and limits are in docs/TESTING.md. Connected design screenshots use mocked public-wallet and fee fixtures. They do not establish signing or a live browser payment. The hosted link above is the current public prototype; the new design's publication is a separate maintainer step.

About

No description, website, or topics provided.

Resources

Contributing

Security policy

Accessibility

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages