Skip to content

Commit 0e18921

Browse files
Merge branch 'main' into feat/accessibility-audit
2 parents e4250ec + 8f55b84 commit 0e18921

21 files changed

Lines changed: 2737 additions & 26 deletions

File tree

.github/workflows/ci.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,39 @@ env:
99
CARGO_TERM_COLOR: always
1010

1111
jobs:
12+
# ── Soroban ABI golden-vector drift guard ────────────────────────────────
13+
golden-vectors:
14+
name: Soroban ABI golden vectors
15+
runs-on: ubuntu-latest
16+
# Run whenever contracts or backend builder code changes
17+
if: |
18+
github.event_name == 'push' ||
19+
contains(toJson(github.event.pull_request.changed_files), 'contracts/') ||
20+
contains(toJson(github.event.pull_request.changed_files), 'backend/src/soroban/') ||
21+
contains(toJson(github.event.pull_request.changed_files), 'backend/src/tx/')
22+
defaults:
23+
run:
24+
working-directory: backend
25+
steps:
26+
- uses: actions/checkout@v4
27+
28+
- uses: actions/setup-node@v4
29+
with:
30+
node-version: 22
31+
32+
- run: npm install
33+
34+
- name: Run golden-vector encoding tests
35+
run: npx jest --testPathPattern="golden-vectors" --no-coverage
36+
37+
- name: Verify vectors are up-to-date (no uncommitted drift)
38+
run: |
39+
npx ts-node ../scripts/refresh-vectors.ts
40+
if ! git diff --exit-code backend/src/soroban/golden-vectors.json; then
41+
echo "::error::golden-vectors.json is stale. Run 'npm run refresh-vectors' locally, review the diff, and commit the updated file."
42+
exit 1
43+
fi
44+
1245
# ── Smart contract ────────────────────────────────────────────────────────
1346
contract:
1447
name: Contract (Rust / Soroban)

CONTRIBUTING.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
feat/accessibility-audit
12
# Contributing to NiffyInsur
23

34
## Accessibility Testing
@@ -79,3 +80,55 @@ When adding new interactive components:
7980
3. Multi-step wizards **must** move focus to a step heading on step change.
8081
4. Modals **must** trap focus and return it to the trigger on close (Radix Dialog handles this automatically).
8182
5. Animations **must** respect `prefers-reduced-motion` via the global CSS rule in `globals.css`.
83+
84+
# Contributing
85+
86+
## Soroban ABI golden vectors
87+
88+
The file `backend/src/soroban/golden-vectors.json` records the exact ScVal
89+
type and argument order for every critical contract invocation. CI fails if
90+
the builders produce output that no longer matches these vectors.
91+
92+
### When to refresh
93+
94+
Refresh the vectors whenever you change:
95+
96+
- Any function signature in `contracts/niffyinsure/src/lib.rs`
97+
- Argument builders in `backend/src/soroban/soroban.client.ts` or `backend/src/tx/tx.service.ts`
98+
- Enum variants in `contracts/niffyinsure/src/types.rs`
99+
100+
### How to refresh
101+
102+
```bash
103+
cd backend
104+
npm run refresh-vectors
105+
```
106+
107+
Review the diff carefully:
108+
109+
```bash
110+
git diff backend/src/soroban/golden-vectors.json
111+
```
112+
113+
- If the contract ABI changed (argument order, types, new/removed args), bump
114+
`_meta.contractSemver` in the JSON to match the new contract semver tag.
115+
- If only the builder logic changed without an ABI change, leave `contractSemver`
116+
as-is and explain in the PR description.
117+
118+
Commit the updated file and open a PR. A second engineer must review and
119+
approve any vector changes before merge.
120+
121+
### Release checklist item
122+
123+
Before tagging a release:
124+
125+
- [ ] Run `npm run refresh-vectors` and confirm the diff is empty (or intentional).
126+
- [ ] Confirm `_meta.contractSemver` matches the contract's `Cargo.toml` version.
127+
- [ ] Update `contracts/deployment-registry.json` with the new wasm hash.
128+
129+
### Security rules
130+
131+
- **Never** commit real private keys (Stellar secret keys start with `S`).
132+
- Use only placeholder G-addresses and C-addresses in vector `inputs`.
133+
- The CI job checks for secret-key patterns and will fail if any are found.
134+
main

EVENT_DICTIONARY.md

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
# niffyInsure — Event Dictionary
2+
3+
> **Schema version: 1**
4+
> Breaking changes (field removed / type changed) → semver-major contract release + `SCHEMA_VERSION` bump.
5+
> Adding new optional fields is backward-compatible; no bump required.
6+
7+
## Units
8+
9+
| Type | Unit | Notes |
10+
|------|------|-------|
11+
| Token amounts | **stroops** (i128 as string) | 1 XLM = 10 000 000 stroops (7 decimals). Never use floats. |
12+
| Time | **ledger sequence** (u32) | 1 ledger ≈ 5 s on Stellar mainnet. Multiply by 5 for wall-clock seconds. |
13+
| Boolean flags | **u32** (0 / 1) | Matches ABI encoding. `1 = true`, `0 = false`. |
14+
| Addresses | **Stellar address string** | Holder = `G…`, contract/asset = `C…`. |
15+
| Image reference | **FNV-1a u64 hash** | Hash of concatenated IPFS CIDs. Full CIDs stored off-chain. |
16+
17+
---
18+
19+
## Topic layout
20+
21+
Every event has at least two topics:
22+
23+
```
24+
topic[0] namespace "niffyins" (claim/admin events) | "niffyinsure" (policy events)
25+
topic[1] event name see table below
26+
topic[2+] identifiers claim_id, holder, asset, … (event-specific)
27+
```
28+
29+
The indexer discriminates events by `${topic[0]}:${topic[1]}`.
30+
31+
---
32+
33+
## Claim events (`namespace = "niffyins"`)
34+
35+
### `clm_filed` — claim filed
36+
37+
**Topics:** `("niffyins", "clm_filed", claim_id: u64, holder: Address)`
38+
39+
```json
40+
{
41+
"version": 1,
42+
"policy_id": 3,
43+
"amount": "5000000",
44+
"image_hash": 2864434397,
45+
"filed_at": 1234567
46+
}
47+
```
48+
49+
| Field | Type | Description |
50+
|-------|------|-------------|
51+
| `policy_id` | u32 | Per-holder policy identifier |
52+
| `amount` | string (stroops) | Requested payout |
53+
| `image_hash` | u64 | FNV-1a hash of IPFS CIDs |
54+
| `filed_at` | u32 (ledger) | Ledger when claim was filed |
55+
56+
---
57+
58+
### `vote_cast` — ballot cast
59+
60+
**Topics:** `("niffyins", "vote_cast", claim_id: u64, voter: Address)`
61+
62+
```json
63+
{
64+
"version": 1,
65+
"vote": "Approve",
66+
"approve_votes": 2,
67+
"reject_votes": 1,
68+
"at_ledger": 1234568
69+
}
70+
```
71+
72+
| Field | Type | Description |
73+
|-------|------|-------------|
74+
| `vote` | `"Approve"` \| `"Reject"` | This voter's choice |
75+
| `approve_votes` | u32 | Running approve tally after this vote |
76+
| `reject_votes` | u32 | Running reject tally after this vote |
77+
78+
---
79+
80+
### `clm_final` — claim finalized
81+
82+
Emitted when voting reaches majority **or** the vote window expires.
83+
84+
**Topics:** `("niffyins", "clm_final", claim_id: u64)`
85+
86+
```json
87+
{
88+
"version": 1,
89+
"status": "Approved",
90+
"approve_votes": 3,
91+
"reject_votes": 1,
92+
"at_ledger": 1355527
93+
}
94+
```
95+
96+
| Field | Type | Description |
97+
|-------|------|-------------|
98+
| `status` | `"Approved"` \| `"Rejected"` | Final outcome |
99+
100+
---
101+
102+
### `clm_paid` — payout executed
103+
104+
**Topics:** `("niffyins", "clm_paid", claim_id: u64)`
105+
106+
```json
107+
{
108+
"version": 1,
109+
"recipient": "G...",
110+
"amount": "5000000",
111+
"asset": "C...",
112+
"at_ledger": 1355528
113+
}
114+
```
115+
116+
| Field | Type | Description |
117+
|-------|------|-------------|
118+
| `amount` | string (stroops) | Actual payout transferred |
119+
| `asset` | string (C…) | Asset contract used for payout |
120+
121+
---
122+
123+
## Policy lifecycle events (`namespace = "niffyinsure"`)
124+
125+
### `PolicyInitiated` — policy bound
126+
127+
**Topics:** `("niffyinsure", "PolicyInitiated", holder: Address)`
128+
129+
```json
130+
{
131+
"version": 1,
132+
"policy_id": 1,
133+
"premium": "500000",
134+
"asset": "C...",
135+
"policy_type": "Auto",
136+
"region": "Medium",
137+
"coverage": "50000000",
138+
"start_ledger": 1234567,
139+
"end_ledger": 2285767
140+
}
141+
```
142+
143+
| Field | Type | Description |
144+
|-------|------|-------------|
145+
| `policy_id` | u32 | Per-holder identifier (not globally unique; use `holder + policy_id`) |
146+
| `premium` | string (stroops) | Premium paid at bind time |
147+
| `policy_type` | `"Auto"` \| `"Health"` \| `"Property"` | Coverage category |
148+
| `region` | `"Low"` \| `"Medium"` \| `"High"` | Geographic risk tier |
149+
| `coverage` | string (stroops) | Maximum payout |
150+
| `end_ledger` | u32 (ledger) | Expiry ledger |
151+
152+
---
153+
154+
### `PolicyRenewed` — policy renewed
155+
156+
**Topics:** `("niffyinsure", "PolicyRenewed", holder: Address)`
157+
158+
```json
159+
{
160+
"version": 1,
161+
"policy_id": 1,
162+
"premium": "500000",
163+
"new_end_ledger": 3336967
164+
}
165+
```
166+
167+
---
168+
169+
### `PolicyTerminated` — policy terminated
170+
171+
**Topics:** `("niffyinsure", "policy_terminated", holder: Address, policy_id: u32)`
172+
173+
```json
174+
{
175+
"reason_code": 1,
176+
"terminated_by_admin": 0,
177+
"open_claim_bypass": 0,
178+
"open_claims": 0,
179+
"at_ledger": 1234600
180+
}
181+
```
182+
183+
| `reason_code` | Meaning |
184+
|---------------|---------|
185+
| 1 | VoluntaryCancellation |
186+
| 2 | LapsedNonPayment |
187+
| 3 | UnderwritingVoid |
188+
| 4 | FraudOrMisrepresentation |
189+
| 5 | RegulatoryAction |
190+
| 6 | AdminOverride |
191+
192+
---
193+
194+
## Admin / config events (`namespace = "niffyins"`)
195+
196+
| Event | Topics | Key payload fields |
197+
|-------|--------|--------------------|
198+
| `tbl_upd` | `(NS, "tbl_upd")` | `table_version: u32` |
199+
| `asset_set` | `(NS, "asset_set", asset)` | `allowed: 0\|1` |
200+
| `adm_prop` | `(NS, "adm_prop", old_admin, new_admin)` | `version` only |
201+
| `adm_acc` | `(NS, "adm_acc", old_admin, new_admin)` | `version` only |
202+
| `adm_can` | `(NS, "adm_can", admin, cancelled_pending)` | `version` only |
203+
| `adm_tok` | `(NS, "adm_tok")` | `old_token`, `new_token` |
204+
| `adm_paus` | `(NS, "adm_paus", admin)` | `paused: 0\|1` |
205+
| `adm_drn` | `(NS, "adm_drn", admin)` | `recipient`, `amount` (stroops) |
206+
207+
---
208+
209+
## Versioning & migration
210+
211+
1. `SCHEMA_VERSION` in `events.rs` and `events.schema.ts` must stay in sync.
212+
2. A version bump is **required** when any field is removed or its type changes.
213+
3. The `EVENT_PARSERS` table in `events.schema.ts` maps `version → parser`; add a new entry for each bump and keep old entries for historical replay.
214+
4. CI regression tests in `events.test.ts` will fail on shape changes — this is intentional.
215+
216+
---
217+
218+
## What is NOT in events
219+
220+
- Raw IPFS URLs (use `image_hash` to look up off-chain).
221+
- Claim description text.
222+
- Voter lists (derive from `vote_cast` stream).
223+
- PII of any kind.

backend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"test:cov": "jest --coverage",
1717
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
1818
"test:e2e": "jest --config ./test/jest-e2e.json",
19+
"refresh-vectors": "ts-node ../scripts/refresh-vectors.ts",
1920
"docker:build": "docker build -t niffyinsure-backend .",
2021
"docker:up": "docker compose up -d",
2122
"docker:down": "docker compose down",

0 commit comments

Comments
 (0)