@pushchain/core 5.1.5-alpha.1 → 5.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/package.json +1 -1
  3. package/src/lib/constants/abi/index.d.ts +2 -0
  4. package/src/lib/constants/abi/index.js +6 -1
  5. package/src/lib/constants/abi/uniswap-v3-router.evm.d.ts +58 -0
  6. package/src/lib/constants/abi/uniswap-v3-router.evm.js +41 -0
  7. package/src/lib/constants/abi/wpc.evm.d.ts +24 -0
  8. package/src/lib/constants/abi/wpc.evm.js +27 -0
  9. package/src/lib/constants/chain.d.ts +1 -0
  10. package/src/lib/constants/chain.js +8 -6
  11. package/src/lib/constants/tokens.js +3 -3
  12. package/src/lib/generated/uexecutor/v2/types.d.ts +14 -0
  13. package/src/lib/generated/uexecutor/v2/types.js +96 -3
  14. package/src/lib/orchestrator/internals/cascade.d.ts +1 -1
  15. package/src/lib/orchestrator/internals/cascade.js +521 -239
  16. package/src/lib/orchestrator/internals/confirmation.js +60 -15
  17. package/src/lib/orchestrator/internals/context.d.ts +23 -0
  18. package/src/lib/orchestrator/internals/context.js +54 -0
  19. package/src/lib/orchestrator/internals/execute-funds-only.js +47 -21
  20. package/src/lib/orchestrator/internals/execute-funds-payload.js +53 -31
  21. package/src/lib/orchestrator/internals/execute-standard.js +137 -26
  22. package/src/lib/orchestrator/internals/gas-calculator.d.ts +5 -2
  23. package/src/lib/orchestrator/internals/gas-calculator.js +59 -11
  24. package/src/lib/orchestrator/internals/gas-usd-sizer.d.ts +53 -0
  25. package/src/lib/orchestrator/internals/gas-usd-sizer.js +165 -0
  26. package/src/lib/orchestrator/internals/helpers.js +2 -0
  27. package/src/lib/orchestrator/internals/inbound-tracker.d.ts +83 -0
  28. package/src/lib/orchestrator/internals/inbound-tracker.js +322 -0
  29. package/src/lib/orchestrator/internals/index.d.ts +3 -1
  30. package/src/lib/orchestrator/internals/index.js +8 -2
  31. package/src/lib/orchestrator/internals/outbound-sync.d.ts +32 -1
  32. package/src/lib/orchestrator/internals/outbound-sync.js +154 -17
  33. package/src/lib/orchestrator/internals/pc-usd-oracle.d.ts +28 -0
  34. package/src/lib/orchestrator/internals/pc-usd-oracle.js +285 -0
  35. package/src/lib/orchestrator/internals/progress-route-hooks.d.ts +23 -0
  36. package/src/lib/orchestrator/internals/progress-route-hooks.js +72 -0
  37. package/src/lib/orchestrator/internals/push-chain-tx.d.ts +27 -1
  38. package/src/lib/orchestrator/internals/push-chain-tx.js +111 -15
  39. package/src/lib/orchestrator/internals/rescue.js +1 -1
  40. package/src/lib/orchestrator/internals/response-builder.d.ts +5 -0
  41. package/src/lib/orchestrator/internals/response-builder.js +242 -66
  42. package/src/lib/orchestrator/internals/route-handlers.js +480 -206
  43. package/src/lib/orchestrator/internals/tx-transformer.js +293 -35
  44. package/src/lib/orchestrator/orchestrator.d.ts +7 -1
  45. package/src/lib/orchestrator/orchestrator.js +108 -56
  46. package/src/lib/orchestrator/orchestrator.types.d.ts +111 -5
  47. package/src/lib/orchestrator/payload-builders.d.ts +16 -0
  48. package/src/lib/orchestrator/payload-builders.js +6 -5
  49. package/src/lib/orchestrator/route-detector.js +20 -8
  50. package/src/lib/orchestrator/svm-idl/normalize-address.d.ts +19 -0
  51. package/src/lib/orchestrator/svm-idl/normalize-address.js +56 -0
  52. package/src/lib/orchestrator/svm-idl/registry.d.ts +13 -4
  53. package/src/lib/orchestrator/svm-idl/registry.js +15 -14
  54. package/src/lib/orchestrator/svm-idl/resolve.js +5 -2
  55. package/src/lib/progress-hook/progress-hook.d.ts +28 -0
  56. package/src/lib/progress-hook/progress-hook.js +545 -131
  57. package/src/lib/progress-hook/progress-hook.types.d.ts +76 -29
  58. package/src/lib/progress-hook/progress-hook.types.js +74 -35
  59. package/src/lib/push-client/push-client.d.ts +7 -0
  60. package/src/lib/push-client/push-client.js +18 -1
  61. package/src/lib/universal-tx-detector/__tests__/fixtures/builders.d.ts +32 -0
  62. package/src/lib/universal-tx-detector/__tests__/fixtures/builders.js +65 -0
  63. package/src/lib/universal-tx-detector/__tests__/fixtures/scenarios.d.ts +72 -0
  64. package/src/lib/universal-tx-detector/__tests__/fixtures/scenarios.js +669 -0
  65. package/src/lib/universal-tx-detector/auto-detect.d.ts +37 -0
  66. package/src/lib/universal-tx-detector/auto-detect.js +112 -0
  67. package/src/lib/universal-tx-detector/cascade.d.ts +64 -0
  68. package/src/lib/universal-tx-detector/cascade.js +254 -0
  69. package/src/lib/universal-tx-detector/child-inbounds.d.ts +58 -0
  70. package/src/lib/universal-tx-detector/child-inbounds.js +145 -0
  71. package/src/lib/universal-tx-detector/classify.d.ts +33 -0
  72. package/src/lib/universal-tx-detector/classify.js +228 -0
  73. package/src/lib/universal-tx-detector/detector-svm.d.ts +46 -0
  74. package/src/lib/universal-tx-detector/detector-svm.js +324 -0
  75. package/src/lib/universal-tx-detector/detector.d.ts +13 -0
  76. package/src/lib/universal-tx-detector/detector.js +233 -0
  77. package/src/lib/universal-tx-detector/events.d.ts +765 -0
  78. package/src/lib/universal-tx-detector/events.js +241 -0
  79. package/src/lib/universal-tx-detector/index.d.ts +18 -0
  80. package/src/lib/universal-tx-detector/index.js +36 -0
  81. package/src/lib/universal-tx-detector/svm-events.d.ts +41 -0
  82. package/src/lib/universal-tx-detector/svm-events.js +68 -0
  83. package/src/lib/universal-tx-detector/types.d.ts +100 -0
  84. package/src/lib/universal-tx-detector/types.js +3 -0
  85. package/src/lib/utils.d.ts +30 -13
  86. package/src/lib/utils.js +47 -21
@@ -25,7 +25,39 @@ const context_1 = require("./context");
25
25
  const helpers_1 = require("./helpers");
26
26
  const svm_helpers_1 = require("./svm-helpers");
27
27
  const outbound_tracker_1 = require("./outbound-tracker");
28
+ const inbound_tracker_1 = require("./inbound-tracker");
28
29
  const tx_transformer_1 = require("./tx-transformer");
30
+ const progress_route_hooks_1 = require("./progress-route-hooks");
31
+ const outbound_sync_1 = require("./outbound-sync");
32
+ /**
33
+ * Intermediate Push-success markers (199-99-99 for R3, 299-99 for R2) are
34
+ * internal transition signals — they tell response-builder's wait-phase
35
+ * branch that the Push leg has settled so it can skip re-emitting the
36
+ * intermediate marker on top of a reconstructed stream. They are not part
37
+ * of the published 1XX/2XX/3XX/4XX spec and are suppressed at the consumer
38
+ * dispatch boundary. `printLog` still fires for these so internal traces
39
+ * retain the transition record.
40
+ */
41
+ const INTERMEDIATE_INTERNAL_IDS = new Set([
42
+ progress_hook_types_1.PROGRESS_HOOK.SEND_TX_199_99_99,
43
+ progress_hook_types_1.PROGRESS_HOOK.SEND_TX_299_99,
44
+ ]);
45
+ /**
46
+ * Fan out a progress event to one or two registered callbacks, deduping
47
+ * when both slots hold the same reference. Keeps the emission policy in a
48
+ * single place shared by `emitProgress` (execute phase) and `emit` (wait
49
+ * phase) — so consumers that wire the same callback at multiple levels
50
+ * never see double-invocation. Intermediate internal markers are dropped
51
+ * here so they never reach the consumer.
52
+ */
53
+ function fanOut(event, primary, secondary) {
54
+ if (INTERMEDIATE_INTERNAL_IDS.has(event.id))
55
+ return;
56
+ if (primary)
57
+ primary(event);
58
+ if (secondary && secondary !== primary)
59
+ secondary(event);
60
+ }
29
61
  // ============================================================================
30
62
  // queryUniversalTxStatusFromGatewayTx
31
63
  // ============================================================================
@@ -107,8 +139,8 @@ txHash, evmGatewayMethod) {
107
139
  }, null, 2));
108
140
  // Fetch UniversalTx via gRPC with linear-then-exponential retry
109
141
  const LINEAR_ATTEMPTS = 10;
110
- const LINEAR_DELAY_MS = 3000;
111
- const EXPONENTIAL_BASE_MS = 4000;
142
+ const LINEAR_DELAY_MS = 3600;
143
+ const EXPONENTIAL_BASE_MS = 4800;
112
144
  const MAX_ATTEMPTS = 15;
113
145
  let universalTxObj;
114
146
  for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
@@ -148,6 +180,7 @@ txHash, evmGatewayMethod) {
148
180
  // ============================================================================
149
181
  function trackTransaction(ctx, txHash, options, callbacks) {
150
182
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
183
+ var _a, _b;
151
184
  if (!callbacks) {
152
185
  throw new Error('trackTransaction requires ResponseBuilderCallbacks');
153
186
  }
@@ -155,18 +188,13 @@ function trackTransaction(ctx, txHash, options, callbacks) {
155
188
  const { timeout = 300000, pollingIntervalMs = 1000, rpcUrls = {} } = advanced;
156
189
  // Event buffer for replay via response.progressHook()
157
190
  const eventBuffer = [];
158
- // Helper to emit progress events
191
+ // Helper to emit progress events. Per-tx hook (passed to trackTransaction)
192
+ // fires first, then the orchestrator-level hook — deduped via `fanOut` so
193
+ // a caller that wires the same callback at both levels isn't double-fired.
159
194
  const emitProgress = (event) => {
160
195
  eventBuffer.push(event);
161
196
  (0, context_1.printLog)(ctx, event.message);
162
- // Per-transaction hook called FIRST
163
- if (progressHook) {
164
- progressHook(event);
165
- }
166
- // Orchestrator-level hook called SECOND
167
- if (ctx.progressHook) {
168
- ctx.progressHook(event);
169
- }
197
+ fanOut(event, progressHook, ctx.progressHook);
170
198
  };
171
199
  // Create client for target chain with optional RPC override
172
200
  const chainRPCs = rpcUrls[chain] || ctx.rpcUrls[chain] || chain_1.CHAIN_INFO[chain].defaultRPC;
@@ -205,7 +233,7 @@ function trackTransaction(ctx, txHash, options, callbacks) {
205
233
  universalTxData = utxResponse.universalTx;
206
234
  }
207
235
  }
208
- catch (_a) {
236
+ catch (_c) {
209
237
  // Ignore - try computed ID next
210
238
  }
211
239
  if (!universalTxData) {
@@ -217,7 +245,7 @@ function trackTransaction(ctx, txHash, options, callbacks) {
217
245
  universalTxData = utxResponse.universalTx;
218
246
  }
219
247
  }
220
- catch (_b) {
248
+ catch (_d) {
221
249
  // Ignore - try Cosmos event extraction next
222
250
  }
223
251
  }
@@ -232,7 +260,7 @@ function trackTransaction(ctx, txHash, options, callbacks) {
232
260
  }
233
261
  }
234
262
  }
235
- catch (_c) {
263
+ catch (_e) {
236
264
  // Ignore - no universal tx data available
237
265
  }
238
266
  }
@@ -243,12 +271,48 @@ function trackTransaction(ctx, txHash, options, callbacks) {
243
271
  const detectedRoute = (0, tx_transformer_1.detectRouteFromUniversalTxData)(universalTxData);
244
272
  if (detectedRoute) {
245
273
  universalTxResponse.route = detectedRoute;
274
+ // Mirror the route on the context so emission code in wait()/cascade
275
+ // picks the correct ID range — keeps trackTransaction in lockstep with
276
+ // sendUniversalTransaction, which sets currentRoute in execute().
277
+ ctx.currentRoute = detectedRoute;
278
+ // Populate the destination/source chain on the response so
279
+ // reconstructR2/R3 (and any downstream emission that reads
280
+ // universalTxResponse.chain) emit the proper friendly chain name
281
+ // instead of the "external" fallback. The first outbound's destination
282
+ // is the target chain for R2 and the source CEA chain for R3.
283
+ const firstOutboundChain = (_b = (_a = universalTxData === null || universalTxData === void 0 ? void 0 : universalTxData.outboundTx) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.destinationChain;
284
+ if (firstOutboundChain && !universalTxResponse.chain) {
285
+ universalTxResponse.chain = firstOutboundChain;
286
+ universalTxResponse.chainNamespace = firstOutboundChain;
287
+ }
288
+ // For R3 replays, enable inbound round-trip tracking so `.wait()` runs
289
+ // the 310-01 → 399-01 sequence. Live execute sets this in
290
+ // route-handlers.ts via `amount > 0n`; trackTransaction has no
291
+ // execute-phase args, so we enable it unconditionally for CEA_TO_PUSH.
292
+ // The inbound block tolerates no-inbound (payload-only R3) by timing
293
+ // out — same as live behaviour when the source tx has no round-trip.
294
+ if (detectedRoute === route_detector_1.TransactionRoute.CEA_TO_PUSH) {
295
+ universalTxResponse._expectsInboundRoundTrip = true;
296
+ }
246
297
  }
247
298
  // Reconstruct and emit SEND-TX-* progress events
248
299
  const reconstructedEvents = (0, tx_transformer_1.reconstructProgressEvents)(universalTxResponse, universalTxData);
249
300
  for (const event of reconstructedEvents) {
250
301
  emitProgress(event);
251
302
  }
303
+ // Mark the response so the wait() closure (when later invoked) doesn't
304
+ // call trackTransaction a second time and re-emit the same reconstructed
305
+ // events to the user's progressHook.
306
+ universalTxResponse._eventsReconstructed = true;
307
+ // Auto-register the caller's per-call progressHook as the response's
308
+ // registeredProgressHook so that tracked.wait() fires wait-phase events
309
+ // (209-xx / 299-xx / 399-xx) to the same callback without requiring
310
+ // tracked.progressHook(cb) to be called again. Skip replay of buffered
311
+ // events because we just fired them above via emitProgress — calling
312
+ // response.progressHook(cb) here would re-emit the reconstructed stream.
313
+ if (progressHook && universalTxResponse._setProgressHookNoReplay) {
314
+ universalTxResponse._setProgressHookNoReplay(progressHook);
315
+ }
252
316
  return universalTxResponse;
253
317
  });
254
318
  }
@@ -280,18 +344,36 @@ function transformToUniversalTxResponse(ctx_1, tx_1) {
280
344
  }
281
345
  from = (0, viem_1.getAddress)(tx.to);
282
346
  let decoded;
347
+ let decodedPayload = null;
283
348
  if (tx.input !== '0x') {
284
- decoded = (0, viem_1.decodeFunctionData)({
285
- abi: uea_evm_1.UEA_EVM,
286
- data: tx.input,
287
- });
288
- if (!(decoded === null || decoded === void 0 ? void 0 : decoded.args)) {
289
- throw new Error('Failed to decode function data');
349
+ // Try to decode the input as a known UEA_EVM call. Older txs — or
350
+ // txs sent to a UEA that was upgraded to expose a function not yet
351
+ // reflected in the bundled `uea.evm.ts` ABI — carry a selector that
352
+ // viem can't match. In that case the reconstructor should still
353
+ // produce a usable response (and emit progress hooks) using the raw
354
+ // tx fields, rather than throwing before any hook fires.
355
+ try {
356
+ decoded = (0, viem_1.decodeFunctionData)({ abi: uea_evm_1.UEA_EVM, data: tx.input });
357
+ if (decoded === null || decoded === void 0 ? void 0 : decoded.args) {
358
+ decodedPayload = decoded.args[0];
359
+ }
360
+ }
361
+ catch (_a) {
362
+ decodedPayload = null;
363
+ }
364
+ if (decodedPayload) {
365
+ to = decodedPayload.to;
366
+ value = BigInt(decodedPayload.value);
367
+ data = decodedPayload.data;
368
+ }
369
+ else {
370
+ // Unknown / unsupported UEA function — fall back to raw fields so
371
+ // transformToUniversalTxResponse can still return a valid response
372
+ // and downstream progress-hook reconstruction can proceed.
373
+ to = (0, viem_1.getAddress)(tx.to);
374
+ value = tx.value;
375
+ data = tx.input;
290
376
  }
291
- const universalPayload = decoded === null || decoded === void 0 ? void 0 : decoded.args[0];
292
- to = universalPayload.to;
293
- value = BigInt(universalPayload.value);
294
- data = universalPayload.data;
295
377
  // Extract 'to' from single-element multicall
296
378
  if (data && data.length >= 10) {
297
379
  const multicallSelector = (0, viem_1.keccak256)((0, viem_1.toBytes)('UEA_MULTICALL')).slice(0, 10);
@@ -313,7 +395,7 @@ function transformToUniversalTxResponse(ctx_1, tx_1) {
313
395
  to = (0, viem_1.getAddress)(decodedCalls[0].to);
314
396
  }
315
397
  }
316
- catch (_a) {
398
+ catch (_b) {
317
399
  // Keep original 'to' if decoding fails
318
400
  }
319
401
  }
@@ -381,7 +463,7 @@ function transformToUniversalTxResponse(ctx_1, tx_1) {
381
463
  from = '0x0000000000000000000000000000000000000000';
382
464
  }
383
465
  }
384
- catch (_b) {
466
+ catch (_c) {
385
467
  // Keep original values if decoding fails
386
468
  }
387
469
  }
@@ -397,7 +479,7 @@ function transformToUniversalTxResponse(ctx_1, tx_1) {
397
479
  yParity: tx.yParity,
398
480
  };
399
481
  }
400
- catch (_c) {
482
+ catch (_d) {
401
483
  // Fallback signature if parsing fails
402
484
  signature = {
403
485
  r: '0x0000000000000000000000000000000000000000000000000000000000000000',
@@ -453,65 +535,159 @@ function transformToUniversalTxResponse(ctx_1, tx_1) {
453
535
  maxPriorityFeePerGas: tx.maxPriorityFeePerGas,
454
536
  accessList: Array.isArray(tx.accessList) ? [...tx.accessList] : [],
455
537
  // 6. Utilities
456
- wait: () => tslib_1.__awaiter(this, void 0, void 0, function* () {
457
- // Use trackTransaction with registered hook if available
458
- let baseReceipt;
459
- if (registeredProgressHook) {
460
- const trackedResponse = yield callbacks.trackTransaction(tx.hash, {
461
- waitForCompletion: true,
462
- progressHook: registeredProgressHook,
463
- });
464
- // Get receipt from the tracked response
465
- const receipt = yield tx.wait();
466
- baseReceipt = callbacks.transformToUniversalTxReceipt(receipt, trackedResponse);
467
- }
468
- else {
469
- const receipt = yield tx.wait();
470
- baseReceipt = callbacks.transformToUniversalTxReceipt(receipt, universalTxResponse);
471
- }
538
+ wait: (waitOptions) => tslib_1.__awaiter(this, void 0, void 0, function* () {
539
+ var _a, _b, _c, _d, _e, _f;
540
+ // For trackTransaction replays the tx is historical — cosmos already
541
+ // has the full graph indexed, so the 20s/30s initial-wait buffers
542
+ // designed for live sends are pure deadweight. Default them to 0 when
543
+ // replaying; callers can still override via WaitOptions.
544
+ const isReplay = universalTxResponse._eventsReconstructed === true;
545
+ const outboundTimeoutMs = (_a = waitOptions === null || waitOptions === void 0 ? void 0 : waitOptions.outboundTimeoutMs) !== null && _a !== void 0 ? _a : callbacks.outboundConstants.maxTimeoutMs;
546
+ const inboundTimeoutMs = (_b = waitOptions === null || waitOptions === void 0 ? void 0 : waitOptions.inboundTimeoutMs) !== null && _b !== void 0 ? _b : callbacks.inboundConstants.maxTimeoutMs;
547
+ const outboundInitialWaitMs = (_c = waitOptions === null || waitOptions === void 0 ? void 0 : waitOptions.outboundInitialWaitMs) !== null && _c !== void 0 ? _c : (isReplay ? 0 : callbacks.outboundConstants.initialWaitMs);
548
+ const outboundPollingIntervalMs = (_d = waitOptions === null || waitOptions === void 0 ? void 0 : waitOptions.outboundPollingIntervalMs) !== null && _d !== void 0 ? _d : callbacks.outboundConstants.pollingIntervalMs;
549
+ const inboundInitialWaitMs = isReplay
550
+ ? 0
551
+ : callbacks.inboundConstants.initialWaitMs;
552
+ // Track whether reconstruction already emitted the route's
553
+ // intermediate Push-success marker (299-99 / 199-99-99). When it has,
554
+ // skip re-emitting from the outbound branch below to avoid duplicates.
555
+ // This flag is only true when `this` response was created by
556
+ // trackTransaction() and the reconstructed stream already fired.
557
+ const alreadyReconstructed = universalTxResponse._eventsReconstructed === true;
558
+ let intermediateAlreadyEmitted = alreadyReconstructed;
559
+ // Build the base receipt directly from this response. Any per-call
560
+ // `tx.progressHook(cb)` already replayed buffered execute-phase events
561
+ // to `cb` via the progressHook setter — re-running trackTransaction
562
+ // here would duplicate the reconstructed stream on top of that replay,
563
+ // which is exactly what broke R1 parity. Wait-phase events (209-xx /
564
+ // 299-xx / 399-xx) still reach `registeredProgressHook` and
565
+ // `ctx.progressHook` via the `emit` closure in the outbound branch.
566
+ const receipt = yield tx.wait();
567
+ let baseReceipt = callbacks.transformToUniversalTxReceipt(receipt, universalTxResponse);
472
568
  // If outbound route, poll for external chain details
473
- const routeInfo = universalTxResponse.route
474
- ? (0, route_detector_1.getRouteInfo)(universalTxResponse.route)
475
- : undefined;
569
+ const route = universalTxResponse.route;
570
+ const routeInfo = route ? (0, route_detector_1.getRouteInfo)(route) : undefined;
476
571
  if (routeInfo === null || routeInfo === void 0 ? void 0 : routeInfo.isOutbound) {
477
- // Response-level only — NOT routed through ctx.progressHook because
478
- // the default ui-kit toast lacks icon/hide-timer handling for these
479
- // events. A follow-up ui-kit PR will add handling and can route
480
- // through both hooks.
481
- registeredProgressHook === null || registeredProgressHook === void 0 ? void 0 : registeredProgressHook(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_08_01]());
572
+ // Pick route-specific hook builders. R4 (CEA_TO_CEA) currently has
573
+ // no spec'd IDs — pickWaitHooks returns a no-op set that yields
574
+ // events with `id: ''`. `emit` drops those so the consumer never
575
+ // sees placeholder events, while receipt mutation still runs.
576
+ const hooks = (0, progress_route_hooks_1.pickWaitHooks)(route);
577
+ const targetChain = ((_e = universalTxResponse.chain) !== null && _e !== void 0 ? _e : 'external');
578
+ const emit = (event) => {
579
+ if (!event.id)
580
+ return;
581
+ fanOut(event, registeredProgressHook, ctx.progressHook);
582
+ };
583
+ // Intermediate INFO marker — Push Chain tx confirmed, external/inbound
584
+ // leg still pending. Per spec, R1 keeps 199-01 as terminal; R2/R3
585
+ // reclassify Push success as intermediate. Skip when trackTransaction
586
+ // (called above) already reconstructed the same marker via
587
+ // reconstructR2/R3, otherwise the consumer sees 299-99 / 199-99-99
588
+ // twice in their stream.
589
+ if (hooks.intermediatePushOk && !intermediateAlreadyEmitted) {
590
+ emit(hooks.intermediatePushOk(targetChain, tx.hash));
591
+ }
592
+ // Awaiting relay
593
+ emit(hooks.awaiting(targetChain));
482
594
  let lastEmittedStatus;
483
595
  const outboundTranslator = (event) => {
484
- if (!registeredProgressHook)
485
- return;
486
596
  if (event.status === 'polling' && lastEmittedStatus !== 'polling') {
487
597
  lastEmittedStatus = 'polling';
488
- registeredProgressHook(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_08_02](event.elapsed));
598
+ emit(hooks.polling(targetChain, event.elapsed));
489
599
  }
490
600
  };
491
601
  try {
492
602
  const outboundDetails = yield callbacks.waitForOutboundTx(tx.hash, {
493
- initialWaitMs: callbacks.outboundConstants.initialWaitMs,
494
- pollingIntervalMs: callbacks.outboundConstants.pollingIntervalMs,
495
- timeout: callbacks.outboundConstants.maxTimeoutMs,
603
+ initialWaitMs: outboundInitialWaitMs,
604
+ pollingIntervalMs: outboundPollingIntervalMs,
605
+ timeout: outboundTimeoutMs,
496
606
  progressHook: outboundTranslator,
497
607
  });
498
- registeredProgressHook === null || registeredProgressHook === void 0 ? void 0 : registeredProgressHook(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_03](outboundDetails));
608
+ emit(hooks.success(outboundDetails));
499
609
  // Merge external chain details into receipt
500
- baseReceipt = Object.assign(Object.assign({}, baseReceipt), { externalTxHash: outboundDetails.externalTxHash, externalChain: outboundDetails.destinationChain, externalExplorerUrl: outboundDetails.explorerUrl, externalRecipient: outboundDetails.recipient, externalAmount: outboundDetails.amount, externalAssetAddr: outboundDetails.assetAddr });
610
+ baseReceipt = Object.assign(Object.assign({}, baseReceipt), { externalTxHash: outboundDetails.externalTxHash, externalChain: outboundDetails.destinationChain, externalExplorerUrl: outboundDetails.explorerUrl, externalRecipient: outboundDetails.recipient, externalAmount: outboundDetails.amount, externalAssetAddr: outboundDetails.assetAddr, externalStatus: 'success' });
611
+ // R3 round-trip: poll Push Chain for the inbound tx that the
612
+ // source-chain CEA's sendUniversalTxToUEA call produces. Correlate
613
+ // via the outbound external tx hash → child UTX id → child pcTx.
614
+ // Only run when the CEA payload actually fires sendUniversalTxToUEA;
615
+ // payload-only R3 (no funds flowing back) has no child UTX and would
616
+ // otherwise time out after 300s.
617
+ if (route === route_detector_1.TransactionRoute.CEA_TO_PUSH &&
618
+ universalTxResponse._expectsInboundRoundTrip === true) {
619
+ emit(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_310_01](targetChain));
620
+ let inboundLastEmitted;
621
+ try {
622
+ const inbound = yield (0, inbound_tracker_1.waitForInboundPushTx)(ctx, outboundDetails.externalTxHash, targetChain, {
623
+ initialWaitMs: inboundInitialWaitMs,
624
+ pollingIntervalMs: callbacks.inboundConstants.pollingIntervalMs,
625
+ timeout: inboundTimeoutMs,
626
+ progressHook: (event) => {
627
+ if (event.status === 'polling' &&
628
+ inboundLastEmitted !== 'polling') {
629
+ inboundLastEmitted = 'polling';
630
+ emit(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_310_02](targetChain, event.elapsedMs));
631
+ }
632
+ },
633
+ });
634
+ if (inbound.status === 'confirmed') {
635
+ emit(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_399_01](targetChain, inbound.pushTxHash, baseReceipt));
636
+ baseReceipt = Object.assign(Object.assign({}, baseReceipt), { pushInboundTxHash: inbound.pushTxHash, pushInboundUtxId: inbound.childUtxId });
637
+ }
638
+ else {
639
+ // status === 'failed' (terminal failure status from chain)
640
+ const failMsg = (_f = inbound.errorMessage) !== null && _f !== void 0 ? _f : 'inbound execution failed on Push Chain';
641
+ emit(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_399_02](failMsg, 'inbound', targetChain));
642
+ baseReceipt = Object.assign(Object.assign({}, baseReceipt), { pushInboundUtxId: inbound.childUtxId, pushInboundTxHash: inbound.pushTxHash || undefined, externalStatus: 'failed', externalError: failMsg });
643
+ }
644
+ }
645
+ catch (inboundErr) {
646
+ const errMsg = inboundErr instanceof Error
647
+ ? inboundErr.message
648
+ : String(inboundErr);
649
+ const isTimeout = inboundErr instanceof inbound_tracker_1.InboundTimeoutError;
650
+ // Annotate receipt for inbound-leg outcome too.
651
+ baseReceipt = Object.assign(Object.assign({}, baseReceipt), { externalStatus: isTimeout ? 'timeout' : 'failed', externalError: errMsg });
652
+ emit(isTimeout
653
+ ? progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_399_03](targetChain, inboundTimeoutMs, 'inbound')
654
+ : progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_399_02](errMsg, 'inbound', targetChain));
655
+ callbacks.printLog(`[wait] R3 inbound tracking failed: ${errMsg}`);
656
+ }
657
+ }
501
658
  }
502
659
  catch (error) {
503
660
  const errMsg = error instanceof Error ? error.message : String(error);
504
- const isTimeout = errMsg.startsWith('Timeout waiting for outbound transaction');
505
- registeredProgressHook === null || registeredProgressHook === void 0 ? void 0 : registeredProgressHook(isTimeout
506
- ? progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_04](callbacks.outboundConstants.maxTimeoutMs)
507
- : progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_05](errMsg));
508
- // Outbound polling timed out - return partial receipt (don't throw)
509
- // Push Chain tx succeeded, external tracking can be retried later
510
- callbacks.printLog(`[wait] External chain tracking failed: ${errMsg}`);
661
+ const isTimeout = error instanceof outbound_sync_1.OutboundTimeoutError;
662
+ emit(isTimeout
663
+ ? hooks.timeout(targetChain, outboundTimeoutMs)
664
+ : hooks.failed(targetChain, errMsg));
665
+ // Annotate the receipt so callers can distinguish external-leg
666
+ // outcomes without sniffing errMsg. Push Chain leg still succeeded
667
+ // (status stays 1); inspect `externalStatus` / `externalError` for
668
+ // the external/inbound leg outcome. On a reverted outbound the
669
+ // observed tx hash is carried by OutboundFailedError — expose it
670
+ // as externalTxHash so consumers can link to the explorer.
671
+ const failedExternalTxHash = error instanceof outbound_sync_1.OutboundFailedError ? error.externalTxHash : undefined;
672
+ baseReceipt = Object.assign(Object.assign(Object.assign({}, baseReceipt), { externalStatus: isTimeout ? 'timeout' : 'failed', externalError: errMsg }), (failedExternalTxHash
673
+ ? { externalTxHash: failedExternalTxHash }
674
+ : {}));
675
+ // Outbound polling timed out or failed — return the annotated
676
+ // receipt without throwing. Push Chain tx succeeded, external
677
+ // tracking can be retried later.
678
+ callbacks.printLog(`[wait] External chain tracking ${isTimeout ? 'timed out' : 'failed'}: ${errMsg}`);
511
679
  }
512
680
  }
513
681
  return baseReceipt;
514
682
  }),
683
+ // Internal: register a wait-phase progressHook without replaying
684
+ // buffered events. Used by trackTransaction() after it has already
685
+ // emitted the reconstructed stream to the caller's per-call hook,
686
+ // so that tracked.wait() can deliver wait-phase events (209-xx /
687
+ // 299-xx / 399-xx) to the same callback without duplicating replay.
688
+ _setProgressHookNoReplay: (callback) => {
689
+ registeredProgressHook = callback;
690
+ },
515
691
  progressHook: (callback) => {
516
692
  registeredProgressHook = callback;
517
693
  // Immediately replay buffered events from execution