Skip to content

feat: bootstrap scripts & metadata and pricing improvements #63

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Feb 14, 2025

Conversation

0xkenj1
Copy link
Contributor

@0xkenj1 0xkenj1 commented Feb 4, 2025

🤖 Linear

Closes GIT-XXX

Description

  • Metadata and pricing improvements
  • Database connection improvements
  • Bootstrap metadata and pricing scripts

Checklist before requesting a review

  • I have conducted a self-review of my code.
  • I have conducted a QA.
  • If it is a core feature, I have included comprehensive tests.

Summary by CodeRabbit

  • New Features

    • Introduced enhanced environment configurations and new bootstrap scripts for metadata and pricing operations.
    • Added a dedicated processing service with expanded integrations for external metadata and pricing providers.
  • Enhancements

    • Improved error handling, caching strategies, and deployment commands for better performance and stability.
    • Streamlined configuration validations tailored for production and staging environments.
  • Documentation

    • Updated configuration examples and migration guides to support the new features and revisions.

@0xkenj1 0xkenj1 marked this pull request as draft February 4, 2025 00:28
@0xkenj1
Copy link
Contributor Author

0xkenj1 commented Feb 5, 2025

@CodeRabbit pls review it :D

@0xkenj1 0xkenj1 requested a review from 0xnigir1 February 5, 2025 12:40
Copy link

coderabbitai bot commented Feb 5, 2025

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get the list of changed files
git diff --name-status origin/main HEAD

# Get the detailed diff
git diff origin/main HEAD

# Look for relevant configuration files
fd -e json -e yaml -e toml -e env

Length of output: 2005


🏁 Script executed:

#!/bin/bash
# Get PR diff using GitHub CLI
gh pr diff 63

# Look specifically at bootstrap-related files
fd bootstrap | xargs cat

Length of output: 81973


🏁 Script executed:

#!/bin/bash
# Look for bootstrap-related files and their content
fd bootstrap -t f | xargs cat

Length of output: 31


🏁 Script executed:

#!/bin/bash
# List files in the bootstrap directory
ls -R scripts/bootstrap/

# Check package.json for bootstrap scripts
cat package.json

# Check if there are any TypeScript files in the bootstrap directory
find scripts/bootstrap -name "*.ts"

Length of output: 3028


🏁 Script executed:

#!/bin/bash
# Read the main bootstrap scripts
cat scripts/bootstrap/src/metadata.script.ts
cat scripts/bootstrap/src/pricing.script.ts
cat scripts/bootstrap/README.md

Length of output: 12844

@0xkenj1 I've reviewed the changes and here's my feedback:

The PR implements two main bootstrap scripts with solid functionality:

  1. Metadata Bootstrap:

    • Fetches metadata from IPFS for all historical events
    • Implements concurrent processing with retry mechanisms
    • Includes checkpoint tracking to resume interrupted operations
    • Uses public gateways with fallback support
  2. Pricing Bootstrap:

    • Fetches historical token prices for all supported tokens
    • Implements caching with proper timestamp handling
    • Includes proper error handling for unsupported tokens
    • Uses configurable concurrency and retry strategies

Suggestions:

  1. Update the README.md in scripts/bootstrap to document the new commands:
    • bootstrap:metadata
    • bootstrap:pricing
  2. Consider adding some basic error statistics logging at the end of the scripts
  3. Add E2E tests as mentioned in the TODO

The implementation looks solid, but as noted in the PR description "DO NOT MERGE, WE NEED TO TEST EVERYTHING", thorough testing should be performed before merging.

Would you like me to help with updating the README or adding any specific tests?

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

coderabbitai bot commented Feb 5, 2025

📝 Walkthrough

Walkthrough

The pull request encompasses a wide range of modifications across configuration files, application code, tests, and scripts. Environment variables have been updated—removing some obsolete ones and introducing new variables like NODE_ENV and METADATA_SOURCE—to better structure configuration settings. The Dockerfile now enables and prepares a specific pnpm version and alters deployment command parameters. The metadata provider refactoring replaces direct instantiation with a factory-based approach, and related tests have been updated accordingly. Several modules across data-flow, pricing, and repository now feature updated method signatures, enhanced caching logic, and refined error handling, including support for null values. New exception classes and type definitions are introduced to improve robustness. In addition, new scripts and configuration files for bootstrapping and database migrations have been added or revised to support environment-specific and domain-specific settings. Overall, these changes update dependency management, streamline environment validation, and enhance infrastructure consistency throughout the project.

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🧹 Nitpick comments (39)
scripts/bootstrap/src/utils/kysely.ts (1)

3-7: Add JSDoc documentation for the interface.

The interface is well-structured but lacks JSDoc documentation. Please add documentation to describe the purpose of the interface and its generic type parameter.

+/**
+ * Configuration for database migrations.
+ * @template T The database schema type.
+ */
 export interface MigrationConfig<T> {
+    /** The Kysely database instance */
     db: Kysely<T>;
+    /** The database schema name */
     schema: string;
+    /** Path to the migrations folder */
     migrationsFolder: string;
 }
scripts/bootstrap/src/schemas/index.ts (2)

6-13: Improve error context for invalid JSON.

Currently, the custom issue logged is generic. Consider enhancing the error message to identify which specific environment variable failed JSON parsing for quicker debugging.

Example enhancement:

-  ctx.addIssue({ code: "custom", message: "Invalid JSON" });
+  ctx.addIssue({
+    code: "custom",
+    message: `Invalid JSON in environment variable: "${str.slice(0, 20)}..."`,
+  });

56-74: Consolidate environment parsing.

getDatabaseConfigFromEnv() and getEnv() both parse process.env with separate schemas. This can inadvertently lead to inconsistencies if the environment changes between calls. Consider consolidating the environment parsing so the code only parses once and reuses results.

scripts/bootstrap/src/pricing.script.ts (2)

22-44: Update documentation block to match actual script behavior.

The docstring references database migrations exclusively, but this script primarily populates a pricing cache from external sources. Consider revising the doc block to accurately reflect token price fetching and storing processes.


102-125: Handle partial fetch failures more robustly.

While unsupported tokens are logged, other errors are simply returned with a "rejected" status. Consider more explicit error reporting or partial retry strategies to avoid halting the entire process if one token fails repeatedly.

scripts/bootstrap/src/metadata.script.ts (3)

91-121: Persisting CIDs to an external file might create fragility.
Storing CIDs in cids.txt is quick to implement, but it’s prone to data inconsistencies if the script crashes mid-process or runs in different environments. Consider storing checkpoint data in a more robust or centralized location (e.g., a database table).


130-131: Fix minor grammar in console output.
Currently the script logs "where fetched"; it should be "were fetched" for correct usage.

- console.log(cids.length, " where fetched");
+ console.log(cids.length, " were fetched");

150-151: Revisit high concurrency limit.
The concurrency limit is set to 1000, which may cause rate-limiting from external gateways. Consider making it configurable or lowering it to avoid throttling or socket exhaustion.

- concurrency: 1000,
+ concurrency: 100,
packages/data-flow/src/orchestrator.ts (1)

333-346: Make concurrency level configurable instead of hardcoding.
A concurrency of 10 is a reasonable start, but the FIXME suggests it’s meant to be environment-dependent or user-configurable. Keeping it hardcoded can limit flexibility and hamper performance tuning.

packages/pricing/src/exceptions/noClosePriceFound.exception.ts (1)

1-5: Consider enhancing the error handling implementation.

  1. Consider extending a more specific error base class (e.g., NonRetriableError) for consistent error handling across the codebase.
  2. Add JSDoc documentation to describe the purpose and usage of this exception.
+/**
+ * Exception thrown when no close price is found for a token.
+ * This is typically a non-retriable error that indicates the requested price data is unavailable.
+ */
-export class NoClosePriceFound extends Error {
+export class NoClosePriceFound extends NonRetriableError {
     constructor() {
         super(`No close price found`);
     }
 }
packages/metadata/src/exceptions/invalidMetadataSource.exception.ts (1)

1-7: Enhance error message and add documentation.

  1. Add JSDoc documentation to describe the purpose and usage of this exception.
  2. Consider including the provided source in the error message for better debugging.
+/**
+ * Exception thrown when an invalid or unsupported metadata source is provided.
+ * This is a non-retriable error that indicates a configuration issue.
+ */
 export class InvalidMetadataSourceException extends NonRetriableError {
-    constructor() {
-        super(`Invalid metadata source`);
+    constructor(source?: string) {
+        super(`Invalid metadata source${source ? `: ${source}` : ''}`);
     }
 }
packages/metadata/src/exceptions/missingDependencies.exception.ts (1)

1-7: Add documentation and consider type safety.

  1. Add JSDoc documentation to describe the purpose and usage of this exception.
  2. Consider adding type safety for the dependencies array.
+/**
+ * Exception thrown when required dependencies are missing.
+ * This is a non-retriable error that indicates a configuration issue.
+ */
 export class MissingDependenciesException extends NonRetriableError {
-    constructor(dependencies: string[]) {
+    constructor(dependencies: readonly string[]) {
         super(`Missing dependencies: ${dependencies.join(", ")}`);
     }
 }
packages/metadata/src/providers/dummy.provider.ts (1)

6-9: Enhance JSDoc documentation.

The @inheritdoc tag alone is insufficient. Please add a comprehensive JSDoc comment describing the purpose of this dummy implementation and its behavior.

-    /* @inheritdoc */
+    /**
+     * A dummy implementation that always returns undefined.
+     * @inheritdoc
+     * @param _ipfsCid - The IPFS CID to fetch metadata for (unused in this implementation)
+     * @returns A Promise that always resolves to undefined
+     */
packages/metadata/src/interfaces/index.ts (1)

3-26: LGTM! Consider adding JSDoc comments.

The type definitions are well-structured with good use of discriminated unions and conditional types for type safety. Consider adding JSDoc comments to document the purpose and usage of each type.

Add JSDoc comments like this:

+/**
+ * Supported metadata provider types
+ */
 export type MetadataProvider = "pinata" | "public-gateway" | "dummy";

+/**
+ * Configuration for dummy metadata provider
+ */
 export type DummyMetadataConfig = {
     metadataSource: "dummy";
 };
scripts/bootstrap/src/utils/parsing.ts (1)

9-14: Consider adding more schema validations.

The schema could benefit from additional validations for the schema name.

Add these validations:

 const zodSchema = z.object({
     schema: z
         .string()
+        .min(1)
+        .regex(/^[a-z0-9_]+$/, 'Schema name must contain only lowercase letters, numbers, and underscores')
         .default(DEFAULT_SCHEMA)
         .describe("Database schema name where migrations are applied"),
 });
packages/data-flow/src/helpers/index.ts (1)

16-33: Consider refactoring decode attempts for better maintainability.

The current implementation has repetitive try-catch blocks. Consider refactoring to reduce duplication and improve maintainability.

 for (const event of events) {
     if ("metadata" in event.params) {
         ids.add(event.params.metadata[1]);
     } else if ("data" in event.params) {
-        try {
-            const decoded = decodeDGApplicationData(event.params.data);
-            ids.add(decoded.metadata.pointer);
-        } catch (error) {}
-        try {
-            const decoded = decodeDVMDApplicationData(event.params.data);
-            ids.add(decoded.metadata.pointer);
-        } catch (error) {}
-        try {
-            const decoded = decodeDVMDExtendedApplicationData(event.params.data);
-            ids.add(decoded.metadata.pointer);
-        } catch (error) {}
+        const decoders = [
+            decodeDGApplicationData,
+            decodeDVMDApplicationData,
+            decodeDVMDExtendedApplicationData
+        ];
+        
+        for (const decoder of decoders) {
+            try {
+                const decoded = decoder(event.params.data);
+                ids.add(decoded.metadata.pointer);
+                break; // Stop if successful
+            } catch (error) {
+                console.warn(`Failed to decode with ${decoder.name}: ${error}`);
+            }
+        }
     }
 }
packages/repository/src/repositories/memory/prices.repository.ts (1)

33-45: Add timestamp range validation.

The getPricesByTimeRange method should validate that startTimestampMs is less than or equal to endTimestampMs.

 async getPricesByTimeRange(
     tokenCode: TokenCode,
     startTimestampMs: TimestampMs,
     endTimestampMs: TimestampMs,
 ): Promise<TokenPrice[]> {
+    if (startTimestampMs > endTimestampMs) {
+        throw new Error('startTimestampMs must be less than or equal to endTimestampMs');
+    }
     const keyString = `${tokenCode}`;
     const result = Array.from(this.cache.get(keyString)?.values() ?? []);
     return result.filter(
         (price) => price.timestampMs >= startTimestampMs && price.timestampMs <= endTimestampMs,
     );
 }
scripts/migrations/src/utils/parsing.ts (1)

14-19: Consider extracting folder names to constants.

The migration folder names are hardcoded in multiple places. Consider extracting them to named constants for better maintainability.

+const MIGRATION_FOLDERS = {
+    PROCESSING: 'processing',
+    EXTERNAL_SERVICES_CACHE: 'external-services-cache'
+} as const;
+
 migrationsFolder: z
     .string()
-    .refine((value) => ["processing", "external-services-cache"].includes(value), {
+    .refine((value) => Object.values(MIGRATION_FOLDERS).includes(value), {
         message: "Invalid migrations folder",
     })
-    .default("processing"),
+    .default(MIGRATION_FOLDERS.PROCESSING),
packages/metadata/src/providers/pinata.provider.ts (3)

16-17: Remove commented out console.log statements.

Remove debug console.log statements that were left commented out. These should not be committed to the codebase.

-        // console.log("pinataJwt", this.pinataJwt);
-        // console.log("pinataGateway", this.pinataGateway);
-            // console.log("ipfsCid", ipfsCid);
-            // console.log("pinataClient", this.pinataClient.gateways);
-            // console.log("pinataResponse", pinataResponse);

Also applies to: 30-31, 36-36


8-22: Add JSDoc documentation for class and constructor.

Add comprehensive JSDoc documentation to improve code maintainability.

+/**
+ * Provider implementation for fetching metadata from Pinata IPFS gateway.
+ * @implements {IMetadataProvider}
+ */
 export class PinataProvider implements IMetadataProvider {
     private readonly pinataClient: PinataSDK;
 
+    /**
+     * Creates a new PinataProvider instance.
+     * @param pinataJwt - JWT token for Pinata authentication
+     * @param pinataGateway - Pinata gateway URL
+     * @param logger - Logger instance for error and debug logging
+     */
     constructor(

42-48: Specify error type in catch block.

Add type annotation for the error parameter to ensure proper error handling.

-        } catch (error) {
+        } catch (error: unknown) {
packages/metadata/src/factory/index.ts (2)

14-16: Fix incorrect JSDoc comments.

The JSDoc comments mention "pricing" instead of "metadata":

 /**
- * Factory class for creating pricing providers.
+ * Factory class for creating metadata providers.
  */
     /**
-     * Creates a pricing provider based on the provided configuration.
-     * @param options - The pricing configuration.
+     * Creates a metadata provider based on the provided configuration.
+     * @param options - The metadata configuration.
      * @param deps - dependencies to inject into the pricing provider.
-     * @returns The created pricing provider.
+     * @returns The created metadata provider.

Also applies to: 19-23


17-56: Consider using a namespace or functions instead of a static class.

The class contains only static members. Consider using a namespace or simple functions instead.

-export class MetadataProviderFactory {
-    static create(
+export const MetadataProviderFactory = {
+    create(
         options: MetadataConfig<MetadataProvider>,
         deps?: {
             logger?: ILogger;
         },
     ): IMetadataProvider {
🧰 Tools
🪛 Biome (1.9.4)

[error] 17-56: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

packages/metadata/src/providers/publicGateway.provider.ts (1)

20-20: Consider making timeout configurable.

The hardcoded timeout of 3000ms might not be suitable for all environments or network conditions. Consider making it configurable through constructor parameters with this as a default.

-        this.axiosInstance = axios.create({ timeout: 3000 });
+        this.axiosInstance = axios.create({ timeout: options?.timeout ?? 3000 });

And update the constructor:

     constructor(
         private readonly gateways: string[],
         private readonly logger: ILogger,
+        private readonly options?: { timeout?: number },
     ) {
packages/repository/src/repositories/kysely/prices.repository.ts (1)

69-99: Consider adding an index for query optimization.

The getPricesByTimeRange implementation is correct, but frequent queries might benefit from an index on (tokenCode, timestampMs).

Consider adding the following index to improve query performance:

CREATE INDEX idx_price_cache_token_timestamp 
ON priceCache(tokenCode, timestampMs);
packages/processors/src/processors/strategy/donationVotingMerkleDistributionDirectTransfer/handlers/allocated.handler.ts (1)

68-73: Consider using a constant for timestamp conversion.

The multiplication by 1000 to convert seconds to milliseconds would be clearer with a named constant.

+const SECONDS_TO_MS = 1000;
+
 const { amountInUsd } = await getTokenAmountInUsd(
     this.dependencies.pricingProvider,
     token,
     amount,
     this.event.blockTimestamp,
 );
 // ...
-timestamp: new Date(this.event.blockTimestamp * 1000),
+timestamp: new Date(this.event.blockTimestamp * SECONDS_TO_MS),

Also applies to: 103-103

apps/processing/src/services/sharedDependencies.service.ts (1)

52-58: Consider using an enum for environment types.

The environment check could be more maintainable using an enum.

+enum Environment {
+  Development = 'development',
+  Staging = 'staging',
+  Production = 'production'
+}
+
 const kyselyDatabase = createKyselyDatabase(
     {
         connectionString: env.DATABASE_URL,
-        isProduction: env.NODE_ENV === "production" || env.NODE_ENV === "staging",
+        isProduction: [Environment.Production, Environment.Staging].includes(env.NODE_ENV as Environment),
     },
     logger,
 );
packages/pricing/src/providers/coingecko.provider.ts (1)

18-18: Remove commented-out code.

These commented-out lines should be removed as they add noise to the codebase.

Also applies to: 40-40, 48-48, 59-59

packages/indexer-client/src/providers/envioIndexerClient.ts (1)

270-299: Add JSDoc comments for the new method.

The new method getBlockRangeTimestampByChainId should include JSDoc comments to document its purpose, parameters, and return type.

Apply this diff to add JSDoc comments:

+/**
+ * Get the earliest and latest block timestamps for a specific chain ID.
+ * @param chainId - The chain ID to query.
+ * @returns A promise that resolves to an object containing the earliest (from) and latest (to) block timestamps.
+ * @throws {Error} If no block range is found for the specified chain ID.
+ */
async getBlockRangeTimestampByChainId(chainId: ChainId): Promise<{ from: number; to: number }> {
packages/shared/src/tokens/tokens.ts (1)

26-36: Consider performance optimization for large token maps.

The implementation is correct but could be optimized for large token maps by using a more efficient data structure.

Consider this alternative implementation using Object.values() with reduce:

-export const extractAllPriceSourceCodes = (tokensMap: TokensMap): TokenCode[] => {
-    const priceSourceCodes = new Set<TokenCode>();
-
-    Object.values(tokensMap).forEach((chainTokens) => {
-        Object.values(chainTokens).forEach((token) => {
-            priceSourceCodes.add(token.priceSourceCode);
-        });
-    });
-
-    return Array.from(priceSourceCodes);
-};
+export const extractAllPriceSourceCodes = (tokensMap: TokensMap): TokenCode[] => 
+    Array.from(
+        new Set(
+            Object.values(tokensMap).reduce<TokenCode[]>(
+                (codes, chainTokens) => codes.concat(
+                    Object.values(chainTokens).map(token => token.priceSourceCode)
+                ),
+                []
+            )
+        )
+    );
packages/indexer-client/test/unit/envioIndexerClient.spec.ts (1)

36-36: Fix typo in comment.

The comment contains a typo: "datåa" should be "data".

-        // Sample test datåa
+        // Sample test data
packages/data-flow/test/unit/orchestrator.spec.ts (1)

34-35: Clean up commented code.

Large blocks of commented-out test code and imports should be removed if they are no longer needed, or documented if they are temporarily disabled.

If these tests are obsolete, please remove them. If they are temporarily disabled, add a TODO comment explaining why and when they will be re-enabled.

Also applies to: 775-869

scripts/migrations/package.json (1)

19-20: Added Cache Migration and Reset Scripts
The new scripts:

  • "db:cache:migrate": "tsx src/migrateDb.script.ts --migrationsFolder external-services-cache"
  • "db:cache:reset": "tsx src/resetDb.script.ts --migrationsFolder external-services-cache"
    are introduced to handle cache-specific migrations. Please ensure these scripts are well documented and that environment variables (if any) are adequately configured for different environments.
package.json (1)

11-12: Bootstrap Scripts Added for Metadata and Pricing
The addition of "bootstrap:metadata" and "bootstrap:pricing" scripts using the run filter is well-integrated. Please confirm that corresponding documentation (and any related tests) reflect these commands to avoid confusion during handoffs.

scripts/bootstrap/README.md (2)

73-73: Typographical Issue in Documentation Link
The migration file path in the instructions ([packages/repository/src/migrations](../../packages//repository/migrations)) contains a double slash (//). Please correct it to a single slash (../../packages/repository/migrations) for clarity and correctness.


112-112: End-to-End Tests Reminder
The README includes a TODO to add E2E tests for the scripts. This is critical for ensuring reliability, so please prioritize adding these tests before merging.

scripts/migrations/README.md (3)

57-64: Fenced Code Block Lacks Language Specification.
In the "Migrating Cache" section (lines 61–63), the fenced code block does not specify a language. This may trigger markdown lint warnings (see MD040). Consider adding the appropriate language (e.g., bash) to improve readability and tooling support.

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

61-61: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


86-92: Missing Language Specification in Reset Cache Code Block.
Similar to the "Migrating Cache" section, the "Resetting Cache" code block (lines 91–92) lacks a language designation. Adding bash (or the relevant shell) will help maintain consistency and clarity in documentation.

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

90-90: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


143-143: Reminder to Add E2E Tests.
The TODO note at line 143 reminds contributors to add end-to-end tests for these scripts. Please ensure E2E tests are implemented and validated prior to merging.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2494cb6 and 37c63ae.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (70)
  • .env.example (0 hunks)
  • Dockerfile (1 hunks)
  • apps/processing/src/config/env.ts (4 hunks)
  • apps/processing/src/services/sharedDependencies.service.ts (3 hunks)
  • apps/processing/test/unit/sharedDependencies.service.spec.ts (3 hunks)
  • docker-compose.yaml (3 hunks)
  • package.json (2 hunks)
  • packages/data-flow/package.json (1 hunks)
  • packages/data-flow/src/external.ts (1 hunks)
  • packages/data-flow/src/helpers/index.ts (1 hunks)
  • packages/data-flow/src/internal.ts (1 hunks)
  • packages/data-flow/src/orchestrator.ts (9 hunks)
  • packages/data-flow/test/unit/eventsFetcher.spec.ts (1 hunks)
  • packages/data-flow/test/unit/orchestrator.spec.ts (2 hunks)
  • packages/indexer-client/src/interfaces/indexerClient.ts (1 hunks)
  • packages/indexer-client/src/providers/envioIndexerClient.ts (2 hunks)
  • packages/indexer-client/test/unit/envioIndexerClient.spec.ts (3 hunks)
  • packages/metadata/package.json (1 hunks)
  • packages/metadata/src/exceptions/invalidMetadataSource.exception.ts (1 hunks)
  • packages/metadata/src/exceptions/missingDependencies.exception.ts (1 hunks)
  • packages/metadata/src/external.ts (1 hunks)
  • packages/metadata/src/factory/index.ts (1 hunks)
  • packages/metadata/src/interfaces/index.ts (1 hunks)
  • packages/metadata/src/interfaces/metadata.interface.ts (1 hunks)
  • packages/metadata/src/internal.ts (1 hunks)
  • packages/metadata/src/providers/cachingProxy.provider.ts (1 hunks)
  • packages/metadata/src/providers/dummy.provider.ts (1 hunks)
  • packages/metadata/src/providers/index.ts (1 hunks)
  • packages/metadata/src/providers/pinata.provider.ts (1 hunks)
  • packages/metadata/src/providers/publicGateway.provider.ts (4 hunks)
  • packages/pricing/src/constants/index.ts (1 hunks)
  • packages/pricing/src/exceptions/noClosePriceFound.exception.ts (1 hunks)
  • packages/pricing/src/providers/cachingProxy.provider.ts (8 hunks)
  • packages/pricing/src/providers/coingecko.provider.ts (5 hunks)
  • packages/pricing/test/providers/cachingProxy.provider.spec.ts (2 hunks)
  • packages/pricing/test/providers/coingecko.provider.spec.ts (2 hunks)
  • packages/processors/src/external.ts (1 hunks)
  • packages/processors/src/internal.ts (1 hunks)
  • packages/processors/src/processors/allo/handlers/poolCreated.handler.ts (0 hunks)
  • packages/processors/src/processors/strategy/directAllocation/handlers/directAllocated.handler.ts (2 hunks)
  • packages/processors/src/processors/strategy/donationVotingMerkleDistributionDirectTransfer/handlers/allocated.handler.ts (2 hunks)
  • packages/repository/src/db/connection.ts (2 hunks)
  • packages/repository/src/external.ts (1 hunks)
  • packages/repository/src/repositories/kysely/prices.repository.ts (2 hunks)
  • packages/repository/src/repositories/memory/prices.repository.ts (2 hunks)
  • packages/shared/src/external.ts (1 hunks)
  • packages/shared/src/retry/exponentialBackoff.strategy.ts (1 hunks)
  • packages/shared/src/tokens/tokens.ts (4 hunks)
  • packages/shared/src/utils/testing.ts (1 hunks)
  • scripts/bootstrap/.env.example (1 hunks)
  • scripts/bootstrap/README.md (1 hunks)
  • scripts/bootstrap/package.json (1 hunks)
  • scripts/bootstrap/src/metadata.script.ts (1 hunks)
  • scripts/bootstrap/src/pricing.script.ts (1 hunks)
  • scripts/bootstrap/src/schemas/index.ts (1 hunks)
  • scripts/bootstrap/src/utils/index.ts (1 hunks)
  • scripts/bootstrap/src/utils/kysely.ts (1 hunks)
  • scripts/bootstrap/src/utils/parsing.ts (1 hunks)
  • scripts/bootstrap/tsconfig.build.json (1 hunks)
  • scripts/bootstrap/tsconfig.json (1 hunks)
  • scripts/bootstrap/vitest.config.ts (1 hunks)
  • scripts/migrations/README.md (3 hunks)
  • scripts/migrations/package.json (1 hunks)
  • scripts/migrations/src/migrateDb.script.ts (2 hunks)
  • scripts/migrations/src/migrations/external-services-cache/20250127T000000_add_cache_tables.ts (1 hunks)
  • scripts/migrations/src/migrations/processing/20241029T120000_initial.ts (2 hunks)
  • scripts/migrations/src/resetDb.script.ts (2 hunks)
  • scripts/migrations/src/schemas/index.ts (1 hunks)
  • scripts/migrations/src/utils/kysely.ts (3 hunks)
  • scripts/migrations/src/utils/parsing.ts (2 hunks)
💤 Files with no reviewable changes (2)
  • .env.example
  • packages/processors/src/processors/allo/handlers/poolCreated.handler.ts
✅ Files skipped from review due to trivial changes (8)
  • scripts/bootstrap/tsconfig.json
  • packages/data-flow/src/internal.ts
  • scripts/migrations/src/migrations/processing/20241029T120000_initial.ts
  • scripts/bootstrap/tsconfig.build.json
  • scripts/bootstrap/src/utils/index.ts
  • scripts/bootstrap/.env.example
  • packages/metadata/src/providers/index.ts
  • scripts/bootstrap/package.json
🧰 Additional context used
📓 Path-based instructions (5)
`**/*.ts`: Review TypeScript files for adherence to the fo...

**/*.ts: Review TypeScript files for adherence to the following guidelines:
- Avoid over-abstraction; prioritize composition over inheritance.
- Use dependency injection and follow SOLID principles.
- Avoid any; use unknown when necessary.
- Use runtime type-checking for environment variables (e.g., Zod).
- Prevent circular dependencies with the internal module pattern.
- Libraries should have an external.ts file explicitly listing public exports.
- Use bigint as-is; cast to Number only when exposing values via APIs.
- Document all code with JSDoc.
- Encourage static async factory functions for constructors.

  • Avoid overly nitpicky feedback beyond these best practices.
  • packages/pricing/src/exceptions/noClosePriceFound.exception.ts
  • packages/metadata/src/exceptions/missingDependencies.exception.ts
  • packages/metadata/src/internal.ts
  • scripts/migrations/src/schemas/index.ts
  • scripts/migrations/src/migrations/external-services-cache/20250127T000000_add_cache_tables.ts
  • packages/data-flow/test/unit/eventsFetcher.spec.ts
  • packages/metadata/src/exceptions/invalidMetadataSource.exception.ts
  • packages/pricing/src/constants/index.ts
  • packages/metadata/src/providers/dummy.provider.ts
  • packages/data-flow/src/external.ts
  • packages/data-flow/test/unit/orchestrator.spec.ts
  • packages/shared/src/utils/testing.ts
  • packages/processors/src/external.ts
  • packages/shared/src/retry/exponentialBackoff.strategy.ts
  • packages/data-flow/src/helpers/index.ts
  • apps/processing/test/unit/sharedDependencies.service.spec.ts
  • scripts/migrations/src/utils/kysely.ts
  • packages/repository/src/db/connection.ts
  • packages/processors/src/processors/strategy/donationVotingMerkleDistributionDirectTransfer/handlers/allocated.handler.ts
  • packages/metadata/src/external.ts
  • packages/pricing/test/providers/coingecko.provider.spec.ts
  • scripts/migrations/src/migrateDb.script.ts
  • packages/repository/src/external.ts
  • packages/pricing/src/providers/coingecko.provider.ts
  • scripts/migrations/src/utils/parsing.ts
  • packages/metadata/src/interfaces/metadata.interface.ts
  • scripts/bootstrap/src/pricing.script.ts
  • scripts/migrations/src/resetDb.script.ts
  • packages/metadata/src/factory/index.ts
  • packages/shared/src/external.ts
  • packages/processors/src/processors/strategy/directAllocation/handlers/directAllocated.handler.ts
  • scripts/bootstrap/src/metadata.script.ts
  • packages/pricing/test/providers/cachingProxy.provider.spec.ts
  • packages/metadata/src/providers/pinata.provider.ts
  • scripts/bootstrap/vitest.config.ts
  • packages/metadata/src/interfaces/index.ts
  • packages/metadata/src/providers/cachingProxy.provider.ts
  • scripts/bootstrap/src/utils/parsing.ts
  • packages/metadata/src/providers/publicGateway.provider.ts
  • packages/indexer-client/test/unit/envioIndexerClient.spec.ts
  • scripts/bootstrap/src/schemas/index.ts
  • packages/repository/src/repositories/memory/prices.repository.ts
  • packages/indexer-client/src/providers/envioIndexerClient.ts
  • packages/repository/src/repositories/kysely/prices.repository.ts
  • packages/data-flow/src/orchestrator.ts
  • apps/processing/src/services/sharedDependencies.service.ts
  • packages/processors/src/internal.ts
  • scripts/bootstrap/src/utils/kysely.ts
  • apps/processing/src/config/env.ts
  • packages/pricing/src/providers/cachingProxy.provider.ts
  • packages/indexer-client/src/interfaces/indexerClient.ts
  • packages/shared/src/tokens/tokens.ts
`scripts/**/*.ts`: Ensure scripts: - Use `process.cw...

scripts/**/*.ts: Ensure scripts:
- Use process.cwd() for root references.
- Follow folder conventions (infra/ for infra scripts, utilities/ for utilities).
- Are organized in package.json with script:infra:{name} or script:util:{name}.

  • Be concise and avoid overly nitpicky feedback outside of these best practices.
  • scripts/migrations/src/schemas/index.ts
  • scripts/migrations/src/migrations/external-services-cache/20250127T000000_add_cache_tables.ts
  • scripts/migrations/src/utils/kysely.ts
  • scripts/migrations/src/migrateDb.script.ts
  • scripts/migrations/src/utils/parsing.ts
  • scripts/bootstrap/src/pricing.script.ts
  • scripts/migrations/src/resetDb.script.ts
  • scripts/bootstrap/src/metadata.script.ts
  • scripts/bootstrap/vitest.config.ts
  • scripts/bootstrap/src/utils/parsing.ts
  • scripts/bootstrap/src/schemas/index.ts
  • scripts/bootstrap/src/utils/kysely.ts
`**/*.spec.ts`: Review the unit test files with the followin...

**/*.spec.ts: Review the unit test files with the following guidelines:
- Avoid using the word "should" in test descriptions.
- Ensure descriptive test names convey the intent of each test.
- Validate adherence to the Mocha/Chai/Jest test library best practices.

  • Be concise and avoid overly nitpicky feedback outside of these best practices.
  • packages/data-flow/test/unit/eventsFetcher.spec.ts
  • packages/data-flow/test/unit/orchestrator.spec.ts
  • apps/processing/test/unit/sharedDependencies.service.spec.ts
  • packages/pricing/test/providers/coingecko.provider.spec.ts
  • packages/pricing/test/providers/cachingProxy.provider.spec.ts
  • packages/indexer-client/test/unit/envioIndexerClient.spec.ts
`**/providers/**/*.ts`: Review provider files for the follow...

**/providers/**/*.ts: Review provider files for the following:

  • Providers should supply narrowly scoped data/resources.
  • Ensure classes interacting with metadata sources (e.g., GitHub, JSON files, IPFS) implement the IMetadataProvider interface and follow naming conventions (e.g., GithubProvider, JsonFileProvider).
  • Be concise and avoid overly nitpicky feedback outside of these best practices.
  • packages/metadata/src/providers/dummy.provider.ts
  • packages/pricing/test/providers/coingecko.provider.spec.ts
  • packages/pricing/src/providers/coingecko.provider.ts
  • packages/pricing/test/providers/cachingProxy.provider.spec.ts
  • packages/metadata/src/providers/pinata.provider.ts
  • packages/metadata/src/providers/cachingProxy.provider.ts
  • packages/metadata/src/providers/publicGateway.provider.ts
  • packages/indexer-client/src/providers/envioIndexerClient.ts
  • packages/pricing/src/providers/cachingProxy.provider.ts
`**/services/**/*.ts`: Review service files with the followi...

**/services/**/*.ts: Review service files with the following considerations:
- A Service encapsulates a broader business workflow and might orchestrate various components or interact with multiple data sources or APIs.
- Ensure proper composability: Services should use Providers for data/resource access and apply business/orchestration logic.
- Validate naming conventions for aggregating services (e.g., AggregatorService or domain-specific names like MetricsService).

  • apps/processing/src/services/sharedDependencies.service.ts
🪛 Biome (1.9.4)
packages/metadata/src/providers/dummy.provider.ts

[error] 4-4: This constructor is unnecessary.

Unsafe fix: Remove the unnecessary constructor.

(lint/complexity/noUselessConstructor)

packages/metadata/src/factory/index.ts

[error] 17-56: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

🪛 markdownlint-cli2 (0.17.2)
scripts/migrations/README.md

61-61: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


90-90: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

🪛 LanguageTool
scripts/bootstrap/README.md

[grammar] ~74-~74: An auxiliary verb seems to be missing from this progressive structure. Did you mean “it's using”, “it is using”, or “it was using”?
Context: ...ackages//repository/migrations) 2. Name it using the format: `YYYYMMDDTHHmmss_descriptio...

(PRP_VBG)

🪛 GitHub Check: lint / Run Linters
packages/pricing/src/providers/cachingProxy.provider.ts

[failure] 7-7:
'CacheResult' is defined but never used


[failure] 99-99:
'fromTimestampMs' is never reassigned. Use 'const' instead


[failure] 100-100:
'toTimestampMs' is never reassigned. Use 'const' instead

🪛 ESLint
packages/pricing/src/providers/cachingProxy.provider.ts

[error] 7-7: 'CacheResult' is defined but never used.

(@typescript-eslint/no-unused-vars)


[error] 99-99: 'fromTimestampMs' is never reassigned. Use 'const' instead.

(prefer-const)


[error] 100-100: 'toTimestampMs' is never reassigned. Use 'const' instead.

(prefer-const)

🪛 GitHub Actions: Main Workflow
packages/pricing/src/providers/cachingProxy.provider.ts

[error] 7-7: 'CacheResult' is defined but never used

🔇 Additional comments (54)
packages/shared/src/external.ts (1)

12-12: LGTM! Well-organized exports.

The changes maintain a clean public API surface by explicitly listing exports and grouping them logically.

Also applies to: 20-26

scripts/bootstrap/src/schemas/index.ts (1)

20-24: Verify required environment variables for production.

You have optional and defaulted fields for development, but ensure that PUBLIC_GATEWAY_URLS, CHAIN_IDS, etc., are properly tested or validated for production scenarios.

Would you like to add stricter runtime checks for production mode to ensure no critical fields are left at defaults?

apps/processing/src/config/env.ts (1)

26-26: Ensure consistent usage of optional secrets and metadata source defaults.

By making INDEXER_ADMIN_SECRET optional and defaulting METADATA_SOURCE to "dummy," you risk runtime misconfigurations if these values are unintentionally omitted in production. Review and confirm these defaults align with the intended deployment scenario.

Also applies to: 35-37

scripts/bootstrap/src/metadata.script.ts (1)

67-71: Consider verifying table existence before inserting dummy rows.
This block attempts an insert into the metadataCache table without validating that the table was created or whether migrations were applied. While this may work in dev environments, it could cause unexpected errors in production or CI pipelines if the table doesn’t exist yet.

Would you like me to generate a shell script to confirm the table existence prior to these test inserts?

packages/data-flow/src/orchestrator.ts (1)

137-143: Verify the 30-minute threshold assumption carefully.
Here you only bulk-fetch metadata and prices if the first event in the batch is older than 30 minutes. Consider whether events near the 30-minute boundary might be missed. Confirm that this logic aligns with your indexing and data completeness goals.

packages/metadata/src/internal.ts (1)

5-5: LGTM!

The new export follows the established pattern and maintains consistency with the internal module pattern.

packages/pricing/src/constants/index.ts (1)

1-3: LGTM! Well-documented constant update.

The change to MIN_GRANULARITY_MS is properly documented with a reference to Coingecko's Enterprise plans, and the value is correctly formatted for readability.

packages/metadata/src/external.ts (1)

1-6: LGTM! Well-organized exports.

The exports are properly organized and follow the guideline of having an external.ts file for explicit public exports.

packages/processors/src/internal.ts (1)

16-17: LGTM! Well-organized export addition.

The new decoder export follows the established pattern and is properly categorized with a clear comment.

packages/data-flow/src/external.ts (1)

8-8: LGTM!

The new export is properly added to the existing export block, following the TypeScript guideline of having an external.ts file explicitly listing public exports.

packages/processors/src/external.ts (1)

10-14: LGTM!

The new exports are properly added in a separate export block, following the TypeScript guideline of having an external.ts file explicitly listing public exports.

packages/metadata/src/interfaces/metadata.interface.ts (1)

10-17: LGTM!

The interface follows TypeScript best practices:

  • Uses generics for type safety
  • Uses Zod for runtime type-checking
  • Has clear documentation explaining the return type changes
scripts/migrations/src/schemas/index.ts (1)

5-5: LGTM!

The schema follows TypeScript best practices:

  • Uses Zod for runtime type-checking of environment variables
  • Has a default value for development
  • Uses standard environment names
scripts/bootstrap/vitest.config.ts (1)

1-22: LGTM! Well-structured Vitest configuration.

The configuration is comprehensive with:

  • Proper test file patterns
  • Multiple coverage reporters
  • Path alias setup
  • Clear exclusions for node_modules and dist
scripts/migrations/src/migrations/external-services-cache/20250127T000000_add_cache_tables.ts (1)

17-17: Verify the nullable metadata design.

The metadata column is defined as nullable. This could lead to cache entries with no metadata, which might cause issues in the application logic. Consider:

  1. Whether this is intentional
  2. How the application handles null metadata
  3. If validation should be added at the application level

Let's check how the metadata column is used:

packages/indexer-client/src/interfaces/indexerClient.ts (1)

36-41: LGTM! Well-documented interface addition.

The new method is properly documented and follows TypeScript best practices with clear type definitions.

packages/metadata/src/providers/pinata.provider.ts (1)

41-41: Add runtime type validation for metadata.

The type casting as T is unsafe. Consider adding runtime type validation using Zod schema.

-            return pinataResponse.data as T;
+            if (validateContent) {
+                return validateContent.parse(pinataResponse.data);
+            }
+            return pinataResponse.data as T;
✅ Verification successful

Error: No shell scripts or web queries found


Runtime Type Validation Update Needed

The original code performs an unsafe type assertion by casting the response data directly. The proposed diff correctly adds a runtime validation check using a schema validation function (e.g., one from Zod) when available:

  • At packages/metadata/src/providers/pinata.provider.ts (line 41), the suggestion is to:
    • Check if a validateContent function is provided.
    • If so, run validateContent.parse(pinataResponse.data) to validate the incoming data.
    • Otherwise, fall back to returning the data with an unsafe cast.

This change improves the type safety of the metadata by ensuring that the runtime data conforms to expectations before being used.

packages/shared/src/retry/exponentialBackoff.strategy.ts (1)

24-26: Verify the impact of more aggressive retry settings.

The retry strategy has been made more aggressive:

  • baseDelay reduced from 5000ms to 200ms
  • factor reduced from 2 to 1.5
  • maxAttempts increased from 3 to 10

This could potentially lead to increased load on external services. Please ensure these values are appropriate for your use case.

packages/metadata/src/providers/cachingProxy.provider.ts (1)

25-52: LGTM! Robust null handling implementation.

The changes properly handle null values in the cache, with appropriate type updates and conditional checks. The error handling and logging are well implemented.

packages/repository/src/external.ts (1)

79-79: LGTM!

The addition of IPricingCache to the exports is well-organized, grouped with related pricing types.

scripts/migrations/src/migrateDb.script.ts (2)

45-45: LGTM! Production safeguards added.

Good addition of the isProduction flag based on environment, which helps configure database connections appropriately for production/staging environments.


36-36: Verify NODE_ENV validation.

Consider validating the NODE_ENV value to ensure it only accepts valid environments.

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Check if NODE_ENV validation exists in the schema
rg -A 5 'NODE_ENV.*z\.' ./scripts/migrations/src/schemas/

Length of output: 517


NODE_ENV is properly validated in the schema.

  • The NODE_ENV field is validated with a Zod enumeration in ./scripts/migrations/src/schemas/index.ts using z.enum(["development", "staging", "production"]).default("development").
packages/metadata/src/providers/publicGateway.provider.ts (1)

41-44: LGTM! Improved error handling and validation.

Good improvements:

  • Added JSON validation before processing
  • Clear error messages for invalid content
  • Throwing error after exhausting all gateways instead of silent failure

Also applies to: 57-57

packages/data-flow/test/unit/eventsFetcher.spec.ts (1)

21-21: LGTM!

The mock function addition is consistent with the interface changes and follows testing best practices.

packages/processors/src/processors/strategy/directAllocation/handlers/directAllocated.handler.ts (1)

60-65: LGTM! Improved timestamp handling.

The changes correctly use the block timestamp for donation records instead of the price timestamp, which is more accurate for recording when the donation occurred. The conversion from seconds to milliseconds is properly handled.

Also applies to: 81-81

scripts/migrations/src/utils/kysely.ts (1)

19-19: LGTM! Enhanced database organization with domain-specific migration tables.

The addition of the domain property and its use in migration table names improves database organization by preventing conflicts when multiple domains share the same database.

Also applies to: 56-57, 98-99

packages/repository/src/db/connection.ts (1)

111-115: ⚠️ Potential issue

Review SSL configuration security.

Setting rejectUnauthorized: false in production disables SSL certificate validation, which could expose the application to man-in-the-middle attacks. Consider:

  1. Using valid SSL certificates in production
  2. Setting rejectUnauthorized: true for proper certificate validation
  3. Using environment variables to configure SSL options for different environments
packages/pricing/test/providers/cachingProxy.provider.spec.ts (1)

3-3: LGTM! Test updates align with interface changes.

The changes correctly reflect the new IPricingCache interface and include the new getPricesByTimeRange method in the mock implementation.

Also applies to: 19-19, 21-21

packages/repository/src/repositories/kysely/prices.repository.ts (1)

8-21: LGTM! Well-documented interface extension.

The IPricingCache interface is well-defined with clear JSDoc documentation.

apps/processing/src/services/sharedDependencies.service.ts (1)

94-96: LGTM! Factory pattern improves provider initialization.

The use of MetadataProviderFactory enhances maintainability and configuration flexibility.

apps/processing/test/unit/sharedDependencies.service.spec.ts (3)

4-4: LGTM!

The import statement correctly reflects the switch from IpfsProvider to PublicGatewayProvider.


109-110: LGTM!

The mockEnv type and object correctly include the new METADATA_SOURCE configuration.

Also applies to: 116-116


134-134: LGTM!

The test expectation correctly verifies the initialization of PublicGatewayProvider with the updated environment configuration.

packages/pricing/test/providers/coingecko.provider.spec.ts (1)

224-224: LGTM!

The test cases correctly assert timestamp parameters as TimestampMs[] type, improving type safety.

Also applies to: 229-231, 242-244

packages/pricing/src/providers/coingecko.provider.ts (2)

148-148: LGTM!

The method signature correctly uses TimestampMs[] type for better type safety.


157-202: LGTM!

The implementation correctly handles:

  1. Empty timestamps array
  2. Minimum granularity for timestamps
  3. Segmentation of requests spanning more than 90 days
  4. Parallel processing of segments
packages/indexer-client/src/providers/envioIndexerClient.ts (1)

23-28: LGTM!

The constructor correctly handles the optional secret parameter.

packages/shared/src/tokens/tokens.ts (2)

20-24: LGTM! Well-structured type definition.

The TokensMap type provides a clear and type-safe way to represent the token mapping structure.


96-96: Verify the impact of changing the price source code.

The price source code for token at address 0x93A5347036f69BC6f37Ed2b59CBcDDa927719217 is changed from "DAI" to "GIST".

Run this script to check for any dependencies on the old price source code:

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Search for any hardcoded references to the DAI price source code for this token
rg -l "0x93A5347036f69BC6f37Ed2b59CBcDDa927719217.*DAI"

Length of output: 57


I'll run additional searches to gather more context around this token in the codebase. One moment while I execute a shell script to check for any remaining references to the hardcoded "DAI" price source for the token.

Please run this script so we can verify the surrounding context and ensure that there are no remaining dependencies on the old "DAI" string for the token.


🏁 Script executed:

#!/bin/bash
# Checking for any references to the token's address and its price source code context in tokens.ts
echo "Checking token address context in packages/shared/src/tokens/tokens.ts:"
rg -n "0x93A5347036f69BC6f37Ed2b59CBcDDa927719217" packages/shared/src/tokens/tokens.ts -C 3

echo "Searching for any assignment of the price source code 'DAI' in tokens.ts:"
rg -n 'priceSourceCode:\s*"DAI"' packages/shared/src/tokens/tokens.ts -C 3

Length of output: 3492


Impact Verification: Price source update localized and confirmed

The investigation shows that the token data for address 0x93A5347036f69BC6f37Ed2b59CBcDDa927719217 uses "GIST" for both code and priceSourceCode, and there are no detected dependencies or hardcoded references combining this token’s address with "DAI". The change appears to be isolated to this token entry.

packages/indexer-client/test/unit/envioIndexerClient.spec.ts (1)

112-114: Document the reason for removing the header test.

The test for x-hasura-admin-secret header is commented out without explanation.

Please add a comment explaining why this test was removed or if it should be deleted entirely.

Dockerfile (2)

5-5: LGTM! Explicit version pinning improves reproducibility.

Pinning pnpm to version 9.12.0 ensures consistent builds across environments.


12-12: Verify the impact of removing --prod flag.

The --prod flag has been removed from the deploy command which might affect the production dependencies.

Please confirm if this change is intentional and that all required dependencies will be included in the production build.

packages/metadata/package.json (1)

31-36: New Dependency Added: "pinata"
The new "pinata": "1.10.1" dependency is added correctly under the dependencies section. Ensure that the corresponding provider (e.g., PinataProvider) properly integrates this package for IPFS-related functionality.

packages/data-flow/package.json (1)

37-39: New Dependency "p-map" Introduced
The addition of "p-map": "7.0.3" enhances the package’s ability to manage concurrent asynchronous operations. Verify that its usage in orchestration logic (e.g., in bulkFetchMetadataAndPricesForBatch) aligns with expected concurrency patterns.

package.json (2)

16-17: Cache Migration Scripts Reflected at Repository Root
The inclusion of "db:cache:migrate" and "db:cache:reset" at the root level mirrors the updates in the migrations package. Ensure consistency of behavior between these wrappers and the underlying migration scripts.


56-56: Package Manager Version Update
The update to "packageManager": "[email protected]" helps standardize the development environment. Verify that all CI/CD pipelines and local development setups are compatible with this version upgrade.

scripts/bootstrap/README.md (1)

7-11: Well-Structured Script Overview
The inclusion of a table that outlines available scripts (db:migrate and db:reset) and their descriptions provides clear guidance for users.

scripts/migrations/README.md (3)

7-12: Updated Scripts Table is clear and informative.
The table now cleanly displays the available migration and cache management scripts, ensuring users quickly understand the available commands.


65-71: Migrating Cache Instructions are Well-Structured.
The steps outlining the cache migration process (loading environment variables, connecting to the database, running migrations, and logging results) are clear.


93-99: Reset Cache Section Instructions Are Clear.
The instructions for resetting the cache schema are well outlined and mirror the structure of the migrating cache section.

docker-compose.yaml (4)

41-47: Commented Console Assets Setting – Verify Intent.
The commented-out line for HASURA_GRAPHQL_CONSOLE_ASSETS_DIR (line 46) is retained with an inline note. Please confirm that leaving it commented is intentional for the offline console assets usage scenario, or consider removing it if no longer needed.


58-67: New Processing Service Configuration: Consider Networking Needs.
The newly added processing service (lines 58–67) is configured with build context, restart policy, dependency, and environment file. However, it does not specify any networks. If this service is expected to communicate with other services (e.g., the database or other internal services), it may be advisable to explicitly join one or more networks (such as datalayer or a dedicated network).


121-122: Indexer-GraphQL-API Service Network Update.
The addition of the datalayer network to the indexer-graphql-api service (lines 121–122) appears intended for enhanced inter-service communication. Please verify that this change meets your networking and security requirements without inadvertently exposing the service to unwanted internal traffic.


123-128: Indexer Service Network Adjustment is Consistent.
The removal of the datalayer network from the indexer service is consistent with the intended network segmentation. Just ensure that this change does not impact any inter-service communication patterns that might require the indexer service to interact with components on the datalayer network.

Comment on lines +13 to +19
export const getSchemaName = (schema: SchemaModule): string => {
let name = "public";
schema.createTable("test").$call((b) => {
name = b.toOperationNode().table.table.schema?.name ?? "public";
});
return name;
};
Copy link

@coderabbitai coderabbitai bot Feb 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider cleanup and error handling for schema name extraction.

While the workaround is clever, creating a temporary table without cleanup could have side effects. Consider:

  1. Using a more unique temporary table name to avoid conflicts
  2. Adding cleanup logic to drop the temporary table
  3. Adding error handling for table creation failures
 export const getSchemaName = (schema: SchemaModule): string => {
     let name = "public";
-    schema.createTable("test").$call((b) => {
+    const tempTableName = `_temp_schema_check_${Date.now()}`;
+    try {
+        schema.createTable(tempTableName).$call((b) => {
             name = b.toOperationNode().table.table.schema?.name ?? "public";
-    });
+        });
+    } finally {
+        // Clean up the temporary table
+        schema.dropTable(tempTableName).$call(() => {});
+    }
     return name;
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const getSchemaName = (schema: SchemaModule): string => {
let name = "public";
schema.createTable("test").$call((b) => {
name = b.toOperationNode().table.table.schema?.name ?? "public";
});
return name;
};
export const getSchemaName = (schema: SchemaModule): string => {
let name = "public";
const tempTableName = `_temp_schema_check_${Date.now()}`;
try {
schema.createTable(tempTableName).$call((b) => {
name = b.toOperationNode().table.table.schema?.name ?? "public";
});
} finally {
// Clean up the temporary table
schema.dropTable(tempTableName).$call(() => {});
}
return name;
};

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the table is not actually created since .execute is not called, so the current approach is fine

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@0xnigir1 Ah, you're absolutely right! Thank you for the clarification.


✏️ Learnings added
Learnt from: 0xnigir1
PR: defi-wonderland/grants-stack-indexer-v2#63
File: scripts/bootstrap/src/utils/kysely.ts:13-19
Timestamp: 2025-02-05T17:41:48.691Z
Learning: In Kysely, using `$call()` on a query builder only constructs the operation node for inspection without executing the actual SQL query. The query is only executed when `.execute()` is called.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

Comment on lines 45 to 53
export function isJSON(item: unknown): item is ObjectType {
try {
JSON.parse(item as string);
return true;
} catch (e) {
console.log(e);
return false;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix type guard implementation and add documentation.

The current implementation has several issues:

  1. The type guard is incorrect as it returns true for any valid JSON, not just objects.
  2. Console logging should be removed from utility functions.
  3. Missing JSDoc documentation.

Apply this diff to fix the issues:

+/**
+ * Type guard to check if a value is a valid JSON object string
+ * @param item The value to check
+ * @returns True if the value is a valid JSON object string, false otherwise
+ */
-export function isJSON(item: unknown): item is ObjectType {
+export function isJSON(item: unknown): item is string {
     try {
-        JSON.parse(item as string);
-        return true;
+        const parsed = JSON.parse(item as string);
+        return isObject(parsed);
     } catch (e) {
-        console.log(e);
         return false;
     }
 }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 83 to 112
})
.and(
z
.discriminatedUnion("METADATA_SOURCE", [
dummyMetadataSchema,
pinataMetadataSchema,
publicGatewayMetadataSchema,
])
.transform((val) => {
if (val.METADATA_SOURCE === "dummy") {
return { metadataSource: val.METADATA_SOURCE, ...val };
}
if (val.METADATA_SOURCE === "pinata") {
return {
metadataSource: val.METADATA_SOURCE,
jwt: val.PINATA_JWT,
gateway: val.PINATA_GATEWAY_URL,
...val,
};
}
if (val.METADATA_SOURCE === "public-gateway") {
return {
metadataSource: val.METADATA_SOURCE,
gateways: val.PUBLIC_GATEWAY_URLS,
...val,
};
}
throw new Error("Invalid metadata source");
}),
);
Copy link

@coderabbitai coderabbitai bot Feb 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Simplify chained discriminated unions for maintainability.

The combination of two .discriminatedUnion(...) calls using .and(...) can be tricky to maintain. If you add or remove a source, you must carefully update each chain of transforms. Consider extracting smaller schema parts or adopting a single union structure with refined branching.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be interesting to see what solution or apporach you suggest on this?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@0xnigir1 Here's a suggested approach to simplify the schema using a single discriminated union:

// Define all possible combinations of sources
const sourceSchema = z.object({
  sourceType: z.enum([
    'dummy-all',
    'dummy-pricing-coingecko-metadata',
    'coingecko-pricing-dummy-metadata',
    'coingecko-pricing-pinata-metadata',
    'coingecko-pricing-public-gateway-metadata',
  ]),
});

// Define source-specific configurations
const sourcesConfig = {
  'dummy-all': baseSchema.extend({
    sourceType: z.literal('dummy-all'),
    DUMMY_PRICE: z.coerce.number().optional().default(1),
  }),
  'dummy-pricing-coingecko-metadata': baseSchema.extend({
    sourceType: z.literal('dummy-pricing-coingecko-metadata'),
    DUMMY_PRICE: z.coerce.number().optional().default(1),
    COINGECKO_API_KEY: z.string().min(1),
    COINGECKO_API_TYPE: z.enum(['demo', 'pro']).default('pro'),
  }),
  // ... other combinations
};

const validationSchema = z
  .discriminatedUnion('sourceType', Object.values(sourcesConfig))
  .transform((val) => {
    const config: Record<string, unknown> = {
      pricingSource: val.sourceType.includes('dummy-pricing') ? 'dummy' : 'coingecko',
      metadataSource: val.sourceType.includes('dummy-metadata') ? 'dummy' : 
                      val.sourceType.includes('pinata') ? 'pinata' : 
                      val.sourceType.includes('public-gateway') ? 'public-gateway' : 'dummy',
    };

    // Add source-specific configurations
    if (config.pricingSource === 'dummy') {
      config.dummyPrice = val.DUMMY_PRICE;
    }
    if (config.pricingSource === 'coingecko') {
      config.apiKey = val.COINGECKO_API_KEY;
      config.apiType = val.COINGECKO_API_TYPE;
    }
    // ... handle metadata sources similarly

    return { ...val, ...config };
  });

This approach:

  1. Uses a single sourceType discriminator that combines both pricing and metadata sources
  2. Defines all possible valid combinations upfront
  3. Simplifies the transform logic by deriving configurations from the source type
  4. Makes it easier to add new source combinations by just extending the sourcesConfig

Let me know if you'd like me to explain any part in more detail!

Comment on lines 99 to 100
let fromTimestampMs = timestamps[0] as TimestampMs;
let toTimestampMs = timestamps[timestamps.length - 1] as TimestampMs;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use constant declarations for non-reassigned variables.
These variables are never reassigned and can cause lint failures.

Apply this change to fix the lint warnings:

- let fromTimestampMs = timestamps[0] as TimestampMs;
- let toTimestampMs = timestamps[timestamps.length - 1] as TimestampMs;
+ const fromTimestampMs = timestamps[0] as TimestampMs;
+ const toTimestampMs = timestamps[timestamps.length - 1] as TimestampMs;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let fromTimestampMs = timestamps[0] as TimestampMs;
let toTimestampMs = timestamps[timestamps.length - 1] as TimestampMs;
const fromTimestampMs = timestamps[0] as TimestampMs;
const toTimestampMs = timestamps[timestamps.length - 1] as TimestampMs;
🧰 Tools
🪛 GitHub Check: lint / Run Linters

[failure] 99-99:
'fromTimestampMs' is never reassigned. Use 'const' instead


[failure] 100-100:
'toTimestampMs' is never reassigned. Use 'const' instead

🪛 ESLint

[error] 99-99: 'fromTimestampMs' is never reassigned. Use 'const' instead.

(prefer-const)


[error] 100-100: 'toTimestampMs' is never reassigned. Use 'const' instead.

(prefer-const)

Comment on lines 7 to 10
type CacheResult = {
timestampMs: TimestampMs;
price: TokenPrice | undefined;
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove unused type to fix pipeline failure.
The type CacheResult is declared but never used, triggering lint errors.

Apply this diff to remove the unused type and resolve the lint issue:

-type CacheResult = {
-    timestampMs: TimestampMs;
-    price: TokenPrice | undefined;
-};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
type CacheResult = {
timestampMs: TimestampMs;
price: TokenPrice | undefined;
};
// (No code remains from this snippet as the unused type has been removed)
🧰 Tools
🪛 GitHub Check: lint / Run Linters

[failure] 7-7:
'CacheResult' is defined but never used

🪛 ESLint

[error] 7-7: 'CacheResult' is defined but never used.

(@typescript-eslint/no-unused-vars)

🪛 GitHub Actions: Main Workflow

[error] 7-7: 'CacheResult' is defined but never used

Comment on lines +20 to +23
try {
const decoded = decodeDGApplicationData(event.params.data);
ids.add(decoded.metadata.pointer);
} catch (error) {}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error logging in catch blocks.

Empty catch blocks silently swallow errors, making it difficult to debug issues with metadata decoding. Consider logging these errors or implementing proper error handling.

 try {
     const decoded = decodeDGApplicationData(event.params.data);
     ids.add(decoded.metadata.pointer);
-} catch (error) {}
+} catch (error) {
+    console.warn(`Failed to decode DG application data: ${error}`);
+}

Also applies to: 24-27, 28-31

Comment on lines 24 to +30
async set(key: PriceCacheKey, value: TokenPrice): Promise<void> {
const { tokenCode, timestampMs } = key;

const keyString = `${tokenCode}-${timestampMs}`;
const keyString = `${tokenCode}`;

this.cache.get(keyString)?.set(timestampMs, value);
}
Copy link

@coderabbitai coderabbitai bot Feb 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix potential undefined error in set method.

The set method assumes the nested map exists but doesn't initialize it if it doesn't, which could lead to runtime errors.

 async set(key: PriceCacheKey, value: TokenPrice): Promise<void> {
     const { tokenCode, timestampMs } = key;
     const keyString = `${tokenCode}`;
-    this.cache.get(keyString)?.set(timestampMs, value);
+    if (!this.cache.has(keyString)) {
+        this.cache.set(keyString, new Map());
+    }
+    this.cache.get(keyString)!.set(timestampMs, value);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async set(key: PriceCacheKey, value: TokenPrice): Promise<void> {
const { tokenCode, timestampMs } = key;
const keyString = `${tokenCode}-${timestampMs}`;
const keyString = `${tokenCode}`;
this.cache.get(keyString)?.set(timestampMs, value);
}
async set(key: PriceCacheKey, value: TokenPrice): Promise<void> {
const { tokenCode, timestampMs } = key;
const keyString = `${tokenCode}`;
if (!this.cache.has(keyString)) {
this.cache.set(keyString, new Map());
}
this.cache.get(keyString)!.set(timestampMs, value);
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rabbit is correct

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@0xnigir1 Thank you for confirming! The fix will help prevent potential runtime errors by ensuring the nested map is properly initialized.

(\ /)
(^.^)
c(")_(") Happy to help!


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

Comment on lines 46 to 50
export const getMigrationsFolder = (type: string): string => {
const migrationsFolder = path.join(
path.dirname(new URL(import.meta.url).pathname),
`../migrations`,
`../migrations/${type}`,
);
Copy link

@coderabbitai coderabbitai bot Feb 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use process.cwd() for root references.

The current path construction using URL pathname might not work correctly across different platforms. Consider using process.cwd() as per the coding guidelines.

 export const getMigrationsFolder = (type: string): string => {
     const migrationsFolder = path.join(
-        path.dirname(new URL(import.meta.url).pathname),
+        process.cwd(),
         `../migrations/${type}`,
     );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const getMigrationsFolder = (type: string): string => {
const migrationsFolder = path.join(
path.dirname(new URL(import.meta.url).pathname),
`../migrations`,
`../migrations/${type}`,
);
export const getMigrationsFolder = (type: string): string => {
const migrationsFolder = path.join(
process.cwd(),
`../migrations/${type}`,
);
// Further processing may follow...
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replace '-' with '_' in names

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@0xkenj1 I'll update the choices array to use underscores instead of hyphens:

.options("migrationsFolder", {
    alias: "m",
    type: "string",
    choices: ["processing", "external_services_cache"],
    demandOption: true,
    description: "Migrations folder",
    default: "processing",
})


const logger = Logger.getInstance();

const db = createKyselyDatabase(
{
connectionString: DATABASE_URL,
withSchema: schema,
isProduction: NODE_ENV === "production" || NODE_ENV === "staging",
Copy link

@coderabbitai coderabbitai bot Feb 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add additional safeguard for destructive operations in production.

Since this is a destructive operation that drops the schema, consider adding an additional confirmation step or environment check before proceeding in production/staging environments.

-            isProduction: NODE_ENV === "production" || NODE_ENV === "staging",
+            isProduction: NODE_ENV === "production" || NODE_ENV === "staging",
+        },
+        logger,
+    );
+
+    if (NODE_ENV === "production" || NODE_ENV === "staging") {
+        const confirmation = process.env.CONFIRM_DESTRUCTIVE_ACTION;
+        if (confirmation !== "YES_I_KNOW_THIS_WILL_DELETE_ALL_DATA") {
+            throw new Error("Destructive action not confirmed for production environment");
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
isProduction: NODE_ENV === "production" || NODE_ENV === "staging",
isProduction: NODE_ENV === "production" || NODE_ENV === "staging",
},
logger,
);
if (NODE_ENV === "production" || NODE_ENV === "staging") {
const confirmation = process.env.CONFIRM_DESTRUCTIVE_ACTION;
if (confirmation !== "YES_I_KNOW_THIS_WILL_DELETE_ALL_DATA") {
throw new Error("Destructive action not confirmed for production environment");
}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is just a nice to have for now , will tackle it on future improvements

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@0xkenj1 Understood! Feel free to implement these safeguards when you tackle the improvements later.

Comment on lines 1 to 3
# grants-stack-indexer: migrations scripts

This package contains scripts for managing the database schema and migrations.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Title and Directory Consistency Check
The README begins with # grants-stack-indexer: migrations scripts while it resides in the scripts/bootstrap directory. If this document is intended to cover bootstrap scripts (e.g., metadata and pricing bootstrapping as per the PR objectives), please update the title to reflect that. Otherwise, if it should document migration scripts, consider moving it to the appropriate directory for clarity.

coderabbitai bot added a commit that referenced this pull request Feb 5, 2025
Docstrings generation was requested by @0xkenj1.

* #63 (comment)

The following files were modified:

* `packages/shared/src/utils/testing.ts`
* `scripts/bootstrap/src/schemas/index.ts`
* `scripts/migrations/src/migrations/external-services-cache/20250127T000000_add_cache_tables.ts`
* `scripts/migrations/src/migrations/processing/20241029T120000_initial.ts`
* `scripts/migrations/src/utils/kysely.ts`
Copy link

coderabbitai bot commented Feb 5, 2025

Note

Generated docstrings for this pull request at #65

@@ -0,0 +1,5 @@
export class NoClosePriceFound extends Error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should extend RetriableError or NonRetriableError if we consider that there's no chance that a correct response will ever be returned

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import { ICacheable, ILogger, TimestampMs, TokenCode } from "@grants-stack-indexer/shared";

import { IPricingProvider, TokenPrice } from "../internal.js";
import { NoClosePriceFound } from "../exceptions/noClosePriceFound.exception.js";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

export error in exceptions/index.ts and import from ..internal.js

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


type CacheResult = {
timestampMs: TimestampMs;
price: TokenPrice | undefined;
};

const PROXIMITY_GRANULARITY_MS_FACTOR = 5;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move this to the constants file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change naming

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 99 to 100
let fromTimestampMs = timestamps[0] as TimestampMs;
let toTimestampMs = timestamps[timestamps.length - 1] as TimestampMs;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we sort the array first or we trust that input is sorted?

const cachedPrices = (
await this.getCachedPrices(
tokenCode,
((fromTimestampMs as number) - MIN_GRANULARITY_MS) as TimestampMs,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why here isn't (fromTimestampMs as number) - MIN_GRANULARITY_MS * PROXIMITY_GRANULARITY_MS_FACTOR ?

probably we want a private method calculateLowerBoundTimestamp

@@ -112,11 +113,9 @@ export class CoingeckoProvider implements IPricingProvider {
return undefined;
}

const path = `/coins/${tokenId}/market_chart/range?vs_currency=usd&from=${startTimestampMs}&to=${endTimestampMs}&precision=full`;

const path = `/coins/${tokenId}/market_chart/range?vs_currency=usd&from=${startTimestampMs / 1000}&to=${endTimestampMs / 1000}&precision=full`;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we sure that we have to send timestamp in seconds?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes we are xd

Comment on lines 164 to 165
const oneDayMs = 24 * 60 * 60 * 1000; // 1 day in milliseconds
const ninetyDaysMs = 90 * oneDayMs; // 90 days in milliseconds
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move this to constants

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +174 to 179
// Log if the difference is greater than 90 days
if (effectiveMax - effectiveMin > ninetyDaysMs) {
const segments: Promise<TokenPrice[]>[] = [];
const segmentDuration = 88 * oneDayMs; // 88 days in milliseconds
let currentStart = effectiveMin;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why it talks about 90 days but uses 88 days? also, it's not logging anything

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this logic related to smth on the Coingecko API or what's the reason behind the 90 days segmentation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, its related to coingecko, 88 is to make sure we return data in samples of 1h

Copy link
Contributor Author

@0xkenj1 0xkenj1 Feb 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove log comment

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

niceee 💯

just delete commented out lines

Comment on lines -36 to +31
const gateway = this.gateways[(startIndex + i) % this.gateways.length]!;
const gateway = this.gateways[i];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you think this isn't necessary? i've done it to avoid always start fetching with the same Gateway which leads to rate-limiting it faster (from what i tested locally)

Comment on lines 83 to 112
})
.and(
z
.discriminatedUnion("METADATA_SOURCE", [
dummyMetadataSchema,
pinataMetadataSchema,
publicGatewayMetadataSchema,
])
.transform((val) => {
if (val.METADATA_SOURCE === "dummy") {
return { metadataSource: val.METADATA_SOURCE, ...val };
}
if (val.METADATA_SOURCE === "pinata") {
return {
metadataSource: val.METADATA_SOURCE,
jwt: val.PINATA_JWT,
gateway: val.PINATA_GATEWAY_URL,
...val,
};
}
if (val.METADATA_SOURCE === "public-gateway") {
return {
metadataSource: val.METADATA_SOURCE,
gateways: val.PUBLIC_GATEWAY_URLS,
...val,
};
}
throw new Error("Invalid metadata source");
}),
);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be interesting to see what solution or apporach you suggest on this?

if (
events[0] &&
Math.abs(new Date().getTime() - events[0].blockTimestamp!) >
1000 * 60 * 60 * 0.5 // 30 minutes
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move this to a constant

also, why is this condition? for under 30 minutes from the present you've seen any sub-optimization on the bulk fetch?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

Comment on lines +174 to +177
this.logger.info(`Processed events: ${processedEvents}/${totalEvents}`, {
className: Orchestrator.name,
chainId: this.chainId,
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

Comment on lines 274 to 280
from: raw_events(
where: { chain_id: { _eq: $chainId } }
order_by: { block_timestamp: asc }
limit: 1
) {
block_timestamp
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from can be cached right? it's always the same

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but wont be fixed for now, since it is only used by scripts

Comment on lines 67 to 71
//This is only to try in advance if we have the tables and db in sync
await db
.insertInto("metadataCache")
.values({ id: "asd", metadata: { asd: "asd" }, createdAt: new Date() })
.execute();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

didn't understand this, why we need to make an insert to test if table exists? can't we make a select?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

await db.selectFrom("metadataCache").selectAll().execute();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 17 to 42
/**
* This script handles database reset for the grants-stack-indexer project.
*
* It performs the following steps:
* 1. Loads environment variables from .env file
* 2. Gets database configuration (URL and schema name) from environment
* 3. Creates a Kysely database connection with the specified schema
* 4. Drops and recreates the database schema
* 5. Reports success/failure of reset operation
* 6. Closes database connection and exits
*
* Environment variables required:
* - DATABASE_URL: PostgreSQL connection string
*
* Script arguments:
* - schema: Database schema name where migrations are applied
*
* The script will:
* - Drop the schema if it exists
* - Recreate an empty schema
* - Log results of the reset operation
* - Exit with code 0 on success, 1 on failure
*
* WARNING: This is a destructive operation that will delete all data in the schema.
* Make sure you have backups if needed before running this script.
*/
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jsdocs doesn't match script's task

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 92 to 94
if (fs.existsSync("cids.txt")) {
cids.push(...fs.readFileSync("cids.txt", "utf-8").split("\n"));
} else {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should add a TODO to validate the txt file content

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

);

// This is only to try in advance if we have the tables and db in sync
await db
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delay: 1000,
};
for await (const tokenCode of pMapIterable(
TOKENS_SOURCE_CODES,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is effective to search for all tokens?

@0xkenj1 0xkenj1 marked this pull request as ready for review February 11, 2025 19:33
Copy link
Contributor Author

@0xkenj1 0xkenj1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:D

Comment on lines 46 to 50
export const getMigrationsFolder = (type: string): string => {
const migrationsFolder = path.join(
path.dirname(new URL(import.meta.url).pathname),
`../migrations`,
`../migrations/${type}`,
);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replace '-' with '_' in names

Comment on lines 32 to 43
export const getMigrationsFolder = (): string => {
const migrationsFolder = path.join(
path.dirname(new URL(import.meta.url).pathname),
`../migrations`,
);

if (!existsSync(migrationsFolder)) {
throw new Error(`Migrations folder not found`);
}

return migrationsFolder;
};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we dont need to be compliant with windows os


const logger = Logger.getInstance();

const db = createKyselyDatabase(
{
connectionString: DATABASE_URL,
withSchema: schema,
isProduction: NODE_ENV === "production" || NODE_ENV === "staging",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is just a nice to have for now , will tackle it on future improvements

Comment on lines 67 to 71
//This is only to try in advance if we have the tables and db in sync
await db
.insertInto("metadataCache")
.values({ id: "asd", metadata: { asd: "asd" }, createdAt: new Date() })
.execute();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 17 to 42
/**
* This script handles database reset for the grants-stack-indexer project.
*
* It performs the following steps:
* 1. Loads environment variables from .env file
* 2. Gets database configuration (URL and schema name) from environment
* 3. Creates a Kysely database connection with the specified schema
* 4. Drops and recreates the database schema
* 5. Reports success/failure of reset operation
* 6. Closes database connection and exits
*
* Environment variables required:
* - DATABASE_URL: PostgreSQL connection string
*
* Script arguments:
* - schema: Database schema name where migrations are applied
*
* The script will:
* - Drop the schema if it exists
* - Recreate an empty schema
* - Log results of the reset operation
* - Exit with code 0 on success, 1 on failure
*
* WARNING: This is a destructive operation that will delete all data in the schema.
* Make sure you have backups if needed before running this script.
*/
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 8 to 21
export interface IPricingCache extends ICache<PriceCacheKey, TokenPrice> {
/**
* Get the prices for a given token and time range.
* @param tokenCode - The code of the token.
* @param startTimestampMs - The start timestamp.
* @param endTimestampMs - The end timestamp.
* @returns The prices for the given token and time range.
*/
getPricesByTimeRange(
tokenCode: TokenCode,
startTimestampMs: TimestampMs,
endTimestampMs: TimestampMs,
): Promise<TokenPrice[]>;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -53,6 +66,38 @@ export class KyselyPricingCache implements ICache<PriceCacheKey, TokenPrice> {
}
}

async getPricesByTimeRange(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import { ICacheable, ILogger, TimestampMs, TokenCode } from "@grants-stack-indexer/shared";

import { IPricingProvider, TokenPrice } from "../internal.js";
import { NoClosePriceFound } from "../exceptions/noClosePriceFound.exception.js";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


type CacheResult = {
timestampMs: TimestampMs;
price: TokenPrice | undefined;
};

const PROXIMITY_GRANULARITY_MS_FACTOR = 5;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -0,0 +1,5 @@
export class NoClosePriceFound extends Error {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@0xkenj1 0xkenj1 changed the title feat: bootstrap scripts feat: bootstrap scripts & metadata and pricing improvements Feb 11, 2025
@0xkenj1 0xkenj1 requested a review from 0xnigir1 February 11, 2025 19:45
@jahabeebs
Copy link
Collaborator

@coderabbitai full review

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (37)
packages/shared/src/retry/exponentialBackoff.strategy.ts (1)

23-27: LGTM! Consider documenting the rationale for the new retry values.

The new default values make the retry strategy more suitable for bootstrap operations:

  • Faster initial retry (200ms) helps with transient failures
  • Gentler backoff (1.5x) prevents excessive delays
  • More attempts (10) improve resilience for flaky services

Consider updating the JSDoc to reflect these new defaults and their rationale:

     /**
      * @param options - Configuration options
-     * @param options.baseDelay - Initial delay in milliseconds (default: 5000)
-     * @param options.factor - Multiplier for exponential increase (default: 2)
-     * @param options.maxAttempts - Maximum number of retry attempts (default: 3)
+     * @param options.baseDelay - Initial delay in milliseconds (default: 200)
+     * @param options.factor - Multiplier for exponential increase (default: 1.5)
+     * @param options.maxAttempts - Maximum number of retry attempts (default: 10)
      * @param options.maxDelay - Optional maximum delay cap in milliseconds
+     * 
+     * Note: Default values are optimized for bootstrap operations that need to handle
+     * many concurrent requests to potentially flaky services (IPFS, pricing APIs).
      */
packages/indexer-client/src/interfaces/indexerClient.ts (1)

36-41: Enhance JSDoc documentation for return value format.

The JSDoc comments should specify that the returned timestamps are in seconds.

 /**
  * Get the block range timestamp by chain id from the indexer service
  * @param chainId Id of the chain
- * @returns Block range from the indexer service
+ * @returns Block range from the indexer service with timestamps in seconds
+ *          {from: number, to: number} where from is the earliest block timestamp
+ *          and to is the latest block timestamp
  */
packages/indexer-client/src/providers/envioIndexerClient.ts (1)

270-306: Consider optimizing the GraphQL query.

The current implementation uses two separate queries to fetch the earliest and latest timestamps. This could be optimized into a single aggregate query.

-async getBlockRangeTimestampByChainId(chainId: ChainId): Promise<{ from: number; to: number }> {
-    try {
-        const response = (await this.client.request(
-            gql`
-                query getBlockRangeByChainId($chainId: Int!) {
-                    from: raw_events(
-                        where: { chain_id: { _eq: $chainId } }
-                        order_by: { block_timestamp: asc }
-                        limit: 1
-                    ) {
-                        block_timestamp
-                    }
-                    to: raw_events(
-                        where: { chain_id: { _eq: $chainId } }
-                        order_by: { block_timestamp: desc }
-                        limit: 1
-                    ) {
-                        block_timestamp
-                    }
-                }
-            `,
-            { chainId },
-        )) as { from: { block_timestamp: number }[]; to: { block_timestamp: number }[] };
-        if (!response.from[0] || !response.to[0]) {
-            throw new IndexerClientError("No block range found", {
-                className: EnvioIndexerClient.name,
-                methodName: "getBlockRangeTimestampByChainId",
-            });
-        }
-        return {
-            from: response.from[0].block_timestamp,
-            to: response.to[0].block_timestamp,
-        };
+async getBlockRangeTimestampByChainId(chainId: ChainId): Promise<{ from: number; to: number }> {
+    try {
+        const response = (await this.client.request(
+            gql`
+                query getBlockRangeByChainId($chainId: Int!) {
+                    raw_events_aggregate(where: { chain_id: { _eq: $chainId } }) {
+                        min {
+                            block_timestamp
+                        }
+                        max {
+                            block_timestamp
+                        }
+                    }
+                }
+            `,
+            { chainId },
+        )) as { raw_events_aggregate: { min: { block_timestamp: number }; max: { block_timestamp: number } } };
+        const { min, max } = response.raw_events_aggregate;
+        if (!min.block_timestamp || !max.block_timestamp) {
+            throw new IndexerClientError("No block range found", {
+                className: EnvioIndexerClient.name,
+                methodName: "getBlockRangeTimestampByChainId",
+            });
+        }
+        return {
+            from: min.block_timestamp,
+            to: max.block_timestamp,
+        };
     } catch (error) {
         throw this.handleError(error, "getBlockRangeTimestampByChainId");
     }
 }
packages/indexer-client/test/unit/envioIndexerClient.spec.ts (1)

711-738: Add edge case tests for the block range timestamps.

The test suite covers basic scenarios but could benefit from additional edge cases.

Add these test cases:

it("handles single block in range", async () => {
    graphqlClient.request.mockResolvedValue({
        from: [{ block_timestamp: 123123123000 }],
        to: [{ block_timestamp: 123123123000 }],
    });
    const result = await envioIndexerClient.getBlockRangeTimestampByChainId(1 as ChainId);
    expect(result).toEqual({
        from: 123123123000,
        to: 123123123000,
    });
});

it("handles partial response with missing to timestamp", async () => {
    graphqlClient.request.mockResolvedValue({
        from: [{ block_timestamp: 123123123000 }],
        to: [],
    });
    await expect(
        envioIndexerClient.getBlockRangeTimestampByChainId(1 as ChainId),
    ).rejects.toThrow(IndexerClientError);
});
packages/repository/src/repositories/memory/prices.repository.ts (1)

32-45: Consider optimizing time-range lookups.

For large timestamp sets, converting everything to an array and filtering can become costly. A sorted structure (like a sorted list of timestamps) or an interval-based lookup strategy could reduce the complexity of filtering.

packages/metadata/src/factory/index.ts (1)

16-49: Avoid classes with only static members.

Static analysis warns about classes containing only static methods. You can replace this class with a simple function to reduce complexity:

-export class MetadataProviderFactory {
-    static create(
-        options: MetadataConfig<MetadataProvider>,
-        deps?: { logger?: ILogger },
-    ): IMetadataProvider {
-        // ...
-    }
-}
+export function createMetadataProvider(
+    options: MetadataConfig<MetadataProvider>,
+    deps?: { logger?: ILogger },
+): IMetadataProvider {
+    // ...
+}
🧰 Tools
🪛 Biome (1.9.4)

[error] 16-49: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

packages/metadata/src/providers/publicGateway.provider.ts (4)

20-20: Check if the 3-second timeout is appropriate.
A 3-second timeout may be too short in some network conditions or too long in congested environments. Ensure this value is tested in staging or with real-world loads.


30-38: Consider adding a retry mechanism for each gateway.
There's a TODO comment about retrying each gateway request. A standard approach is exponential backoff or retry-with-jitter. This would improve the resiliency of your fetch process.


41-44: Consolidate the invalid JSON check.
Currently, you're verifying both typeof data === "object" and isJSON(data). If data is not already parsed JSON, it might pass one check while failing the other; consider streamlining into a single validation step for clarity.


57-57: Use a custom error instead of a generic Error.
Throwing a domain-specific exception (e.g., MetadataFetchException) would allow upstream error handling to differentiate between IPFS failures vs. other errors.

- throw new Error(`Failed to fetch IPFS data for CID ${ipfsCid} from all gateways.`);
+ import { MetadataFetchException } from "../exceptions/metadataFetch.exception.js";
+ 
+ throw new MetadataFetchException(`Failed to fetch IPFS data for CID ${ipfsCid} from all gateways.`);
apps/processing/src/config/env.ts (2)

55-57: dummyMetadataSchema introduced.
This schema is minimal, which is fine for tests or placeholder usage. Make sure you have test coverage to confirm it doesn't break with future expansions.


77-97: Consider consolidating the double discriminated union into a single schema.
Chaining discriminated unions can complicate future expansions. A single union with refined branching logic may be more maintainable.

packages/repository/src/repositories/kysely/prices.repository.ts (1)

54-85: New getPricesByTimeRange method is well-structured.
This method helps handle queries for historical pricing data efficiently. Note that if concurrency is a major concern, consider adding an index on (tokenCode, timestampMs) for faster lookups, if not already present.

packages/pricing/src/providers/cachingProxy.provider.ts (2)

31-41: Extract threshold calculation into a helper function.
You repeatedly cast (startTimestampMs as number) - MIN_GRANULARITY_MS * PROXIMITY_FACTOR to TimestampMs. For cleaner code, consider extracting this offset calculation into a helper or constant.

- cachedPrices = (
-   await this.cache.getPricesByTimeRange(
-     tokenCode,
-     ((startTimestampMs as number) - MIN_GRANULARITY_MS * PROXIMITY_FACTOR) as TimestampMs,
-     startTimestampMs,
-   )
- ).sort((a, b) => a.timestampMs - b.timestampMs);
+ const fromTs = ((startTimestampMs as number) - MIN_GRANULARITY_MS * PROXIMITY_FACTOR) as TimestampMs;
+ cachedPrices = (await this.cache.getPricesByTimeRange(
+   tokenCode,
+   fromTs,
+   startTimestampMs,
+ )).sort((a, b) => a.timestampMs - b.timestampMs);

185-193: Centralize boundary checks for clarity.
The boundary checks for timestamps vs. prices[0] and prices[prices.length - 1] are correct, but you may wish to unify them or store the threshold = MIN_GRANULARITY_MS * PROXIMITY_FACTOR in a single local variable to enhance readability.

packages/pricing/test/providers/cachingProxy.provider.spec.ts (1)

136-214: Avoid using "should" in test descriptions.
Per the coding guidelines, rephrase test descriptions to use more declarative phrasing. For example, replace "should return ..." with "returns ...".

- it("should return an empty array when no timestamps are provided", ...
+ it("returns an empty array when no timestamps are provided", ...
packages/data-flow/src/orchestrator.ts (4)

108-108: Typed environment
Explicitly declaring the environment as "development" | "staging" | "production" is a good approach. Consider adding runtime validation (e.g., Zod) if you anticipate unexpected values.


182-186: Frequent info logging
Logging on every event might be too verbose with large batches. Consider switching to debug if logs get cluttered, or batch these logs less frequently.


292-292: Unresolved TODO
A TODO comment references discussing allowPartialLastBlock. If needed, I can help open a new issue to track this question.


347-359: Hardcoded concurrency
The pMap concurrency is currently hardcoded at 10. Consider making it configurable via environment variables or constructor arguments to make the script more flexible.

packages/metadata/src/exceptions/invalidMetadataSource.exception.ts (1)

3-7: Enhance error message with more details.

Consider making the error message more descriptive by including the invalid source value to help with debugging.

 export class InvalidMetadataSourceException extends NonRetriableError {
-    constructor() {
-        super(`Invalid metadata source`);
+    constructor(source: string) {
+        super(`Invalid metadata source: ${source}`);
     }
 }
packages/metadata/src/providers/dummy.provider.ts (1)

4-4: Add proper JSDoc documentation.

Replace @inheritdoc with proper JSDoc documentation to describe the method's purpose and parameters.

-    /* @inheritdoc */
+    /**
+     * Returns undefined for any IPFS CID, as this is a dummy implementation.
+     * @param ipfsCid - The IPFS CID to fetch metadata for
+     * @returns A Promise that resolves to undefined
+     */
packages/metadata/src/exceptions/missingDependencies.exception.ts (1)

3-7: Add JSDoc documentation to the exception class.

As per our TypeScript guidelines, please add JSDoc documentation to describe the purpose of this exception class and its constructor parameter.

Add the following documentation:

+/**
+ * Exception thrown when required dependencies are missing during initialization.
+ */
 export class MissingDependenciesException extends NonRetriableError {
+    /**
+     * Creates a new instance of MissingDependenciesException.
+     * @param dependencies - Array of missing dependency names.
+     */
     constructor(dependencies: string[]) {
         super(`Missing dependencies: ${dependencies.join(", ")}`);
     }
 }
packages/metadata/src/interfaces/index.ts (1)

5-18: Consider adding runtime validation for gateway URLs.

While the types provide good compile-time safety, consider adding runtime validation using Zod to ensure that the gateways array contains valid URLs.

Example implementation:

import { z } from 'zod';

const gatewayMetadataConfigSchema = z.object({
  metadataSource: z.literal('public-gateway'),
  gateways: z.array(z.string().url())
});

type GatewayMetadataConfig = z.infer<typeof gatewayMetadataConfigSchema>;
packages/data-flow/test/unit/helpers/index.spec.ts (1)

7-38: Add test cases for better coverage.

While the current test cases cover basic functionality, consider adding tests for:

  1. Events with invalid data to verify error handling.
  2. Events with multiple metadata CIDs to verify uniqueness.
  3. Events with different types of data (DG, DVMD, DVMDExtended) to verify all decoders.
packages/data-flow/src/helpers/index.ts (1)

13-44: Optimize the function for better readability.

Consider refactoring the function to reduce nesting and improve readability:

 export const getMetadataCidsFromEvents = (
     events: AnyIndexerFetchedEvent[],
     logger: ILogger,
 ): string[] => {
     const ids = new Set<string>();
 
     for (const event of events) {
-        if ("metadata" in event.params) {
-            ids.add(event.params.metadata[1]);
-        } else if ("data" in event.params) {
-            try {
-                const decoded = decodeDGApplicationData(event.params.data);
-                ids.add(decoded.metadata.pointer);
-            } catch (error) {}
-            try {
-                const decoded = decodeDVMDApplicationData(event.params.data);
-                ids.add(decoded.metadata.pointer);
-            } catch (error) {}
-            try {
-                const decoded = decodeDVMDExtendedApplicationData(event.params.data);
-                ids.add(decoded.metadata.pointer);
-            } catch (error) {
-                logger.warn("Failed to decode DVMD extended application data", {
-                    error,
-                    event,
-                });
-            }
-        }
+        if (!("metadata" in event.params) && !("data" in event.params)) {
+            continue;
+        }
+
+        if ("metadata" in event.params) {
+            ids.add(event.params.metadata[1]);
+            continue;
+        }
+
+        const decoders = [
+            { fn: decodeDGApplicationData, name: "DG" },
+            { fn: decodeDVMDApplicationData, name: "DVMD" },
+            { fn: decodeDVMDExtendedApplicationData, name: "DVMDExtended" },
+        ];
+
+        for (const { fn, name } of decoders) {
+            try {
+                const decoded = fn(event.params.data);
+                ids.add(decoded.metadata.pointer);
+            } catch (error) {
+                logger.warn(`Failed to decode ${name} application data`, {
+                    error,
+                    event,
+                });
+            }
+        }
     }
 
     return Array.from(ids);
 };
scripts/migrations/src/utils/parsing.ts (1)

46-61: Improve error handling with specific error messages.

The error messages in the try-catch block are identical. Consider providing more specific error messages to help with debugging.

 export const getMigrationsFolder = (type: string): string => {
     try {
         const migrationsFolder = path.join(
             path.dirname(new URL(import.meta.url).pathname),
             `../migrations/${type}`,
         );

         if (!existsSync(migrationsFolder)) {
-            throw new Error(`Migrations folder not found`);
+            throw new Error(`Migrations folder not found at path: ${migrationsFolder}`);
         }

         return migrationsFolder;
     } catch (error) {
-        throw new Error(`Migrations folder not found`);
+        throw new Error(`Failed to resolve migrations folder for type '${type}': ${error.message}`);
     }
 };
packages/metadata/test/factory/metadata.factory.spec.ts (2)

30-32: Use a proper logger mock instead of an empty object.

An empty object as logger mock might hide potential issues. Consider using a proper mock implementation.

-        const metadataProvider = MetadataProviderFactory.create(options, {
-            logger: {} as ILogger,
-        });
+        const loggerMock: ILogger = {
+            debug: vi.fn(),
+            info: vi.fn(),
+            warn: vi.fn(),
+            error: vi.fn(),
+        };
+        const metadataProvider = MetadataProviderFactory.create(options, {
+            logger: loggerMock,
+        });

48-51: Avoid type assertion for invalid metadata source test.

Using type assertion to create invalid options might hide type errors. Consider using a more type-safe approach.

-        const options = {
-            metadataSource: "invalid",
-        } as unknown as MetadataConfig<"dummy">;
+        const options: MetadataConfig<"dummy" | "public-gateway"> & { metadataSource: string } = {
+            metadataSource: "invalid",
+        };
packages/metadata/src/providers/cachingProxy.provider.ts (2)

8-13: Update documentation to reflect null handling.

The class documentation should be updated to explain when and why null values might be returned or cached.

 /**
  * A metadata provider that caches metadata lookups from the underlying provider.
  * When a metadata is requested, it first checks the cache. If found, returns the cached metadata.
  * If not found in cache, fetches from the underlying provider and caches the result before returning.
  * Cache failures (both reads and writes) are logged but do not prevent the provider from functioning.
+ * 
+ * @remarks
+ * The provider can return null values, which are treated as valid cached results.
+ * This allows distinguishing between "not found" (null) and "error fetching" (undefined) cases.
  */

17-17: Consider using a more specific type for cache value.

The cache type could be more specific to match the actual values being stored.

-        private readonly cache: ICache<string, unknown> & Partial<ICacheable>,
+        private readonly cache: ICache<string, T | null> & Partial<ICacheable>,
scripts/bootstrap/src/pricing.script.ts (2)

94-97: Consider making retry and concurrency options configurable.

The retry options and concurrency limit are hardcoded. Consider making these configurable through environment variables for better flexibility.

+const RETRY_MAX_ATTEMPTS = process.env.RETRY_MAX_ATTEMPTS ? parseInt(process.env.RETRY_MAX_ATTEMPTS) : 10;
+const RETRY_DELAY_MS = process.env.RETRY_DELAY_MS ? parseInt(process.env.RETRY_DELAY_MS) : 1000;
+const CONCURRENCY_LIMIT = process.env.CONCURRENCY_LIMIT ? parseInt(process.env.CONCURRENCY_LIMIT) : 10;

 const retryOptions: RetryOptions = {
-    maxTry: 10,
-    delay: 1000,
+    maxTry: RETRY_MAX_ATTEMPTS,
+    delay: RETRY_DELAY_MS,
 };

Update the concurrency limit:

-    { concurrency: 10 },
+    { concurrency: CONCURRENCY_LIMIT },

Also applies to: 118-118


111-116: Enhance error logging for better debugging.

The error handling could provide more detailed information about the failure.

 if (error instanceof UnsupportedToken) {
-    console.log(`${tokenCode} is not supported `);
+    console.log(`Token ${tokenCode} is not supported: ${error.message}`);
+} else {
+    console.error(`Failed to fetch prices for token ${tokenCode}:`, error);
 }
scripts/bootstrap/src/metadata.script.ts (1)

144-146: Enhance error logging for better debugging.

The error handling could provide more context about the failure.

             } catch (error) {
+                console.error(`Failed to fetch metadata for CID ${cid}:`, error);
                 return { status: "rejected", error };
             }
scripts/bootstrap/README.md (1)

61-66: Add language identifier to code blocks.

Add language identifiers to the code blocks for better syntax highlighting.

-```
+```bash
 pnpm bootstrap:metadata --schema=schema_name


Also applies to: 71-72

</blockquote></details>
<details>
<summary>scripts/migrations/README.md (2)</summary><blockquote>

`61-63`: **Add language identifier to code blocks.**

Add language identifiers to the code blocks for better syntax highlighting.

```diff
-```
+```bash
 pnpm db:cache:migrate --schema=schema_name


Also applies to: 90-92

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

61-61: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

</details>

</details>

---

`143-143`: **Address TODO for E2E tests.**

The TODO comment indicates that E2E tests are needed for the scripts.

Would you like me to help generate E2E tests for the migration scripts?

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 6ead1ef14cd90699d115f3b97482e6c6270787a2 and 17bcc8b4c4d2070c03737b6955f7947f10ffac35.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `pnpm-lock.yaml` is excluded by `!**/pnpm-lock.yaml`

</details>

<details>
<summary>📒 Files selected for processing (79)</summary>

* `.env.example` (0 hunks)
* `Dockerfile` (1 hunks)
* `apps/processing/src/config/env.ts` (4 hunks)
* `apps/processing/src/services/sharedDependencies.service.ts` (3 hunks)
* `apps/processing/test/unit/sharedDependencies.service.spec.ts` (5 hunks)
* `docker-compose.yaml` (3 hunks)
* `package.json` (2 hunks)
* `packages/data-flow/package.json` (1 hunks)
* `packages/data-flow/src/external.ts` (1 hunks)
* `packages/data-flow/src/helpers/index.ts` (1 hunks)
* `packages/data-flow/src/internal.ts` (1 hunks)
* `packages/data-flow/src/orchestrator.ts` (9 hunks)
* `packages/data-flow/test/unit/eventsFetcher.spec.ts` (1 hunks)
* `packages/data-flow/test/unit/helpers/index.spec.ts` (1 hunks)
* `packages/data-flow/test/unit/orchestrator.spec.ts` (1 hunks)
* `packages/indexer-client/src/interfaces/indexerClient.ts` (1 hunks)
* `packages/indexer-client/src/providers/envioIndexerClient.ts` (2 hunks)
* `packages/indexer-client/test/unit/envioIndexerClient.spec.ts` (4 hunks)
* `packages/metadata/package.json` (1 hunks)
* `packages/metadata/src/exceptions/index.ts` (1 hunks)
* `packages/metadata/src/exceptions/invalidMetadataSource.exception.ts` (1 hunks)
* `packages/metadata/src/exceptions/missingDependencies.exception.ts` (1 hunks)
* `packages/metadata/src/external.ts` (1 hunks)
* `packages/metadata/src/factory/index.ts` (1 hunks)
* `packages/metadata/src/interfaces/index.ts` (1 hunks)
* `packages/metadata/src/interfaces/metadata.interface.ts` (1 hunks)
* `packages/metadata/src/internal.ts` (1 hunks)
* `packages/metadata/src/providers/cachingProxy.provider.ts` (1 hunks)
* `packages/metadata/src/providers/dummy.provider.ts` (1 hunks)
* `packages/metadata/src/providers/index.ts` (1 hunks)
* `packages/metadata/src/providers/publicGateway.provider.ts` (4 hunks)
* `packages/metadata/test/factory/metadata.factory.spec.ts` (1 hunks)
* `packages/metadata/test/providers/publicGateway.provider.spec.ts` (3 hunks)
* `packages/pricing/src/constants/index.ts` (1 hunks)
* `packages/pricing/src/exceptions/index.ts` (1 hunks)
* `packages/pricing/src/exceptions/noClosePriceFound.exception.ts` (1 hunks)
* `packages/pricing/src/providers/cachingProxy.provider.ts` (7 hunks)
* `packages/pricing/src/providers/coingecko.provider.ts` (5 hunks)
* `packages/pricing/test/providers/cachingProxy.provider.spec.ts` (4 hunks)
* `packages/pricing/test/providers/coingecko.provider.spec.ts` (5 hunks)
* `packages/processors/src/external.ts` (1 hunks)
* `packages/processors/src/internal.ts` (1 hunks)
* `packages/processors/src/processors/allo/handlers/poolCreated.handler.ts` (0 hunks)
* `packages/processors/src/processors/strategy/directAllocation/handlers/directAllocated.handler.ts` (2 hunks)
* `packages/processors/src/processors/strategy/donationVotingMerkleDistributionDirectTransfer/handlers/allocated.handler.ts` (2 hunks)
* `packages/processors/test/mocks/event.mock.ts` (1 hunks)
* `packages/processors/test/strategy/donationVotingMerkleDistributionDirectTransfer/handlers/allocated.handler.spec.ts` (2 hunks)
* `packages/repository/src/db/connection.ts` (2 hunks)
* `packages/repository/src/external.ts` (1 hunks)
* `packages/repository/src/interfaces/index.ts` (1 hunks)
* `packages/repository/src/interfaces/pricingCache.interface.ts` (1 hunks)
* `packages/repository/src/repositories/kysely/prices.repository.ts` (2 hunks)
* `packages/repository/src/repositories/memory/prices.repository.ts` (2 hunks)
* `packages/shared/src/external.ts` (1 hunks)
* `packages/shared/src/retry/exponentialBackoff.strategy.ts` (1 hunks)
* `packages/shared/src/tokens/tokens.ts` (4 hunks)
* `packages/shared/src/utils/testing.ts` (1 hunks)
* `packages/shared/test/retry/exponentialBackoff.strategy.spec.ts` (1 hunks)
* `scripts/bootstrap/.env.example` (1 hunks)
* `scripts/bootstrap/README.md` (1 hunks)
* `scripts/bootstrap/package.json` (1 hunks)
* `scripts/bootstrap/src/metadata.script.ts` (1 hunks)
* `scripts/bootstrap/src/pricing.script.ts` (1 hunks)
* `scripts/bootstrap/src/schemas/index.ts` (1 hunks)
* `scripts/bootstrap/src/utils/index.ts` (1 hunks)
* `scripts/bootstrap/src/utils/kysely.ts` (1 hunks)
* `scripts/bootstrap/src/utils/parsing.ts` (1 hunks)
* `scripts/bootstrap/tsconfig.build.json` (1 hunks)
* `scripts/bootstrap/tsconfig.json` (1 hunks)
* `scripts/bootstrap/vitest.config.ts` (1 hunks)
* `scripts/migrations/README.md` (3 hunks)
* `scripts/migrations/package.json` (1 hunks)
* `scripts/migrations/src/migrateDb.script.ts` (2 hunks)
* `scripts/migrations/src/migrations/external_services_cache/20250127T000000_add_cache_tables.ts` (1 hunks)
* `scripts/migrations/src/migrations/processing/20241029T120000_initial.ts` (2 hunks)
* `scripts/migrations/src/resetDb.script.ts` (2 hunks)
* `scripts/migrations/src/schemas/index.ts` (1 hunks)
* `scripts/migrations/src/utils/kysely.ts` (3 hunks)
* `scripts/migrations/src/utils/parsing.ts` (2 hunks)

</details>

<details>
<summary>💤 Files with no reviewable changes (2)</summary>

* .env.example
* packages/processors/src/processors/allo/handlers/poolCreated.handler.ts

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>📓 Path-based instructions (5)</summary>

<details>
<summary>`**/*.ts`:   Review TypeScript files for adherence to the fo...</summary>

> `**/*.ts`:   Review TypeScript files for adherence to the following guidelines:
>         - Avoid over-abstraction; prioritize composition over inheritance.
>         - Use dependency injection and follow SOLID principles.
>         - Avoid `any`; use `unknown` when necessary.
>         - Use runtime type-checking for environment variables (e.g., Zod).
>         - Prevent circular dependencies with the internal module pattern.
>         - Libraries should have an `external.ts` file explicitly listing public exports.
>         - Use `bigint` as-is; cast to `Number` only when exposing values via APIs.
>         - Document all code with JSDoc.
>         - Encourage static async factory functions for constructors.
> - Avoid overly nitpicky feedback beyond these best practices.

- `packages/data-flow/src/internal.ts`
- `packages/metadata/src/internal.ts`
- `packages/repository/src/interfaces/index.ts`
- `scripts/bootstrap/src/utils/index.ts`
- `packages/metadata/src/exceptions/missingDependencies.exception.ts`
- `packages/metadata/src/providers/dummy.provider.ts`
- `packages/data-flow/test/unit/helpers/index.spec.ts`
- `packages/data-flow/src/external.ts`
- `packages/metadata/src/exceptions/invalidMetadataSource.exception.ts`
- `packages/processors/src/internal.ts`
- `packages/pricing/src/exceptions/noClosePriceFound.exception.ts`
- `packages/metadata/src/exceptions/index.ts`
- `packages/processors/test/mocks/event.mock.ts`
- `packages/metadata/src/external.ts`
- `scripts/migrations/src/migrations/external_services_cache/20250127T000000_add_cache_tables.ts`
- `packages/shared/src/external.ts`
- `apps/processing/test/unit/sharedDependencies.service.spec.ts`
- `packages/repository/src/external.ts`
- `packages/shared/src/utils/testing.ts`
- `packages/metadata/test/factory/metadata.factory.spec.ts`
- `scripts/bootstrap/src/utils/parsing.ts`
- `packages/processors/src/processors/strategy/directAllocation/handlers/directAllocated.handler.ts`
- `packages/metadata/src/providers/index.ts`
- `packages/processors/src/external.ts`
- `scripts/bootstrap/vitest.config.ts`
- `scripts/migrations/src/schemas/index.ts`
- `packages/pricing/src/exceptions/index.ts`
- `packages/indexer-client/src/interfaces/indexerClient.ts`
- `packages/indexer-client/src/providers/envioIndexerClient.ts`
- `scripts/migrations/src/migrations/processing/20241029T120000_initial.ts`
- `packages/shared/test/retry/exponentialBackoff.strategy.spec.ts`
- `packages/data-flow/src/helpers/index.ts`
- `packages/repository/src/repositories/memory/prices.repository.ts`
- `scripts/migrations/src/migrateDb.script.ts`
- `packages/processors/test/strategy/donationVotingMerkleDistributionDirectTransfer/handlers/allocated.handler.spec.ts`
- `packages/shared/src/retry/exponentialBackoff.strategy.ts`
- `packages/data-flow/test/unit/orchestrator.spec.ts`
- `scripts/migrations/src/resetDb.script.ts`
- `packages/repository/src/interfaces/pricingCache.interface.ts`
- `scripts/bootstrap/src/metadata.script.ts`
- `packages/metadata/src/interfaces/metadata.interface.ts`
- `packages/metadata/src/factory/index.ts`
- `packages/indexer-client/test/unit/envioIndexerClient.spec.ts`
- `packages/metadata/test/providers/publicGateway.provider.spec.ts`
- `packages/repository/src/repositories/kysely/prices.repository.ts`
- `packages/pricing/test/providers/coingecko.provider.spec.ts`
- `packages/pricing/src/providers/coingecko.provider.ts`
- `packages/processors/src/processors/strategy/donationVotingMerkleDistributionDirectTransfer/handlers/allocated.handler.ts`
- `packages/data-flow/src/orchestrator.ts`
- `apps/processing/src/services/sharedDependencies.service.ts`
- `scripts/migrations/src/utils/kysely.ts`
- `scripts/bootstrap/src/utils/kysely.ts`
- `scripts/migrations/src/utils/parsing.ts`
- `scripts/bootstrap/src/schemas/index.ts`
- `packages/data-flow/test/unit/eventsFetcher.spec.ts`
- `packages/metadata/src/providers/publicGateway.provider.ts`
- `packages/pricing/test/providers/cachingProxy.provider.spec.ts`
- `packages/pricing/src/providers/cachingProxy.provider.ts`
- `packages/repository/src/db/connection.ts`
- `apps/processing/src/config/env.ts`
- `packages/pricing/src/constants/index.ts`
- `packages/shared/src/tokens/tokens.ts`
- `scripts/bootstrap/src/pricing.script.ts`
- `packages/metadata/src/providers/cachingProxy.provider.ts`
- `packages/metadata/src/interfaces/index.ts`

</details>
<details>
<summary>`scripts/**/*.ts`: Ensure scripts:
        - Use `process.cw...</summary>

> `scripts/**/*.ts`: Ensure scripts:
>         - Use `process.cwd()` for root references.
>         - Follow folder conventions (`infra/` for infra scripts, `utilities/` for utilities).
>         - Are organized in `package.json` with `script:infra:{name}` or `script:util:{name}`.
>  - Be concise and avoid overly nitpicky feedback outside of these best practices.

- `scripts/bootstrap/src/utils/index.ts`
- `scripts/migrations/src/migrations/external_services_cache/20250127T000000_add_cache_tables.ts`
- `scripts/bootstrap/src/utils/parsing.ts`
- `scripts/bootstrap/vitest.config.ts`
- `scripts/migrations/src/schemas/index.ts`
- `scripts/migrations/src/migrations/processing/20241029T120000_initial.ts`
- `scripts/migrations/src/migrateDb.script.ts`
- `scripts/migrations/src/resetDb.script.ts`
- `scripts/bootstrap/src/metadata.script.ts`
- `scripts/migrations/src/utils/kysely.ts`
- `scripts/bootstrap/src/utils/kysely.ts`
- `scripts/migrations/src/utils/parsing.ts`
- `scripts/bootstrap/src/schemas/index.ts`
- `scripts/bootstrap/src/pricing.script.ts`

</details>
<details>
<summary>`**/providers/**/*.ts`: Review provider files for the follow...</summary>

> `**/providers/**/*.ts`: Review provider files for the following:
> - Providers should supply narrowly scoped data/resources.
> - Ensure classes interacting with metadata sources (e.g., GitHub, JSON files, IPFS) implement the `IMetadataProvider` interface and follow naming conventions (e.g., `GithubProvider`, `JsonFileProvider`).
>  - Be concise and avoid overly nitpicky feedback outside of these best practices.

- `packages/metadata/src/providers/dummy.provider.ts`
- `packages/metadata/src/providers/index.ts`
- `packages/indexer-client/src/providers/envioIndexerClient.ts`
- `packages/metadata/test/providers/publicGateway.provider.spec.ts`
- `packages/pricing/test/providers/coingecko.provider.spec.ts`
- `packages/pricing/src/providers/coingecko.provider.ts`
- `packages/metadata/src/providers/publicGateway.provider.ts`
- `packages/pricing/test/providers/cachingProxy.provider.spec.ts`
- `packages/pricing/src/providers/cachingProxy.provider.ts`
- `packages/metadata/src/providers/cachingProxy.provider.ts`

</details>
<details>
<summary>`**/*.spec.ts`: Review the unit test files with the followin...</summary>

> `**/*.spec.ts`: Review the unit test files with the following guidelines:
>         - Avoid using the word "should" in test descriptions.
>         - Ensure descriptive test names convey the intent of each test.
>         - Validate adherence to the Mocha/Chai/Jest test library best practices.
>  - Be concise and avoid overly nitpicky feedback outside of these best practices.

- `packages/data-flow/test/unit/helpers/index.spec.ts`
- `apps/processing/test/unit/sharedDependencies.service.spec.ts`
- `packages/metadata/test/factory/metadata.factory.spec.ts`
- `packages/shared/test/retry/exponentialBackoff.strategy.spec.ts`
- `packages/processors/test/strategy/donationVotingMerkleDistributionDirectTransfer/handlers/allocated.handler.spec.ts`
- `packages/data-flow/test/unit/orchestrator.spec.ts`
- `packages/indexer-client/test/unit/envioIndexerClient.spec.ts`
- `packages/metadata/test/providers/publicGateway.provider.spec.ts`
- `packages/pricing/test/providers/coingecko.provider.spec.ts`
- `packages/data-flow/test/unit/eventsFetcher.spec.ts`
- `packages/pricing/test/providers/cachingProxy.provider.spec.ts`

</details>
<details>
<summary>`**/services/**/*.ts`: Review service files with the followi...</summary>

> `**/services/**/*.ts`: Review service files with the following considerations:
>         - A Service encapsulates a broader business workflow and might orchestrate various components or interact with multiple data sources or APIs.
>         - Ensure proper composability: Services should use Providers for data/resource access and apply business/orchestration logic.
>         - Validate naming conventions for aggregating services (e.g., `AggregatorService` or domain-specific names like `MetricsService`).

- `apps/processing/src/services/sharedDependencies.service.ts`

</details>

</details><details>
<summary>🪛 LanguageTool</summary>

<details>
<summary>scripts/bootstrap/README.md</summary>

[style] ~15-~15: Consider using a different verb for a more formal wording.
Context: ...     | Runs formatter and automatically fixes issues              | | `lint`         ...

(FIX_RESOLVE)

</details>

</details>
<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

<details>
<summary>scripts/migrations/README.md</summary>

61-61: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

---

90-90: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

</details>

</details>
<details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>packages/metadata/src/factory/index.ts</summary>

[error] 16-49: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (103)</summary><blockquote>

<details>
<summary>packages/shared/test/retry/exponentialBackoff.strategy.spec.ts (2)</summary>

`7-12`: **LGTM! Default options test case is updated correctly.**

The test case properly validates the new default options for the ExponentialBackoff strategy. The changes reflect more reasonable defaults:
- Shorter base delay (200ms) for faster initial retries
- Gentler growth factor (1.5) for more gradual backoff
- More retry attempts (10) for better resilience

---

`1-108`: **LGTM! Test suite follows best practices.**

The test suite is well-structured with:
- Clear describe blocks for logical grouping
- Descriptive test names that convey intent
- Comprehensive coverage of all strategy behaviors
- Good use of mocking for deterministic tests
- Proper validation of edge cases

</details>
<details>
<summary>packages/processors/test/mocks/event.mock.ts (1)</summary>

`45-45`: **LGTM!**

The timestamp adjustment maintains consistency by rounding to the nearest second, which is a good practice for testing.

</details>
<details>
<summary>packages/processors/src/processors/strategy/directAllocation/handlers/directAllocated.handler.ts (1)</summary>

`60-81`: **LGTM! Improved timestamp accuracy.**

Using the block timestamp directly for the donation timestamp is more accurate as it represents when the transaction was actually mined on-chain, rather than when the price was fetched.

</details>
<details>
<summary>packages/processors/src/processors/strategy/donationVotingMerkleDistributionDirectTransfer/handlers/allocated.handler.ts (1)</summary>

`68-103`: **LGTM! Consistent timestamp handling.**

The changes align with the DirectAllocatedHandler implementation, ensuring consistent timestamp handling across different handlers by using the block timestamp.

</details>
<details>
<summary>packages/processors/test/strategy/donationVotingMerkleDistributionDirectTransfer/handlers/allocated.handler.spec.ts (1)</summary>

`111-111`: **LGTM! Tests updated to match implementation.**

The test assertions now correctly verify that donations use the block timestamp from the event, ensuring the tests match the actual implementation.



Also applies to: 174-174

</details>
<details>
<summary>packages/data-flow/test/unit/eventsFetcher.spec.ts (1)</summary>

`21-21`: **LGTM!**

The mock method is correctly added to match the interface.

</details>
<details>
<summary>packages/indexer-client/src/providers/envioIndexerClient.ts (1)</summary>

`23-28`: **LGTM!**

The constructor changes safely handle optional secret parameter.

</details>
<details>
<summary>packages/indexer-client/test/unit/envioIndexerClient.spec.ts (1)</summary>

`115-119`: **LGTM!**

The test correctly verifies that the header is not set when secret is omitted.

</details>
<details>
<summary>packages/repository/src/repositories/memory/prices.repository.ts (2)</summary>

`1-1`: **Looks good.**  
No issues spotted in these lines.  



Also applies to: 3-3, 9-9, 10-10, 16-16, 18-18, 23-23

---

`24-30`: **Initialize nested map to prevent potential runtime errors.**  

This was addressed in a past review but remains unresolved. The set method will fail silently if the nested map is missing. Ensure it’s initialized:


```diff
async set(key: PriceCacheKey, value: TokenPrice): Promise<void> {
    const { tokenCode, timestampMs } = key;
    const keyString = `${tokenCode}`;

-   this.cache.get(keyString)?.set(timestampMs, value);
+   if (!this.cache.has(keyString)) {
+       this.cache.set(keyString, new Map());
+   }
+   this.cache.get(keyString)!.set(timestampMs, value);
}
scripts/bootstrap/src/schemas/index.ts (1)

1-58: No further issues found.

The schema-based validation approach with Zod is well-structured and helps ensure environment correctness.

Also applies to: 62-68, 72-75

packages/metadata/src/providers/publicGateway.provider.ts (3)

4-4: No concerns regarding the updated import.
Importing ILogger, isJSON, and stringify from the shared module is appropriate and consistent with the rest of the code.


9-9: Renaming IpfsProvider to PublicGatewayProvider is clear and consistent.
This change better reflects the broader functionality of fetching from multiple gateways rather than a single IPFS implementation.


26-26: Verify the necessity of returning null in addition to undefined.
With Promise<T | undefined | null>, you now have three potential "empty" states (undefined, null, and valid data). Confirm that each of these states is distinctly handled throughout the codebase.

apps/processing/src/config/env.ts (5)

26-26: Proper usage of NODE_ENV with a default.
Providing a default dev environment is a good practice to avoid unexpected behavior in CI or locally.


35-35: Making INDEXER_ADMIN_SECRET optional.
Confirm downstream usage to ensure no runtime errors occur if this secret is missing.


37-37: New METADATA_SOURCE introduced.
This allows flexibility for various metadata-fetching providers. Ensure that all references to METADATA_SOURCE are updated accordingly.


40-40: Check growth of exponential backoff factor.
With RETRY_FACTOR defaulting to 2, exponential backoff grows quickly. Verify that this aligns with your infrastructure requirements to avoid prolonged waiting times.


59-63: publicGatewayMetadataSchema ensures URL validation.
Good approach using stringToJSONSchema plus .array(z.string().url()). This robustly validates that all provided gateways are valid URLs.

packages/repository/src/repositories/kysely/prices.repository.ts (2)

5-6: Replacing ICache with IPricingCache is suitable.
This clarifies that the repository is specifically intended for pricing operations instead of a generic cache.


14-14: Renaming class to KyselyPricingCache is clear.
Keeping the class name aligned with its function makes the code more readable and discoverable for future maintainers.

apps/processing/src/services/sharedDependencies.service.ts (3)

3-3: Dependency injection looks consistent.
Good job replacing direct imports (IpfsProvider, etc.) with MetadataProviderFactory, which helps maintain flexibility and keep the code aligned with SOLID.


55-55: Confirm environment equivalence for staging and production.
Using || env.NODE_ENV === "staging" for a production-like setting may be intentional, but please verify if staging needs distinctive cloning or migration steps.


93-96: Kudos on using the metadata factory.
Creating the metadata provider via a factory with logger injection supports maintainability. No issues found here.

packages/pricing/src/providers/cachingProxy.provider.ts (1)

203-210: Direct match logic is handled properly.
The direct equality check (prices[mid]!.timestampMs === targetTimestamp) followed by a proximity check is well-structured for ensuring accurate closeness detection.

packages/data-flow/src/orchestrator.ts (8)

2-2: Usage of p-map is a good choice for concurrency.
This import looks fine. No issues identified.


35-35: Reference to TOKENS_SOURCE_CODES
Reusing this constant for enumerating tokens is a clean solution.


42-42: Helper import recognized
Importing getMetadataCidsFromEvents centralizes this logic and promotes maintainability.


46-46: Narrower type for token
Defining token as { priceSourceCode: Token["priceSourceCode"] } neatly restricts usage.


135-137: Local event counters
Maintaining counters locally in-memory may lead to inaccuracies in multi-instance scenarios. Verify if your deployment model might need external or persistent tracking. Otherwise, this is acceptable for single-instance usage.


145-145: Incrementing totalEvents
Updating totalEvents with the batch size is correct. No issues found.


305-305: Early return when no events
Returning early is a clean pattern and prevents unnecessary calls. Looks good.


310-316: Constructing tokens list
Creating a tokens array from TOKENS_SOURCE_CODES is clear and straightforward.

scripts/bootstrap/src/utils/index.ts (1)

1-2: Re-exporting utility modules
These exports make it convenient for consumers to import from a central location. No issues found.

packages/metadata/src/providers/index.ts (1)

1-2: Consolidated metadata provider exports
Exporting publicGateway.provider.js and dummy.provider.js provides clear, flexible options for metadata retrieval.

packages/metadata/src/internal.ts (1)

5-5: LGTM!

The new export statement follows the internal module pattern and aligns with the PR objectives for metadata improvements.

packages/pricing/src/exceptions/noClosePriceFound.exception.ts (1)

1-7: LGTM!

The implementation correctly extends NonRetriableError as suggested in the past review, with a clear error message.

packages/metadata/src/providers/dummy.provider.ts (1)

5-7: LGTM!

The implementation correctly follows the interface contract and TypeScript best practices.

packages/pricing/src/exceptions/index.ts (1)

6-6: LGTM!

The new export follows the established pattern and maintains consistency with other exports.

packages/metadata/src/external.ts (1)

1-5: LGTM!

The exports follow our TypeScript guidelines by explicitly listing public exports in the external.ts file.

packages/processors/src/internal.ts (1)

16-17: LGTM!

The new decoder exports are well-organized with a clear category comment and follow the established pattern.

packages/data-flow/src/external.ts (1)

1-9: LGTM! Clean export structure.

The new export getMetadataCidsFromEvents is properly added to the existing export list, maintaining a clean separation between type exports and value exports.

packages/data-flow/src/internal.ts (1)

1-13: LGTM! Clean modular structure.

The new re-export from "./helpers/index.js" is properly added, maintaining a clean separation between different module exports.

packages/processors/src/external.ts (1)

9-14: LGTM! Clean export structure.

The new decoding function exports are properly grouped together, maintaining a clean separation between type exports and value exports.

packages/metadata/src/interfaces/index.ts (1)

3-3: LGTM! Well-defined union type.

The MetadataProvider type effectively constrains the possible values to "public-gateway" or "dummy".

packages/metadata/src/interfaces/metadata.interface.ts (1)

10-17: LGTM! Clear documentation and improved type safety.

The changes improve type safety by explicitly handling invalid CID cases with null and provide clear documentation about the return values.

scripts/migrations/src/schemas/index.ts (1)

5-5: LGTM! Well-structured environment validation.

The addition of NODE_ENV with proper enum values and a default follows best practices for environment variable validation using Zod.

packages/pricing/src/constants/index.ts (1)

1-9: LGTM! Well-documented time constants.

The changes:

  • Align with Coingecko's Enterprise plan limitations
  • Improve code readability with descriptive constant names
  • Use accurate time calculations
scripts/bootstrap/src/utils/kysely.ts (2)

3-7: LGTM! Well-structured migration configuration interface.

The interface follows TypeScript best practices with clear type definitions.


13-19: LGTM! Valid workaround for Kysely schema name extraction.

The implementation is correct as it only constructs the operation node without executing the SQL query (no .execute() call).

packages/repository/src/interfaces/index.ts (1)

12-12: LGTM! Export statement follows consistent pattern.

The new export for pricingCache.interface.js maintains consistency with other interface exports and correctly uses the .js extension for ESM compatibility.

packages/repository/src/interfaces/pricingCache.interface.ts (1)

1-20: LGTM! Well-structured interface with comprehensive documentation.

The interface is well-designed with:

  • Clear type definitions using shared package types
  • Proper extension of the base cache interface
  • Complete JSDoc documentation for the method
  • Strong typing for all parameters
scripts/bootstrap/src/utils/parsing.ts (2)

7-12: LGTM! Well-structured schema definition.

The zod schema is well-defined with:

  • Appropriate type constraints
  • Default value handling
  • Clear description for the schema parameter

14-28: LGTM! Robust argument parsing implementation.

The parseArguments function is well-implemented with:

  • Proper yargs configuration
  • Schema validation through zod
  • Appropriate error handling
  • Synchronous parsing which is suitable for bootstrap scripts
scripts/bootstrap/vitest.config.ts (1)

4-22: LGTM! Comprehensive test configuration.

The Vitest configuration is well-structured with:

  • Appropriate test environment settings
  • Comprehensive coverage configuration with multiple reporters
  • Clear file inclusion/exclusion patterns
  • Proper path alias setup matching TypeScript configuration
scripts/migrations/src/migrations/external_services_cache/20250127T000000_add_cache_tables.ts (1)

17-17: Verify the intent of making the metadata column nullable.

The metadata column in the metadataCache table is now nullable. Please confirm if this is intentional to handle cases where metadata is temporarily unavailable or failed to fetch.

packages/shared/src/external.ts (1)

12-12: LGTM!

The changes to exports are well-organized and properly typed, following TypeScript best practices.

Also applies to: 20-26

packages/data-flow/src/helpers/index.ts (1)

23-31: Add error logging in catch blocks.

Empty catch blocks silently swallow errors, making it difficult to debug issues with metadata decoding. Consider logging these errors or implementing proper error handling.

scripts/migrations/src/utils/parsing.ts (1)

46-61: Use process.cwd() for root references.

The current path construction using URL pathname might not work correctly across different platforms.

packages/repository/src/external.ts (1)

80-80: LGTM! Clean addition of IPricingCache export.

The export is properly grouped with related pricing types and follows TypeScript best practices.

scripts/migrations/src/migrateDb.script.ts (2)

36-37: LGTM! Environment-aware database configuration.

The addition of NODE_ENV and isProduction flag properly handles different environments for database connections.

Also applies to: 45-45


56-58: Verify migration folder structure.

Ensure that using migrationsFolder as the domain name aligns with your migration organization strategy, as it will affect the migration table names.

❌ Verification inconclusive

🏁 Script executed:

#!/bin/bash
# Check migration folder structure
fd -t d . "migrations" -x echo "Found migrations folder: {}"

Length of output: 158


Manual Verification of Migration Folder Structure

The current automated search command produced errors because its arguments cause FD to treat "migrations" as a search path rather than a pattern. This means we cannot conclusively verify that a proper migrations folder exists and is consistently used as the domain name in the migration strategy. Please manually verify that the migrations folder is correctly organized in your project and that setting the domain to the migrations folder aligns with your migration naming conventions.

scripts/migrations/src/resetDb.script.ts (2)

39-40: LGTM! Consistent environment handling with migrateDb.script.

The changes maintain consistency with the migration script's environment handling approach.

Also applies to: 48-48


48-48: Add additional safeguard for destructive operations in production.

Since this is a destructive operation that drops the schema, consider adding an additional confirmation step or environment check before proceeding in production/staging environments.

scripts/migrations/src/utils/kysely.ts (2)

19-19: LGTM! Domain support for migration configuration.

The addition of the domain property enhances migration organization capabilities.


56-57: LGTM! Consistent domain-based table naming.

The implementation consistently uses the domain for both migration and lock table names across migrate and reset operations.

Also applies to: 98-99

packages/metadata/test/providers/publicGateway.provider.spec.ts (2)

10-10: LGTM! Consistent renaming from IpfsProvider to PublicGatewayProvider.

The renaming is consistently applied across the test suite, improving clarity about the provider's purpose.

Also applies to: 13-13, 21-21, 26-26, 36-36


68-74: LGTM! Updated error handling test case.

The test case now correctly asserts that an error is thrown when all gateways fail, which is a more robust error handling approach compared to returning undefined.

packages/repository/src/db/connection.ts (2)

36-41: LGTM! Well-documented isProduction property.

The JSDoc comments clearly explain the purpose and default value of the isProduction property.


111-115: LGTM! Secure SSL configuration for production environments.

The SSL configuration is properly set based on the environment:

  • Production/staging: SSL enabled with rejectUnauthorized: false
  • Other environments: SSL disabled
scripts/bootstrap/src/metadata.script.ts (1)

149-149: Review the high concurrency limit.

A concurrency limit of 1000 might be too aggressive and could lead to resource exhaustion or rate limiting issues with the IPFS gateways.

Consider reducing the concurrency limit and making it configurable:

-            concurrency: 1000,
+            concurrency: process.env.METADATA_CONCURRENCY ? parseInt(process.env.METADATA_CONCURRENCY) : 100,
apps/processing/test/unit/sharedDependencies.service.spec.ts (4)

4-4: LGTM! Import changes reflect the shift to factory pattern.

The change from direct IpfsProvider import to MetadataProviderFactory aligns with the PR's objective of improving metadata handling through a factory pattern.

Also applies to: 13-17


111-112: LGTM! Environment variables updated for metadata source and environment type.

The addition of METADATA_SOURCE and NODE_ENV to mockEnv properly supports the new metadata provider factory pattern and environment-aware configuration.

Also applies to: 119-120


127-131: LGTM! Database initialization updated with production awareness.

The addition of isProduction check in database initialization enhances environment-specific configuration handling.


140-142: LGTM! Test assertions updated for metadata provider factory.

The test assertions have been correctly updated to verify the initialization of the metadata provider factory with the appropriate environment and logger parameters.

packages/pricing/src/providers/coingecko.provider.ts (4)

18-19: LGTM! Time interval constants added.

The addition of ninetyDaysMs and oneDayMs constants improves code readability and maintainability.


41-42: LGTM! Unsupported tokens commented out.

The commented-out tokens (GIST, GcV, MTK) are clearly marked as unsupported.

Also applies to: 49-50, 60-61


117-117: LGTM! Timestamp conversion to seconds.

The division by 1000 correctly converts timestamps from milliseconds to seconds for the Coingecko API.


172-198: LGTM! Enhanced handling of large date ranges.

The implementation correctly segments requests for date ranges exceeding 90 days into 88-day intervals, processes them in parallel, and combines the results.

packages/pricing/test/providers/coingecko.provider.spec.ts (4)

13-17: LGTM! Import structure improved.

The change to multi-line imports enhances code readability and maintainability.


236-254: LGTM! Test coverage added for valid timestamps.

The test case thoroughly verifies the behavior of getTokenPrices with valid timestamps.


256-274: LGTM! Test coverage added for approximate matches.

The test case properly verifies that the closest available prices are returned when exact timestamps are not available.


276-282: LGTM! Error handling test added.

The test case properly verifies that unknown pricing errors are handled correctly.

scripts/migrations/src/migrations/processing/20241029T120000_initial.ts (2)

3-3: LGTM! Import path updated.

The import path for getSchemaName has been correctly updated to reflect the new directory structure.


22-22: LGTM! Removed debug logging.

The schema logging has been commented out, which is appropriate for production code.

packages/shared/src/tokens/tokens.ts (3)

20-24: LGTM! Well-structured type definition.

The TokensMap type provides a clear and type-safe way to map chain IDs to token addresses and their details.


26-36: LGTM! Efficient implementation of price source code extraction.

The function uses a Set to ensure uniqueness and provides a clean way to extract all price source codes.


94-100: Verify the impact of price source code change.

The token's price source code has been changed from "DAI" to "GIST". This change might affect price lookups.

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Description: Check for any direct references to the old price source code

# Search for "DAI" price source code references
rg -l "priceSourceCode.*DAI" --type ts

Length of output: 77


Price Source Code Change Impact Verified

  • The search shows that the only occurrence of a reference to an old "DAI" price source is within the tokens file itself.
  • The updated token entry (address "0x93A5347036f69BC6f37Ed2b59CBcDDa927719217") now uses "GIST" for its priceSourceCode, while other token entries in the same file still use "DAI".
  • There is no evidence that any logic (e.g., price lookup or conditional checks) relies on a constant string "DAI". This suggests that the change to "GIST" is isolated to this token definition and should not adversely affect price lookups.
packages/data-flow/test/unit/orchestrator.spec.ts (1)

35-36: Verify if these imports are still needed.

The Token and TokenCode imports are commented out but not removed. If they're no longer needed, they should be removed to maintain code cleanliness.

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Description: Check if these types are used in the file

# Search for Token and TokenCode usage
rg -w "Token|TokenCode" "packages/data-flow/test/unit/orchestrator.spec.ts"

Length of output: 107


Clean up unused commented imports in orchestrator.spec.ts

The grep output confirms that "Token" and "TokenCode" appear only in commented lines and aren’t used in any active code. If these imports are not required for upcoming changes or tests, it is best to remove them to clean up the file.

packages/metadata/src/exceptions/index.ts (1)

4-5: LGTM! Consistent exception exports.

The new exception exports follow the established pattern and maintain consistency with existing exports.

scripts/bootstrap/tsconfig.build.json (1)

1-11: LGTM! Well-structured TypeScript configuration.

The configuration follows best practices by:

  • Extending the base configuration
  • Enabling declaration files and source maps
  • Setting up proper output directory
  • Including appropriate files while excluding test and build artifacts
Dockerfile (2)

5-5: LGTM! Version locking pnpm improves build reproducibility.

Explicitly preparing [email protected] ensures consistent builds across environments.


12-12: Verify impact of removing --prod flag.

The removal of --prod flag from the deploy command might include development dependencies in the production build, potentially increasing the image size.

Consider keeping the --prod flag to ensure a lean production build:

-RUN pnpm deploy --filter=./apps/processing  /prod/processing
+RUN pnpm deploy --filter=./apps/processing --prod /prod/processing
scripts/bootstrap/.env.example (1)

4-4: LGTM! Redundant gateway configuration improves reliability.

The multiple public gateway URLs provide good fallback options if some gateways are unavailable.

packages/metadata/package.json (1)

34-34: LGTM! Adding Pinata enhances IPFS capabilities.

The addition of the Pinata dependency will improve IPFS interaction capabilities. The version is appropriately locked for stability.

packages/data-flow/package.json (1)

38-38: LGTM! p-map addition will improve concurrent processing.

Adding p-map is a good choice for handling concurrent operations in metadata and pricing processing. The version is appropriately locked for stability.

scripts/migrations/package.json (1)

19-20: LGTM! Well-structured cache migration scripts.

The new cache migration scripts follow the existing pattern and maintain good separation of concerns between cache and main database migrations.

scripts/bootstrap/package.json (1)

1-46: LGTM! Well-structured package configuration.

The package is well-organized with appropriate dependencies and comprehensive scripts for metadata and pricing bootstrap operations.

package.json (1)

11-12: LGTM! New scripts and package manager update.

The new bootstrap and cache-related scripts enhance the project's functionality, and the package manager update is appropriate.

Also applies to: 16-17, 56-56

docker-compose.yaml (3)

7-9: Address TODO about processing service persistence.

The TODO comment indicates that volume mounting is pending until the processing service persists the latest event processed.

Please confirm if this feature has been implemented in the processing service.


58-67: LGTM! Well-configured processing service.

The processing service is properly configured with appropriate build context, restart policy, and dependencies.


120-121: Verify network configuration change impact.

Adding the datalayer network to indexer-graphql-api might affect service communication.

Please confirm that this network configuration change is intentional and required for the service to function properly.

Comment on lines 1 to 4
{
"extends": "../../tsconfig.json",
"include": ["src/**/*", "test/**/*"]
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

LGTM! Configuration extends root tsconfig and includes relevant paths.

The configuration correctly extends the root tsconfig.json and includes both source and test files, which aligns with the bootstrap scripts implementation mentioned in the PR objectives.

Let's verify the paths and structure:


🏁 Script executed:

#!/bin/bash
# Description: Verify the TypeScript configuration structure

# Check if root tsconfig exists
if [ ! -f "tsconfig.json" ]; then
    echo "Error: Root tsconfig.json not found"
    exit 1
fi

# Check if src and test directories exist in scripts/bootstrap
if [ ! -d "scripts/bootstrap/src" ] || [ ! -d "scripts/bootstrap/test" ]; then
    echo "Warning: src or test directories not found in scripts/bootstrap"
fi

# Compare with other similar configurations
echo "Similar TypeScript configurations in the project:"
fd tsconfig.json --exclude node_modules

Length of output: 833


Review Update: Incorrect Include Paths in TSConfig

Our verification indicates that the paths specified in the "include" property ("src/**/*" and "test/**/*") do not exist in the scripts/bootstrap directory. Please confirm whether these directories should be created or if the configuration needs to be updated to reference the correct locations.

  • Missing directories: scripts/bootstrap/src and/or scripts/bootstrap/test do not exist.
  • Action: Adjust the include paths to point to the correct directories, or create them if intended.

Comment on lines +59 to +61
if (!result.success) {
console.error("❌ Invalid environment variables:", result.error.format());
throw new Error("Invalid environment variables");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Prevent potential secret leakage in logs.

Using console.error with result.error.format() might reveal sensitive environment variables (e.g., keys). Restrict logs to non-sensitive details:

- console.error("❌ Invalid environment variables:", result.error.format());
+ console.error("❌ Invalid environment variables. Please check the logs carefully for details.");

Also applies to: 69-71

Comment on lines +45 to +57
/**
* Type guard to check if a value is a valid JSON object string
* @param item The value to check
* @returns True if the value is a valid JSON object string, false otherwise
*/
export function isJSON(item: unknown): item is string {
try {
JSON.parse(item as string);
return true;
} catch (e) {
return false;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix type guard implementation to properly validate JSON objects.

The current implementation has several issues:

  1. It returns true for any valid JSON (including arrays, numbers, strings)
  2. The type guard claims to return item is string but should return item is ObjectType
  3. It doesn't utilize the existing isObject function
 /**
  * Type guard to check if a value is a valid JSON object string
  * @param item The value to check
  * @returns True if the value is a valid JSON object string, false otherwise
  */
-export function isJSON(item: unknown): item is string {
+export function isJSON(item: unknown): item is ObjectType {
     try {
-        JSON.parse(item as string);
-        return true;
+        const parsed = JSON.parse(item as string);
+        return isObject(parsed);
     } catch (e) {
         return false;
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Type guard to check if a value is a valid JSON object string
* @param item The value to check
* @returns True if the value is a valid JSON object string, false otherwise
*/
export function isJSON(item: unknown): item is string {
try {
JSON.parse(item as string);
return true;
} catch (e) {
return false;
}
}
/**
* Type guard to check if a value is a valid JSON object string
* @param item The value to check
* @returns True if the value is a valid JSON object string, false otherwise
*/
export function isJSON(item: unknown): item is ObjectType {
try {
const parsed = JSON.parse(item as string);
return isObject(parsed);
} catch (e) {
return false;
}
}

cids.push(...getMetadataCidsFromEvents(flattedEvents, { ...console }));
// Save checkpoint logic here (e.g., save cids or event data)
}
fs.writeFileSync("cids.txt", cids.join("\n"));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for file operations.

The file write operation lacks error handling and could fail silently.

-        fs.writeFileSync("cids.txt", cids.join("\n"));
+        try {
+            fs.writeFileSync("cids.txt", cids.join("\n"));
+        } catch (error) {
+            console.error("Failed to write CIDs to file:", error);
+            process.exit(1);
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fs.writeFileSync("cids.txt", cids.join("\n"));
try {
fs.writeFileSync("cids.txt", cids.join("\n"));
} catch (error) {
console.error("Failed to write CIDs to file:", error);
process.exit(1);
}

@@ -0,0 +1,9 @@
DATABASE_URL=postgre://postgres:testing@localhost:5434/datalayer-postgres-db
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use placeholder for database credentials.

The database URL contains hardcoded credentials. For security, use placeholders in the example file:

-DATABASE_URL=postgre://postgres:testing@localhost:5434/datalayer-postgres-db
+DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
DATABASE_URL=postgre://postgres:testing@localhost:5434/datalayer-postgres-db
DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}

Comment on lines 8 to 9
COINGECKO_API_KEY=CG-9B9jasdasdasd
COINGECKO_API_TYPE=pro
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove API key from example file.

The example file contains what appears to be a real CoinGecko API key. Replace it with a placeholder:

-COINGECKO_API_KEY=CG-9B9jasdasdasd
+COINGECKO_API_KEY=your-api-key-here
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
COINGECKO_API_KEY=CG-9B9jasdasdasd
COINGECKO_API_TYPE=pro
COINGECKO_API_KEY=your-api-key-here
COINGECKO_API_TYPE=pro

# Pricing source
PRICING_SOURCE=coingecko
# Coingecko API key
COINGECKO_API_KEY=CG-9B9jasdasdasd
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Replace example API key with a placeholder.

The example Coingecko API key should use a placeholder value like your-api-key-here to avoid potential security concerns.

-    COINGECKO_API_KEY=CG-9B9jasdasdasd
+    COINGECKO_API_KEY=your-api-key-here
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
COINGECKO_API_KEY=CG-9B9jasdasdasd
COINGECKO_API_KEY=your-api-key-here

Copy link
Collaborator

@0xnigir1 0xnigir1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

huge work here, a few comments but now is looking really solid 💪

Comment on lines 23 to 40
try {
const decoded = decodeDGApplicationData(event.params.data);
ids.add(decoded.metadata.pointer);
} catch (error) {}
try {
const decoded = decodeDVMDApplicationData(event.params.data);
ids.add(decoded.metadata.pointer);
} catch (error) {}
try {
const decoded = decodeDVMDExtendedApplicationData(event.params.data);
ids.add(decoded.metadata.pointer);
} catch (error) {
logger.warn("Failed to decode DVMD extended application data", {
error,
event,
});
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now im thinking, we could write a decode method that doesn't throw but instead returns [boolean, result | undefined] so we don't have this nested try/catch blocks. i think it would make it a bit cleaner

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would leave it as it is, it doesn't bother imo

expect(result).toEqual(["bafkreihrjyu5tney6wia2hmkertc74nzfpsgxw2epvnxm72bxj6ifnd4ku"]);
});

it("should skip events without metadata or data", () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be nice to add test cases when metadata is extracted from data using the decode functions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeaaa, i will pick some event from envio :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 35 to 36
// Token,
// TokenCode,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Token,
// TokenCode,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -33,7 +33,7 @@ describe("EnvioIndexerClient", () => {
let testEvents: EnvioEvent[];

beforeEach(() => {
// Sample test data
// Sample test datåa
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Sample test datåa

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 13 to 24
/**
* Factory class for creating pricing providers.
*/
export class MetadataProviderFactory {
/**
* Creates a pricing provider based on the provided configuration.
* @param options - The pricing configuration.
* @param deps - dependencies to inject into the pricing provider.
* @returns The created pricing provider.
* @throws {InvalidMetadataSource} if the pricing source is invalid.
* @throws {MissingDependencies} if the dependencies are missing.
*/
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/**
* Factory class for creating pricing providers.
*/
export class MetadataProviderFactory {
/**
* Creates a pricing provider based on the provided configuration.
* @param options - The pricing configuration.
* @param deps - dependencies to inject into the pricing provider.
* @returns The created pricing provider.
* @throws {InvalidMetadataSource} if the pricing source is invalid.
* @throws {MissingDependencies} if the dependencies are missing.
*/
/**
* Factory class for creating metadata providers.
*/
export class MetadataProviderFactory {
/**
* Creates a metadata provider based on the provided configuration.
* @param options - The metadata configuration.
* @param deps - dependencies to inject into the metadata provider.
* @returns The created metadata provider.
* @throws {InvalidMetadataSource} if the metadata source is invalid.
* @throws {MissingDependencies} if the dependencies are missing.
*/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ty

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gr8 🙌

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

230e42c xd

@0xkenj1 0xkenj1 requested review from jahabeebs and 0xyaco February 12, 2025 19:34
0xyaco
0xyaco previously approved these changes Feb 12, 2025
Copy link
Collaborator

@0xyaco 0xyaco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀

...val,
};
}
throw new Error("Invalid metadata source");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something to have in mind that could avoid throwing errors:
https://github.com/colinhacks/zod?tab=readme-ov-file#validating-during-transform

0xyaco
0xyaco previously approved these changes Feb 12, 2025
jahabeebs
jahabeebs previously approved these changes Feb 12, 2025
Copy link
Collaborator

@jahabeebs jahabeebs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good I think you just have to link the linear issue in the description (if there is one)

@0xkenj1 0xkenj1 dismissed stale reviews from jahabeebs and 0xyaco via 218e933 February 13, 2025 20:11
@0xkenj1 0xkenj1 requested review from 0xnigir1 and 0xyaco February 14, 2025 13:32
Copy link
Collaborator

@0xnigir1 0xnigir1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's goooo

@0xkenj1 0xkenj1 merged commit 3beb758 into dev Feb 14, 2025
7 checks passed
@0xkenj1 0xkenj1 deleted the feat/bootstrap-scripts branch February 14, 2025 14:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants