Skip to content

Conversation

@stwiname
Copy link
Contributor

@stwiname stwiname commented Nov 2, 2025

Description

Algorand block timestamps do not include MS so when converting to a date it would produce an incorrect time. This has now been fiexed by adding MS.

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

Checklist

  • I have tested locally
  • I have performed a self review of my changes
  • Updated any relevant documentation
  • Linked to any relevant issues
  • I have added tests relevant to my changes
  • Any dependent changes have been merged and published in downstream modules
  • My code is up to date with the base branch
  • I have updated relevant changelogs. We suggest using chan

Summary by CodeRabbit

  • Bug Fixes

    • Corrected Algorand block timestamp conversion so block times are interpreted correctly (seconds→milliseconds), ensuring displayed timestamps are accurate.
  • Tests

    • Added a test validating Algorand block timestamp conversion to prevent regressions and ensure consistent timestamp handling.

@coderabbitai
Copy link

coderabbitai bot commented Nov 2, 2025

Walkthrough

Adds algorandBlockToHeader that converts an Algorand block into a header object, changing block.timestamp (seconds) into a JavaScript Date by multiplying by 1000. A unit test verifies the produced header timestamp equals 2023-07-13T10:44:35.000Z.

Changes

Cohort / File(s) Summary
Algorand Block Conversion
packages/node/src/algorand/utils.algorand.ts
Implements algorandBlockToHeader(block) and computes timestamp as new Date(block.timestamp * 1000) (seconds → milliseconds).
Test Coverage
packages/node/src/algorand/utils.algorand.spec.ts
Adds a test in the Algorand Filters -> Transaction Filters suite asserting algorandBlockToHeader(block).timestamp equals 2023-07-13T10:44:35.000Z.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Block as AlgorandBlock (source)
  participant Util as algorandBlockToHeader()
  participant Header as Header object

  rect #e6f7ff
    Block->>Util: pass block (timestamp in seconds)
    note right of Util: compute timestamp_ms = timestamp * 1000\ncreate Date(timestamp_ms)
    Util->>Header: return header { timestamp: Date(...), ... }
  end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Review timestamp multiplication by 1000 for correctness across inputs (ints, strings, null/undefined).
  • Confirm test uses representative block fixture and asserts Date equality robustly (use toISOString or valueOf).

Poem

🐰 I hop through blocks with nimble feet,

seconds to ms — a tidy feat,
A header born with timestamp bright,
Verified in test's soft light,
Hooray! The chain keeps bouncing right. 🥕✨

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "Fix timestamp parsing in block header" directly and accurately reflects the main change in the changeset. The modifications across the spec and implementation files demonstrate a specific bug fix: correcting the timestamp conversion in the algorandBlockToHeader function from seconds to milliseconds by multiplying by 1000. The title is concise, uses clear language ("Fix" indicates a bug fix), and is specific enough that a developer scanning the repository history would immediately understand that this addresses a timestamp parsing issue in Algorand block headers. The title avoids vague terminology and noise, and fully aligns with the PR's stated objective to fix incorrect time conversion for Algorand block timestamps.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-block-timestamp

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 938fa36 and ed345cb.

📒 Files selected for processing (2)
  • packages/node/src/algorand/utils.algorand.spec.ts (2 hunks)
  • packages/node/src/algorand/utils.algorand.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/node/src/algorand/utils.algorand.spec.ts (1)
packages/node/src/algorand/utils.algorand.ts (1)
  • algorandBlockToHeader (16-23)
🔇 Additional comments (3)
packages/node/src/algorand/utils.algorand.ts (1)

16-23: LGTM! Timestamp conversion is correct.

The multiplication by 1000 correctly converts Algorand's seconds-based timestamp to milliseconds for the JavaScript Date constructor.

packages/node/src/algorand/utils.algorand.spec.ts (2)

8-8: LGTM! Import addition is appropriate.

The algorandBlockToHeader import is correctly added to support the new timestamp test.


98-102: LGTM! Test validates the timestamp conversion fix.

The test correctly verifies that algorandBlockToHeader produces the expected timestamp by converting Algorand's seconds-based timestamp to a JavaScript Date object.


Comment @coderabbitai help to get the list of available commands and usage tips.

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: 0

🧹 Nitpick comments (2)
packages/node/src/algorand/utils.algorand.ts (1)

16-23: LGTM! Timestamp conversion fix is correct.

The multiplication by 1000 correctly converts Algorand's Unix timestamp (seconds) to milliseconds for the JavaScript Date constructor. However, consider making the comment more descriptive for maintainability.

Apply this diff to improve the comment:

-    timestamp: new Date(block.timestamp * 1000), // Add MS
+    timestamp: new Date(block.timestamp * 1000), // Convert from seconds to milliseconds
packages/node/src/algorand/utils.algorand.spec.ts (1)

98-102: LGTM! Test correctly validates the timestamp conversion fix.

The test validates that the timestamp conversion from seconds to milliseconds produces the expected Date value. The test implementation is correct and will catch any regression in the timestamp conversion logic.

Consider moving this test to a separate describe block for "Block Header Conversion" since it's testing the algorandBlockToHeader function rather than transaction filtering. This would improve test organization:

describe('Block Header Conversion', () => {
  let app: INestApplication;
  let api: AlgorandApiService;
  let block: AlgorandBlock;

  beforeAll(async () => {
    [app, api] = await prepareApiService();
    block = await api.api.getBlockByHeight(30478896);
  });

  afterAll(async () => {
    await app?.close();
  });

  it('can correctly get a block timestamp date', () => {
    const header = algorandBlockToHeader(block);
    expect(header.timestamp).toEqual(new Date('2023-07-13T10:44:35.000Z'));
  });
});
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cebd78f and 938fa36.

📒 Files selected for processing (2)
  • packages/node/src/algorand/utils.algorand.spec.ts (2 hunks)
  • packages/node/src/algorand/utils.algorand.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/node/src/algorand/utils.algorand.spec.ts (1)
packages/node/src/algorand/utils.algorand.ts (1)
  • algorandBlockToHeader (16-23)
🔇 Additional comments (2)
packages/node/src/algorand/utils.algorand.spec.ts (1)

8-8: LGTM!

The import addition is necessary for the new test case and correctly formatted.

packages/node/src/algorand/utils.algorand.ts (1)

85-98: I need to check the actual codebase to verify the timestamp format handling. Let me search for the AlgorandBlock type and how timestamps are processed.

Based on my verification, I've found evidence supporting the reviewer's concern:

Algorand's block timestamp is in seconds since epoch, while SubQuery's timestamp field is stored as a unix timestamp in milliseconds.

The review comment correctly identifies a timestamp format inconsistency in the code:

  • Line 21 (algorandBlockToHeader) converts the timestamp to milliseconds
  • Line 92 (filterBlockTimestamp) receives block.timestamp directly in seconds

This appears to be a legitimate issue that needs verification against the actual filterBlockTimestamp implementation to confirm the expected format.

Confirm that filterBlockTimestamp expects milliseconds format and fix line 92 accordingly.

If filterBlockTimestamp from @subql/node-core expects milliseconds (consistent with SubQuery's timestamp storage), then line 92 should convert the timestamp to milliseconds before passing it to the function, similar to what algorandBlockToHeader does at line 21.

@stwiname stwiname force-pushed the fix-block-timestamp branch from 938fa36 to ed345cb Compare November 3, 2025 00:29
@github-actions
Copy link
Contributor

github-actions bot commented Nov 3, 2025

Coverage report

Caution

Test run failed

St.
Category Percentage Covered / Total
🔴 Statements 33.57% 899/2678
🔴 Branches 54.89% 73/133
🔴 Functions 29.85% 40/134
🔴 Lines 33.57% 899/2678

Test suite run failed

Failed tests: 3/19. Failed suites: 2/4.
  ● dictionary v1 › successfully validates metatada

    ApolloError: Only absolute URLs are supported

      at new ApolloError (node_modules/@apollo/client/errors/index.js:40:28)
      at node_modules/@apollo/client/core/QueryManager.js:785:71
      at both (node_modules/@apollo/client/utilities/observables/asyncMap.js:22:31)
      at node_modules/@apollo/client/utilities/observables/asyncMap.js:11:72
      at Object.then (node_modules/@apollo/client/utilities/observables/asyncMap.js:11:24)
      at Object.error (node_modules/@apollo/client/utilities/observables/asyncMap.js:24:49)
      at notifySubscription (node_modules/zen-observable/lib/Observable.js:140:18)
      at onNotify (node_modules/zen-observable/lib/Observable.js:179:3)
      at SubscriptionObserver.error (node_modules/zen-observable/lib/Observable.js:240:7)
      at node_modules/@apollo/client/utilities/observables/iteration.js:7:68
          at Array.forEach (<anonymous>)
      at iterateObserversSafely (node_modules/@apollo/client/utilities/observables/iteration.js:7:25)
      at Object.error (node_modules/@apollo/client/utilities/observables/Concast.js:76:21)
      at notifySubscription (node_modules/zen-observable/lib/Observable.js:140:18)
      at onNotify (node_modules/zen-observable/lib/Observable.js:179:3)
      at SubscriptionObserver.error (node_modules/zen-observable/lib/Observable.js:240:7)
      at handleError (node_modules/@apollo/client/link/http/parseAndCheckHttpResponse.js:170:14)
      at node_modules/@apollo/client/link/http/createHttpLink.js:145:17

    Cause:
    TypeError: Only absolute URLs are supported

      146 |       chainId,
      147 |     );
    > 148 |     await dictionary.init();
          |                      ^
      149 |     return dictionary;
      150 |   }
      151 |

      at getNodeRequestOptions (node_modules/node-fetch/lib/index.js:1327:9)
      at node_modules/node-fetch/lib/index.js:1440:19
      at fetch (node_modules/node-fetch/lib/index.js:1437:9)
      at fetch (node_modules/cross-fetch/dist/node-ponyfill.js:10:20)
      at node_modules/@apollo/client/link/http/createHttpLink.js:127:13
      at new Subscription (node_modules/zen-observable/lib/Observable.js:197:34)
      at Observable.subscribe (node_modules/zen-observable/lib/Observable.js:279:14)
      at Object.complete (node_modules/@apollo/client/utilities/observables/Concast.js:111:43)
      at Concast.Object.<anonymous>.Concast.start (node_modules/@apollo/client/utilities/observables/Concast.js:152:23)
      at new Concast (node_modules/@apollo/client/utilities/observables/Concast.js:137:19)
      at QueryManager.Object.<anonymous>.QueryManager.getObservableFromLink (node_modules/@apollo/client/core/QueryManager.js:711:35)
      at QueryManager.Object.<anonymous>.QueryManager.getResultsFromLink (node_modules/@apollo/client/core/QueryManager.js:748:30)
      at resultsFromLink (node_modules/@apollo/client/core/QueryManager.js:1049:26)
      at QueryManager.Object.<anonymous>.QueryManager.fetchQueryByPolicy (node_modules/@apollo/client/core/QueryManager.js:1112:52)
      at fromVariables (node_modules/@apollo/client/core/QueryManager.js:819:41)
      at QueryManager.Object.<anonymous>.QueryManager.fetchConcastWithInfo (node_modules/@apollo/client/core/QueryManager.js:861:35)
      at QueryManager.Object.<anonymous>.QueryManager.fetchQuery (node_modules/@apollo/client/core/QueryManager.js:374:21)
      at QueryManager.Object.<anonymous>.QueryManager.query (node_modules/@apollo/client/core/QueryManager.js:468:21)
      at ApolloClient.Object.<anonymous>.ApolloClient.query (node_modules/@apollo/client/core/ApolloClient.js:233:34)
      at AlgorandDictionaryV1.init (node_modules/@subql/node-core/src/indexer/dictionary/v1/dictionaryV1.ts:57:21)
      at AlgorandDictionaryV1.create (packages/node/src/indexer/dictionary/v1/algorandDictionaryV1.ts:148:22)
      at Object.<anonymous> (packages/node/src/indexer/dictionary/v1/algorandDictionaryV1.spec.ts:72:45)

  ● dictionary v1 › build correct dictionary entries and remove undefined fields in filter

    ApolloError: Only absolute URLs are supported

      at new ApolloError (node_modules/@apollo/client/errors/index.js:40:28)
      at node_modules/@apollo/client/core/QueryManager.js:785:71
      at both (node_modules/@apollo/client/utilities/observables/asyncMap.js:22:31)
      at node_modules/@apollo/client/utilities/observables/asyncMap.js:11:72
      at Object.then (node_modules/@apollo/client/utilities/observables/asyncMap.js:11:24)
      at Object.error (node_modules/@apollo/client/utilities/observables/asyncMap.js:24:49)
      at notifySubscription (node_modules/zen-observable/lib/Observable.js:140:18)
      at onNotify (node_modules/zen-observable/lib/Observable.js:179:3)
      at SubscriptionObserver.error (node_modules/zen-observable/lib/Observable.js:240:7)
      at node_modules/@apollo/client/utilities/observables/iteration.js:7:68
          at Array.forEach (<anonymous>)
      at iterateObserversSafely (node_modules/@apollo/client/utilities/observables/iteration.js:7:25)
      at Object.error (node_modules/@apollo/client/utilities/observables/Concast.js:76:21)
      at notifySubscription (node_modules/zen-observable/lib/Observable.js:140:18)
      at onNotify (node_modules/zen-observable/lib/Observable.js:179:3)
      at SubscriptionObserver.error (node_modules/zen-observable/lib/Observable.js:240:7)
      at handleError (node_modules/@apollo/client/link/http/parseAndCheckHttpResponse.js:170:14)
      at node_modules/@apollo/client/link/http/createHttpLink.js:145:17

    Cause:
    TypeError: Only absolute URLs are supported

      146 |       chainId,
      147 |     );
    > 148 |     await dictionary.init();
          |                      ^
      149 |     return dictionary;
      150 |   }
      151 |

      at getNodeRequestOptions (node_modules/node-fetch/lib/index.js:1327:9)
      at node_modules/node-fetch/lib/index.js:1440:19
      at fetch (node_modules/node-fetch/lib/index.js:1437:9)
      at fetch (node_modules/cross-fetch/dist/node-ponyfill.js:10:20)
      at node_modules/@apollo/client/link/http/createHttpLink.js:127:13
      at new Subscription (node_modules/zen-observable/lib/Observable.js:197:34)
      at Observable.subscribe (node_modules/zen-observable/lib/Observable.js:279:14)
      at Object.complete (node_modules/@apollo/client/utilities/observables/Concast.js:111:43)
      at Concast.Object.<anonymous>.Concast.start (node_modules/@apollo/client/utilities/observables/Concast.js:152:23)
      at new Concast (node_modules/@apollo/client/utilities/observables/Concast.js:137:19)
      at QueryManager.Object.<anonymous>.QueryManager.getObservableFromLink (node_modules/@apollo/client/core/QueryManager.js:711:35)
      at QueryManager.Object.<anonymous>.QueryManager.getResultsFromLink (node_modules/@apollo/client/core/QueryManager.js:748:30)
      at resultsFromLink (node_modules/@apollo/client/core/QueryManager.js:1049:26)
      at QueryManager.Object.<anonymous>.QueryManager.fetchQueryByPolicy (node_modules/@apollo/client/core/QueryManager.js:1112:52)
      at fromVariables (node_modules/@apollo/client/core/QueryManager.js:819:41)
      at QueryManager.Object.<anonymous>.QueryManager.fetchConcastWithInfo (node_modules/@apollo/client/core/QueryManager.js:861:35)
      at QueryManager.Object.<anonymous>.QueryManager.fetchQuery (node_modules/@apollo/client/core/QueryManager.js:374:21)
      at QueryManager.Object.<anonymous>.QueryManager.query (node_modules/@apollo/client/core/QueryManager.js:468:21)
      at ApolloClient.Object.<anonymous>.ApolloClient.query (node_modules/@apollo/client/core/ApolloClient.js:233:34)
      at AlgorandDictionaryV1.init (node_modules/@subql/node-core/src/indexer/dictionary/v1/dictionaryV1.ts:57:21)
      at AlgorandDictionaryV1.create (packages/node/src/indexer/dictionary/v1/algorandDictionaryV1.ts:148:22)
      at Object.<anonymous> (packages/node/src/indexer/dictionary/v1/algorandDictionaryV1.spec.ts:72:45)


  ● ApiService › waits on pending block to fetch hash

    Unable to get block hash for round 55194765

      250 |       }
      251 |     }
    > 252 |     throw new Error(`Unable to get block hash for round ${round}`);
          |           ^
      253 |   }
      254 | }
      255 |

      at AlgorandApi.getBlockHash (packages/node/src/algorand/api.algorand.ts:252:11)
      at packages/node/src/algorand/api.algorand.ts:205:22
          at async Promise.all (index 0)
      at AlgorandApi.fetchBlocks (packages/node/src/algorand/api.algorand.ts:203:29)
      at fetchLatestBlock (packages/node/src/indexer/api.service.spec.ts:87:8)

  ● ApiService › waits on pending block to fetch hash

    Unable to get block hash for round 55194765

      250 |       }
      251 |     }
    > 252 |     throw new Error(`Unable to get block hash for round ${round}`);
          |           ^
      253 |   }
      254 | }
      255 |

      at AlgorandApi.getBlockHash (packages/node/src/algorand/api.algorand.ts:252:11)
      at packages/node/src/algorand/api.algorand.ts:205:22
          at async Promise.all (index 0)
      at AlgorandApi.fetchBlocks (packages/node/src/algorand/api.algorand.ts:203:29)
      at fetchLatestBlock (packages/node/src/indexer/api.service.spec.ts:87:8)
      at Object.<anonymous> (packages/node/src/indexer/api.service.spec.ts:91:19)

Report generated by 🧪jest coverage report action from ed345cb

@stwiname stwiname merged commit 259917a into main Nov 3, 2025
3 of 4 checks passed
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.

2 participants