Skip to content

Commit 66ae1fa

Browse files
changes made
1 parent b165734 commit 66ae1fa

2 files changed

Lines changed: 34 additions & 116 deletions

File tree

context/progress-tracker.md

Lines changed: 1 addition & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ pure chore/docs commits). Direct pushes to main must also be logged here.
66

77
---
88

9-
<<<<<<< Updated upstream
109
## 2026-08-24
1110

1211
- **Session families + refresh-token replay detection** (`sessions.family_id`
@@ -27,15 +26,14 @@ pure chore/docs commits). Direct pushes to main must also be logged here.
2726
- Tests: refresh-family rotation, replay → family-wide revocation + audit
2827
event, blocked-user denial within TTL bound, cache expiry re-query,
2928
cleanup job deletes-only-expired.
30-
=======
29+
3130
## 2026-08-26
3231

3332
- Fixed registration race conditions in `AuthService.register()` by eliminating application-side pre-checks (`findByWallet`, `checkUsernameExists`) and relying directly on DB-level UNIQUE constraints (`users.wallet_address`, `users.username`).
3433
- Added idempotent migration `20260826130000_ensure_users_unique_constraints.sql` to ensure unique indexes exist on `users.wallet_address` and `users.username`.
3534
- Updated `UsersRepository.createProfile()` to catch PostgreSQL unique constraint violation error `23505` and map to structured 409 `ConflictException` (`AUTH_WALLET_EXISTS`, `AUTH_USERNAME_TAKEN`).
3635
- Added cleanup handlers (`deleteAvatar`, `deleteUserById`) in `AuthService.register()` and `UsersRepository` to ensure failed registrations do not leave orphaned avatar files or partial user records.
3736
- Added unit tests covering DB unique constraint error mapping, parallel race conditions for duplicate wallet and username registrations, sequential re-registration compatibility, and avatar/user cleanup on failure.
38-
>>>>>>> Stashed changes
3937

4038
## 2026-07-23
4139

@@ -127,120 +125,7 @@ pure chore/docs commits). Direct pushes to main must also be logged here.
127125

128126
---
129127

130-
<<<<<<< Updated upstream
131128
> Note (2026-07-16): this file previously contained StepFi-Contracts
132129
> content copied from the wrong repo. Replaced with real StepFi-API
133130
> history backfilled from `git log`. Entries older than 2026-06-18 are
134131
> in git history but were never tracked here.
135-
=======
136-
## Completed
137-
138-
### Workspace Cleanup
139-
- Removed dead code: `lp-contract` (superseded by `liquidity-pool-contract`)
140-
- Removed empty placeholder: `adapter-trustless-contract`
141-
- Updated `Cargo.toml` workspace members to reflect 5 active contracts
142-
- Removed `[profile]` sections from individual contract `Cargo.toml` files (profiles belong in workspace root only)
143-
144-
### Renaming
145-
- Renamed `merchant-registry-contract``vendor-registry-contract`
146-
- Updated all Rust source references: `merchant_registry_contract``vendor_registry_contract`
147-
- Updated all struct names: `MerchantRegistry*``VendorRegistry*`
148-
- Updated `Cargo.toml` dependency paths in `creditline-contract`
149-
150-
### Critical Fixes
151-
- Added TTL constants (`PERSISTENT_TTL_THRESHOLD`, `PERSISTENT_TTL_EXTEND_TO`) to `creditline-contract/src/storage.rs`
152-
- Added `upgrade()` function to all 5 contracts: reputation, creditline, liquidity-pool, vendor-registry, parameters
153-
- All 5 contracts build cleanly: `cargo build` passes with zero errors (3 minor unused constant warnings — acceptable)
154-
155-
### Deployment
156-
- Created `scripts/deploy-testnet.sh` — full deployment script covering all 5 contracts in correct dependency order
157-
- Script outputs contract IDs and saves to `.env.contracts`
158-
- StepFi-API deployed on Render ✅
159-
- Supabase project created, 24 migrations applied ✅
160-
- Upstash Redis connected ✅
161-
- Swagger docs live ✅
162-
163-
### Documentation
164-
- `README.md` fully rewritten as StepFi-Contracts
165-
166-
### CI Pipeline
167-
- Created `.github/workflows/ci.yml` — runs on push/PR to `main`
168-
- Steps: checkout → setup Node 20 → `npm ci``npm run build``npm test`
169-
- `node_modules` cached via `actions/cache@v4` keyed on `package-lock.json` hash
170-
- CI status badge added to `README.md` pointing at the workflow
171-
172-
### Vendor Approval Lifecycle
173-
- Created database migration `20260817000001_add_vendor_status.sql` adding `status` column constrained to `pending`, `approved`, `suspended`, `rejected`, defaulting to `pending` and backfilling existing rows.
174-
- Added `buildApproveVendorXdr` and `buildSuspendVendorXdr` methods to `VendorRegistryContractClient` and `IVendorRegistryClient` to construct unsigned Soroban transaction XDRs.
175-
- Created `AdminGuard` to enforce allowlisted wallet access via `ADMIN_WALLETS` (401 for unauthenticated, 403 for non-admin).
176-
- Created `AuditAction` decorator and `AuditInterceptor` for audit-logging privileged admin operations.
177-
- Added `POST /vendors/:id/approve` and `POST /vendors/:id/suspend` endpoints returning unsigned XDRs, guarded with `JwtAuthGuard` and `AdminGuard`, decorated with full Swagger annotations and returning HTTP 409 Conflict for invalid vendor status transitions (`VENDOR_NOT_PENDING`, `VENDOR_NOT_APPROVED`).
178-
- Integrated status updates into `TransactionStatusCheckerProcessor` to update local Supabase `vendors` status only after on-chain transaction confirmation.
179-
### Learner Profile Auto-Creation
180-
- Added automatic creation of `learner_profiles` records upon first sign-in in `AuthService.findOrCreateUser()`, ensuring `GET /learners/me` resolves immediately after authentication.
181-
- Updated `auth.service.spec.ts` unit tests to cover table query and insertion handling for `learner_profiles`.
182-
183-
184-
---
185-
186-
## In Progress
187-
188-
- None currently.
189-
190-
---
191-
192-
## Next Up (In Order)
193-
194-
1. **LoanType enum** — Add `LoanType::LearnerInstallment` variant to `creditline-contract/src/types.rs`
195-
2. **Per-installment tracking** — Add `paid: bool` and `paid_at: u64` fields to `RepaymentInstallment` struct
196-
3. **repay_installment()** — New function targeting a specific installment by index (instead of just reducing remaining balance)
197-
4. **Learner grace period** — Make `grace_period_seconds` per-loan (not just global via parameters)
198-
5. **Vouching contract** — New `vouching-contract` crate: `vouch()`, `revoke_vouch()`, `get_vouches()`, `get_vouch_count()`
199-
6. **Reputation rules** — Update `creditline-contract` to call different reputation adjustments for `LoanType::LearnerInstallment`
200-
7. **Testnet deployment** — Deploy all contracts, capture IDs, add to StepFi-API `.env`
201-
8. **End-to-end validation** — Verify loan lifecycle on testnet via Stellar CLI
202-
203-
---
204-
205-
## Open Questions
206-
207-
- What token is used for loans — native XLM or a USDC anchor? (Affects token contract address in `initialize()`)
208-
- Should the vouching contract be a standalone crate or logic added to `creditline-contract`? (Leaning toward standalone for modularity)
209-
- What is the correct `grace_period_seconds` for learner installment loans? (Longer than standard BNPL — possibly 7-14 days per installment)
210-
- Should sponsor pool deposits go through `liquidity-pool-contract` or a new `sponsor-pool-contract`?
211-
212-
---
213-
214-
## Architecture Decisions
215-
216-
- **5 contracts, not 6**`lp-contract` was dead code, removed. `liquidity-pool-contract` is the canonical LP implementation.
217-
- **Vendor over Merchant** — Renamed to reflect StepFi's learning-focused domain.
218-
- **TTL approach** — Using 60-day threshold / 120-day extension constants. Off-chain indexer is responsible for bumping TTL on active loan entries.
219-
- **Upgrade pattern** — All contracts have `upgrade()` gated by admin `require_auth()`. Admin address is set at `initialize()` and transferable via `set_admin()`.
220-
- **Loan sharding** — 32 shards (`loan_id % 32`) in creditline-contract to distribute persistent storage keys and avoid hot-key contention.
221-
- **Reentrancy** — Boolean `LOCKED` flag in instance storage. Cheaper than mutex, sufficient for Soroban's single-threaded execution model.
222-
223-
---
224-
225-
## Contract Deployment Status
226-
227-
| Contract | Testnet Deployed | Contract ID | Last Deployed |
228-
|---|---|---|---|
229-
| `reputation-contract` | ❌ No |||
230-
| `parameters-contract` | ❌ No |||
231-
| `vendor-registry-contract` | ❌ No |||
232-
| `liquidity-pool-contract` | ❌ No |||
233-
| `creditline-contract` | ❌ No |||
234-
235-
> Update this table after running `scripts/deploy-testnet.sh`
236-
237-
---
238-
239-
## Session Notes
240-
241-
- Always run `cargo build` after any contract change before committing.
242-
- Always run `cargo test` before marking any contract feature complete.
243-
- Never modify storage key structures of a contract that has been deployed — it breaks existing data. Use a migration pattern or deploy a new contract.
244-
- The `creditline-contract` depends on all other contracts — it must be initialized last.
245-
- Do not add new workspace members to `Cargo.toml` without creating the full contract file structure first.
246-
>>>>>>> Stashed changes
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,37 @@
11
-- Ensure DB-level UNIQUE indexes exist on users.wallet_address and users.username
22

3+
-- Keep the oldest row in each duplicate group before adding the constraints.
4+
WITH duplicate_wallets AS (
5+
SELECT id,
6+
ROW_NUMBER() OVER (
7+
PARTITION BY wallet_address
8+
ORDER BY created_at ASC, id ASC
9+
) AS row_number
10+
FROM public.users
11+
WHERE wallet_address IS NOT NULL
12+
), rows_to_delete AS (
13+
SELECT id
14+
FROM duplicate_wallets
15+
WHERE row_number > 1
16+
)
17+
DELETE FROM public.users
18+
WHERE id IN (SELECT id FROM rows_to_delete);
19+
20+
WITH duplicate_usernames AS (
21+
SELECT id,
22+
ROW_NUMBER() OVER (
23+
PARTITION BY username
24+
ORDER BY created_at ASC, id ASC
25+
) AS row_number
26+
FROM public.users
27+
WHERE username IS NOT NULL
28+
), rows_to_delete AS (
29+
SELECT id
30+
FROM duplicate_usernames
31+
WHERE row_number > 1
32+
)
33+
DELETE FROM public.users
34+
WHERE id IN (SELECT id FROM rows_to_delete);
35+
336
CREATE UNIQUE INDEX IF NOT EXISTS users_wallet_address_idx ON public.users (wallet_address);
437
CREATE UNIQUE INDEX IF NOT EXISTS users_username_idx ON public.users (username);

0 commit comments

Comments
 (0)