diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9ddf49a..2e1aba3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -55,28 +55,84 @@ Violations can be reported by opening a private GitHub issue or contacting a mai - Node.js >= 18 (we recommend [nvm](https://github.com/nvm-sh/nvm)) - npm >= 9 +- Git (for cloning the repository) -### Install Dependencies +### 1. Clone the Repository + +If you haven't already, fork the repository on GitHub, then clone your fork locally: ```bash -npm install +git clone https://github.com/YOUR_USERNAME/open-audit.git +cd open-audit ``` -### Environment Variables +### 2. Install Dependencies + +Install the required Node.js dependencies: ```bash -cp .env.example .env.local +npm install ``` -The defaults point to Stellar **testnet**, which is safe for development. No changes are required for running tests. +### 3. Environment Configuration -### Start the Dev Server +Copy the example environment configuration file to `.env`: ```bash -npm run dev +cp .env.example .env ``` -The app will be available at [http://localhost:3000](http://localhost:3000). +#### Minimum Required Variables + +For basic local development, the default values in `.env` are pre-configured to connect to the Stellar **testnet**. The minimum required variables are: + +- `NEXT_PUBLIC_HORIZON_URL`: The Horizon REST API endpoint (defaults to `https://horizon-testnet.stellar.org`). +- `NEXT_PUBLIC_SOROBAN_RPC_URL`: The Soroban RPC endpoint (defaults to `https://soroban-testnet.stellar.org`). +- `NEXT_PUBLIC_NETWORK_PASSPHRASE`: The passphrase matching the target network (defaults to `"Test SDF Network ; September 2015"`). +- `NEXT_PUBLIC_NETWORK`: The network identifier (`testnet`, `mainnet`, or `futurenet`, defaults to `testnet`). + +#### Optional Services: PostgreSQL & Redis + +For basic development with the in-memory mock data path, **PostgreSQL and Redis are optional**. +- If `DATABASE_URL` is not configured, the app automatically falls back to the in-memory mock data path. +- If `REDIS_URL` is not configured, the app falls back to an in-process memory cache. + +#### Setting Up PostgreSQL (Optional) + +If you need to test database persistence or work on features requiring the database: +1. Ensure PostgreSQL is running and create a local database: + ```bash + createdb open_audit + ``` +2. Configure the `DATABASE_URL` variable in your `.env` file, for example: + ```env + DATABASE_URL="postgresql://user:password@localhost:5432/open_audit" + ``` +3. Run the database migrations: + ```bash + npm run db:migrate + ``` +4. Seed the database with test data: + ```bash + npm run db:seed + ``` + +### 4. Start the Development Server + +You can run the application in two modes depending on your needs: + +- **Basic Dashboard**: To run the Next.js development server for the frontend dashboard: + ```bash + npm run dev + ``` + The app will be available at [http://localhost:3000](http://localhost:3000). + +- **Full WebSocket Server**: To run the monolithic server which includes both the frontend and WebSocket event streaming capabilities: + ```bash + npm run dev:ws + ``` + The app will be available at [http://localhost:3000](http://localhost:3000). + --- diff --git a/lib/translator/registry.ts b/lib/translator/registry.ts index 2fac9d9..e149457 100644 --- a/lib/translator/registry.ts +++ b/lib/translator/registry.ts @@ -216,14 +216,56 @@ function buildRegistry(): BlueprintRegistry { } } - // 3. Load SDEX (Stellar Classic Order Book) Blueprints - for (const blueprint of createAllSdexBlueprints()) { - register(blueprint); - } - return registry; } +function interpolate(template: string, values: Record): string { + return template.replace(/\{([^}]+)\}/g, (match, key) => { + const [path, format] = key.split("."); + const val = values[path]; + if (val && typeof val === "object" && format) { + return val[format] ?? match; + } + return val ?? match; + }); +} + +function createTranslateFromMapping(mapping: any) { + return (event: RawEvent, lang: Language): TranslationResult | null => { + // 1. Match topics + for (let i = 0; i < mapping.topics.length; i++) { + if (i === 0) { + if (decodeEventName(event.topics[0]) !== mapping.topics[0]) return null; + } + // Future: support matching other topics too + } + + const fields: Record = {}; + + // 2. Extract topics[1..] + mapping.event_structure.topics.forEach((t: any, i: number) => { + const hex = event.topics[i + 1]; + if (!hex) return; + if (t.type === "address") fields[t.name] = decodeAddress(hex); + else if (t.type === "i128") fields[t.name] = decodeAmount(hex); + else fields[t.name] = hex; + }); + + // 3. Extract data + if (mapping.event_structure.data) { + const d = mapping.event_structure.data; + if (d.type === "i128") fields[d.name] = decodeAmount(event.data); + else if (d.type === "address") fields[d.name] = decodeAddress(event.data); + else fields[d.name] = event.data; + } + + return { + description: interpolate(mapping.english_template, fields), + eventType: mapping.topics[0], + }; + }; +} + /** * Builds a `translate` function from a single event-mapping declaration. * Called by registerUpgrade (eventMappings). Required for the module to load.