-
Notifications
You must be signed in to change notification settings - Fork 35
feat: query CCTX by CCTX hash #403
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
base: main
Are you sure you want to change the base?
Conversation
📝 WalkthroughWalkthroughTwo-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
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes ✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
There was a problem hiding this 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.
📒 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
, andsleep
—are exported inutils/index.ts
, andcctxOptionsSchema
correctly coerces bothdelay
andtimeout
to numbers viaz.coerce.number()
. No changes required.
} 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); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
} 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.
It was only possible to query CCTX by a transaction hash on a connected chain, like so:
With this PR it's also possible to track by CCTX hash:
Returns the same data:
Summary by CodeRabbit
New Features
Documentation