Skip to content

Feature/endorsement chain boe - #164

Closed
manishdex25 wants to merge 13 commits into
betafrom
feature/endorsement-chain-boe
Closed

Feature/endorsement chain boe#164
manishdex25 wants to merge 13 commits into
betafrom
feature/endorsement-chain-boe

Conversation

@manishdex25

@manishdex25 manishdex25 commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Summary

What is the background of this pull request?

Changes

  • What are the changes made in this pull request?
  • Change this and that, etc...

Issues

What are the related issues or stories?

Summary by CodeRabbit

  • Improvements

    • Improved endorsement-chain retrieval across supported providers, including reliable handling of large log ranges and rate limits.
    • Added more efficient scanning for Infura connections.
    • Unified title escrow and obligation status event retrieval.
    • Improved compatibility by skipping unrelated or incompatible logs.
    • Ensured grouped escrow events are identified consistently.
    • Reduced duplicate timestamp requests for faster chain retrieval.
  • Documentation

    • Updated endorsement-chain examples to use the current retrieval method.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a05a7079-73a6-459c-9338-cc7a8b367b55

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Changes

Escrow retrieval and endorsement-chain processing

Layer / File(s) Summary
Adaptive provider log scanning
src/constants.ts, src/core/endorsement-chain/fetchLogsChunked.ts
Infura log queries now use adaptive backward scans with bounded windows, retries, parallel requests, request and duration budgets, and ordered results.
Unified escrow transfer fetching
src/core/endorsement-chain/fetchEscrowTransfer.ts, src/core/endorsement-chain/useEndorsementChain.ts
fetchEscrowTransfersV5 now supports obligation status events, provider-aware retrieval, conditional ABI selection, address resolution, and scan completeness checks.
Deterministic chain processing
src/core/endorsement-chain/helpers.ts, src/core/endorsement-chain/retrieveEndorsementChain.ts
INITIAL events take precedence. Block timestamps are fetched once per unique block and mapped back to log order.
Obligation API consolidation
src/core/endorsement-chain/obligation.ts, README.md
The obsolete obligation helper and its example were removed. The documentation now uses fetchEndorsementChain with encryptionKeyId.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant useEndorsementChain
  participant fetchEscrowTransfersV5
  participant fetchLogsChunked
  participant Infura
  participant retrieveEndorsementChain
  useEndorsementChain->>fetchEscrowTransfersV5: request obligation transfers with status mode
  fetchEscrowTransfersV5->>fetchLogsChunked: start provider-aware scan
  fetchLogsChunked->>Infura: query bounded block ranges
  Infura-->>fetchLogsChunked: return logs or range errors
  fetchLogsChunked-->>fetchEscrowTransfersV5: return ordered logs and scan status
  fetchEscrowTransfersV5-->>useEndorsementChain: return parsed transfers
  retrieveEndorsementChain->>Infura: fetch unique block timestamps
  Infura-->>retrieveEndorsementChain: return timestamps
Loading

Possibly related PRs

  • TrustVC/trustvc#153: Introduces the obligation endorsement-chain implementation that this PR consolidates.
  • TrustVC/trustvc#155: Modifies the same obligation escrow transfer and endorsement-chain paths.
  • TrustVC/trustvc#161: Extends the same backward scanning and timestamp retrieval paths.

Suggested labels: released on @beta``

Suggested reviewers: rishabhs7

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description only repeats the template and provides no background, change details, or related issues. Replace the template text with the PR background, specific implementation changes, and related issue or story references.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title identifies an endorsement-chain feature, which is related to the main changes but does not describe the Infura scanning or API updates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/endorsement-chain-boe

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/core/endorsement-chain/obligation.ts (1)

15-15: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Restore the removed public API before merging.

fetchObligationEndorsementChain was exported and documented at the package root. Keep a deprecated wrapper that delegates to fetchEndorsementChain, or document this as a breaking release with migration notes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/endorsement-chain/obligation.ts` at line 15, Restore the public
fetchObligationEndorsementChain API as a deprecated wrapper that delegates to
fetchEndorsementChain, and re-export it from the package root with its
documentation. Preserve existing behavior while directing callers toward
fetchEndorsementChain.
🧹 Nitpick comments (6)
src/core/endorsement-chain/fetchLogsChunked.ts (3)

14-14: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Match the URL host, not the whole URL string.

INFURA_HOST_RE tests the complete RPC URL. A URL that contains infura.io in the path or query, but has a different host, selects the Infura scan path. Parse the URL and test the hostname.

♻️ Proposed refactor
-const INFURA_HOST_RE = /infura\.io/i;
+const INFURA_HOST_RE = /(^|\.)infura\.io$/i;
@@
 export function isInfuraProvider(provider: Provider | ethersV6.Provider): boolean {
-  return INFURA_HOST_RE.test(getProviderRpcUrl(provider));
+  const url = getProviderRpcUrl(provider);
+  if (!url) return false;
+  try {
+    return INFURA_HOST_RE.test(new URL(url).hostname);
+  } catch {
+    return false;
+  }
 }

Also applies to: 40-58

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/endorsement-chain/fetchLogsChunked.ts` at line 14, Update
INFURA_HOST_RE usage in the fetchLogsChunked flow to parse the RPC URL and test
only its hostname, rather than matching the complete URL string. Preserve Infura
path selection only when the parsed URL’s host matches infura.io, including the
existing logic around the affected lines.

202-255: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reduce the cognitive complexity of scanLogsBackward.

SonarCloud reports a failure for this function (complexity 23, limit 15). The mint-index search and the error branch are separable. Extract the mint handling into a helper, for example sliceFromMint(chunkLogs, isMintLog), and reuse it. This also clears the pipeline gate.

Sonar additionally flags the in-place reverse() at line 254. Use [...chunkGroups].reverse().flat() to avoid mutating the accumulator.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/endorsement-chain/fetchLogsChunked.ts` around lines 202 - 255,
Reduce the cognitive complexity of scanLogsBackward by extracting the mint-index
search and slicing logic into a separate sliceFromMint helper, then use that
helper in the chunk-processing loop while preserving the existing stop and
log-collection behavior. Keep the error handling behavior unchanged, and replace
the mutating chunkGroups.reverse().flat() call with a copied-array reverse
before flattening.

Source: Linters/SAST tools


115-127: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicated bounded-concurrency worker pool. Both files implement the same pattern: a shared index cursor, Math.min(limit, items.length) workers, and a while loop that claims the next index. Extract one helper, for example mapWithConcurrency(items, limit, fn), and call it from both sites.

  • src/core/endorsement-chain/fetchLogsChunked.ts#L115-L127: replace the next++ worker loop in scanLogsParallelFixed with the shared helper over windows.
  • src/core/endorsement-chain/retrieveEndorsementChain.ts#L19-L35: replace the nextBlockIndex++ worker loop with the shared helper over uniqueBlockNumbers.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/endorsement-chain/fetchLogsChunked.ts` around lines 115 - 127,
Extract the duplicated bounded-concurrency worker-loop logic into a shared
mapWithConcurrency(items, limit, fn) helper. In
src/core/endorsement-chain/fetchLogsChunked.ts lines 115-127, update
scanLogsParallelFixed to map over windows through the helper; in
src/core/endorsement-chain/retrieveEndorsementChain.ts lines 19-35, update the
corresponding block in the endorsement-chain retrieval flow to map over
uniqueBlockNumbers through the same helper, preserving result ordering and the
existing concurrency limits.
src/core/endorsement-chain/fetchEscrowTransfer.ts (2)

58-78: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Rename includeObligationStatus to reflect its real meaning.

The flag no longer only adds status events. It selects the contract ABI (ObligationEscrow__factory.abi), the filter set, and the Infura range strategy. A name such as isObligationEscrow describes that behavior and prevents a caller from passing true for a classic V5 escrow.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/endorsement-chain/fetchEscrowTransfer.ts` around lines 58 - 78,
Rename the includeObligationStatus parameter to isObligationEscrow throughout
fetchEscrowTransfersV5 and its related call chain, including the ABI selection,
filter selection, and Infura range strategy, so callers clearly indicate the
escrow contract type rather than merely requesting status events.

271-285: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Do not discard a valid mintBlock when shredBlock() fails.

One try block wraps both calls. If mintBlock() succeeds and shredBlock() reverts or is absent from the ABI, the function returns null. The caller then falls back to the unbounded backward scan even though a valid lower bound is known. Scope the try to each call.

♻️ Proposed fix
-    const shredBlock = Number(await contract.shredBlock());
-    const toBlock =
-      Number.isFinite(shredBlock) && shredBlock > 0 ? shredBlock : await provider.getBlockNumber();
+    let shredBlock = Number.NaN;
+    try {
+      shredBlock = Number(await contract.shredBlock());
+    } catch {
+      // shredBlock is optional; fall back to the chain tip.
+    }
+    const toBlock =
+      Number.isFinite(shredBlock) && shredBlock > 0 ? shredBlock : await provider.getBlockNumber();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/endorsement-chain/fetchEscrowTransfer.ts` around lines 271 - 285,
Update the block-range lookup around mintBlock and shredBlock so failures are
handled per call rather than by one shared try block. Preserve a valid mintBlock
lower bound when shredBlock() fails or is unavailable by falling back to
provider.getBlockNumber(), while still returning null when mintBlock() cannot be
read or is invalid; keep the existing range validation.
src/core/endorsement-chain/helpers.ts (1)

93-105: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Make TYPE_PRECEDENCE exhaustiveness compiler-checked.

The list is typed as TransferEventType[]. If a new member is added to TransferEventType, the compiler accepts the incomplete list, and identifyEventTypeFromLogs throws Unable to identify event type at runtime. Type the precedence data so a missing member fails npm run type-check.

♻️ Proposed refactor
-const TYPE_PRECEDENCE: TransferEventType[] = [
+// Lower number wins. Every TransferEventType must appear, so a new member fails type-check.
+const TYPE_RANK: Record<TransferEventType, number> = {
+  INITIAL: 0,
+  RETURN_TO_ISSUER_REJECTED: 1,
+  // ...remaining members, including TRANSFER_HOLDER / TRANSFER_BENEFICIARY with a low rank
+};

Then order the present types by rank, and keep the existing TRANSFER_OWNERS combination check.

As per coding guidelines: "Before completing work, run npm run type-check and npm run lint".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/endorsement-chain/helpers.ts` around lines 93 - 105, Make
TYPE_PRECEDENCE exhaustively compiler-checked against every TransferEventType
member, using a typed mapped structure or equivalent compile-time enforcement so
newly added members fail npm run type-check until ranked. Preserve the existing
precedence ordering, ensure identifyEventTypeFromLogs still ranks the present
types correctly, and retain the TRANSFER_OWNERS combination check; run npm run
type-check and npm run lint afterward.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@README.md`:
- Around line 905-910: Update the README endorsement-chain example to pass the
encryption key ID as the fourth argument to fetchEndorsementChain, reusing the
options.id value established when encryption is enabled. Keep the existing
obligationRegistry, tokenId, and provider arguments unchanged.

In `@src/core/endorsement-chain/fetchEscrowTransfer.ts`:
- Around line 242-257: Add a bounded deadline or iteration budget to the
backward scan initiated by the visible scanLogsBackward call, and pass it
through so the search cannot continue indefinitely toward block 0 when isMintLog
never matches or decoding fails. Preserve the existing mint/status matching
behavior while ensuring the scan terminates within the configured budget.

In `@src/core/endorsement-chain/fetchLogsChunked.ts`:
- Around line 223-236: The mint-marker search in the chunk grouping logic must
select the earliest matching log so preceding mint events are preserved. Update
the loop around mintIndex in fetchLogsChunked to scan chunkLogs from the
beginning and stop at the first isMintLog match, while keeping the existing
slice and break behavior.
- Around line 159-171: Replace the spread-based logs.push call in the
scanLogsChunked loop with an aggregation approach that does not expand remaining
into function arguments, such as concatenation or reassignment. Preserve the
existing ordering and continue behavior after incorporating the complete result
from scanLogsParallelFixed.
- Around line 103-130: Update scanLogsParallelFixed to add per-window retry
handling around getLogsRange: when a window fails due to a range, response-size,
or transient RPC error, split that window into smaller sub-ranges and retry each
sub-range, recursively or iteratively, before propagating unrecoverable errors.
Preserve successful results from other windows and return logs from all
completed sub-ranges rather than allowing one failed window to discard them
through Promise.all.
- Around line 43-49: Update the provider URL resolution used by
fetchAllTransfers to handle ethers v6 FallbackProvider and BrowserProvider
wrappers, rather than relying only on _getConnection(). Use explicit provider
metadata or inspect supported underlying providers so an embedded Infura
JsonRpcProvider is detected and the ranged path is selected.

---

Outside diff comments:
In `@src/core/endorsement-chain/obligation.ts`:
- Line 15: Restore the public fetchObligationEndorsementChain API as a
deprecated wrapper that delegates to fetchEndorsementChain, and re-export it
from the package root with its documentation. Preserve existing behavior while
directing callers toward fetchEndorsementChain.

---

Nitpick comments:
In `@src/core/endorsement-chain/fetchEscrowTransfer.ts`:
- Around line 58-78: Rename the includeObligationStatus parameter to
isObligationEscrow throughout fetchEscrowTransfersV5 and its related call chain,
including the ABI selection, filter selection, and Infura range strategy, so
callers clearly indicate the escrow contract type rather than merely requesting
status events.
- Around line 271-285: Update the block-range lookup around mintBlock and
shredBlock so failures are handled per call rather than by one shared try block.
Preserve a valid mintBlock lower bound when shredBlock() fails or is unavailable
by falling back to provider.getBlockNumber(), while still returning null when
mintBlock() cannot be read or is invalid; keep the existing range validation.

In `@src/core/endorsement-chain/fetchLogsChunked.ts`:
- Line 14: Update INFURA_HOST_RE usage in the fetchLogsChunked flow to parse the
RPC URL and test only its hostname, rather than matching the complete URL
string. Preserve Infura path selection only when the parsed URL’s host matches
infura.io, including the existing logic around the affected lines.
- Around line 202-255: Reduce the cognitive complexity of scanLogsBackward by
extracting the mint-index search and slicing logic into a separate sliceFromMint
helper, then use that helper in the chunk-processing loop while preserving the
existing stop and log-collection behavior. Keep the error handling behavior
unchanged, and replace the mutating chunkGroups.reverse().flat() call with a
copied-array reverse before flattening.
- Around line 115-127: Extract the duplicated bounded-concurrency worker-loop
logic into a shared mapWithConcurrency(items, limit, fn) helper. In
src/core/endorsement-chain/fetchLogsChunked.ts lines 115-127, update
scanLogsParallelFixed to map over windows through the helper; in
src/core/endorsement-chain/retrieveEndorsementChain.ts lines 19-35, update the
corresponding block in the endorsement-chain retrieval flow to map over
uniqueBlockNumbers through the same helper, preserving result ordering and the
existing concurrency limits.

In `@src/core/endorsement-chain/helpers.ts`:
- Around line 93-105: Make TYPE_PRECEDENCE exhaustively compiler-checked against
every TransferEventType member, using a typed mapped structure or equivalent
compile-time enforcement so newly added members fail npm run type-check until
ranked. Preserve the existing precedence ordering, ensure
identifyEventTypeFromLogs still ranks the present types correctly, and retain
the TRANSFER_OWNERS combination check; run npm run type-check and npm run lint
afterward.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 82e24ca7-4a8a-4d59-bed1-458d7b371625

📥 Commits

Reviewing files that changed from the base of the PR and between a9f85c4 and 4a6be63.

📒 Files selected for processing (7)
  • README.md
  • src/core/endorsement-chain/fetchEscrowTransfer.ts
  • src/core/endorsement-chain/fetchLogsChunked.ts
  • src/core/endorsement-chain/helpers.ts
  • src/core/endorsement-chain/obligation.ts
  • src/core/endorsement-chain/retrieveEndorsementChain.ts
  • src/core/endorsement-chain/useEndorsementChain.ts

Comment thread README.md Outdated
Comment thread src/core/endorsement-chain/fetchEscrowTransfer.ts Outdated
Comment thread src/core/endorsement-chain/fetchLogsChunked.ts
Comment thread src/core/endorsement-chain/fetchLogsChunked.ts Outdated
Comment thread src/core/endorsement-chain/fetchLogsChunked.ts Outdated
Comment thread src/core/endorsement-chain/fetchLogsChunked.ts Outdated
@manishdex25 manishdex25 self-assigned this Aug 11, 2026
@manishdex25
manishdex25 requested a review from RishabhS7 August 11, 2026 17:50

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/core/endorsement-chain/fetchLogsChunked.ts`:
- Around line 173-250: Reduce cognitive complexity in
src/core/endorsement-chain/fetchLogsChunked.ts:173-250 by extracting window
construction, request-budget validation, and batch result processing from
scanLogsBackwardParallel into focused helpers while preserving its retry,
truncation, and mint-detection behavior. Also refactor
src/core/endorsement-chain/fetchLogsChunked.ts:264-332 by extracting the
paid-tier iteration and Free-tier handoff from scanLogsBackward into focused
helpers; both sites require direct changes, and each top-level scan function
must remain at or below the SonarCloud complexity threshold.
- Around line 190-225: Update the chunk-fetch loop around getLogsRange so each
RPC attempt is counted before invocation, including failed and retried requests,
while preserving FREE_TIER_MAX_REQUESTS enforcement. Replace immediate
Promise.all rejection handling with batch settlement that waits for every active
request before retrying or throwing, preventing overlapping batches beyond
FREE_TIER_CONCURRENCY. Enforce FREE_TIER_MAX_DURATION_MS with a deadline applied
to the active batch so slow or hanging calls cannot continue past the scan
budget.

In `@src/core/endorsement-chain/retrieveEndorsementChain.ts`:
- Around line 21-27: Bound concurrency in the timestamp-fetching logic within
retrieveEndorsementChain by processing uniqueBlockNumbers through a fixed-size
limit or bounded batches instead of launching every fetchEventTime call via one
unrestricted Promise.all. Preserve the existing block-number-to-timestamp
pairing and timestampByBlock reconstruction after all requests complete.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 97ddaa88-6c7d-4b02-901b-9dfa8e1f6d79

📥 Commits

Reviewing files that changed from the base of the PR and between 4a6be63 and ce752ea.

📒 Files selected for processing (6)
  • README.md
  • src/constants.ts
  • src/core/endorsement-chain/fetchEscrowTransfer.ts
  • src/core/endorsement-chain/fetchLogsChunked.ts
  • src/core/endorsement-chain/helpers.ts
  • src/core/endorsement-chain/retrieveEndorsementChain.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • README.md
  • src/core/endorsement-chain/fetchEscrowTransfer.ts

Comment on lines +173 to +250
const scanLogsBackwardParallel = async (
provider: Provider | ethersV6.Provider,
address: string,
fromBlock: number,
toBlockFloor: number,
chunkSize: number,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
isMintLog?: (log: any) => boolean,
): Promise<ScanLogsBackwardResult> => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const chunkGroups: any[][] = [];
let cursor = fromBlock;
let windowSize = Math.max(Math.min(chunkSize, FREE_TIER_MAX_CHUNK_SIZE), MIN_CHUNK_SIZE);
let requestsUsed = 0;
const startedAt = Date.now();
let foundMint = false;

while (cursor >= toBlockFloor) {
if (Date.now() - startedAt > FREE_TIER_MAX_DURATION_MS) {
throw new Error(
`Infura Free-tier scan time budget exhausted after ${FREE_TIER_MAX_DURATION_MS}ms`,
);
}

const windows: Array<{ start: number; end: number }> = [];
let winCursor = cursor;
for (let i = 0; i < FREE_TIER_CONCURRENCY && winCursor >= toBlockFloor; i++) {
const start = Math.max(winCursor - windowSize + 1, toBlockFloor);
windows.push({ start, end: winCursor });
if (start <= toBlockFloor) break;
winCursor = start - 1;
}

if (requestsUsed + windows.length > FREE_TIER_MAX_REQUESTS) {
throw new Error(
`Infura Free-tier scan request budget exhausted (${FREE_TIER_MAX_REQUESTS} eth_getLogs calls)`,
);
}

let results: Awaited<ReturnType<typeof getLogsRange>>[];
try {
results = await Promise.all(
windows.map(({ start, end }) => getLogsRange(provider, address, start, end)),
);
requestsUsed += windows.length;
} catch (err) {
const message = errorMessage(err);
if (RANGE_TOO_LARGE_ERROR_RE.test(message) && windowSize > MIN_CHUNK_SIZE) {
// Keep already-collected chunkGroups; only shrink and retry this batch.
windowSize = Math.max(Math.floor(windowSize / 4), MIN_CHUNK_SIZE);
continue;
}
throw err;
}

for (const chunkLogs of results) {
if (pushMintSliceIfFound(chunkLogs, isMintLog, chunkGroups)) {
foundMint = true;
break;
}
chunkGroups.push(chunkLogs);
}

if (foundMint) {
return { logs: flattenOldestFirst(chunkGroups), foundMint: true, truncated: false };
}

const oldest = windows[windows.length - 1];
if (oldest.start <= toBlockFloor) break;
cursor = oldest.start - 1;
}

return {
logs: flattenOldestFirst(chunkGroups),
foundMint: false,
truncated: Boolean(isMintLog),
};
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Refactor the scan orchestration to satisfy the SonarCloud complexity gate.

SonarCloud reports cognitive complexity of 26 for scanLogsBackwardParallel and 17 for scanLogsBackward. Both exceed the allowed value of 15.

  • src/core/endorsement-chain/fetchLogsChunked.ts#L173-L250: Extract window construction, budget validation, and batch processing into focused helpers.
  • src/core/endorsement-chain/fetchLogsChunked.ts#L264-L332: Extract the paid-tier iteration and Free-tier handoff into focused helpers.
🧰 Tools
🪛 GitHub Check: SonarCloud Code Analysis

[failure] 181-181: Refactor this function to reduce its Cognitive Complexity from 26 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=TrustVC_trustvc&issues=AZ_yAE56Rar6iBThA1v7&open=AZ_yAE56Rar6iBThA1v7&pullRequest=164


[warning] 240-240: Prefer .at(…) over [….length - index].

See more on https://sonarcloud.io/project/issues?id=TrustVC_trustvc&issues=AZ_x4n5crIcTy-_nMoHG&open=AZ_x4n5crIcTy-_nMoHG&pullRequest=164

📍 Affects 1 file
  • src/core/endorsement-chain/fetchLogsChunked.ts#L173-L250 (this comment)
  • src/core/endorsement-chain/fetchLogsChunked.ts#L264-L332
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/endorsement-chain/fetchLogsChunked.ts` around lines 173 - 250,
Reduce cognitive complexity in
src/core/endorsement-chain/fetchLogsChunked.ts:173-250 by extracting window
construction, request-budget validation, and batch result processing from
scanLogsBackwardParallel into focused helpers while preserving its retry,
truncation, and mint-detection behavior. Also refactor
src/core/endorsement-chain/fetchLogsChunked.ts:264-332 by extracting the
paid-tier iteration and Free-tier handoff from scanLogsBackward into focused
helpers; both sites require direct changes, and each top-level scan function
must remain at or below the SonarCloud complexity threshold.

Source: Linters/SAST tools

Comment on lines +190 to +225
while (cursor >= toBlockFloor) {
if (Date.now() - startedAt > FREE_TIER_MAX_DURATION_MS) {
throw new Error(
`Infura Free-tier scan time budget exhausted after ${FREE_TIER_MAX_DURATION_MS}ms`,
);
}

const windows: Array<{ start: number; end: number }> = [];
let winCursor = cursor;
for (let i = 0; i < FREE_TIER_CONCURRENCY && winCursor >= toBlockFloor; i++) {
const start = Math.max(winCursor - windowSize + 1, toBlockFloor);
windows.push({ start, end: winCursor });
if (start <= toBlockFloor) break;
winCursor = start - 1;
}

if (requestsUsed + windows.length > FREE_TIER_MAX_REQUESTS) {
throw new Error(
`Infura Free-tier scan request budget exhausted (${FREE_TIER_MAX_REQUESTS} eth_getLogs calls)`,
);
}

let results: Awaited<ReturnType<typeof getLogsRange>>[];
try {
results = await Promise.all(
windows.map(({ start, end }) => getLogsRange(provider, address, start, end)),
);
requestsUsed += windows.length;
} catch (err) {
const message = errorMessage(err);
if (RANGE_TOO_LARGE_ERROR_RE.test(message) && windowSize > MIN_CHUNK_SIZE) {
// Keep already-collected chunkGroups; only shrink and retry this batch.
windowSize = Math.max(Math.floor(windowSize / 4), MIN_CHUNK_SIZE);
continue;
}
throw err;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Enforce the request and duration limits for actual RPC calls.

requestsUsed increases only after a full batch succeeds. Retries and failed range batches do not count against FREE_TIER_MAX_REQUESTS.

If one request rejects, Promise.all rejects immediately. The remaining requests continue in the background, and the next loop starts another batch. This can exceed FREE_TIER_CONCURRENCY.

The duration check runs before Promise.all. A slow or hanging RPC batch can run past FREE_TIER_MAX_DURATION_MS.

Count each request before provider.getLogs. Apply a deadline to the active batch. Wait for the batch to settle before retrying a smaller range.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/endorsement-chain/fetchLogsChunked.ts` around lines 190 - 225,
Update the chunk-fetch loop around getLogsRange so each RPC attempt is counted
before invocation, including failed and retried requests, while preserving
FREE_TIER_MAX_REQUESTS enforcement. Replace immediate Promise.all rejection
handling with batch settlement that waits for every active request before
retrying or throwing, preventing overlapping batches beyond
FREE_TIER_CONCURRENCY. Enforce FREE_TIER_MAX_DURATION_MS with a deadline applied
to the active batch so slow or hanging calls cannot continue past the scan
budget.

Comment on lines +21 to +27
const timestampByBlock = new Map(
await Promise.all(
uniqueBlockNumbers.map(
async (blockNumber) => [blockNumber, await fetchEventTime(blockNumber, provider)] as const,
),
),
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Bound concurrent block timestamp requests.

Promise.all starts a getBlock request for every unique block at once. A large endorsement chain can create thousands of concurrent RPC requests.

Process unique block numbers with a fixed concurrency limit or bounded batches. Preserve the timestampByBlock reconstruction after the requests complete.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/endorsement-chain/retrieveEndorsementChain.ts` around lines 21 - 27,
Bound concurrency in the timestamp-fetching logic within
retrieveEndorsementChain by processing uniqueBlockNumbers through a fixed-size
limit or bounded batches instead of launching every fetchEventTime call via one
unrestricted Promise.all. Preserve the existing block-number-to-timestamp
pairing and timestampByBlock reconstruction after all requests complete.

@sonarqubecloud

Copy link
Copy Markdown

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.

1 participant