From 8a2f6d75eba83e4710e3d3031e31f21981abf63c Mon Sep 17 00:00:00 2001 From: Ved-viraj Date: Fri, 24 Jul 2026 14:22:44 +0100 Subject: [PATCH 1/4] fix(dashboard): align notification cards across screen sizes Resolve overlapping Copy controls and duplicate table renders in Event Explorer, restore broken card CSS, and improve responsive spacing so notification cards stay aligned without content overlap (#441). --- dashboard/src/App.tsx | 239 ++++-------------- .../src/components/EventExplorerCard.tsx | 28 +- .../src/components/EventExplorerTable.tsx | 11 +- dashboard/src/index.css | 117 ++++++++- dashboard/src/pages/EventExplorerPage.tsx | 7 +- 5 files changed, 186 insertions(+), 216 deletions(-) diff --git a/dashboard/src/App.tsx b/dashboard/src/App.tsx index f494468..20abb9d 100644 --- a/dashboard/src/App.tsx +++ b/dashboard/src/App.tsx @@ -1,106 +1,40 @@ -import { useEffect, useMemo, useState } from 'react'; -import { EventExplorerPage } from './pages/EventExplorerPage'; -import { NotificationPreferencesPage } from './pages/NotificationPreferencesPage'; - -const VALID_TAB_KEYS = ['explorer', 'preferences'] as const; - -type TabKey = (typeof VALID_TAB_KEYS)[number]; - -function parseActiveTab(search: string): TabKey { - const params = new URLSearchParams(search); - const tab = params.get('tab'); - return VALID_TAB_KEYS.includes(tab as TabKey) ? (tab as TabKey) : 'explorer'; -} - -export function App() { - const initialSearch = typeof window !== 'undefined' ? window.location.search : ''; - const [activeTab, setActiveTab] = useState(() => parseActiveTab(initialSearch)); - - useEffect(() => { - if (typeof window === 'undefined') { - return; - } - - const handlePopState = () => { - setActiveTab(parseActiveTab(window.location.search)); - }; - - window.addEventListener('popstate', handlePopState); - return () => window.removeEventListener('popstate', handlePopState); - }, []); - - useEffect(() => { - if (typeof window === 'undefined') { - return; - } - - const params = new URLSearchParams(window.location.search); - params.set('tab', activeTab); - window.history.replaceState({}, '', `${window.location.pathname}?${params.toString()}`); - }, [activeTab]); - - const tabLabel = useMemo( - () => (activeTab === 'preferences' ? 'Notification Preferences' : 'Event Explorer'), - [activeTab] - ); - - return ( -
-
-
-

Notify Chain

-

{tabLabel}

-
- - -
- - {activeTab === 'preferences' ? : } - className={`nav-tab-btn ${activeTab === 'exports' ? 'nav-tab-btn--active' : ''}`} - onClick={() => setActiveTab('exports')} - > - Export Center - -
- - - {activeTab === 'explorer' ? : } +import { ActivityFeed } from './components/ActivityFeed'; import { DeliveryHeatmap } from './components/DeliveryHeatmap'; +import { NotificationTimelineView } from './components/NotificationTimelineView'; import { ThemeToggle } from './components/ThemeToggle'; import { useTheme } from './hooks/useTheme'; +import { EventExplorerPage } from './pages/EventExplorerPage'; +import { ExportHistoryPage } from './pages/ExportHistoryPage'; +import { NotificationPreferencesPage } from './pages/NotificationPreferencesPage'; +import { NotificationSearchPage } from './pages/NotificationSearchPage'; +import { TemplatesPage } from './pages/TemplatesPage'; +import { WebhookDashboardPage } from './pages/WebhookDashboardPage'; import { useEventStore } from './store/eventStore'; +type Tab = + | 'explorer' + | 'preferences' + | 'timeline' + | 'activity' + | 'webhooks' + | 'export-history' + | 'search' + | 'templates'; + +const TABS: Array<{ id: Tab; label: string }> = [ + { id: 'explorer', label: 'Event Explorer' }, + { id: 'preferences', label: 'Preferences' }, + { id: 'timeline', label: 'Delivery Timeline' }, + { id: 'activity', label: 'Activity Feed' }, + { id: 'webhooks', label: 'Webhook Performance' }, + { id: 'export-history', label: 'Export History' }, + { id: 'search', label: 'Notification Search' }, + { id: 'templates', label: 'Templates' }, +]; + export function App() { + const [tab, setTab] = useState('explorer'); const { theme, toggleTheme } = useTheme(); const events = useEventStore((state) => state.events); @@ -109,108 +43,35 @@ export function App() {
- - -import { TemplatePreviewDemoPage } from './pages/TemplatePreviewDemoPage'; -type Page = 'events' | 'templates'; - -export function App() { - const [currentPage, setCurrentPage] = useState('templates'); - - return ( -
- - -
- {currentPage === 'events' && } - {currentPage === 'templates' && } -
- role="tab" - aria-selected={tab === 'timeline'} - className={`app-tabs__btn${tab === 'timeline' ? ' app-tabs__btn--active' : ''}`} - onClick={() => setTab('timeline')} - > - Delivery Timeline - - - - - + {TABS.map(({ id, label }) => ( + + ))} - {tab === 'explorer' && } + {tab === 'explorer' && ( + <> + + + + )} + {tab === 'preferences' && } {tab === 'timeline' && } {tab === 'activity' && } {tab === 'webhooks' && } {tab === 'export-history' && } {tab === 'search' && } + {tab === 'templates' && }
); } diff --git a/dashboard/src/components/EventExplorerCard.tsx b/dashboard/src/components/EventExplorerCard.tsx index cd905f6..e160f2f 100644 --- a/dashboard/src/components/EventExplorerCard.tsx +++ b/dashboard/src/components/EventExplorerCard.tsx @@ -35,17 +35,15 @@ interface EventExplorerCardProps { onCopyContract: (contractAddress: string) => void; isCopied: boolean; onSelect?: (event: BlockchainEvent) => void; -} - -export function EventExplorerCard({ event, onCopyContract, isCopied, onSelect }: EventExplorerCardProps) { - contractStatuses: ContractStatus[]; + contractStatuses?: ContractStatus[]; } export function EventExplorerCard({ event, onCopyContract, isCopied, - contractStatuses, + onSelect, + contractStatuses = [], }: EventExplorerCardProps) { const contractStatus = contractStatuses.find((c) => c.address === event.contractAddress); const isPaused = contractStatus?.paused ?? false; @@ -73,26 +71,18 @@ export function EventExplorerCard({ aria-label={onSelect ? `View details for ${label} notification` : undefined} >
-
+

{shortenAddress(event.contractAddress)}

- -
+
+ + + ), + }, +}; + +export const Interactive: Story = { + render: function InteractiveModal(args) { + const [isOpen, setIsOpen] = useState(true); + + return ( +
+ + setIsOpen(false)} /> +
+ ); + }, +}; diff --git a/dashboard/src/components/NotificationSearchBar.tsx b/dashboard/src/components/NotificationSearchBar.tsx index 67e5730..f27acd2 100644 --- a/dashboard/src/components/NotificationSearchBar.tsx +++ b/dashboard/src/components/NotificationSearchBar.tsx @@ -2,9 +2,9 @@ import { useState, useEffect, memo } from 'react'; import { useEventStore } from '../store/eventStore'; import { useEventFilters } from '../hooks/useEventSelectors'; import { useDebounce } from '../hooks/useDebounce'; -import type { NotificationStatus } from '../types/event'; +import type { NotificationReadFilter } from '../types/event'; -const STATUS_OPTIONS: { value: NotificationStatus; label: string }[] = [ +const STATUS_OPTIONS: { value: NotificationReadFilter; label: string }[] = [ { value: 'all', label: 'All' }, { value: 'unread', label: 'Unread' }, { value: 'read', label: 'Read' }, diff --git a/dashboard/src/components/PaginationControls.stories.tsx b/dashboard/src/components/PaginationControls.stories.tsx new file mode 100644 index 0000000..2563abf --- /dev/null +++ b/dashboard/src/components/PaginationControls.stories.tsx @@ -0,0 +1,64 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { useState } from 'react'; +import { PaginationControls } from './PaginationControls'; + +const meta: Meta = { + title: 'Components/PaginationControls', + component: PaginationControls, + tags: ['autodocs'], + args: { + page: 2, + pageCount: 8, + limit: 12, + totalCount: 96, + onPageChange: () => undefined, + onLimitChange: () => undefined, + }, +}; + +export default meta; +type Story = StoryObj; + +export const MiddlePage: Story = {}; + +export const FirstPage: Story = { + args: { + page: 1, + }, +}; + +export const LastPage: Story = { + args: { + page: 8, + }, +}; + +export const CustomPageSizes: Story = { + args: { + pageSizeOptions: [5, 10, 25], + limit: 10, + summaryLabel: 'exports', + }, +}; + +export const Interactive: Story = { + render: function InteractivePagination(args) { + const [page, setPage] = useState(args.page); + const [limit, setLimit] = useState(args.limit); + const pageCount = Math.max(1, Math.ceil(args.totalCount / limit)); + + return ( + { + setLimit(nextLimit); + setPage(1); + }} + /> + ); + }, +}; diff --git a/dashboard/src/components/ThemeToggle.stories.tsx b/dashboard/src/components/ThemeToggle.stories.tsx new file mode 100644 index 0000000..20e2518 --- /dev/null +++ b/dashboard/src/components/ThemeToggle.stories.tsx @@ -0,0 +1,40 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { useState } from 'react'; +import { ThemeToggle } from './ThemeToggle'; +import type { Theme } from '../hooks/useTheme'; + +const meta: Meta = { + title: 'Components/ThemeToggle', + component: ThemeToggle, + tags: ['autodocs'], +}; + +export default meta; +type Story = StoryObj; + +export const Dark: Story = { + args: { + theme: 'dark', + onToggle: () => undefined, + }, +}; + +export const Light: Story = { + args: { + theme: 'light', + onToggle: () => undefined, + }, +}; + +export const Interactive: Story = { + render: function InteractiveThemeToggle() { + const [theme, setTheme] = useState('dark'); + + return ( + setTheme((current) => (current === 'dark' ? 'light' : 'dark'))} + /> + ); + }, +}; diff --git a/dashboard/src/components/WebhookSummaryCards.stories.tsx b/dashboard/src/components/WebhookSummaryCards.stories.tsx new file mode 100644 index 0000000..3c88e04 --- /dev/null +++ b/dashboard/src/components/WebhookSummaryCards.stories.tsx @@ -0,0 +1,48 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { WebhookSummaryCards } from './WebhookSummaryCards'; +import type { WebhookSummaryMetrics } from '../types/webhook'; + +const filledSummary: WebhookSummaryMetrics = { + totalAttempts: 1842, + successCount: 1760, + failedCount: 82, + successRate: 95.55, + avgLatencyMs: 186, + p95LatencyMs: 412, +}; + +const strugglingSummary: WebhookSummaryMetrics = { + totalAttempts: 640, + successCount: 410, + failedCount: 230, + successRate: 64.06, + avgLatencyMs: 890, + p95LatencyMs: 2100, +}; + +const meta: Meta = { + title: 'Components/WebhookSummaryCards', + component: WebhookSummaryCards, + tags: ['autodocs'], + args: { + summary: filledSummary, + isLoading: false, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Filled: Story = {}; + +export const Loading: Story = { + args: { + isLoading: true, + }, +}; + +export const DegradedPerformance: Story = { + args: { + summary: strugglingSummary, + }, +}; diff --git a/dashboard/src/store/eventStore.ts b/dashboard/src/store/eventStore.ts index 3c1a777..5b907d6 100644 --- a/dashboard/src/store/eventStore.ts +++ b/dashboard/src/store/eventStore.ts @@ -1,6 +1,5 @@ import { create } from 'zustand'; -import type { BlockchainEvent, EventFilters, NotificationStatus } from '../types/event'; -import { NOTIFICATION_STATUS_EVENTS } from '../types/event'; +import type { BlockchainEvent, EventFilters, NotificationReadFilter, NotificationStatus } from '../types/event'; import { filterEvents } from '../utils/eventData'; interface EventStoreState { @@ -22,7 +21,7 @@ interface EventStoreState { setSearch: (search: string) => void; setContractFilter: (contractAddress: string) => void; setEventTypeFilter: (eventType: string) => void; - setStatusFilter: (status: NotificationStatus) => void; + setStatusFilter: (status: NotificationReadFilter) => void; setDateFrom: (dateFrom: string) => void; setDateTo: (dateTo: string) => void; setLoading: (isLoading: boolean) => void; diff --git a/dashboard/src/stories/fixtures/events.ts b/dashboard/src/stories/fixtures/events.ts new file mode 100644 index 0000000..369532b --- /dev/null +++ b/dashboard/src/stories/fixtures/events.ts @@ -0,0 +1,32 @@ +import type { BlockchainEvent } from '../types/event'; + +export const sampleEvent: BlockchainEvent = { + eventId: 'evt_notify_001', + contractAddress: 'CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC', + eventName: 'TaskCreated', + ledger: 512_348, + type: 'contract', + topic: ['task', 'created'], + value: '10000000', + txHash: 'a1b2c3d4e5f60718293a4b5c6d7e8f901234567890abcdef1234567890abcd', + receivedAt: Date.UTC(2026, 6, 24, 12, 0, 0), + notificationStatus: 'active', + read: false, +}; + +export const sampleSystemEvent: BlockchainEvent = { + ...sampleEvent, + eventId: 'evt_sys_002', + eventName: 'NotificationExpired', + type: 'system', + notificationStatus: 'expired', + value: '0', +}; + +export const sampleWithdrawalEvent: BlockchainEvent = { + ...sampleEvent, + eventId: 'evt_fin_003', + eventName: 'Withdrawal', + type: 'contract', + value: '2500000', +}; diff --git a/dashboard/src/types/event.ts b/dashboard/src/types/event.ts index 58f4e32..e9f9f7d 100644 --- a/dashboard/src/types/event.ts +++ b/dashboard/src/types/event.ts @@ -49,13 +49,14 @@ export interface BlockchainEvent { read?: boolean; } -export type NotificationStatus = 'all' | 'read' | 'unread'; +/** UI filter for read/unread notification search (not on-chain lifecycle status). */ +export type NotificationReadFilter = 'all' | 'read' | 'unread'; export interface EventFilters { search: string; contractAddress: string; eventType: string; - status: NotificationStatus; + status: NotificationReadFilter; dateFrom: string; // ISO date string "YYYY-MM-DD" or "" dateTo: string; // ISO date string "YYYY-MM-DD" or "" } diff --git a/dashboard/src/utils/eventData.ts b/dashboard/src/utils/eventData.ts index e093ad0..47538e3 100644 --- a/dashboard/src/utils/eventData.ts +++ b/dashboard/src/utils/eventData.ts @@ -37,7 +37,7 @@ export function filterEvents( search: string, contractAddress: string, eventType: string, - status: import('../types/event').NotificationStatus = 'all', + status: import('../types/event').NotificationReadFilter = 'all', dateFrom = '', dateTo = '' ): BlockchainEvent[] { From 53d5fa09f0921767604c88a4f9fdd016912bafd5 Mon Sep 17 00:00:00 2001 From: Ved-viraj Date: Fri, 24 Jul 2026 14:32:52 +0100 Subject: [PATCH 3/4] docs: standardize repository documentation consistency Align terminology, formatting, clone/setup URLs, and outdated file references across primary docs so contributors share one consistent NotifyChain vocabulary and onboarding path (#444). --- CONTRIBUTING.md | 15 ++++++++ CONTRIBUTOR_ARCHITECTURE_DEEP_DIVE.md | 40 ++++++++++---------- CONTRIBUTOR_DEVELOPMENT_WORKFLOW_GUIDE.md | 2 +- CONTRIBUTOR_SETUP.md | 16 +++----- CREATE_PR_INSTRUCTIONS.md | 12 +++--- DEPLOYMENT_PLAYBOOK.md | 12 +++--- DEVELOPMENT.md | 25 +++++++----- EVENT_CATALOG.md | 46 +++++++++++------------ IMPLEMENTATION_SUMMARY.md | 2 +- LOCAL_DEVELOPMENT.md | 12 +++++- NOTIFICATION_LIFECYCLE.md | 6 ++- README.md | 18 +++++---- TROUBLESHOOTING.md | 4 +- contract/README.md | 6 ++- docs/MONITORING_INTEGRATION.md | 22 +++++------ docs/notifications/lifecycle.md | 2 + 16 files changed, 136 insertions(+), 104 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 90ef20f..89f500a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,6 +4,21 @@ Thank you for your interest in contributing to NotifyChain! This document provid **Start here instead (recommended):** [`CONTRIBUTOR_DEVELOPMENT_WORKFLOW_GUIDE.md`](CONTRIBUTOR_DEVELOPMENT_WORKFLOW_GUIDE.md) +## Documentation conventions + +Use these names consistently in docs and PRs: + +| Concept | Standard form | +|---------|----------------| +| Product name (prose/titles) | **NotifyChain** | +| GitHub repository / clone directory | **Notify-Chain** (`Core-Foundry/Notify-Chain`) | +| Off-chain service | **Listener** (`listener/`) | +| React + Vite UI | **Dashboard** (`dashboard/`) | +| Legacy analytics app | **Frontend** (`frontend/`) | +| On-chain code | **Smart contracts** (`contract/`, `Documents/Task Bounty/`) | + +Canonical setup path: workflow guide → [`LOCAL_DEVELOPMENT.md`](LOCAL_DEVELOPMENT.md) (quick) → [`CONTRIBUTOR_SETUP.md`](CONTRIBUTOR_SETUP.md) (detailed). + ## Code of Conduct - Be respectful and inclusive diff --git a/CONTRIBUTOR_ARCHITECTURE_DEEP_DIVE.md b/CONTRIBUTOR_ARCHITECTURE_DEEP_DIVE.md index 5fcca83..d3f2c75 100644 --- a/CONTRIBUTOR_ARCHITECTURE_DEEP_DIVE.md +++ b/CONTRIBUTOR_ARCHITECTURE_DEEP_DIVE.md @@ -52,9 +52,9 @@ NotifyChain is split into three decoupled components, separated by network and t └────────────────────────────────────────────────────────────────────────┘ ``` -1. **Smart Contracts (On-Chain)**: Written in Rust for the Soroban smart contract platform. They run in a WebAssembly (WASM) sandbox, mutate ledger state, and emit structured events. Located in [contract/contracts/hello-world](file:///workspaces/Notify-Chain/contract/contracts/hello-world) and [Documents/Task Bounty](file:///workspaces/Notify-Chain/Documents/Task%20Bounty). -2. **Listener Service (Off-Chain Engine)**: Written in Node.js and TypeScript. It polls the Stellar RPC, parses, deduplicates, and stores events in SQLite, and dispatches real-time alerts. Located in [listener](file:///workspaces/Notify-Chain/listener). -3. **React Dashboard (Frontend)**: A standard Vite + React SPA that consumes the REST API exposed by the listener service to display events and schedule performance statistics. Located in [dashboard](file:///workspaces/Notify-Chain/dashboard). +1. **Smart Contracts (On-Chain)**: Written in Rust for the Soroban smart contract platform. They run in a WebAssembly (WASM) sandbox, mutate ledger state, and emit structured events. Located in [contract/contracts/hello-world](contract/contracts/hello-world) and [Documents/Task Bounty](Documents/Task%20Bounty). +2. **Listener Service (Off-Chain Engine)**: Written in Node.js and TypeScript. It polls the Stellar RPC, parses, deduplicates, and stores events in SQLite, and dispatches real-time alerts. Located in [listener](listener). +3. **React Dashboard (Frontend)**: A standard Vite + React SPA that consumes the REST API exposed by the listener service to display events and schedule performance statistics. Located in [dashboard](dashboard). --- @@ -63,14 +63,14 @@ NotifyChain is split into three decoupled components, separated by network and t NotifyChain supports two smart contract interaction patterns with different design structures: ### 2.1 Struct Event Pattern (AutoShare) -Implemented in [base/events.rs](file:///workspaces/Notify-Chain/contract/contracts/hello-world/src/base/events.rs). The contract defines dedicated struct types decorated with `#[contractevent]`. Each event carries standard routing topics: +Implemented in [base/events.rs](contract/contracts/hello-world/src/base/events.rs). The contract defines dedicated struct types decorated with `#[contractevent]`. Each event carries standard routing topics: - **`NotificationCategory`**: A 4-variant enum mapping events to functional domains (`Group`, `Admin`, `Financial`, `Notification`). - **`NotificationPriority`**: A 4-variant enum detailing severity (`Low`, `Medium`, `High`, `Critical`). These are appended as the last two indexed topics to ensure backward compatibility for simpler indexers. ### 2.2 Subject-Action Event Pattern (TaskBounty) -Implemented in [src/events.rs](file:///workspaces/Notify-Chain/Documents/Task%20Bounty/src/events.rs). The contract does not use routing metadata structures. Instead, it emits events as tuples of short symbols matching the `(Subject, Action)` schema: +Implemented in [src/events.rs](Documents/Task%20Bounty/src/events.rs). The contract does not use routing metadata structures. Instead, it emits events as tuples of short symbols matching the `(Subject, Action)` schema: - E.g., `(symbol_short!("task"), symbol_short!("created"))` or `(symbol_short!("sub"), symbol_short!("approved"))`. - Payload arguments (like task ID, amount, and creator) are passed as tuples in the event data field. @@ -116,15 +116,15 @@ sequenceDiagram ``` ### 3.1 Step-by-Step Processing Pipeline: -1. **Polling**: The [EventSubscriber](file:///workspaces/Notify-Chain/listener/src/services/event-subscriber.ts) wakes up at configured intervals (default: 30 seconds) and invokes `getEvents` on the Stellar RPC using the last persisted ledger sequence. -2. **Persistent Deduplication**: The [EventDeduplicationService](file:///workspaces/Notify-Chain/listener/src/services/event-deduplication-service.ts) checks each incoming event. It references the `processed_events` SQLite table to see if the event ID has already been indexed. +1. **Polling**: The [EventSubscriber](listener/src/services/event-subscriber.ts) wakes up at configured intervals (default: 30 seconds) and invokes `getEvents` on the Stellar RPC using the last persisted ledger sequence. +2. **Persistent Deduplication**: The [EventDeduplicationService](listener/src/services/event-deduplication-service.ts) checks each incoming event. It references the `processed_events` SQLite table to see if the event ID has already been indexed. 3. **Reorg Handling**: - If the RPC returns an event with a ledger number *lower* than the last processed ledger cursor, the system flags a blockchain reorganization. - It sets `is_reorg_duplicate = true` on the event. - The event is stored in SQLite for integrity, but the pipeline **skips sending Discord alerts or notifications** to prevent duplicate spam. 4. **In-Memory Cache (LRU)**: A secondary fast-path `NotificationDeduplicator` stores the last 60 seconds of event hashes in memory to prevent database roundtrips for duplicate frames. 5. **Persistence**: The event is formatted and saved to the `events` table. The `polling_cursors` table is updated with the new ledger index. -6. **Dispatch**: The [NotificationDispatcher](file:///workspaces/Notify-Chain/listener/src/services/notification-dispatcher.ts) formats the event and pushes it to active channels (e.g. Discord webhook). +6. **Dispatch**: The [NotificationDispatcher](listener/src/services/notification-dispatcher.ts) formats the event and pushes it to active channels (e.g. Discord webhook). --- @@ -206,19 +206,19 @@ Because SQLite executes writes sequentially and locks the database, only one wor Here are the critical paths and files that implement the core functionality of NotifyChain: ### 5.1 Smart Contracts -- [contract/contracts/hello-world/src/lib.rs](file:///workspaces/Notify-Chain/contract/contracts/hello-world/src/lib.rs): Entry point for the AutoShare contract. Declares the functions and maps calls to the logic module. -- [autoshare_logic.rs](file:///workspaces/Notify-Chain/contract/contracts/hello-world/src/autoshare_logic.rs): Core business logic for AutoShare groups, members, subscriptions, withdrawals, and scheduled notification parameters. -- [base/events.rs](file:///workspaces/Notify-Chain/contract/contracts/hello-world/src/base/events.rs): Structure definitions for category-priority routed events. -- [Documents/Task Bounty/src/lib.rs](file:///workspaces/Notify-Chain/Documents/Task%20Bounty/src/lib.rs): Entry point for the TaskBounty contract. -- [Documents/Task Bounty/src/events.rs](file:///workspaces/Notify-Chain/Documents/Task%20Bounty/src/events.rs): Emits the unstructured subject-action events for tasks, submissions, and disputes. +- [contract/contracts/hello-world/src/lib.rs](contract/contracts/hello-world/src/lib.rs): Entry point for the AutoShare contract. Declares the functions and maps calls to the logic module. +- [autoshare_logic.rs](contract/contracts/hello-world/src/autoshare_logic.rs): Core business logic for AutoShare groups, members, subscriptions, withdrawals, and scheduled notification parameters. +- [base/events.rs](contract/contracts/hello-world/src/base/events.rs): Structure definitions for category-priority routed events. +- [Documents/Task Bounty/src/lib.rs](Documents/Task%20Bounty/src/lib.rs): Entry point for the TaskBounty contract. +- [Documents/Task Bounty/src/events.rs](Documents/Task%20Bounty/src/events.rs): Emits the unstructured subject-action events for tasks, submissions, and disputes. ### 5.2 Off-Chain Listener Service -- [listener/src/index.ts](file:///workspaces/Notify-Chain/listener/src/index.ts): Initializer script. Bootstraps the HTTP API server, SQLite store, subscriber, and scheduler loops. -- [listener/src/services/event-subscriber.ts](file:///workspaces/Notify-Chain/listener/src/services/event-subscriber.ts): Polls Stellar RPC logs and drives the ingestion loop. -- [listener/src/services/event-deduplication-service.ts](file:///workspaces/Notify-Chain/listener/src/services/event-deduplication-service.ts): SQLite-backed deduplication layer; houses reorg-detection safeguards. -- [listener/src/services/notification-scheduler.ts](file:///workspaces/Notify-Chain/listener/src/services/notification-scheduler.ts): Periodically queries SQLite for due scheduled notifications, acquires locks, dispatches alerts, and performs recovery. -- [listener/src/store/](file:///workspaces/Notify-Chain/listener/src/store/): Holds the repositories (`EventRepository`, `ScheduleRepository`) managing queries to SQLite. +- [listener/src/index.ts](listener/src/index.ts): Initializer script. Bootstraps the HTTP API server, SQLite store, subscriber, and scheduler loops. +- [listener/src/services/event-subscriber.ts](listener/src/services/event-subscriber.ts): Polls Stellar RPC logs and drives the ingestion loop. +- [listener/src/services/event-deduplication-service.ts](listener/src/services/event-deduplication-service.ts): SQLite-backed deduplication layer; houses reorg-detection safeguards. +- [listener/src/services/notification-scheduler.ts](listener/src/services/notification-scheduler.ts): Periodically queries SQLite for due scheduled notifications, acquires locks, dispatches alerts, and performs recovery. +- [listener/src/store/](listener/src/store/): Holds the repositories (`EventRepository`, `ScheduleRepository`) managing queries to SQLite. ### 5.3 Frontend Dashboard -- [dashboard/src/pages/](file:///workspaces/Notify-Chain/dashboard/src/pages/): Contains top-level dashboard pages: `Events` (real-time stream), `Schedules` (notification queue stats), and `Stats` (overview charts). -- [dashboard/src/hooks/](file:///workspaces/Notify-Chain/dashboard/src/hooks/): React hooks for fetching event feeds, managing poll intervals, and querying status counts from the listener. +- [dashboard/src/pages/](dashboard/src/pages/): Contains top-level dashboard pages: `Events` (real-time stream), `Schedules` (notification queue stats), and `Stats` (overview charts). +- [dashboard/src/hooks/](dashboard/src/hooks/): React hooks for fetching event feeds, managing poll intervals, and querying status counts from the listener. diff --git a/CONTRIBUTOR_DEVELOPMENT_WORKFLOW_GUIDE.md b/CONTRIBUTOR_DEVELOPMENT_WORKFLOW_GUIDE.md index 3fadfa7..c922dcc 100644 --- a/CONTRIBUTOR_DEVELOPMENT_WORKFLOW_GUIDE.md +++ b/CONTRIBUTOR_DEVELOPMENT_WORKFLOW_GUIDE.md @@ -21,7 +21,7 @@ A single, end-to-end workflow for local setup, branching, testing, and submittin - Rust (stable) with WebAssembly target: - `rustup target add wasm32-unknown-unknown` - Stellar CLI: - - `cargo install stellar-cli` + - `cargo install --locked stellar-cli --features opt` - Node.js: - Listener uses **Node 20** - Dashboard uses **Node 18** diff --git a/CONTRIBUTOR_SETUP.md b/CONTRIBUTOR_SETUP.md index 1ee2496..c44b451 100644 --- a/CONTRIBUTOR_SETUP.md +++ b/CONTRIBUTOR_SETUP.md @@ -7,7 +7,10 @@ This guide walks you through setting up a local development environment for NotifyChain. By the end, you will have the listener service, the dashboard, the frontend analytics app, and the smart contracts building and running on your machine. -This guide walks you through setting up a local development environment for NotifyChain. By the end, you will have the listener service, the dashboard, and the smart contracts building and running on your machine. + +For the canonical contribution workflow, start with +[`CONTRIBUTOR_DEVELOPMENT_WORKFLOW_GUIDE.md`](CONTRIBUTOR_DEVELOPMENT_WORKFLOW_GUIDE.md). +For a shorter setup path, see [`LOCAL_DEVELOPMENT.md`](LOCAL_DEVELOPMENT.md). --- @@ -24,11 +27,6 @@ This guide walks you through setting up a local development environment for Noti 9. [Running Tests](#9-running-tests) 10. [VS Code Setup (Recommended)](#10-vs-code-setup-recommended) 11. [Troubleshooting & FAQ](#11-troubleshooting--faq) -5. [Smart Contracts Setup](#5-smart-contracts-setup) -6. [Environment Variables Reference](#6-environment-variables-reference) -7. [Running Tests](#7-running-tests) -8. [VS Code Setup (Recommended)](#8-vs-code-setup-recommended) -9. [Troubleshooting & FAQ](#9-troubleshooting--faq) --- @@ -40,7 +38,7 @@ This guide walks you through setting up a local development environment for Noti |----------------|-----------------|-----------------------------------------|--------------------| | Rust | stable | [rustup.rs](https://rustup.rs) | Smart contracts | | `wasm32-unknown-unknown` | — | `rustup target add wasm32-unknown-unknown` | Soroban contracts | -| Stellar CLI | latest | `cargo install stellar-cli` | Contract build/deploy | +| Stellar CLI | latest | `cargo install --locked stellar-cli --features opt` | Contract build/deploy | | Node.js | **18** (dashboard), **20** (listener) | [nodejs.org](https://nodejs.org) or `nvm` | Listener, Dashboard | | npm | comes with Node | — | Package management | | Git | — | Your package manager or [git-scm.com](https://git-scm.com) | Version control | @@ -94,7 +92,6 @@ rustc --version && cargo --version && stellar --version ## 2. Clone the Repository ```bash -git clone https://github.com/CollinsC1O/Notify-Chain.git git clone https://github.com/Core-Foundry/Notify-Chain.git cd Notify-Chain ``` @@ -104,7 +101,6 @@ If you plan to contribute, fork the repository first, then clone your fork: ```bash git clone https://github.com/YOUR-USERNAME/Notify-Chain.git cd Notify-Chain -git remote add upstream https://github.com/CollinsC1O/Notify-Chain.git git remote add upstream https://github.com/Core-Foundry/Notify-Chain.git ``` @@ -786,7 +782,7 @@ cd frontend && npm install ### Still Stuck? -1. Search [open issues](https://github.com/CollinsC1O/Notify-Chain/issues) — your problem may already be reported. +1. Search [open issues](https://github.com/Core-Foundry/Notify-Chain/issues) — your problem may already be reported. 1. Search [open issues](https://github.com/Core-Foundry/Notify-Chain/issues) — your problem may already be reported. 2. Read the detailed [Troubleshooting Guide](TROUBLESHOOTING.md). 3. Open a new issue with: diff --git a/CREATE_PR_INSTRUCTIONS.md b/CREATE_PR_INSTRUCTIONS.md index f122d85..2c08376 100644 --- a/CREATE_PR_INSTRUCTIONS.md +++ b/CREATE_PR_INSTRUCTIONS.md @@ -21,7 +21,7 @@ You now have a complete, working notification template preview feature ready to 1. **Go to your fork on GitHub:** ``` - https://github.com/coderolisa/Notify-Chain + https://github.com/Core-Foundry/Notify-Chain ``` 2. **You should see a yellow banner** saying: @@ -122,7 +122,7 @@ gh pr create \ Click this link (replace YOUR_USERNAME): ``` -https://github.com/coderolisa/Notify-Chain/pull/new/feature/notification-template-preview +https://github.com/Core-Foundry/Notify-Chain/pull/new/feature/notification-template-preview ``` ## 📝 PR Checklist @@ -279,15 +279,15 @@ If you encounter any issues: ### Branch Info ``` -Repository: https://github.com/coderolisa/Notify-Chain +Repository: https://github.com/Core-Foundry/Notify-Chain Branch: feature/notification-template-preview Status: ✅ Ready for PR ``` ### Key URLs -- Your Fork: `https://github.com/coderolisa/Notify-Chain` -- Create PR: `https://github.com/coderolisa/Notify-Chain/pull/new/feature/notification-template-preview` -- Branch: `https://github.com/coderolisa/Notify-Chain/tree/feature/notification-template-preview` +- Your Fork: `https://github.com/Core-Foundry/Notify-Chain` +- Create PR: `https://github.com/Core-Foundry/Notify-Chain/pull/new/feature/notification-template-preview` +- Branch: `https://github.com/Core-Foundry/Notify-Chain/tree/feature/notification-template-preview` ## ✨ You're All Set! diff --git a/DEPLOYMENT_PLAYBOOK.md b/DEPLOYMENT_PLAYBOOK.md index c7bf490..af06bdc 100644 --- a/DEPLOYMENT_PLAYBOOK.md +++ b/DEPLOYMENT_PLAYBOOK.md @@ -81,7 +81,7 @@ These variables are commonly exported during CLI deployment scripting: - `DISPUTE_RESOLVER_ADDRESS`: The designated address authorized to resolve disputed tasks in `TaskBounty`. ### 2.2 Off-Chain Listener Configuration (`listener/.env`) -Once the contracts are deployed, their IDs must be registered in the listener's environment config [listener/.env.example](file:///workspaces/Notify-Chain/listener/.env.example): +Once the contracts are deployed, their IDs must be registered in the listener's environment config [listener/.env.example](listener/.env.example): - `STELLAR_NETWORK`: Set to `testnet` or `public` (mainnet). - `STELLAR_RPC_URL`: The Stellar RPC endpoint URL (e.g., `https://soroban-testnet.stellar.org:443`). - `CONTRACT_ADDRESSES`: A JSON array specifying the contract addresses and events to subscribe to. @@ -93,7 +93,7 @@ Once the contracts are deployed, their IDs must be registered in the listener's ``` ### 2.3 Frontend Dashboard Configuration (`dashboard/.env`) -Provide the frontend dashboard [dashboard/.env.example](file:///workspaces/Notify-Chain/dashboard/.env.example) with details to query the listener: +Provide the frontend dashboard [dashboard/.env.example](dashboard/.env.example) with details to query the listener: - `VITE_EVENTS_API_URL`: HTTP URL of the listener event feed (e.g., `http://localhost:8787/api/events`). - `VITE_STELLAR_NETWORK`: Network context (`TESTNET` or `PUBLIC`). @@ -102,8 +102,8 @@ Provide the frontend dashboard [dashboard/.env.example](file:///workspaces/Notif ## 3. Step-by-Step Deployment Examples NotifyChain contains two primary smart contracts that must be compiled and deployed: -1. `AutoShare` - Subscription and group management contract located in [contract/contracts/hello-world](file:///workspaces/Notify-Chain/contract/contracts/hello-world). -2. `TaskBounty` - Decentralized task and reward board contract located in [Documents/Task Bounty](file:///workspaces/Notify-Chain/Documents/Task%20Bounty). +1. `AutoShare` - Subscription and group management contract located in [contract/contracts/hello-world](contract/contracts/hello-world). +2. `TaskBounty` - Decentralized task and reward board contract located in [Documents/Task Bounty](Documents/Task%20Bounty). --- @@ -142,7 +142,7 @@ export AUTOSHARE_CONTRACT_ID= ``` #### Step A.4: Initialize the Contract -The `AutoShare` contract requires initializing the administrator identity before it can accept groups and payments. Call the [initialize_admin](file:///workspaces/Notify-Chain/contract/contracts/hello-world/src/lib.rs#L38) function: +The `AutoShare` contract requires initializing the administrator identity before it can accept groups and payments. Call the [initialize_admin](contract/contracts/hello-world/src/lib.rs#L38) function: ```bash stellar contract invoke \ --id $AUTOSHARE_CONTRACT_ID \ @@ -186,7 +186,7 @@ export TASKBOUNTY_CONTRACT_ID= ``` #### Step B.4: Initialize the Contract -The `TaskBounty` contract requires setting up the admin address and a dispute resolver. Call the [initialize](file:///workspaces/Notify-Chain/Documents/Task%20Bounty/src/lib.rs#L38) function: +The `TaskBounty` contract requires setting up the admin address and a dispute resolver. Call the [initialize](Documents/Task%20Bounty/src/lib.rs#L38) function: ```bash stellar contract invoke \ --id $TASKBOUNTY_CONTRACT_ID \ diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index fcc2ebc..732406d 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -27,7 +27,7 @@ Before starting, ensure you have the following software installed on your machin | Tool | Minimum Version | Purpose | Installation Link | |------|----------------|---------|-------------------| -| **Node.js** | v18.0.0+ | JavaScript runtime for listener & dashboard | [nodejs.org](https://nodejs.org/) | +| **Node.js** | v18+ (v20 recommended for Listener) | JavaScript runtime for listener & dashboard | [nodejs.org](https://nodejs.org/) | | **npm** | v9.0.0+ | Package manager (bundled with Node.js) | Comes with Node.js | | **Rust** | Latest stable | Smart contract development | [rustup.rs](https://rustup.rs/) | | **Stellar CLI** | Latest | Deploy & interact with contracts | See [installation](#installing-stellar-cli) | @@ -48,7 +48,7 @@ Before starting, ensure you have the following software installed on your machin ``` NotifyChain/ -├── 📂 contract/ # Soroban smart contracts (Rust) +├── contract/ # Soroban smart contracts (Rust) │ ├── contracts/ │ │ └── hello-world/ # AutoShare contract │ │ ├── src/ @@ -61,7 +61,7 @@ NotifyChain/ │ │ └── Makefile │ └── Cargo.toml # Workspace configuration │ -├── 📂 listener/ # Off-chain event listener (Node.js/TypeScript) +├── listener/ # Off-chain event listener (Node.js/TypeScript) │ ├── src/ │ │ ├── api/ # REST API endpoints │ │ ├── database/ # SQLite database layer @@ -81,7 +81,7 @@ NotifyChain/ │ ├── tsconfig.json │ └── jest.config.js │ -├── 📂 dashboard/ # React frontend dashboard +├── dashboard/ # React frontend dashboard │ ├── src/ │ │ ├── components/ # React components │ │ ├── hooks/ # Custom React hooks @@ -95,7 +95,7 @@ NotifyChain/ │ ├── vite.config.ts │ └── tsconfig.json │ -├── 📂 Documents/ +├── Documents/ │ └── Task Bounty/ # TaskBounty contract (alternative example) │ ├── .github/ @@ -124,8 +124,8 @@ NotifyChain/ ### 1. Clone the Repository ```bash -git clone https://github.com/your-org/NotifyChain.git -cd NotifyChain +git clone https://github.com/Core-Foundry/Notify-Chain.git +cd Notify-Chain ``` ### 2. Install Node.js Dependencies @@ -739,6 +739,11 @@ test: Add tests for Discord service ### Documentation - [README.md](./README.md) - Project overview +- [LOCAL_DEVELOPMENT.md](./LOCAL_DEVELOPMENT.md) - Quick local setup +- [CONTRIBUTOR_SETUP.md](./CONTRIBUTOR_SETUP.md) - Detailed contributor setup +- [CONTRIBUTOR_DEVELOPMENT_WORKFLOW_GUIDE.md](./CONTRIBUTOR_DEVELOPMENT_WORKFLOW_GUIDE.md) - Canonical contribution workflow +- [docs/](./docs/) - Additional architecture and API docs +- [dashboard/STORYBOOK.md](./dashboard/STORYBOOK.md) - Dashboard component Storybook - [listener/INSTALLATION.md](./listener/INSTALLATION.md) - Detailed listener setup - [listener/README-SCHEDULER.md](./listener/README-SCHEDULER.md) - Scheduler documentation - [listener/TEST-FIXTURE-MIGRATION-GUIDE.md](./listener/TEST-FIXTURE-MIGRATION-GUIDE.md) - Testing guide @@ -753,8 +758,8 @@ test: Add tests for Discord service ### Community -- [GitHub Issues](https://github.com/your-org/NotifyChain/issues) -- [GitHub Discussions](https://github.com/your-org/NotifyChain/discussions) +- [GitHub Issues](https://github.com/Core-Foundry/Notify-Chain/issues) +- [GitHub Discussions](https://github.com/Core-Foundry/Notify-Chain/discussions) --- @@ -776,4 +781,4 @@ Before considering your setup complete, verify: **You're ready to contribute to NotifyChain!** 🚀 -For questions or issues, please open a [GitHub Issue](https://github.com/your-org/NotifyChain/issues). +For questions or issues, please open a [GitHub Issue](https://github.com/Core-Foundry/Notify-Chain/issues). diff --git a/EVENT_CATALOG.md b/EVENT_CATALOG.md index 74d9bdb..f12e535 100644 --- a/EVENT_CATALOG.md +++ b/EVENT_CATALOG.md @@ -11,9 +11,9 @@ NotifyChain leverages these on-chain events to feed the off-chain listener, inde NotifyChain contracts use a structured event design that allows off-chain services to quickly categorize and filter events without needing to parse the full event payload first. ### 1.1 Notification Category -Every event emitted by the [AutoShareContract](file:///workspaces/Notify-Chain/contract/contracts/hello-world/src/lib.rs#L22) carries a `NotificationCategory` as one of its indexed topics. This allows listeners to filter for whole categories of events. +Every event emitted by the [AutoShareContract](contract/contracts/hello-world/src/lib.rs#L22) carries a `NotificationCategory` as one of its indexed topics. This allows listeners to filter for whole categories of events. -The enum is defined in [events.rs](file:///workspaces/Notify-Chain/contract/contracts/hello-world/src/base/events.rs#L18): +The enum is defined in [events.rs](contract/contracts/hello-world/src/base/events.rs#L18): | Variant | Value (U32) | Description | | :--- | :--- | :--- | @@ -25,7 +25,7 @@ The enum is defined in [events.rs](file:///workspaces/Notify-Chain/contract/cont ### 1.2 Notification Priority Every event emitted by `AutoShareContract` also carries a `NotificationPriority` topic to help downstream routers filter or trigger alerts based on severity. -The enum is defined in [events.rs](file:///workspaces/Notify-Chain/contract/contracts/hello-world/src/base/events.rs#L46): +The enum is defined in [events.rs](contract/contracts/hello-world/src/base/events.rs#L46): | Variant | Value (U32) | Description | | :--- | :--- | :--- | @@ -41,7 +41,7 @@ To prevent breaking changes in downstream indexers, category and priority topics ## 2. AutoShare Contract Events -These events are defined in [base/events.rs](file:///workspaces/Notify-Chain/contract/contracts/hello-world/src/base/events.rs) within the AutoShare contract. +These events are defined in [base/events.rs](contract/contracts/hello-world/src/base/events.rs) within the AutoShare contract. In Soroban, CamelCase struct names compile to snake_case symbols by default (e.g., `AutoshareCreated` is emitted on-chain with the first topic `Symbol("autoshare_created")`). @@ -50,7 +50,7 @@ In Soroban, CamelCase struct names compile to snake_case symbols by default (e.g ### 2.1 `autoshare_created` Emitted when a new AutoShare group is created. -- **Trigger Method**: [create](file:///workspaces/Notify-Chain/contract/contracts/hello-world/src/lib.rs#L63) +- **Trigger Method**: [create](contract/contracts/hello-world/src/lib.rs#L63) - **Topics**: 1. `Symbol("autoshare_created")` 2. `creator: Address` (Indexed creator of the group) @@ -63,7 +63,7 @@ Emitted when a new AutoShare group is created. ### 2.2 `autoshare_updated` Emitted when the members list of an AutoShare group is updated. -- **Trigger Method**: [update_members](file:///workspaces/Notify-Chain/contract/contracts/hello-world/src/lib.rs#L77) +- **Trigger Method**: [update_members](contract/contracts/hello-world/src/lib.rs#L77) - **Topics**: 1. `Symbol("autoshare_updated")` 2. `updater: Address` (Address that updated the members) @@ -76,7 +76,7 @@ Emitted when the members list of an AutoShare group is updated. ### 2.3 `group_deactivated` Emitted when an AutoShare group is deactivated by its creator. -- **Trigger Method**: [deactivate_group](file:///workspaces/Notify-Chain/contract/contracts/hello-world/src/lib.rs#L123) +- **Trigger Method**: [deactivate_group](contract/contracts/hello-world/src/lib.rs#L123) - **Topics**: 1. `Symbol("group_deactivated")` 2. `creator: Address` (Creator performing deactivation) @@ -89,7 +89,7 @@ Emitted when an AutoShare group is deactivated by its creator. ### 2.4 `group_activated` Emitted when a deactivated group is reactivated. -- **Trigger Method**: [activate_group](file:///workspaces/Notify-Chain/contract/contracts/hello-world/src/lib.rs#L128) +- **Trigger Method**: [activate_group](contract/contracts/hello-world/src/lib.rs#L128) - **Topics**: 1. `Symbol("group_activated")` 2. `creator: Address` (Creator performing reactivation) @@ -102,7 +102,7 @@ Emitted when a deactivated group is reactivated. ### 2.5 `contract_paused` Emitted when the contract is paused by the administrator. -- **Trigger Method**: [pause](file:///workspaces/Notify-Chain/contract/contracts/hello-world/src/lib.rs#L43) +- **Trigger Method**: [pause](contract/contracts/hello-world/src/lib.rs#L43) - **Topics**: 1. `Symbol("contract_paused")` 2. `category: NotificationCategory` (Always `Admin` / `1`) @@ -114,7 +114,7 @@ Emitted when the contract is paused by the administrator. ### 2.6 `contract_unpaused` Emitted when the contract is unpaused by the administrator. -- **Trigger Method**: [unpause](file:///workspaces/Notify-Chain/contract/contracts/hello-world/src/lib.rs#L48) +- **Trigger Method**: [unpause](contract/contracts/hello-world/src/lib.rs#L48) - **Topics**: 1. `Symbol("contract_unpaused")` 2. `category: NotificationCategory` (Always `Admin` / `1`) @@ -126,7 +126,7 @@ Emitted when the contract is unpaused by the administrator. ### 2.7 `admin_transferred` Emitted when admin privileges are transferred to a new account. -- **Trigger Method**: [transfer_admin](file:///workspaces/Notify-Chain/contract/contracts/hello-world/src/lib.rs#L143) +- **Trigger Method**: [transfer_admin](contract/contracts/hello-world/src/lib.rs#L143) - **Topics**: 1. `Symbol("admin_transferred")` 2. `old_admin: Address` (Old admin address) @@ -139,7 +139,7 @@ Emitted when admin privileges are transferred to a new account. ### 2.8 `withdrawal` Emitted when the admin withdraws collected usage fees from the contract. -- **Trigger Method**: [withdraw](file:///workspaces/Notify-Chain/contract/contracts/hello-world/src/lib.rs#L148) +- **Trigger Method**: [withdraw](contract/contracts/hello-world/src/lib.rs#L148) - **Topics**: 1. `Symbol("withdrawal")` 2. `token: Address` (Token withdrawn) @@ -165,7 +165,7 @@ Emitted when an unauthorized operation attempt is detected on-chain. ### 2.10 `scheduled_notification_cancelled` Emitted when a scheduled notification is cancelled on-chain. -- **Trigger Method**: [cancel_notification](file:///workspaces/Notify-Chain/contract/contracts/hello-world/src/lib.rs#L253) +- **Trigger Method**: [cancel_notification](contract/contracts/hello-world/src/lib.rs#L253) - **Topics**: 1. `Symbol("scheduled_notification_cancelled")` 2. `caller: Address` (The user who triggered cancellation) @@ -178,7 +178,7 @@ Emitted when a scheduled notification is cancelled on-chain. ### 2.11 `notification_scheduled` Emitted when a notification is scheduled on-chain with a bounded lifetime. -- **Trigger Method**: [schedule_notification](file:///workspaces/Notify-Chain/contract/contracts/hello-world/src/lib.rs#L265) +- **Trigger Method**: [schedule_notification](contract/contracts/hello-world/src/lib.rs#L265) - **Topics**: 1. `Symbol("notification_scheduled")` 2. `creator: Address` (The creator of the notification) @@ -191,7 +191,7 @@ Emitted when a notification is scheduled on-chain with a bounded lifetime. ### 2.12 `notification_expired` Emitted when a scheduled notification's lifetime elapses, marking it expired. -- **Trigger Method**: [expire_notification](file:///workspaces/Notify-Chain/contract/contracts/hello-world/src/lib.rs#L289) +- **Trigger Method**: [expire_notification](contract/contracts/hello-world/src/lib.rs#L289) - **Topics**: 1. `Symbol("notification_expired")` 2. `notification_id: BytesN<32>` (ID of the expired notification) @@ -204,7 +204,7 @@ Emitted when a scheduled notification's lifetime elapses, marking it expired. ### 2.13 `notification_revoked` Emitted when a scheduled notification is revoked by its creator or admin. -- **Trigger Method**: [revoke_notification](file:///workspaces/Notify-Chain/contract/contracts/hello-world/src/lib.rs#L297) +- **Trigger Method**: [revoke_notification](contract/contracts/hello-world/src/lib.rs#L297) - **Topics**: 1. `Symbol("notification_revoked")` 2. `notification_id: BytesN<32>` (ID of the revoked notification) @@ -217,14 +217,14 @@ Emitted when a scheduled notification is revoked by its creator or admin. ## 3. TaskBounty Contract Events -These events are defined in [events.rs](file:///workspaces/Notify-Chain/Documents/Task%20Bounty/src/events.rs) within the TaskBounty contract. They do not use the category/priority helper wrapper but emit multi-topic symbols directly. +These events are defined in [events.rs](Documents/Task%20Bounty/src/events.rs) within the TaskBounty contract. They do not use the category/priority helper wrapper but emit multi-topic symbols directly. --- ### 3.1 `task_created` Emitted when a new task is created and reward tokens are escrowed in the contract. -- **Trigger Method**: [create_task](file:///workspaces/Notify-Chain/Documents/Task%20Bounty/src/lib.rs#L58) +- **Trigger Method**: [create_task](Documents/Task%20Bounty/src/lib.rs#L58) - **Topics**: 1. `Symbol("task")` 2. `Symbol("created")` @@ -240,7 +240,7 @@ Emitted when a new task is created and reward tokens are escrowed in the contrac ### 3.2 `work_submitted` Emitted when a contributor submits work for a task. -- **Trigger Method**: [submit_work](file:///workspaces/Notify-Chain/Documents/Task%20Bounty/src/lib.rs#L92) +- **Trigger Method**: [submit_work](Documents/Task%20Bounty/src/lib.rs#L92) - **Topics**: 1. `Symbol("work")` 2. `Symbol("submit")` @@ -255,7 +255,7 @@ Emitted when a contributor submits work for a task. ### 3.3 `submission_approved` Emitted when the poster approves a work submission, releasing escrowed rewards. -- **Trigger Method**: [approve_submission](file:///workspaces/Notify-Chain/Documents/Task%20Bounty/src/lib.rs#L110) +- **Trigger Method**: [approve_submission](Documents/Task%20Bounty/src/lib.rs#L110) - **Topics**: 1. `Symbol("sub")` 2. `Symbol("approved")` @@ -270,7 +270,7 @@ Emitted when the poster approves a work submission, releasing escrowed rewards. ### 3.4 `submission_rejected` Emitted when the poster rejects a submission. -- **Trigger Method**: [reject_submission](file:///workspaces/Notify-Chain/Documents/Task%20Bounty/src/lib.rs#L128) +- **Trigger Method**: [reject_submission](Documents/Task%20Bounty/src/lib.rs#L128) - **Topics**: 1. `Symbol("sub")` 2. `Symbol("rejected")` @@ -284,7 +284,7 @@ Emitted when the poster rejects a submission. ### 3.5 `task_cancelled` Emitted when a task is cancelled and funds are refunded to the poster. -- **Trigger Method**: [cancel_task](file:///workspaces/Notify-Chain/Documents/Task%20Bounty/src/lib.rs#L145) +- **Trigger Method**: [cancel_task](Documents/Task%20Bounty/src/lib.rs#L145) - **Topics**: 1. `Symbol("task")` 2. `Symbol("cancel")` @@ -297,7 +297,7 @@ Emitted when a task is cancelled and funds are refunded to the poster. ### 3.6 `dispute_raised` Emitted when a poster or contributor raises a dispute over a work submission. -- **Trigger Method**: [raise_dispute](file:///workspaces/Notify-Chain/Documents/Task%20Bounty/src/lib.rs#L158) +- **Trigger Method**: [raise_dispute](Documents/Task%20Bounty/src/lib.rs#L158) - **Topics**: 1. `Symbol("dispute")` 2. `Symbol("raised")` diff --git a/IMPLEMENTATION_SUMMARY.md b/IMPLEMENTATION_SUMMARY.md index 259858e..cca6a8c 100644 --- a/IMPLEMENTATION_SUMMARY.md +++ b/IMPLEMENTATION_SUMMARY.md @@ -501,7 +501,7 @@ The feature is: ### Branch Information ``` Branch: feature/notification-template-preview -Remote: https://github.com/coderolisa/Notify-Chain.git +Remote: https://github.com/Core-Foundry/Notify-Chain.git Status: Ready for Pull Request ``` diff --git a/LOCAL_DEVELOPMENT.md b/LOCAL_DEVELOPMENT.md index 9a905ce..20b3b9c 100644 --- a/LOCAL_DEVELOPMENT.md +++ b/LOCAL_DEVELOPMENT.md @@ -24,7 +24,7 @@ This guide walks you through setting up every component of NotifyChain on your l | Tool | Version | Install | |------|---------|---------| -| Node.js | ≥ 18 | [nodejs.org](https://nodejs.org) | +| Node.js | ≥ 18 (20 recommended for Listener) | [nodejs.org](https://nodejs.org) | | npm | ≥ 9 | Bundled with Node.js | | Rust | stable | `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \| sh` | | Stellar CLI | latest | `cargo install --locked stellar-cli --features opt` | @@ -145,6 +145,14 @@ EVENTS_API_PORT=8787 See [Environment Variables Reference](#environment-variables-reference) for all options. +Use `npm ci` when installing from the lockfile (CI / clean setups). Use `npm install` for local dependency updates. + +### Initialize database + +```bash +npm run migrate +``` + ### Run in development mode ```bash @@ -373,7 +381,7 @@ mkdir -p listener/data rustup target add wasm32-unknown-unknown ``` -### `cargo install stellar-cli` is slow or fails +### `cargo install --locked stellar-cli --features opt` is slow or fails Try with the `--locked` flag to use pinned dependency versions: diff --git a/NOTIFICATION_LIFECYCLE.md b/NOTIFICATION_LIFECYCLE.md index 7cdfb66..58256c5 100644 --- a/NOTIFICATION_LIFECYCLE.md +++ b/NOTIFICATION_LIFECYCLE.md @@ -1,6 +1,6 @@ # Notification Lifecycle -This document explains the implemented notification lifecycle in Notify-Chain. +This document explains the implemented notification lifecycle in NotifyChain. It covers both: - Scheduled notifications managed in SQLite and executed by background schedulers. @@ -8,9 +8,11 @@ It covers both: The focus is accuracy against current code paths in `listener/src`. +See also: [`docs/notifications/lifecycle.md`](docs/notifications/lifecycle.md). + ## High-Level Overview -Notify-Chain currently has two delivery paths: +NotifyChain currently has two delivery paths: 1. Scheduled path (durable, DB-backed) - Creation via `NotificationAPI.scheduleNotification()` or `POST /api/schedule`. diff --git a/README.md b/README.md index 57309aa..3f407f9 100644 --- a/README.md +++ b/README.md @@ -27,12 +27,12 @@ The project enables developers to build reactive decentralized applications with > **Listener service docs**: > - [API Contract and Event Reference](listener/API_CONTRACT_EVENT_REFERENCE.md) > - [API Usage Cookbook](listener/API_USAGE_COOKBOOK.md) -> - [Notification Failure Recovery](NOTIFICATION_FAILURE_RECOVERY.md) — retry lifecycle, configuration, and troubleshooting. -> [Notification Lifecycle](NOTIFICATION_LIFECYCLE.md) — creation, delivery, acknowledgment semantics, retries, and archival. -> [Notification Failure Recovery](NOTIFICATION_FAILURE_RECOVERY.md) — retry lifecycle, configuration, and troubleshooting. -> **Listener service docs**: [Notification Failure Recovery](NOTIFICATION_FAILURE_RECOVERY.md) — retry lifecycle, configuration, and troubleshooting. +> - [Notification Lifecycle](NOTIFICATION_LIFECYCLE.md) — creation, delivery, acknowledgment semantics, retries, and archival +> - [Notification Failure Recovery](NOTIFICATION_FAILURE_RECOVERY.md) — retry lifecycle, configuration, and troubleshooting > > **Event reference**: [Smart Contract Event Reference Guide](CONTRACT_EVENT_REFERENCE.md) — all emitted events, parameters, data types, and usage recommendations for indexers and listeners. +> +> **Dashboard Storybook**: [dashboard/STORYBOOK.md](dashboard/STORYBOOK.md) — component documentation for design and development reviews. --- @@ -328,8 +328,8 @@ stellar --version 1. **Clone the repository**: ```bash - git clone https://github.com/your-org/notify-chain.git - cd notify-chain + git clone https://github.com/Core-Foundry/Notify-Chain.git + cd Notify-Chain ``` 2. **Building the AutoShare contract**: @@ -543,7 +543,7 @@ Common Freighter wallet issues and how to resolve them. ## Wallet UX States -The frontend models four wallet connection states. Every UI that depends on the wallet must handle all of them. +The frontend (legacy Next.js analytics app under `frontend/`) models four wallet connection states. Every UI that depends on the wallet must handle all of them. | State | Description | User-facing message | |-------|-------------|---------------------| @@ -643,7 +643,9 @@ Contributions are welcome! Please follow these steps (or start with the canonica Please follow the project's coding standards and include tests where applicable. For more detailed contribution guidelines, check: -- `Documents/Task Bounty/CONTRIBUTING.md` +- [`CONTRIBUTING.md`](CONTRIBUTING.md) +- [`CONTRIBUTOR_DEVELOPMENT_WORKFLOW_GUIDE.md`](CONTRIBUTOR_DEVELOPMENT_WORKFLOW_GUIDE.md) +- `Documents/Task Bounty/CONTRIBUTING.md` (TaskBounty contract-specific) --- diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index 0c9aa22..3c953e6 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -1,6 +1,6 @@ # Troubleshooting Guide — Local Development -This guide documents the most common setup issues contributors encounter and how to fix them. If your problem is not listed here, please open a [GitHub Issue](https://github.com/CollinsC1O/Notify-Chain/issues). +This guide documents the most common setup issues contributors encounter and how to fix them. If your problem is not listed here, please open a [GitHub Issue](https://github.com/Core-Foundry/Notify-Chain/issues). --- @@ -415,7 +415,7 @@ cd ../dashboard && npm install ### Still stuck? -1. Search [open issues](https://github.com/CollinsC1O/Notify-Chain/issues) — your problem may already be reported. +1. Search [open issues](https://github.com/Core-Foundry/Notify-Chain/issues) — your problem may already be reported. 2. Open a new issue with: - Your OS and version - Output of `rustc --version`, `node --version`, `stellar --version` diff --git a/contract/README.md b/contract/README.md index 557ea3d..b1eb38c 100644 --- a/contract/README.md +++ b/contract/README.md @@ -1,8 +1,10 @@ -# Soroban Project +# AutoShare Contract + +AutoShare lives under `contracts/hello-world` (directory name kept for historical Soroban scaffold layout). ## Project Structure -This repository uses the recommended structure for a Soroban project: +This package uses the recommended structure for a Soroban project: ```text . ├── contracts diff --git a/docs/MONITORING_INTEGRATION.md b/docs/MONITORING_INTEGRATION.md index 57f57fa..f89d8b9 100644 --- a/docs/MONITORING_INTEGRATION.md +++ b/docs/MONITORING_INTEGRATION.md @@ -2,7 +2,7 @@ ## ⚠️ Critical: Avoiding Double-Counting in Metrics -This guide explains how to integrate with Notify-Chain's notification metrics **without double-counting retries**. +This guide explains how to integrate with NotifyChain's notification metrics **without double-counting retries**. --- @@ -29,7 +29,7 @@ Notification ID: 100 **Endpoint**: `GET /api/schedule/execution-metrics` ```bash -curl http://localhost:3000/api/schedule/execution-metrics +curl http://localhost:8787/api/schedule/execution-metrics ``` **Response**: @@ -86,7 +86,7 @@ But if you're counting all log entries for that notification, you might count 3 scrape_configs: - job_name: 'notify-chain' static_configs: - - targets: ['localhost:3000'] + - targets: ['localhost:8787'] metrics_path: '/api/schedule/execution-metrics' scrape_interval: 30s ``` @@ -196,7 +196,7 @@ import requests class NotifyChainCheck(AgentCheck): def check(self, instance): - url = instance.get('url', 'http://localhost:3000/api/schedule/execution-metrics') + url = instance.get('url', 'http://localhost:8787/api/schedule/execution-metrics') try: response = requests.get(url, timeout=5) @@ -228,7 +228,7 @@ class NotifyChainCheck(AgentCheck): init_config: instances: - - url: http://localhost:3000/api/schedule/execution-metrics + - url: http://localhost:8787/api/schedule/execution-metrics min_collection_interval: 30 ``` @@ -244,7 +244,7 @@ const cloudwatch = new AWS.CloudWatch(); exports.handler = async (event) => { try { - const metrics = await fetchMetrics('http://notify-chain:3000/api/schedule/execution-metrics'); + const metrics = await fetchMetrics('http://notify-chain:8787/api/schedule/execution-metrics'); const totalSuccess = metrics.successfulFirstAttempt + metrics.successfulAfterRetry; const successRate = metrics.totalNotifications > 0 @@ -318,7 +318,7 @@ function fetchMetrics(url) { ```json { "dashboard": { - "title": "Notify-Chain Metrics", + "title": "NotifyChain Metrics", "panels": [ { "title": "Success Rate", @@ -525,7 +525,7 @@ index=notify-chain "Notification marked as completed" ```bash # Create a notification that will succeed on first attempt -curl -X POST http://localhost:3000/api/schedule \ +curl -X POST http://localhost:8787/api/schedule \ -H "Content-Type: application/json" \ -d '{ "notificationType": "discord", @@ -542,7 +542,7 @@ curl -X POST http://localhost:3000/api/schedule \ ```bash # Fetch metrics -curl http://localhost:3000/api/schedule/execution-metrics | jq +curl http://localhost:8787/api/schedule/execution-metrics | jq # Expected output structure: # { @@ -563,7 +563,7 @@ curl http://localhost:3000/api/schedule/execution-metrics | jq # Stop Discord service to force failures # Then create notification - it will retry and eventually fail -curl -X POST http://localhost:3000/api/schedule \ +curl -X POST http://localhost:8787/api/schedule \ -H "Content-Type: application/json" \ -d '{ "notificationType": "discord", @@ -578,7 +578,7 @@ curl -X POST http://localhost:3000/api/schedule \ # Wait for retries to complete (2-3 minutes) # Check metrics again -curl http://localhost:3000/api/schedule/execution-metrics | jq +curl http://localhost:8787/api/schedule/execution-metrics | jq # Should show: # { diff --git a/docs/notifications/lifecycle.md b/docs/notifications/lifecycle.md index 655ae16..4c1db03 100644 --- a/docs/notifications/lifecycle.md +++ b/docs/notifications/lifecycle.md @@ -2,6 +2,8 @@ ## Overview +See also the root lifecycle guide: [`NOTIFICATION_LIFECYCLE.md`](../../NOTIFICATION_LIFECYCLE.md). + The NotifyChain notification system is a robust, multi-component system designed to capture events from Soroban smart contracts, deliver them to configured destinations, and track their entire lifecycle for auditing and debugging purposes. This document describes: From 40492b9e5f40cd6fbec6c0a42b48ec04e2a695cb Mon Sep 17 00:00:00 2001 From: Ved-viraj Date: Sat, 25 Jul 2026 14:11:57 +0100 Subject: [PATCH 4/4] fix(dashboard): correct Storybook fixture event type import Use the proper relative path from stories/fixtures to types/event so TypeScript resolves BlockchainEvent correctly. --- dashboard/src/stories/fixtures/events.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dashboard/src/stories/fixtures/events.ts b/dashboard/src/stories/fixtures/events.ts index 369532b..041ed87 100644 --- a/dashboard/src/stories/fixtures/events.ts +++ b/dashboard/src/stories/fixtures/events.ts @@ -1,4 +1,4 @@ -import type { BlockchainEvent } from '../types/event'; +import type { BlockchainEvent } from '../../types/event'; export const sampleEvent: BlockchainEvent = { eventId: 'evt_notify_001',