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
34 changes: 22 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,34 +35,44 @@ For a comprehensive overview of the smart contract architecture, module responsi

## Getting Started

**For complete local development setup with service dependencies, startup order, and troubleshooting, see:**
**For complete local development setup, startup order, and troubleshooting, see:**

- **[Local Development Quickstart](./docs/LOCAL_DEVELOPMENT_QUICKSTART.md)** – Complete setup guide with step-by-step instructions
- **[Service Dependency Matrix](./docs/SERVICE_DEPENDENCY_MATRIX.md)** – Visual dependency graph and service specifications
- **[Local Development Quickstart](./docs/LOCAL_DEVELOPMENT_QUICKSTART.md)** - Backend, contracts, and frontend bootstrap
- **[Service Dependency Matrix](./docs/SERVICE_DEPENDENCY_MATRIX.md)** - Service relationships and operational notes

### Quick Start (5 minutes)
### Quick Start

1. **Start infrastructure** (PostgreSQL + Redis):
1. Start the backend:

```bash
docker-compose up -d postgres redis
cd backend
cp .env.example .env
npm install
npx prisma migrate dev
npm run dev
```

2. **Start backend API** (in one terminal):
2. Start the frontend in a second terminal:

```bash
cd backend && npm install && npx prisma migrate dev && npm run dev
cd frontend
cp .env.example .env
npm install
npm run dev
```

3. **Start frontend** (in another terminal):
3. Optional: run contract tests from the repo root:

```bash
cd frontend && npm install && npm run dev
rustup target add wasm32-unknown-unknown
cargo test
```

4. **Open browser**: http://localhost:5173
4. Open the app at `http://localhost:5173`

The default local workflow uses Prisma's SQLite database and in-memory fallbacks for Redis-backed features unless you explicitly configure additional infrastructure.

For detailed setup instructions, prerequisites, and troubleshooting, see **[Local Development Quickstart](./docs/LOCAL_DEVELOPMENT_QUICKSTART.md)**.
For detailed setup instructions, prerequisites, validation steps, and troubleshooting, see **[Local Development Quickstart](./docs/LOCAL_DEVELOPMENT_QUICKSTART.md)**.

For a complete environment variable reference with defaults, required flags, and production recommendations, see **[Environment Variable Matrix](./docs/ENV_VARIABLE_MATRIX.md)**.

Expand Down
20 changes: 20 additions & 0 deletions backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ npm install

# Create environment file
cp .env.example .env

# Create/update the local Prisma database
npx prisma migrate dev
```

### Development
Expand All @@ -36,6 +39,23 @@ npm run dev

The server will start on `http://localhost:3000`.

For the default local workflow, PostgreSQL and Redis are optional:

- Prisma uses the SQLite datasource in [`prisma/schema.prisma`](/Users/macbook/stellar/YieldVault-RWA/backend/prisma/schema.prisma:1) when `DATABASE_URL` is not set.
- Redis-backed features fall back to in-memory behavior when `REDIS_URL` is not configured.

Minimum local environment values:

```env
PORT=3000
NODE_ENV=development
STELLAR_RPC_URL=https://soroban-testnet.stellar.org
STELLAR_NETWORK_PASSPHRASE=Test SDF Network ; September 2015
VAULT_CONTRACT_ID=
```

For the full monorepo bootstrap order, see [`docs/LOCAL_DEVELOPMENT_QUICKSTART.md`](/Users/macbook/stellar/YieldVault-RWA/docs/LOCAL_DEVELOPMENT_QUICKSTART.md:1).

### Production

```bash
Expand Down
28 changes: 28 additions & 0 deletions backend/src/aws-sdk-client-s3-shim.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
declare module '@aws-sdk/client-s3' {
export interface S3ClientConfig {
region?: string;
endpoint?: string;
forcePathStyle?: boolean;
credentials?: {
accessKeyId: string;
secretAccessKey: string;
};
}

export class S3Client {
constructor(config?: S3ClientConfig);
send(command: unknown): Promise<unknown>;
}

export class PutObjectCommand {
constructor(input?: unknown);
}

export class ListObjectsV2Command {
constructor(input?: unknown);
}

export class DeleteObjectCommand {
constructor(input?: unknown);
}
}
12 changes: 10 additions & 2 deletions backend/src/dbBackupJob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ export interface BackupResult {
deletedCount: number;
}

interface S3ListObjectsResponse {
Contents?: Array<{
Key?: string;
LastModified?: Date;
}>;
NextContinuationToken?: string;
}

// ─── Config helpers ───────────────────────────────────────────────────────────

function getRetentionDays(): number {
Expand Down Expand Up @@ -156,13 +164,13 @@ export async function pruneOldBackups(): Promise<number> {
let continuationToken: string | undefined;

do {
const listResp = await client.send(
const listResp = (await client.send(
new ListObjectsV2Command({
Bucket: bucket,
Prefix: getS3Prefix(),
...(continuationToken ? { ContinuationToken: continuationToken } : {}),
}),
);
)) as S3ListObjectsResponse;

for (const obj of listResp.Contents ?? []) {
if (obj.LastModified && obj.Key && obj.LastModified < cutoff) {
Expand Down
4 changes: 1 addition & 3 deletions backend/src/middleware/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,7 @@ export const WebhookRegisterSchema = z
{ message: 'url must be a valid http or https URL' },
),
eventTypes: z
.array(z.enum(WEBHOOK_EVENT_TYPES), {
invalid_type_error: 'eventTypes must be an array of valid event type strings',
})
.array(z.enum(WEBHOOK_EVENT_TYPES))
.min(1, 'eventTypes must contain at least one event type')
.optional(),
enabled: z.boolean().optional(),
Expand Down
12 changes: 9 additions & 3 deletions backend/src/sorobanBatchClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ export interface BatchClientOptions {
maxConcurrency?: number;
}

interface SimulationSourceAccount {
accountId(): string;
sequenceNumber(): string;
incrementSequenceNumber(): void;
}

// ── Semaphore ─────────────────────────────────────────────────────────────────

/**
Expand Down Expand Up @@ -124,11 +130,11 @@ export const defaultRpcReader: RpcReader = async (

// Build a dummy source account for simulation (sequence=0, no real funds needed)
const dummyKeypair = Keypair.random();
const sourceAccount = {
const sourceAccount: SimulationSourceAccount = {
accountId: () => dummyKeypair.publicKey(),
sequenceNumber: () => '0',
incrementSequenceNumber: () => {},
} as Parameters<typeof TransactionBuilder>[0];
};

const scArgs = (args ?? []).map((a) =>
nativeToScVal(a as Parameters<typeof nativeToScVal>[0]),
Expand All @@ -150,7 +156,7 @@ export const defaultRpcReader: RpcReader = async (
}

// Return the raw result for the caller to decode
return (sim as rpc.Api.SimulateTransactionSuccessResponse).result?.retval ?? null;
return (sim as { result?: { retval?: unknown } }).result?.retval ?? null;
};

// ── SorobanBatchClient ────────────────────────────────────────────────────────
Expand Down
14 changes: 13 additions & 1 deletion backend/src/stellar-sdk-shim.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
declare module '@stellar/stellar-sdk' {
export interface Account {
accountId(): string;
sequenceNumber(): string;
incrementSequenceNumber(): void;
}

export const BASE_FEE: string;

export class Keypair {
static fromSecret(secret: string): Keypair;
static random(): Keypair;
publicKey(): string;
sign(data: Buffer): Buffer;
}
Expand All @@ -21,6 +28,11 @@ declare module '@stellar/stellar-sdk' {
namespace Api {
function isSimulationError(input: unknown): boolean;
function isSimulationRestore(input: unknown): boolean;
interface SimulateTransactionSuccessResponse {
result?: {
retval?: unknown;
};
}
}

function assembleTransaction(tx: unknown, sim: unknown): { build(): { sign(kp: Keypair): void } };
Expand All @@ -41,7 +53,7 @@ declare module '@stellar/stellar-sdk' {
}

export class TransactionBuilder {
constructor(source: unknown, opts: unknown);
constructor(source: Account, opts: unknown);
addOperation(op: unknown): TransactionBuilder;
setTimeout(timeout: number): TransactionBuilder;
build(): unknown;
Expand Down
4 changes: 4 additions & 0 deletions backend/src/webhookDelivery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export type TransactionEventType =
| 'transaction.deposit.created'
| 'transaction.withdrawal.created';

export const WEBHOOK_SCHEMA_VERSION = 1;

export interface TransactionEventPayload {
transactionId: string;
amount: string;
Expand Down Expand Up @@ -521,6 +523,7 @@ export function createWebhookSignature(secret: string, payload: unknown): string
}

export interface WebhookSignedEnvelope {
schemaVersion: number;
eventType: TransactionEventType;
sentAt: string;
payload: TransactionEventPayload;
Expand Down Expand Up @@ -566,6 +569,7 @@ export function buildWebhookSignedEnvelope(
payload: TransactionEventPayload,
): WebhookSignedEnvelope {
return {
schemaVersion: WEBHOOK_SCHEMA_VERSION,
eventType: delivery.eventType,
sentAt: new Date().toISOString(),
payload,
Expand Down
1 change: 1 addition & 0 deletions backend/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"moduleResolution": "node",
"lib": ["ES2020", "DOM"],
"outDir": "./dist",
"rootDir": "./src",
Expand Down
Loading
Loading