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
2 changes: 1 addition & 1 deletion app/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ export default function LoginPage() {
</Button>

<p className="text-xs text-muted-foreground pt-4 border-t border-border/40">
By connecting a wallet, you agree to TaskChain's Terms of Service and Privacy Policy.
By connecting a wallet, you agree to TaskChain&apos;s Terms of Service and Privacy Policy.
</p>
</div>
)}
Expand Down
5 changes: 3 additions & 2 deletions components/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ export function Navbar() {

useEffect(() => {
const savedAddress = localStorage.getItem('stellar_wallet_address')
if (savedAddress) {
if (savedAddress && savedAddress !== address) {
// eslint-disable-next-line react-hooks/set-state-in-effect
setAddress(savedAddress)
}
}, [])
}, [address])

const formatAddress = (addr: string) => {
if (!addr || addr.length <= 10) return addr;
Expand Down
39 changes: 39 additions & 0 deletions docs/blockchain_listener_implementation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Blockchain Event Listener Service

## Overview
Implemented a background worker service that subscribes to Stellar blockchain events to keep the application state in sync. The service handles escrow deposits, releases, and refunds for both entire jobs and individual milestones.

## Key Components

### 1. Database Schema Extensions
- **Milestones Table (`scripts/004-milestones.sql`)**: Stores milestone-level progress, linked to jobs.
- **Notifications Table (`scripts/005-notifications.sql`)**: Stores platform-wide notifications for users.

### 2. Upgraded Background Worker (`scripts/worker.ts`)
- **Stellar Horizon Streaming**: Subscribes to payments for the platform escrow account.
- **Memo-based Logic**: Distinguishes between Job-level (`JOB-{id}`) and Milestone-level (`MIL-{id}`) operations using transaction memos.
- **Role-aware Processing**: Fetches client and freelancer wallet addresses to automatically identify whether a payment from escrow is a **Release** (to freelancer) or a **Refund** (to client).
- **Idempotency**: Checks `escrow_transactions` before processing to prevent duplicate updates from the same blockchain transaction.
- **Automatic Job Completion**: Synchronizes the main Job status to `completed` once all associated milestones have been released.
- **Real Notifications**: Inserts notification records directly into the database during event processing.

## How to Run
1. Ensure the new SQL migrations are applied to your database:
```bash
# Run these in your Neon console or via psql
scripts/004-milestones.sql
scripts/005-notifications.sql
```
2. Set the necessary environment variables in `.env`:
- `DATABASE_URL`: Your Postgres connection string.
- `ESCROW_ACCOUNT_ID`: The Stellar account monitoring for escrow payments.
- `STELLAR_HORIZON_URL`: (Optional) Defaults to Testnet.
3. Start the worker:
```bash
npm run worker
```

## Design Decisions
- **Separation of Concerns**: Kept the worker as a separate script to avoid blocking Next.js API routes.
- **Resilience**: Implemented `SIGINT` handling for graceful shutdown and used SDK streaming for automatic reconnection.
- **Scalability**: Used async/await and non-blocking SQL queries to handle concurrent events.
23 changes: 23 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@
"@radix-ui/react-toggle": "1.1.1",
"@radix-ui/react-toggle-group": "1.1.1",
"@radix-ui/react-tooltip": "1.1.6",
"@stellar/freighter-api": "^6.0.1",
"@stellar/stellar-sdk": "^14.5.0",
"@vercel/analytics": "1.3.1",
"autoprefixer": "^10.4.20",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "1.0.4",
"date-fns": "^4.1.0",
"date-fns": "4.1.0",
"dotenv": "^17.3.1",
"embla-carousel-react": "8.5.1",
Expand Down Expand Up @@ -80,4 +80,4 @@
"tw-animate-css": "1.3.3",
"typescript": "^5"
}
}
}
15 changes: 15 additions & 0 deletions scripts/004-milestones.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-- Create milestones table
CREATE TABLE IF NOT EXISTS milestones (
id SERIAL PRIMARY KEY,
job_id INTEGER NOT NULL REFERENCES jobs(id) ON DELETE CASCADE,
title VARCHAR(255) NOT NULL,
description TEXT,
amount DECIMAL(10, 2) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'in_progress', 'completed', 'released')),
due_date TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX IF NOT EXISTS idx_milestones_job ON milestones(job_id);
CREATE INDEX IF NOT EXISTS idx_milestones_status ON milestones(status);
13 changes: 13 additions & 0 deletions scripts/005-notifications.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-- Create notifications table
CREATE TABLE IF NOT EXISTS notifications (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
title VARCHAR(255) NOT NULL,
message TEXT NOT NULL,
type VARCHAR(50) DEFAULT 'info',
is_read BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX IF NOT EXISTS idx_notifications_user ON notifications(user_id);
CREATE INDEX IF NOT EXISTS idx_notifications_created_at ON notifications(created_at);
Loading
Loading