@pushchain/core 6.0.20 → 6.0.21

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 (111) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/package.json +1 -1
  3. package/src/lib/cache/cache.js +2 -0
  4. package/src/lib/constants/abi/index.d.ts +1 -0
  5. package/src/lib/constants/abi/index.js +8 -1
  6. package/src/lib/constants/abi/pc20.evm.d.ts +217 -0
  7. package/src/lib/constants/abi/pc20.evm.js +150 -0
  8. package/src/lib/constants/abi/prc20.evm.d.ts +172 -0
  9. package/src/lib/constants/abi/prc20.evm.js +97 -0
  10. package/src/lib/constants/abi/universalGatewayV0.json +1 -1
  11. package/src/lib/constants/chain.js +38 -8
  12. package/src/lib/constants/selectors.js +2 -2
  13. package/src/lib/constants/tokens.js +4 -3
  14. package/src/lib/formatters.js +16 -6
  15. package/src/lib/generated/uexecutor/v1/query.js +2 -4
  16. package/src/lib/generated/uexecutor/v1/types.d.ts +14 -0
  17. package/src/lib/generated/uexecutor/v1/types.js +90 -47
  18. package/src/lib/generated/uexecutor/v2/query.js +2 -4
  19. package/src/lib/generated/uexecutor/v2/types.d.ts +20 -0
  20. package/src/lib/generated/uexecutor/v2/types.js +49 -33
  21. package/src/lib/generated/v1/tx.js +30 -37
  22. package/src/lib/index.d.ts +3 -0
  23. package/src/lib/index.js +21 -3
  24. package/src/lib/internal/buffer.js +1 -2
  25. package/src/lib/orchestrator/cea-utils.js +73 -83
  26. package/src/lib/orchestrator/internals/account-manager.js +130 -139
  27. package/src/lib/orchestrator/internals/cascade.d.ts +0 -6
  28. package/src/lib/orchestrator/internals/cascade.js +1184 -1059
  29. package/src/lib/orchestrator/internals/confirmation.js +138 -148
  30. package/src/lib/orchestrator/internals/error-decoder.js +1 -1
  31. package/src/lib/orchestrator/internals/errors.js +12 -3
  32. package/src/lib/orchestrator/internals/execute-funds-only.d.ts +2 -2
  33. package/src/lib/orchestrator/internals/execute-funds-only.js +297 -259
  34. package/src/lib/orchestrator/internals/execute-funds-payload.d.ts +2 -2
  35. package/src/lib/orchestrator/internals/execute-funds-payload.js +220 -220
  36. package/src/lib/orchestrator/internals/execute-standard.d.ts +0 -8
  37. package/src/lib/orchestrator/internals/execute-standard.js +339 -342
  38. package/src/lib/orchestrator/internals/gas-calculator.js +497 -520
  39. package/src/lib/orchestrator/internals/gas-usd-sizer.js +101 -110
  40. package/src/lib/orchestrator/internals/gateway-client.js +102 -103
  41. package/src/lib/orchestrator/internals/helpers.js +30 -27
  42. package/src/lib/orchestrator/internals/inbound-tracker.js +234 -238
  43. package/src/lib/orchestrator/internals/outbound-sync.js +463 -452
  44. package/src/lib/orchestrator/internals/outbound-tracker.js +27 -34
  45. package/src/lib/orchestrator/internals/payload-builder.d.ts +2 -2
  46. package/src/lib/orchestrator/internals/payload-builder.js +119 -100
  47. package/src/lib/orchestrator/internals/pc-usd-oracle.js +121 -129
  48. package/src/lib/orchestrator/internals/pc20/address-codec.d.ts +31 -0
  49. package/src/lib/orchestrator/internals/pc20/address-codec.js +124 -0
  50. package/src/lib/orchestrator/internals/pc20/chain-namespace.d.ts +68 -0
  51. package/src/lib/orchestrator/internals/pc20/chain-namespace.js +136 -0
  52. package/src/lib/orchestrator/internals/pc20/errors.d.ts +160 -0
  53. package/src/lib/orchestrator/internals/pc20/errors.js +236 -0
  54. package/src/lib/orchestrator/internals/pc20/export.d.ts +172 -0
  55. package/src/lib/orchestrator/internals/pc20/export.js +412 -0
  56. package/src/lib/orchestrator/internals/pc20/gate.d.ts +67 -0
  57. package/src/lib/orchestrator/internals/pc20/gate.js +184 -0
  58. package/src/lib/orchestrator/internals/pc20/resolver.d.ts +187 -0
  59. package/src/lib/orchestrator/internals/pc20/resolver.js +658 -0
  60. package/src/lib/orchestrator/internals/pc20/svm.d.ts +189 -0
  61. package/src/lib/orchestrator/internals/pc20/svm.js +286 -0
  62. package/src/lib/orchestrator/internals/pc20/tracking.d.ts +46 -0
  63. package/src/lib/orchestrator/internals/pc20/tracking.js +109 -0
  64. package/src/lib/orchestrator/internals/preflight.js +1 -1
  65. package/src/lib/orchestrator/internals/progress-route-hooks.js +2 -2
  66. package/src/lib/orchestrator/internals/push-chain-tx.js +319 -329
  67. package/src/lib/orchestrator/internals/quote.js +90 -94
  68. package/src/lib/orchestrator/internals/rescue.js +76 -79
  69. package/src/lib/orchestrator/internals/response-builder.d.ts +28 -1
  70. package/src/lib/orchestrator/internals/response-builder.js +817 -701
  71. package/src/lib/orchestrator/internals/route-handlers.d.ts +7 -15
  72. package/src/lib/orchestrator/internals/route-handlers.js +1512 -1178
  73. package/src/lib/orchestrator/internals/signing.js +78 -83
  74. package/src/lib/orchestrator/internals/svm-bridge.d.ts +2 -2
  75. package/src/lib/orchestrator/internals/svm-bridge.js +161 -93
  76. package/src/lib/orchestrator/internals/svm-helpers.d.ts +15 -1
  77. package/src/lib/orchestrator/internals/svm-helpers.js +68 -24
  78. package/src/lib/orchestrator/internals/svm-rent.d.ts +19 -1
  79. package/src/lib/orchestrator/internals/svm-rent.js +163 -141
  80. package/src/lib/orchestrator/internals/tx-transformer.js +26 -23
  81. package/src/lib/orchestrator/internals/uea-manager.js +89 -86
  82. package/src/lib/orchestrator/max-pc-for-gas.js +5 -6
  83. package/src/lib/orchestrator/orchestrator.js +217 -200
  84. package/src/lib/orchestrator/orchestrator.types.d.ts +121 -1
  85. package/src/lib/orchestrator/orchestrator.types.js +19 -0
  86. package/src/lib/orchestrator/payload-builders.d.ts +2 -2
  87. package/src/lib/orchestrator/payload-builders.js +26 -24
  88. package/src/lib/orchestrator/route-detector.js +26 -9
  89. package/src/lib/orchestrator/svm-idl/ix-encoder.js +1 -2
  90. package/src/lib/orchestrator/svm-idl/normalize-address.js +2 -2
  91. package/src/lib/orchestrator/svm-idl/resolve.js +1 -2
  92. package/src/lib/price-fetch/price-fetch.js +67 -68
  93. package/src/lib/progress-hook/progress-hook.js +124 -72
  94. package/src/lib/push-chain/push-chain.js +171 -153
  95. package/src/lib/push-client/push-client.js +223 -237
  96. package/src/lib/universal/account/account.js +349 -365
  97. package/src/lib/universal/signer/signer.js +306 -294
  98. package/src/lib/universal-tx-detector/auto-detect.js +55 -60
  99. package/src/lib/universal-tx-detector/cascade.js +160 -172
  100. package/src/lib/universal-tx-detector/child-inbounds.js +67 -75
  101. package/src/lib/universal-tx-detector/classify.js +2 -3
  102. package/src/lib/universal-tx-detector/detector-svm.js +139 -147
  103. package/src/lib/universal-tx-detector/detector.js +145 -157
  104. package/src/lib/universal-tx-detector/normalize.js +7 -7
  105. package/src/lib/utils/external-tx-hash.js +1 -2
  106. package/src/lib/utils.d.ts +44 -0
  107. package/src/lib/utils.js +762 -683
  108. package/src/lib/vm-client/evm-client.js +186 -202
  109. package/src/lib/vm-client/svm-client.d.ts +1 -1
  110. package/src/lib/vm-client/svm-client.js +220 -224
  111. package/src/lib/vm-client/vm-client.types.d.ts +14 -2
@@ -15,7 +15,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.InboundTimeoutError = void 0;
16
16
  exports.findChildUtxIdFromExternalTx = findChildUtxIdFromExternalTx;
17
17
  exports.waitForInboundPushTx = waitForInboundPushTx;
18
- const tslib_1 = require("tslib");
19
18
  const types_1 = require("../../generated/uexecutor/v2/types");
20
19
  const context_1 = require("./context");
21
20
  const detector_1 = require("../../universal-tx-detector/detector");
@@ -51,58 +50,56 @@ const ensureHex = (id) => id.startsWith('0x') ? id : `0x${id}`;
51
50
  * has seen it yet, or `{ utxId: null, error }` when both paths failed with
52
51
  * recoverable RPC errors.
53
52
  */
54
- function findChildUtxIdFromExternalTx(ctx, externalTxHash, sourceChain) {
55
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
56
- var _a, _b, _c;
57
- // Primary: detector-based deterministic derivation.
58
- if (sourceChain) {
59
- try {
60
- const detection = yield (0, detector_1.detectUniversalTx)(externalTxHash, sourceChain, {
61
- pushClient: ctx.pushClient,
62
- rpcUrls: ctx.rpcUrls,
63
- skipPushChainLookup: true,
64
- });
65
- const resolutions = yield (0, child_inbounds_1.resolveChildInboundsFromDetection)(ctx.pushClient, detection);
66
- // Pick the R3 return-trip child: prefer UniversalTx over RevertUniversalTx.
67
- const match = (_a = resolutions.find((r) => r.sourceEventName === 'UniversalTx')) !== null && _a !== void 0 ? _a : resolutions[0];
68
- if (match === null || match === void 0 ? void 0 : match.universalTxId) {
69
- (0, context_1.printLog)(ctx, `[findChildUtxIdFromExternalTx] detector derived utxId ${match.universalTxId} for ${externalTxHash} on ${sourceChain}`);
70
- return { utxId: ensureHex(match.universalTxId), derivedFrom: 'detector' };
71
- }
72
- }
73
- catch (err) {
74
- const wrapped = err instanceof Error ? err : new Error(String(err));
75
- (0, context_1.printLog)(ctx, `[findChildUtxIdFromExternalTx] detector path failed: ${wrapped.message} — falling back to cosmos search`);
76
- // Fall through to cosmos search.
53
+ async function findChildUtxIdFromExternalTx(ctx, externalTxHash, sourceChain) {
54
+ // Primary: detector-based deterministic derivation.
55
+ if (sourceChain) {
56
+ try {
57
+ const detection = await (0, detector_1.detectUniversalTx)(externalTxHash, sourceChain, {
58
+ pushClient: ctx.pushClient,
59
+ rpcUrls: ctx.rpcUrls,
60
+ skipPushChainLookup: true,
61
+ });
62
+ const resolutions = await (0, child_inbounds_1.resolveChildInboundsFromDetection)(ctx.pushClient, detection);
63
+ // Pick the R3 return-trip child: prefer UniversalTx over RevertUniversalTx.
64
+ const match = resolutions.find((r) => r.sourceEventName === 'UniversalTx') ??
65
+ resolutions[0];
66
+ if (match?.universalTxId) {
67
+ (0, context_1.printLog)(ctx, `[findChildUtxIdFromExternalTx] detector derived utxId ${match.universalTxId} for ${externalTxHash} on ${sourceChain}`);
68
+ return { utxId: ensureHex(match.universalTxId), derivedFrom: 'detector' };
77
69
  }
78
70
  }
79
- // Fallback: cosmos text search (original path).
80
- try {
81
- const query = `universal_tx_created.inbound_tx_hash='${externalTxHash}'`;
82
- const results = yield ctx.pushClient.searchCosmosByQuery(query);
83
- for (const tx of results) {
84
- for (const event of (_b = tx.events) !== null && _b !== void 0 ? _b : []) {
85
- if (event.type === 'universal_tx_created' ||
86
- event.type === 'outbound_created') {
87
- for (const attr of (_c = event.attributes) !== null && _c !== void 0 ? _c : []) {
88
- if (attr.key === 'utx_id' && attr.value) {
89
- return {
90
- utxId: ensureHex(attr.value),
91
- derivedFrom: 'cosmos-fallback',
92
- };
93
- }
71
+ catch (err) {
72
+ const wrapped = err instanceof Error ? err : new Error(String(err));
73
+ (0, context_1.printLog)(ctx, `[findChildUtxIdFromExternalTx] detector path failed: ${wrapped.message} — falling back to cosmos search`);
74
+ // Fall through to cosmos search.
75
+ }
76
+ }
77
+ // Fallback: cosmos text search (original path).
78
+ try {
79
+ const query = `universal_tx_created.inbound_tx_hash='${externalTxHash}'`;
80
+ const results = await ctx.pushClient.searchCosmosByQuery(query);
81
+ for (const tx of results) {
82
+ for (const event of tx.events ?? []) {
83
+ if (event.type === 'universal_tx_created' ||
84
+ event.type === 'outbound_created') {
85
+ for (const attr of event.attributes ?? []) {
86
+ if (attr.key === 'utx_id' && attr.value) {
87
+ return {
88
+ utxId: ensureHex(attr.value),
89
+ derivedFrom: 'cosmos-fallback',
90
+ };
94
91
  }
95
92
  }
96
93
  }
97
94
  }
98
- return { utxId: null };
99
- }
100
- catch (err) {
101
- const wrapped = err instanceof Error ? err : new Error(String(err));
102
- (0, context_1.printLog)(ctx, `[findChildUtxIdFromExternalTx] cosmos fallback also failed: ${wrapped.message}`);
103
- return { utxId: null, error: wrapped };
104
95
  }
105
- });
96
+ return { utxId: null };
97
+ }
98
+ catch (err) {
99
+ const wrapped = err instanceof Error ? err : new Error(String(err));
100
+ (0, context_1.printLog)(ctx, `[findChildUtxIdFromExternalTx] cosmos fallback also failed: ${wrapped.message}`);
101
+ return { utxId: null, error: wrapped };
102
+ }
106
103
  }
107
104
  /**
108
105
  * Polls Push Chain for the inbound tx that closes an R3 round-trip.
@@ -110,91 +107,155 @@ function findChildUtxIdFromExternalTx(ctx, externalTxHash, sourceChain) {
110
107
  * rejects with a Timeout error if neither child UTX nor terminal state is
111
108
  * observed within the configured timeout.
112
109
  */
113
- function waitForInboundPushTx(ctx_1, outboundExternalTxHash_1, sourceChain_1) {
114
- return tslib_1.__awaiter(this, arguments, void 0, function* (ctx, outboundExternalTxHash, sourceChain, opts = {}) {
115
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
116
- const initialWaitMs = (_a = opts.initialWaitMs) !== null && _a !== void 0 ? _a : 30000;
117
- const pollingIntervalMs = (_b = opts.pollingIntervalMs) !== null && _b !== void 0 ? _b : 5000;
118
- const timeout = (_c = opts.timeout) !== null && _c !== void 0 ? _c : 300000;
119
- const progressHook = opts.progressHook;
120
- const startedAt = Date.now();
121
- const elapsed = () => Date.now() - startedAt;
122
- (0, context_1.printLog)(ctx, `[waitForInboundPushTx] Starting | externalTxHash: ${outboundExternalTxHash} | sourceChain: ${sourceChain} | initialWait: ${initialWaitMs}ms | pollInterval: ${pollingIntervalMs}ms | timeout: ${timeout}ms`);
123
- progressHook === null || progressHook === void 0 ? void 0 : progressHook({ status: 'waiting', elapsedMs: 0 });
124
- if (initialWaitMs > 0) {
125
- (0, context_1.printLog)(ctx, `[waitForInboundPushTx] Initial wait of ${initialWaitMs}ms...`);
126
- yield new Promise((r) => setTimeout(r, Math.min(initialWaitMs, timeout)));
127
- (0, context_1.printLog)(ctx, `[waitForInboundPushTx] Initial wait done. Starting polling. Elapsed: ${elapsed()}ms`);
110
+ async function waitForInboundPushTx(ctx, outboundExternalTxHash, sourceChain, opts = {}) {
111
+ const initialWaitMs = opts.initialWaitMs ?? 30000;
112
+ const pollingIntervalMs = opts.pollingIntervalMs ?? 5000;
113
+ const timeout = opts.timeout ?? 300000;
114
+ const progressHook = opts.progressHook;
115
+ const startedAt = Date.now();
116
+ const elapsed = () => Date.now() - startedAt;
117
+ (0, context_1.printLog)(ctx, `[waitForInboundPushTx] Starting | externalTxHash: ${outboundExternalTxHash} | sourceChain: ${sourceChain} | initialWait: ${initialWaitMs}ms | pollInterval: ${pollingIntervalMs}ms | timeout: ${timeout}ms`);
118
+ progressHook?.({ status: 'waiting', elapsedMs: 0 });
119
+ if (initialWaitMs > 0) {
120
+ (0, context_1.printLog)(ctx, `[waitForInboundPushTx] Initial wait of ${initialWaitMs}ms...`);
121
+ await new Promise((r) => setTimeout(r, Math.min(initialWaitMs, timeout)));
122
+ (0, context_1.printLog)(ctx, `[waitForInboundPushTx] Initial wait done. Starting polling. Elapsed: ${elapsed()}ms`);
123
+ }
124
+ let childUtxId = null;
125
+ let strippedChildUtxId;
126
+ let pushTxHash;
127
+ let lastEmittedStatus;
128
+ let consecutiveRpcErrors = 0;
129
+ let pendingTerminalSuccessRetries = 0;
130
+ let pollCount = 0;
131
+ // Heuristic threshold — log a distinct warning when this many back-to-back
132
+ // RPC failures happen so callers can see the difference between
133
+ // "indexer hasn't seen it yet" and "RPC is down."
134
+ const RPC_FAILURE_WARN_THRESHOLD = 5;
135
+ // When chain reports terminal success but pcTx hasn't populated yet, give
136
+ // the indexer one extra poll cycle to catch up before returning empty hash.
137
+ const MAX_PENDING_TERMINAL_RETRIES = 2;
138
+ while (elapsed() < timeout) {
139
+ pollCount++;
140
+ const pollStart = Date.now();
141
+ (0, context_1.printLog)(ctx, `[waitForInboundPushTx] Poll #${pollCount} | Elapsed: ${elapsed()}ms / ${timeout}ms | phase: ${childUtxId ? 'utx-status' : 'child-search'}`);
142
+ // Phase 1 — find child UTX id from the outbound external tx hash
143
+ if (!childUtxId) {
144
+ const search = await findChildUtxIdFromExternalTx(ctx, outboundExternalTxHash, sourceChain);
145
+ (0, context_1.printLog)(ctx, `[waitForInboundPushTx] Poll #${pollCount} child-search result: utxId=${search.utxId ?? 'null'}${search.error ? ` error=${search.error.message}` : ''}`);
146
+ if (search.error) {
147
+ consecutiveRpcErrors++;
148
+ if (consecutiveRpcErrors === RPC_FAILURE_WARN_THRESHOLD) {
149
+ (0, context_1.printLog)(ctx, `[waitForInboundPushTx] ${RPC_FAILURE_WARN_THRESHOLD} consecutive Cosmos search errors — RPC may be unhealthy. Continuing until timeout.`);
150
+ }
151
+ }
152
+ else {
153
+ consecutiveRpcErrors = 0;
154
+ if (search.utxId) {
155
+ childUtxId = search.utxId;
156
+ strippedChildUtxId = stripHex(childUtxId);
157
+ progressHook?.({
158
+ status: 'found',
159
+ elapsedMs: elapsed(),
160
+ childUtxId,
161
+ });
162
+ lastEmittedStatus = 'found';
163
+ }
164
+ else if (lastEmittedStatus !== 'polling') {
165
+ progressHook?.({ status: 'polling', elapsedMs: elapsed() });
166
+ lastEmittedStatus = 'polling';
167
+ }
168
+ }
128
169
  }
129
- let childUtxId = null;
130
- let strippedChildUtxId;
131
- let pushTxHash;
132
- let lastEmittedStatus;
133
- let consecutiveRpcErrors = 0;
134
- let pendingTerminalSuccessRetries = 0;
135
- let pollCount = 0;
136
- // Heuristic threshold — log a distinct warning when this many back-to-back
137
- // RPC failures happen so callers can see the difference between
138
- // "indexer hasn't seen it yet" and "RPC is down."
139
- const RPC_FAILURE_WARN_THRESHOLD = 5;
140
- // When chain reports terminal success but pcTx hasn't populated yet, give
141
- // the indexer one extra poll cycle to catch up before returning empty hash.
142
- const MAX_PENDING_TERMINAL_RETRIES = 2;
143
- while (elapsed() < timeout) {
144
- pollCount++;
145
- const pollStart = Date.now();
146
- (0, context_1.printLog)(ctx, `[waitForInboundPushTx] Poll #${pollCount} | Elapsed: ${elapsed()}ms / ${timeout}ms | phase: ${childUtxId ? 'utx-status' : 'child-search'}`);
147
- // Phase 1 — find child UTX id from the outbound external tx hash
148
- if (!childUtxId) {
149
- const search = yield findChildUtxIdFromExternalTx(ctx, outboundExternalTxHash, sourceChain);
150
- (0, context_1.printLog)(ctx, `[waitForInboundPushTx] Poll #${pollCount} child-search result: utxId=${(_d = search.utxId) !== null && _d !== void 0 ? _d : 'null'}${search.error ? ` error=${search.error.message}` : ''}`);
151
- if (search.error) {
152
- consecutiveRpcErrors++;
153
- if (consecutiveRpcErrors === RPC_FAILURE_WARN_THRESHOLD) {
154
- (0, context_1.printLog)(ctx, `[waitForInboundPushTx] ${RPC_FAILURE_WARN_THRESHOLD} consecutive Cosmos search errors — RPC may be unhealthy. Continuing until timeout.`);
155
- }
170
+ // Phase 2 — once we have the child UTX, query its status until terminal
171
+ if (childUtxId && strippedChildUtxId) {
172
+ try {
173
+ const response = await ctx.pushClient.getUniversalTxByIdV2(strippedChildUtxId);
174
+ consecutiveRpcErrors = 0;
175
+ const utx = response?.universalTx;
176
+ const status = utx?.universalStatus ?? -1;
177
+ if (utx?.pcTx?.[0]?.txHash) {
178
+ pushTxHash = utx.pcTx[0].txHash;
156
179
  }
157
- else {
158
- consecutiveRpcErrors = 0;
159
- if (search.utxId) {
160
- childUtxId = search.utxId;
161
- strippedChildUtxId = stripHex(childUtxId);
162
- progressHook === null || progressHook === void 0 ? void 0 : progressHook({
163
- status: 'found',
180
+ (0, context_1.printLog)(ctx, `[waitForInboundPushTx] Poll #${pollCount} utx-status | status: ${status} | pcTx[0].txHash: '${pushTxHash ?? ''}' | pcTx len: ${utx?.pcTx?.length ?? 0}`);
181
+ const failedPcTx = utx?.pcTx?.find((pcTx) => {
182
+ const pcTxStatus = String(pcTx.status || '').toUpperCase();
183
+ return pcTxStatus === 'FAILED' || Boolean(pcTx.errorMsg);
184
+ });
185
+ if (TERMINAL_SUCCESS_STATUSES.has(status)) {
186
+ // Wait one more poll cycle if pcTx hasn't populated yet — the
187
+ // indexer occasionally flips status before the pcTx hash is queryable.
188
+ if (!pushTxHash && pendingTerminalSuccessRetries < MAX_PENDING_TERMINAL_RETRIES) {
189
+ pendingTerminalSuccessRetries++;
190
+ }
191
+ else {
192
+ progressHook?.({
193
+ status: 'confirmed',
164
194
  elapsedMs: elapsed(),
165
195
  childUtxId,
196
+ pushTxHash,
166
197
  });
167
- lastEmittedStatus = 'found';
168
- }
169
- else if (lastEmittedStatus !== 'polling') {
170
- progressHook === null || progressHook === void 0 ? void 0 : progressHook({ status: 'polling', elapsedMs: elapsed() });
171
- lastEmittedStatus = 'polling';
198
+ return {
199
+ childUtxId,
200
+ pushTxHash: pushTxHash ?? '',
201
+ sourceChain,
202
+ outboundExternalTxHash,
203
+ status: 'confirmed',
204
+ };
172
205
  }
173
206
  }
174
- }
175
- // Phase 2 — once we have the child UTX, query its status until terminal
176
- if (childUtxId && strippedChildUtxId) {
177
- try {
178
- const response = yield ctx.pushClient.getUniversalTxByIdV2(strippedChildUtxId);
179
- consecutiveRpcErrors = 0;
180
- const utx = response === null || response === void 0 ? void 0 : response.universalTx;
181
- const status = (_e = utx === null || utx === void 0 ? void 0 : utx.universalStatus) !== null && _e !== void 0 ? _e : -1;
182
- if ((_g = (_f = utx === null || utx === void 0 ? void 0 : utx.pcTx) === null || _f === void 0 ? void 0 : _f[0]) === null || _g === void 0 ? void 0 : _g.txHash) {
183
- pushTxHash = utx.pcTx[0].txHash;
184
- }
185
- (0, context_1.printLog)(ctx, `[waitForInboundPushTx] Poll #${pollCount} utx-status | status: ${status} | pcTx[0].txHash: '${pushTxHash !== null && pushTxHash !== void 0 ? pushTxHash : ''}' | pcTx len: ${(_j = (_h = utx === null || utx === void 0 ? void 0 : utx.pcTx) === null || _h === void 0 ? void 0 : _h.length) !== null && _j !== void 0 ? _j : 0}`);
186
- const failedPcTx = (_k = utx === null || utx === void 0 ? void 0 : utx.pcTx) === null || _k === void 0 ? void 0 : _k.find((pcTx) => {
187
- const pcTxStatus = String(pcTx.status || '').toUpperCase();
188
- return pcTxStatus === 'FAILED' || Boolean(pcTx.errorMsg);
207
+ else if (TERMINAL_FAILURE_STATUSES.has(status)) {
208
+ const errMsg = failedPcTx?.errorMsg
209
+ ? (0, formatters_1.normalizePcInsufficientFundsError)(failedPcTx.errorMsg)
210
+ : `inbound terminated with status ${status}`;
211
+ progressHook?.({
212
+ status: 'failed',
213
+ elapsedMs: elapsed(),
214
+ childUtxId,
215
+ pushTxHash,
189
216
  });
190
- if (TERMINAL_SUCCESS_STATUSES.has(status)) {
191
- // Wait one more poll cycle if pcTx hasn't populated yet — the
192
- // indexer occasionally flips status before the pcTx hash is queryable.
193
- if (!pushTxHash && pendingTerminalSuccessRetries < MAX_PENDING_TERMINAL_RETRIES) {
194
- pendingTerminalSuccessRetries++;
195
- }
196
- else {
197
- progressHook === null || progressHook === void 0 ? void 0 : progressHook({
217
+ return {
218
+ childUtxId,
219
+ pushTxHash: pushTxHash ?? '',
220
+ sourceChain,
221
+ outboundExternalTxHash,
222
+ status: 'failed',
223
+ errorMessage: errMsg,
224
+ };
225
+ }
226
+ else if (failedPcTx) {
227
+ const errMsg = failedPcTx.errorMsg
228
+ ? (0, formatters_1.normalizePcInsufficientFundsError)(failedPcTx.errorMsg)
229
+ : `inbound Push tx failed while universalStatus=${status}`;
230
+ progressHook?.({
231
+ status: 'failed',
232
+ elapsedMs: elapsed(),
233
+ childUtxId,
234
+ pushTxHash,
235
+ });
236
+ return {
237
+ childUtxId,
238
+ pushTxHash: pushTxHash ?? '',
239
+ sourceChain,
240
+ outboundExternalTxHash,
241
+ status: 'failed',
242
+ errorMessage: errMsg,
243
+ };
244
+ }
245
+ else if (pushTxHash) {
246
+ // Tiebreaker: cosmos has the Push Chain tx hash populated but the
247
+ // indexer hasn't transitioned universalStatus to a terminal yet.
248
+ // Query Push RPC directly for the receipt — if it's confirmed on
249
+ // chain, treat as terminal-success; if it reverted, treat as
250
+ // terminal-failure. Cosmos-indexer lag stops blocking us here.
251
+ try {
252
+ // Archive-fallback variant: a Push-leg receipt outside the prune
253
+ // window still resolves via the archive node. On chains without an
254
+ // archive endpoint this behaves as a plain prune-only lookup.
255
+ const receipt = await ctx.pushClient.getTransactionReceiptWithArchiveFallback(pushTxHash);
256
+ if (receipt?.status === 'success') {
257
+ (0, context_1.printLog)(ctx, `[waitForInboundPushTx] Push RPC tiebreaker: ${pushTxHash} confirmed on chain (cosmos still status=${status}); treating as terminal-success`);
258
+ progressHook?.({
198
259
  status: 'confirmed',
199
260
  elapsedMs: elapsed(),
200
261
  childUtxId,
@@ -202,132 +263,65 @@ function waitForInboundPushTx(ctx_1, outboundExternalTxHash_1, sourceChain_1) {
202
263
  });
203
264
  return {
204
265
  childUtxId,
205
- pushTxHash: pushTxHash !== null && pushTxHash !== void 0 ? pushTxHash : '',
266
+ pushTxHash,
206
267
  sourceChain,
207
268
  outboundExternalTxHash,
208
269
  status: 'confirmed',
209
270
  };
210
271
  }
211
- }
212
- else if (TERMINAL_FAILURE_STATUSES.has(status)) {
213
- const errMsg = (failedPcTx === null || failedPcTx === void 0 ? void 0 : failedPcTx.errorMsg)
214
- ? (0, formatters_1.normalizePcInsufficientFundsError)(failedPcTx.errorMsg)
215
- : `inbound terminated with status ${status}`;
216
- progressHook === null || progressHook === void 0 ? void 0 : progressHook({
217
- status: 'failed',
218
- elapsedMs: elapsed(),
219
- childUtxId,
220
- pushTxHash,
221
- });
222
- return {
223
- childUtxId,
224
- pushTxHash: pushTxHash !== null && pushTxHash !== void 0 ? pushTxHash : '',
225
- sourceChain,
226
- outboundExternalTxHash,
227
- status: 'failed',
228
- errorMessage: errMsg,
229
- };
230
- }
231
- else if (failedPcTx) {
232
- const errMsg = failedPcTx.errorMsg
233
- ? (0, formatters_1.normalizePcInsufficientFundsError)(failedPcTx.errorMsg)
234
- : `inbound Push tx failed while universalStatus=${status}`;
235
- progressHook === null || progressHook === void 0 ? void 0 : progressHook({
236
- status: 'failed',
237
- elapsedMs: elapsed(),
238
- childUtxId,
239
- pushTxHash,
240
- });
241
- return {
242
- childUtxId,
243
- pushTxHash: pushTxHash !== null && pushTxHash !== void 0 ? pushTxHash : '',
244
- sourceChain,
245
- outboundExternalTxHash,
246
- status: 'failed',
247
- errorMessage: errMsg,
248
- };
249
- }
250
- else if (pushTxHash) {
251
- // Tiebreaker: cosmos has the Push Chain tx hash populated but the
252
- // indexer hasn't transitioned universalStatus to a terminal yet.
253
- // Query Push RPC directly for the receipt — if it's confirmed on
254
- // chain, treat as terminal-success; if it reverted, treat as
255
- // terminal-failure. Cosmos-indexer lag stops blocking us here.
256
- try {
257
- // Archive-fallback variant: a Push-leg receipt outside the prune
258
- // window still resolves via the archive node. On chains without an
259
- // archive endpoint this behaves as a plain prune-only lookup.
260
- const receipt = yield ctx.pushClient.getTransactionReceiptWithArchiveFallback(pushTxHash);
261
- if ((receipt === null || receipt === void 0 ? void 0 : receipt.status) === 'success') {
262
- (0, context_1.printLog)(ctx, `[waitForInboundPushTx] Push RPC tiebreaker: ${pushTxHash} confirmed on chain (cosmos still status=${status}); treating as terminal-success`);
263
- progressHook === null || progressHook === void 0 ? void 0 : progressHook({
264
- status: 'confirmed',
265
- elapsedMs: elapsed(),
266
- childUtxId,
267
- pushTxHash,
268
- });
269
- return {
270
- childUtxId,
271
- pushTxHash,
272
- sourceChain,
273
- outboundExternalTxHash,
274
- status: 'confirmed',
275
- };
276
- }
277
- if ((receipt === null || receipt === void 0 ? void 0 : receipt.status) === 'reverted') {
278
- (0, context_1.printLog)(ctx, `[waitForInboundPushTx] Push RPC tiebreaker: ${pushTxHash} REVERTED on chain (cosmos still status=${status}); treating as terminal-failure`);
279
- progressHook === null || progressHook === void 0 ? void 0 : progressHook({
280
- status: 'failed',
281
- elapsedMs: elapsed(),
282
- childUtxId,
283
- pushTxHash,
284
- });
285
- return {
286
- childUtxId,
287
- pushTxHash,
288
- sourceChain,
289
- outboundExternalTxHash,
290
- status: 'failed',
291
- errorMessage: 'Push Chain inbound tx reverted on chain',
292
- };
293
- }
294
- // receipt missing / pending — keep polling.
295
- }
296
- catch (rpcErr) {
297
- // TransactionReceiptNotFoundError means not yet mined — continue
298
- // polling. Other errors get logged but don't break the loop.
299
- const msg = rpcErr instanceof Error ? rpcErr.message : String(rpcErr);
300
- if (!/not found|TransactionReceiptNotFound|could not be found/i.test(msg)) {
301
- (0, context_1.printLog)(ctx, `[waitForInboundPushTx] Push RPC tiebreaker failed for ${pushTxHash}: ${msg}`);
302
- }
272
+ if (receipt?.status === 'reverted') {
273
+ (0, context_1.printLog)(ctx, `[waitForInboundPushTx] Push RPC tiebreaker: ${pushTxHash} REVERTED on chain (cosmos still status=${status}); treating as terminal-failure`);
274
+ progressHook?.({
275
+ status: 'failed',
276
+ elapsedMs: elapsed(),
277
+ childUtxId,
278
+ pushTxHash,
279
+ });
280
+ return {
281
+ childUtxId,
282
+ pushTxHash,
283
+ sourceChain,
284
+ outboundExternalTxHash,
285
+ status: 'failed',
286
+ errorMessage: 'Push Chain inbound tx reverted on chain',
287
+ };
303
288
  }
289
+ // receipt missing / pending — keep polling.
304
290
  }
305
- }
306
- catch (err) {
307
- consecutiveRpcErrors++;
308
- if (consecutiveRpcErrors === RPC_FAILURE_WARN_THRESHOLD) {
309
- (0, context_1.printLog)(ctx, `[waitForInboundPushTx] ${RPC_FAILURE_WARN_THRESHOLD} consecutive getUniversalTxByIdV2 errors — RPC may be unhealthy. Continuing until timeout.`);
291
+ catch (rpcErr) {
292
+ // TransactionReceiptNotFoundError means not yet mined — continue
293
+ // polling. Other errors get logged but don't break the loop.
294
+ const msg = rpcErr instanceof Error ? rpcErr.message : String(rpcErr);
295
+ if (!/not found|TransactionReceiptNotFound|could not be found/i.test(msg)) {
296
+ (0, context_1.printLog)(ctx, `[waitForInboundPushTx] Push RPC tiebreaker failed for ${pushTxHash}: ${msg}`);
297
+ }
310
298
  }
311
- (0, context_1.printLog)(ctx, `[waitForInboundPushTx] getUniversalTxByIdV2 failed: ${err instanceof Error ? err.message : String(err)}`);
312
299
  }
313
- if (lastEmittedStatus !== 'polling') {
314
- progressHook === null || progressHook === void 0 ? void 0 : progressHook({
315
- status: 'polling',
316
- elapsedMs: elapsed(),
317
- childUtxId,
318
- });
319
- lastEmittedStatus = 'polling';
300
+ }
301
+ catch (err) {
302
+ consecutiveRpcErrors++;
303
+ if (consecutiveRpcErrors === RPC_FAILURE_WARN_THRESHOLD) {
304
+ (0, context_1.printLog)(ctx, `[waitForInboundPushTx] ${RPC_FAILURE_WARN_THRESHOLD} consecutive getUniversalTxByIdV2 errors — RPC may be unhealthy. Continuing until timeout.`);
320
305
  }
306
+ (0, context_1.printLog)(ctx, `[waitForInboundPushTx] getUniversalTxByIdV2 failed: ${err instanceof Error ? err.message : String(err)}`);
307
+ }
308
+ if (lastEmittedStatus !== 'polling') {
309
+ progressHook?.({
310
+ status: 'polling',
311
+ elapsedMs: elapsed(),
312
+ childUtxId,
313
+ });
314
+ lastEmittedStatus = 'polling';
321
315
  }
322
- if (elapsed() >= timeout)
323
- break;
324
- (0, context_1.printLog)(ctx, `[waitForInboundPushTx] Poll #${pollCount} not ready (${Date.now() - pollStart}ms). Waiting ${pollingIntervalMs}ms...`);
325
- yield new Promise((r) => setTimeout(r, pollingIntervalMs));
326
316
  }
327
- (0, context_1.printLog)(ctx, `[waitForInboundPushTx] TIMEOUT after ${pollCount} polls | elapsed: ${elapsed()}ms`);
328
- progressHook === null || progressHook === void 0 ? void 0 : progressHook({ status: 'timeout', elapsedMs: elapsed() });
329
- throw new InboundTimeoutError(outboundExternalTxHash, elapsed());
330
- });
317
+ if (elapsed() >= timeout)
318
+ break;
319
+ (0, context_1.printLog)(ctx, `[waitForInboundPushTx] Poll #${pollCount} not ready (${Date.now() - pollStart}ms). Waiting ${pollingIntervalMs}ms...`);
320
+ await new Promise((r) => setTimeout(r, pollingIntervalMs));
321
+ }
322
+ (0, context_1.printLog)(ctx, `[waitForInboundPushTx] TIMEOUT after ${pollCount} polls | elapsed: ${elapsed()}ms`);
323
+ progressHook?.({ status: 'timeout', elapsedMs: elapsed() });
324
+ throw new InboundTimeoutError(outboundExternalTxHash, elapsed());
331
325
  }
332
326
  /**
333
327
  * Thrown by `waitForInboundPushTx` when the polling loop exceeds the
@@ -336,9 +330,11 @@ function waitForInboundPushTx(ctx_1, outboundExternalTxHash_1, sourceChain_1) {
336
330
  * from generic failure (→ 399-02) without relying on error-message prefixes.
337
331
  */
338
332
  class InboundTimeoutError extends Error {
333
+ code = 'INBOUND_TIMEOUT';
334
+ correlationKey;
335
+ elapsedMs;
339
336
  constructor(correlationKey, elapsedMs) {
340
337
  super(`Timeout waiting for inbound Push tx after ${Math.round(elapsedMs / 1000)}s (correlation key: ${correlationKey}).`);
341
- this.code = 'INBOUND_TIMEOUT';
342
338
  this.name = 'InboundTimeoutError';
343
339
  this.correlationKey = correlationKey;
344
340
  this.elapsedMs = elapsedMs;