Skip to content

Conversation

fadeev
Copy link
Member

@fadeev fadeev commented Aug 27, 2025

It was only possible to query CCTX by a transaction hash on a connected chain, like so:

yarn -s zetachain q cctx --hash 0x8e925fa63c69bd27a3aa8e30f4c0f1e67e5fd3fedb23339b387b51b1543e55af
11155111 → 7001 ✅ OutboundMined
CCTX:     0x542b6bd80004f4013b725c2170b9ed01731b8af9dc61bfb5c0534dc2f0d511da
Tx Hash:  0x8e925fa63c69bd27a3aa8e30f4c0f1e67e5fd3fedb23339b387b51b1543e55af (on chain 11155111)
Tx Hash:  0x08e10a86711981a90715a0d91c05b1a3f940142049d7a57181510375c997f34b (on chain 7001)
Sender:   0x4955a3F38ff86ae92A914445099caa8eA2B9bA32
Receiver: 0x93491dF3516B795321b52Bf0d8F62dc6d7Bf1A57
Message:  000000000000000000000000777915d031d1e8144c90d025c594b3b8bf07a08d0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000144955a3f38ff86ae92a914445099caa8ea2b9ba32000000000000000000000000
Amount:   100000000000000000 Gas tokens
Status:   OutboundMined, Status changed from PendingOutbound to OutboundMined

7001 → 80002 ✅ OutboundMined
CCTX:     0x36f41b8ca20bd615b1211de1ff5ec67ea58313a9145628cb25922399472dcc14
Tx Hash:  0x542b6bd80004f4013b725c2170b9ed01731b8af9dc61bfb5c0534dc2f0d511da (on chain 7001)
Tx Hash:  0x49f67ece0c0b59d58312df91342d46b14496abf2d8a52a1a5ce9f4c6136e8d75 (on chain 80002)
Sender:   0x93491dF3516B795321b52Bf0d8F62dc6d7Bf1A57
Receiver: 0x4955a3F38ff86ae92A914445099caa8eA2B9bA32
Amount:   926697400447853988 Gas tokens
Status:   OutboundMined, Status changed from PendingOutbound to OutboundMined

With this PR it's also possible to track by CCTX hash:

yarn -s zetachain q cctx --hash 0x542b6bd80004f4013b725c2170b9ed01731b8af9dc61bfb5c0534dc2f0d511da

Returns the same data:

11155111 → 7001 ✅ OutboundMined
CCTX:     0x542b6bd80004f4013b725c2170b9ed01731b8af9dc61bfb5c0534dc2f0d511da
Tx Hash:  0x8e925fa63c69bd27a3aa8e30f4c0f1e67e5fd3fedb23339b387b51b1543e55af (on chain 11155111)
Tx Hash:  0x08e10a86711981a90715a0d91c05b1a3f940142049d7a57181510375c997f34b (on chain 7001)
Sender:   0x4955a3F38ff86ae92A914445099caa8eA2B9bA32
Receiver: 0x93491dF3516B795321b52Bf0d8F62dc6d7Bf1A57
Message:  000000000000000000000000777915d031d1e8144c90d025c594b3b8bf07a08d0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000144955a3f38ff86ae92a914445099caa8ea2b9ba32000000000000000000000000
Amount:   100000000000000000 Gas tokens
Status:   OutboundMined, Status changed from PendingOutbound to OutboundMined

7001 → 80002 ✅ OutboundMined
CCTX:     0x36f41b8ca20bd615b1211de1ff5ec67ea58313a9145628cb25922399472dcc14
Tx Hash:  0x542b6bd80004f4013b725c2170b9ed01731b8af9dc61bfb5c0534dc2f0d511da (on chain 7001)
Tx Hash:  0x49f67ece0c0b59d58312df91342d46b14496abf2d8a52a1a5ce9f4c6136e8d75 (on chain 80002)
Sender:   0x93491dF3516B795321b52Bf0d8F62dc6d7Bf1A57
Receiver: 0x4955a3F38ff86ae92A914445099caa8eA2B9bA32
Amount:   926697400447853988 Gas tokens
Status:   OutboundMined, Status changed from PendingOutbound to OutboundMined

Summary by CodeRabbit

  • New Features

    • You can now query cross-chain transactions using either an inbound transaction hash or a CCTX hash.
    • The CLI automatically discovers related CCTXs from the provided hash and continues querying relevant items for up-to-date status.
    • Improved resilience when data is temporarily unavailable; retries proceed without blocking subsequent lookups.
  • Documentation

    • Updated CLI help text for the hash option to clarify it accepts an inbound tx hash or a CCTX hash.

Copy link
Contributor

coderabbitai bot commented Aug 27, 2025

📝 Walkthrough

Walkthrough

Two-phase CCTX discovery added: initial round queries by both CCTX hash and inbound hash, then switches to inbound-hash polling once any CCTX is found. New API utility fetches CCTX data by inbound hash. CLI flag description updated to accept inbound tx hash or CCTX hash. Legacy wrapper interface removed.

Changes

Cohort / File(s) Summary of changes
CCTX query workflow
packages/commands/src/query/cctx.ts
Implemented two-phase discovery (getCctxByHash + getCctxDataByInboundHash) with awaitingRootDiscovery flag; enqueues discovered CCTXs and pending observed inbound hashes; switched post-discovery polling to inbound-hash endpoint; removed CctxResponse wrapper; updated imports; updated -h/--hash option description.
API utilities
utils/api.ts
Added exported getCctxDataByInboundHash(api, hash): Promise<CrossChainTx[]> calling /zeta-chain/crosschain/inboundHashToCctxData/${hash}, with 400/404 silent empty result handling and error logging otherwise; ensured CrossChainTx type import.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant CLI as cctxCommand
  participant API as API Client
  participant ZC as Zeta API

  U->>CLI: cctx --hash <hash>
  rect rgb(235, 245, 255)
    note right of CLI: Discovery phase (until first CCTX found)
    CLI->>API: getCctxByHash(hash)
    API->>ZC: /.../getCctxByHash/{hash}
    ZC-->>API: CrossChainTx[] or []
    API-->>CLI: CCTXs
    CLI->>API: getCctxDataByInboundHash(hash)
    API->>ZC: /.../inboundHashToCctxData/{hash}
    ZC-->>API: {CrossChainTxs[]} or []
    API-->>CLI: CCTXs
    alt any CCTXs found
      CLI->>CLI: awaitingRootDiscovery = false<br/>enqueue CCTXs + pending observed_hashes
    else none found
      CLI->>CLI: retry/backoff until timeout
    end
  end

  rect rgb(240, 255, 240)
    note right of CLI: Post-discovery polling (inbound-hash based)
    loop until done/timeout
      CLI->>API: getCctxDataByInboundHash(inbound_hash)
      API->>ZC: /.../inboundHashToCctxData/{inbound_hash}
      ZC-->>API: CrossChainTxs or []
      API-->>CLI: results
      alt CCTXs returned
        CLI->>CLI: update results, enqueue new indices and observed pending hashes
      else empty
        CLI->>CLI: requeue hash for retry
      end
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch query-cctx-by-cctx-hash

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
🪧 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.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

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

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.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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.

@github-actions github-actions bot added the feat label Aug 27, 2025
@fadeev fadeev marked this pull request as ready for review August 27, 2025 14:45
@fadeev fadeev requested review from a team as code owners August 27, 2025 14:45
Copy link
Contributor

@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: 1

🧹 Nitpick comments (4)
utils/api.ts (2)

91-118: Good utility; align 400-handling across endpoints.
This function suppresses 404/400, but getCctxByInboundHash (Lines 79-83) suppresses only 404. For parity during polling (and to avoid noisy logs when API returns 400), handle 400 there too.

-  } catch (error) {
-    // Completely suppress 404 errors - these are expected during polling
-    if (isAxiosError(error) && error.response?.status === 404) {
+  } catch (error) {
+    // Completely suppress 404/400 errors - these are expected during polling
+    if (
+      isAxiosError(error) &&
+      (error.response?.status === 404 || error.response?.status === 400)
+    ) {
       // Don't log anything - these are normal during polling
       return [];
     }

91-118: Minor: extract response type for reuse.
To avoid inline literal types and ease future changes, define a named interface for the response payload.

+interface InboundHashToCctxDataResponse {
+  CrossChainTxs: CrossChainTx[];
+}
 ...
-    const data = await fetchFromApi<{ CrossChainTxs: CrossChainTx[] }>(
+    const data = await fetchFromApi<InboundHashToCctxDataResponse>(
       api,
       `/zeta-chain/crosschain/inboundHashToCctxData/${hash}`
     );
packages/commands/src/query/cctx.ts (2)

107-110: Guard against empty inbound observed_hash before enqueuing.
Avoid adding empty strings to the frontier.

-              if (isPending(tx)) {
-                nextFrontier.add(tx.inbound_params.observed_hash);
-              }
+              if (isPending(tx) && tx.inbound_params.observed_hash) {
+                nextFrontier.add(tx.inbound_params.observed_hash);
+              }

Apply in both occurrences.

Also applies to: 126-129


293-293: CLI UX: avoid using -h for non-help option.
Commander reserves -h for help; reusing it can confuse users and clobber built-in help. Consider -H as the short flag.

-  .requiredOption("-h, --hash <hash>", "Inbound tx hash or CCTX hash")
+  .requiredOption("-H, --hash <hash>", "Inbound tx hash or CCTX hash")
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 698a181 and 1429f30.

📒 Files selected for processing (2)
  • packages/commands/src/query/cctx.ts (4 hunks)
  • utils/api.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: fadeev
PR: zeta-chain/toolkit#346
File: packages/commands/src/ton/depositAndCall.ts:0-0
Timestamp: 2025-06-13T15:33:54.781Z
Learning: fadeev prefers responses in English.
Learnt from: fadeev
PR: zeta-chain/toolkit#346
File: packages/commands/src/ton/deposit.ts:0-0
Timestamp: 2025-06-13T15:32:09.225Z
Learning: User fadeev prefers that responses be in English; avoid replying in Russian in future interactions.
🧬 Code graph analysis (2)
packages/commands/src/query/cctx.ts (2)
types/trackCCTX.types.ts (1)
  • CrossChainTx (57-119)
utils/api.ts (2)
  • getCctxByHash (39-63)
  • getCctxDataByInboundHash (95-118)
utils/api.ts (2)
types/trackCCTX.types.ts (1)
  • CrossChainTx (57-119)
utils/handleError.ts (1)
  • handleError (31-45)
🔇 Additional comments (5)
utils/api.ts (1)

4-4: Import addition looks correct.
Type is used below; no issues.

packages/commands/src/query/cctx.ts (4)

13-17: Updated imports look good.
Matches new utils surface.


73-101: Discovery flow is sound.
Trying getCctxByHash first, then inboundHashToCctxData, and switching modes once discovered is a good approach for flexibility and efficiency.


281-289: Emission strategy LGTM.
Per-round full snapshot emission keeps CLI output coherent.


1-309: Exports and schema coercion confirmed
All three utilities—getCctxByHash, getCctxDataByInboundHash, and sleep—are exported in utils/index.ts, and cctxOptionsSchema correctly coerces both delay and timeout to numbers via z.coerce.number(). No changes required.

Comment on lines +112 to 131
} else {
const cctxs = await getCctxDataByInboundHash(rpc, hash);

// Keep querying while pending
if (isPending(tx)) {
nextFrontier.add(tx.inbound_params.observed_hash);
if (cctxs.length === 0) {
// Still 404 – keep trying
nextFrontier.add(hash);
return;
}

queriedOnce.add(tx.index);
for (const tx of cctxs) {
results.set(tx.index, tx);
if (!queriedOnce.has(tx.index)) {
nextFrontier.add(tx.index);
}
if (isPending(tx)) {
nextFrontier.add(tx.inbound_params.observed_hash);
}
queriedOnce.add(tx.index);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Bug: Post-discovery uses inbound-hash API for CCTX indexes, causing endless 404 loops.
After discovery you enqueue both tx.index (CCTX id) and inbound observed hashes into frontier, but the else-branch always calls getCctxDataByInboundHash for any hash. When hash is actually a CCTX index, this returns 404 forever and re-enqueues the same value.

Use a simple discriminator: if results already contains the key, treat it as CCTX index and refresh via getCctxByHash; otherwise treat it as inbound hash.

-          } else {
-            const cctxs = await getCctxDataByInboundHash(rpc, hash);
-
-            if (cctxs.length === 0) {
-              // Still 404 – keep trying
-              nextFrontier.add(hash);
-              return;
-            }
-
-            for (const tx of cctxs) {
-              results.set(tx.index, tx);
-              if (!queriedOnce.has(tx.index)) {
-                nextFrontier.add(tx.index);
-              }
-              if (isPending(tx)) {
-                nextFrontier.add(tx.inbound_params.observed_hash);
-              }
-              queriedOnce.add(tx.index);
-            }
-          }
+          } else {
+            // If we've seen this key as an index before, refresh via single CCTX endpoint.
+            if (results.has(hash)) {
+              const single = await getCctxByHash(rpc, hash);
+              if (!single) {
+                nextFrontier.add(hash);
+                return;
+              }
+              const tx = single;
+              results.set(tx.index, tx);
+              if (!queriedOnce.has(tx.index)) {
+                nextFrontier.add(tx.index);
+                queriedOnce.add(tx.index);
+              }
+              if (isPending(tx) && tx.inbound_params.observed_hash) {
+                nextFrontier.add(tx.inbound_params.observed_hash);
+              }
+            } else {
+              const cctxs = await getCctxDataByInboundHash(rpc, hash);
+              if (cctxs.length === 0) {
+                nextFrontier.add(hash);
+                return;
+              }
+              for (const tx of cctxs) {
+                results.set(tx.index, tx);
+                if (!queriedOnce.has(tx.index)) {
+                  nextFrontier.add(tx.index);
+                }
+                if (isPending(tx) && tx.inbound_params.observed_hash) {
+                  nextFrontier.add(tx.inbound_params.observed_hash);
+                }
+                queriedOnce.add(tx.index);
+              }
+            }
+          }
📝 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
} else {
const cctxs = await getCctxDataByInboundHash(rpc, hash);
// Keep querying while pending
if (isPending(tx)) {
nextFrontier.add(tx.inbound_params.observed_hash);
if (cctxs.length === 0) {
// Still 404 – keep trying
nextFrontier.add(hash);
return;
}
queriedOnce.add(tx.index);
for (const tx of cctxs) {
results.set(tx.index, tx);
if (!queriedOnce.has(tx.index)) {
nextFrontier.add(tx.index);
}
if (isPending(tx)) {
nextFrontier.add(tx.inbound_params.observed_hash);
}
queriedOnce.add(tx.index);
}
}
} else {
// If we've seen this key as an index before, refresh via the single‐CCTX endpoint.
if (results.has(hash)) {
const single = await getCctxByHash(rpc, hash);
if (!single) {
// Still 404 – keep trying
nextFrontier.add(hash);
return;
}
const tx = single;
results.set(tx.index, tx);
if (!queriedOnce.has(tx.index)) {
nextFrontier.add(tx.index);
queriedOnce.add(tx.index);
}
if (isPending(tx) && tx.inbound_params.observed_hash) {
nextFrontier.add(tx.inbound_params.observed_hash);
}
} else {
// Otherwise treat it as an inbound‐hash lookup
const cctxs = await getCctxDataByInboundHash(rpc, hash);
if (cctxs.length === 0) {
// Still 404 – keep trying
nextFrontier.add(hash);
return;
}
for (const tx of cctxs) {
results.set(tx.index, tx);
if (!queriedOnce.has(tx.index)) {
nextFrontier.add(tx.index);
}
if (isPending(tx) && tx.inbound_params.observed_hash) {
nextFrontier.add(tx.inbound_params.observed_hash);
}
queriedOnce.add(tx.index);
}
}
}
🤖 Prompt for AI Agents
In packages/commands/src/query/cctx.ts around lines 112 to 131, the code always
calls getCctxDataByInboundHash(rpc, hash) causing 404 loops when the frontier
item is actually a CCTX index; change the branch to discriminate by whether
results already has the hash (i.e., results.has(hash) means this is a CCTX
index) and in that case call getCctxByHash(rpc, hash) to refresh the CCTX;
otherwise call getCctxDataByInboundHash for inbound hashes. Mirror the existing
handling for pushing tx.index, tx.inbound_params.observed_hash, queriedOnce and
nextFrontier in each path so you don't re-enqueue the same 404-producing value
and avoid infinite retry loops.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant