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
62 changes: 62 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Contributing to Neurowealth Backend

Thank you for contributing! Please follow these guidelines to help us maintain quality and consistency.

## Local Setup

1. Fork the repository and create a branch from `main`
2. Run `npm install` to install dependencies
3. Copy `.env.example` to `.env` and configure your local environment
4. Start the database: `docker-compose up -d`
5. Run migrations: `npx prisma migrate deploy`
6. Start development server: `npm run dev`

## Development Commands

| Command | Description |
|---------|-------------|
| `npm test` | Run all tests |
| `npm run test:unit` | Run unit tests only |
| `npm run lint` | Run ESLint |
| `npm run typecheck` | Run TypeScript type check |
| `npm run format` | Format code with Prettier |
| `npm run format:check` | Check formatting |

## PR Conventions

### Branch Naming
- Use descriptive branch names: `fix/scopes-erasure-travelrule`
- Prefix with the issue type: `fix/`, `feat/`, `docs/`

### Commit Messages
- Use clear, descriptive commit messages
- Reference issues: `closes #390`
- Keep messages concise but informative

### Pull Request Description
- Include a summary of changes
- Reference closed issues using `closes #ISSUE_NUMBER`
- Include steps to verify the changes

### Git Hooks
- Husky hooks are configured for lint and commit message validation
- See `.husky/` directory for hook details
- Pre-commit: runs lint-staged
- Commit-msg: validates commit message format

## How Issues Map to PRs

1. Issue is created in the tracker
2. Developer creates a branch from `main`
3. Developer implements the fix/feature
4. Developer writes or updates tests
5. Developer ensures lint and typecheck pass
6. PR is submitted with issue reference
7. Maintainers review and merge

## Code Style

- Follow the existing code patterns in the repository
- Run `npm run lint` before submitting PR
- Run `npm run typecheck` to ensure type safety
- Format code with `npm run format`
95 changes: 29 additions & 66 deletions PR_DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -1,76 +1,39 @@
# Path-Payment DEX Auto-Routing, Claimable-Balance Ingestion, Split-Custody Treasury, and Liquidity Risk Estimation
## PR Description

## Summary
This PR resolves four issues:

This PR implements minimal infrastructure for four major features:
closes #390
closes #395
closes #394
closes #391

1. **#338 - Path-Payment DEX Auto-Routing**: Foundation for atomic asset conversion at deposit/withdrawal time with explicit slippage bounds
2. **#340 - Claimable-Balance & Unmatched-Inbound Ingestion**: Infrastructure for detecting and claiming claimable balances and reconciling direct inbound payments
3. **#341 - Split-Custody Treasury**: Hot/warm/cold account tiering with automated sweeps and multi-signature support
4. **#350 - Liquidity Risk & Time-to-Exit Estimation**: Liquidity metrics per position including exitable amounts and time-to-full-exit estimates
### Summary

## Changes Made
This PR addresses four issues in the Neurowealth Backend:

### #338 - Path-Payment DEX Auto-Routing
- Added `AssetConversion` model for routing audit trail
- Added `src/stellar/routing.ts` with:
- `findStrictSendPath()` and `findStrictReceivePath()` for path finding
- `RoutedQuote` type with slippage protection and quote TTL
- `buildPathPaymentOp()` for operation construction
- Quote validation and slippage clamping utilities
- Added migration for `asset_conversions` table
1. **#390 - API key scopes enforcement**: Added `requireScope` guards to all write endpoints that correspond to their respective `USER_SCOPES` entries. Previously, only `withdraw.ts` and `keys.ts` had scope enforcement, while `deposit.ts`, `goals.ts`, `recurring-deposits.ts`, `strategies.ts`, `webhooks.ts`, `vault.ts`, `alerts.ts`, and `fiat.ts` only called `requireAuth` without scope checks. A read-only-scoped API key can now be properly rejected (403) from write endpoints.

### #340 - Claimable-Balance & Unmatched-Inbound Ingestion
- Added `INBOUND_TRANSFER` and `CLAIMABLE_BALANCE_CLAIM` transaction types
- Added `InboundOperation` model for idempotency on `(txHash, operationIndex)`
- Added `InboundCursor` model for per-account ledger tracking
- Added `src/stellar/claimableBalances.ts` with:
- `pollClaimableBalances()` for claimable balance detection
- `evaluatePredicate()` for local predicate evaluation
- `reconcileInboundOperations()` for unmatched payment detection
- Added migration for inbound operations and cursor tables
2. **#395 - Root documentation**: Added `README.md` and `CONTRIBUTING.md` at the repository root. README provides project overview, quickstart, and documentation links. CONTRIBUTING documents local setup, test/lint/typecheck commands, PR conventions, and how issues map to PRs.

### #341 - Split-Custody Treasury
- Added `TREASURY_SWEEP` to `OutboxOpKind` enum
- Added `TreasuryTier` enum (HOT, WARM, COLD)
- Added `TreasuryAccount` model with tiered balance bands
- Added `TreasurySweep` model for sweep operation tracking
- Added `MultisigEnvelope` model for signature collection
- Added `src/stellar/multisig.ts` with:
- `buildMultisigEnvelope()` for envelope creation
- `addSignature()` for signature collection
- `assembleTransaction()` for transaction assembly
- Added `src/jobs/treasurySweep.ts` with:
- `evaluateTreasuryBalances()` for balance evaluation
- `executeSweep()` for sweep execution
- `validateHysteresis()` for band validation
- Added migration for treasury account tables
3. **#394 - GDPR/CCPA right-to-erasure**: Added an erasure job (`src/jobs/erasureJob.ts`) that walks the `erasurePolicies` map and applies DELETE/ANONYMIZE per model while leaving IMMUTABLE tables (AuditBlock, OutboxOp) untouched. Added admin endpoint `POST /api/admin/erasure` with dry-run mode. Added unit tests covering each policy type and immutable-table exclusion.

### #350 - Liquidity Risk & Time-to-Exit Estimation
- Added `ProtocolLiquiditySnapshot` model for pool depth and TVL tracking
- Added `src/analytics/liquidity.ts` with pure core functions:
- `maxExitWithinSlippage()` for exitable amount calculation
- `timeToFullExit()` for exit duration estimation
- `liquidityScore()` for 0-100 liquidity scoring
- Added configuration constants for slippage targets and snapshot TTL
- Added migration for protocol liquidity snapshots table
4. **#391 - Travel Rule records**: Updated `detectTravelRule` to look up the user's data from the database and populate `originator` and `beneficiary` fields with VASP + customer information. Transitions status to `READY` when data is available, or `PENDING_DATA` when missing. Added unit coverage for both populated and missing-data paths.

## Test Plan
### Changes by file

- All existing tests pass (1403 tests)
- Prisma client regenerated successfully
- Migrations include rollback scripts
- Code follows existing patterns and linting rules

## Breaking Changes

None - these are additive changes that extend the existing schema and functionality.

## Notes

This is a minimal implementation focused on infrastructure and data models. Full integration with existing systems (outbox dispatcher, event listener, API endpoints, etc.) would be addressed in follow-up issues. The implementations provide the foundational types, database schema, and core utilities needed for each feature.

Closes #338
Closes #340
Closes #341
Closes #350
- `src/routes/deposit.ts` - added `requireScope('deposit:write')`
- `src/routes/goals.ts` - added `requireScope('goals:write')` on POST/PATCH/DELETE
- `src/routes/recurring-deposits.ts` - added `requireScope('recurring_deposits:write')` on POST/PATCH/DELETE
- `src/routes/strategies.ts` - added `requireScope('strategies:write')` on publish
- `src/routes/webhooks.ts` - added `requireScope('webhooks:manage')` on POST/PATCH/DELETE
- `src/routes/vault.ts` - added `requireScope('vault:write')` on build-transaction
- `src/routes/alerts.ts` - added `requireScope('alerts:manage')` on POST/PATCH/DELETE
- `src/routes/fiat.ts` - added `requireScope('fiat:write')` on create order
- `src/jobs/erasureJob.ts` - new file with erasure job and policies
- `src/middleware/adminAuth.ts` - added `erasure:write` and `erasure:read` scopes
- `src/routes/admin.ts` - added `POST /api/admin/erasure` endpoint
- `src/compliance/travelRule.ts` - populate originator/beneficiary from user data
- `tests/unit/middleware/apiKeyAuth.test.ts` - added scope denial tests for all write scopes
- `tests/unit/compliance/travelRule.test.ts` - new file with travel rule tests
- `README.md` - new file with project overview and quickstart
- `CONTRIBRIBUTING.md` - new file with contributing guidelines
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Neurowealth Backend

API backend for Neurowealth - a financial planning and investment platform.

## Quickstart

1. Copy `.env.example` to `.env` and fill in the required values.
2. Start the database via Docker Compose: `docker-compose up -d`
3. Run migrations: `npx prisma migrate deploy`
4. Start the development server: `npm run dev`

## Available Scripts

| Script | Description |
|--------|-------------|
| `npm run dev` | Start development server with nodemon |
| `npm test` | Run all tests |
| `npm run test:unit` | Run unit tests only |
| `npm run lint` | Run ESLint |
| `npm run typecheck` | Run TypeScript type check |
| `npm run build` | Build the TypeScript project |

## Environment Variables

See `.env.example` for all required environment variables, including:

- `DATABASE_URL` - PostgreSQL connection string
- `STELLAR_NETWORK` - testnet or mainnet
- `STELLAR_AGENT_SECRET_KEY` - Stellar secret key
- `VAULT_CONTRACT_ID` - deployed vault contract ID
- `DATABASE_HOST`, `DATABASE_PORT`, `DATABASE_USER`, `DB_PASSWORD`
- `JWT_SECRET`, `REFRESH_TOKEN_SECRET`
- And more...

## Documentation

- Full documentation is available in the [docs/](docs/) folder
- Start with [DOCUMENTATION_INDEX.md](docs/DOCUMENTATION_INDEX.md)
- Key guides: [DEPLOYMENT.md](docs/DEPLOYMENT.md), [QUICK_REFERENCE.md](docs/QUICK_REFERENCE.md)

## Prerequisites

- Node.js 18+
- PostgreSQL 14+
- Docker & Docker Compose (for local development)
40 changes: 35 additions & 5 deletions src/compliance/travelRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,49 @@ const TRAVEL_RULE_THRESHOLD = 1000 // e.g. USD
export async function detectTravelRule(
amountInBaseCurrency: number,
outboxOpId: string,
direction: 'INBOUND' | 'OUTBOUND'
direction: 'INBOUND' | 'OUTBOUND',
userId: string
) {
if (amountInBaseCurrency >= TRAVEL_RULE_THRESHOLD) {
const user = await db.user.findUnique({
where: { id: userId },
select: {
id: true,
walletAddress: true,
displayName: true,
email: true,
network: true,
},
})

const originator = user
? {
name: user.displayName || 'Unknown User',
accountOrWallet: user.walletAddress,
address: user.walletAddress,
idType: 'wallet',
}
: {}

const beneficiary = user
? {
name: user.displayName || 'Unknown User',
accountOrWallet: user.walletAddress,
}
: {}

const status = user ? 'READY' : 'PENDING_DATA'

await db.travelRuleRecord.create({
data: {
transactionId: outboxOpId,
direction,
amountBaseCcy: amountInBaseCurrency,
baseCurrency: 'USD',
originator: {}, // pull from KycProfile
beneficiary: {},
dataSource: 'SYSTEM',
status: 'PENDING_DATA',
originator,
beneficiary,
dataSource: user ? 'USER_ATTESTED' : 'SYSTEM',
status,
},
})
}
Expand Down
Loading
Loading