Skip to content

Commit ae5a91e

Browse files
Merge pull request #62 from onelove-dev/feature/blockchain-event-listener-service
Blockchain Event Listener Service
2 parents 4d863e3 + 6a18f6b commit ae5a91e

8 files changed

Lines changed: 292 additions & 81 deletions

File tree

app/login/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ export default function LoginPage() {
166166
</Button>
167167

168168
<p className="text-xs text-muted-foreground pt-4 border-t border-border/40">
169-
By connecting a wallet, you agree to TaskChain's Terms of Service and Privacy Policy.
169+
By connecting a wallet, you agree to TaskChain&apos;s Terms of Service and Privacy Policy.
170170
</p>
171171
</div>
172172
)}

components/navbar.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ export function Navbar() {
1313

1414
useEffect(() => {
1515
const savedAddress = localStorage.getItem('stellar_wallet_address')
16-
if (savedAddress) {
16+
if (savedAddress && savedAddress !== address) {
17+
// eslint-disable-next-line react-hooks/set-state-in-effect
1718
setAddress(savedAddress)
1819
}
19-
}, [])
20+
}, [address])
2021

2122
const formatAddress = (addr: string) => {
2223
if (!addr || addr.length <= 10) return addr;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Blockchain Event Listener Service
2+
3+
## Overview
4+
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.
5+
6+
## Key Components
7+
8+
### 1. Database Schema Extensions
9+
- **Milestones Table (`scripts/004-milestones.sql`)**: Stores milestone-level progress, linked to jobs.
10+
- **Notifications Table (`scripts/005-notifications.sql`)**: Stores platform-wide notifications for users.
11+
12+
### 2. Upgraded Background Worker (`scripts/worker.ts`)
13+
- **Stellar Horizon Streaming**: Subscribes to payments for the platform escrow account.
14+
- **Memo-based Logic**: Distinguishes between Job-level (`JOB-{id}`) and Milestone-level (`MIL-{id}`) operations using transaction memos.
15+
- **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).
16+
- **Idempotency**: Checks `escrow_transactions` before processing to prevent duplicate updates from the same blockchain transaction.
17+
- **Automatic Job Completion**: Synchronizes the main Job status to `completed` once all associated milestones have been released.
18+
- **Real Notifications**: Inserts notification records directly into the database during event processing.
19+
20+
## How to Run
21+
1. Ensure the new SQL migrations are applied to your database:
22+
```bash
23+
# Run these in your Neon console or via psql
24+
scripts/004-milestones.sql
25+
scripts/005-notifications.sql
26+
```
27+
2. Set the necessary environment variables in `.env`:
28+
- `DATABASE_URL`: Your Postgres connection string.
29+
- `ESCROW_ACCOUNT_ID`: The Stellar account monitoring for escrow payments.
30+
- `STELLAR_HORIZON_URL`: (Optional) Defaults to Testnet.
31+
3. Start the worker:
32+
```bash
33+
npm run worker
34+
```
35+
36+
## Design Decisions
37+
- **Separation of Concerns**: Kept the worker as a separate script to avoid blocking Next.js API routes.
38+
- **Resilience**: Implemented `SIGINT` handling for graceful shutdown and used SDK streaming for automatic reconnection.
39+
- **Scalability**: Used async/await and non-blocking SQL queries to handle concurrent events.

package-lock.json

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@
3939
"@radix-ui/react-toggle": "1.1.1",
4040
"@radix-ui/react-toggle-group": "1.1.1",
4141
"@radix-ui/react-tooltip": "1.1.6",
42+
"@stellar/freighter-api": "^6.0.1",
4243
"@stellar/stellar-sdk": "^14.5.0",
4344
"@vercel/analytics": "1.3.1",
4445
"autoprefixer": "^10.4.20",
4546
"class-variance-authority": "^0.7.1",
4647
"clsx": "^2.1.1",
4748
"cmdk": "1.0.4",
48-
"date-fns": "^4.1.0",
4949
"date-fns": "4.1.0",
5050
"dotenv": "^17.3.1",
5151
"embla-carousel-react": "8.5.1",
@@ -80,4 +80,4 @@
8080
"tw-animate-css": "1.3.3",
8181
"typescript": "^5"
8282
}
83-
}
83+
}

scripts/004-milestones.sql

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-- Create milestones table
2+
CREATE TABLE IF NOT EXISTS milestones (
3+
id SERIAL PRIMARY KEY,
4+
job_id INTEGER NOT NULL REFERENCES jobs(id) ON DELETE CASCADE,
5+
title VARCHAR(255) NOT NULL,
6+
description TEXT,
7+
amount DECIMAL(10, 2) NOT NULL,
8+
status VARCHAR(20) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'in_progress', 'completed', 'released')),
9+
due_date TIMESTAMP,
10+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
11+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
12+
);
13+
14+
CREATE INDEX IF NOT EXISTS idx_milestones_job ON milestones(job_id);
15+
CREATE INDEX IF NOT EXISTS idx_milestones_status ON milestones(status);

scripts/005-notifications.sql

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
-- Create notifications table
2+
CREATE TABLE IF NOT EXISTS notifications (
3+
id SERIAL PRIMARY KEY,
4+
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
5+
title VARCHAR(255) NOT NULL,
6+
message TEXT NOT NULL,
7+
type VARCHAR(50) DEFAULT 'info',
8+
is_read BOOLEAN DEFAULT FALSE,
9+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
10+
);
11+
12+
CREATE INDEX IF NOT EXISTS idx_notifications_user ON notifications(user_id);
13+
CREATE INDEX IF NOT EXISTS idx_notifications_created_at ON notifications(created_at);

0 commit comments

Comments
 (0)