@pushchain/core 6.0.15 → 6.0.16

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.
@@ -15,6 +15,39 @@ const enums_1 = require("../../constants/enums");
15
15
  const progress_hook_types_1 = require("../../progress-hook/progress-hook.types");
16
16
  const svm_client_1 = require("../../vm-client/svm-client");
17
17
  const context_2 = require("./context");
18
+ const EVM_RECEIPT_POLL_MS = 3000;
19
+ const EVM_RECEIPT_NOT_FOUND_RE = /not found|TransactionNotFound|TransactionReceiptNotFound|could not be found|No transaction found/i;
20
+ function getErrorMessage(err) {
21
+ var _a, _b;
22
+ if (err instanceof Error)
23
+ return err.message;
24
+ if (typeof err === 'object' && err !== null) {
25
+ const maybe = err;
26
+ return String((_b = (_a = maybe.shortMessage) !== null && _a !== void 0 ? _a : maybe.message) !== null && _b !== void 0 ? _b : err);
27
+ }
28
+ return String(err);
29
+ }
30
+ function waitForEvmReceipt(evmClient, txHash, timeoutMs) {
31
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
32
+ const start = Date.now();
33
+ // eslint-disable-next-line no-constant-condition
34
+ while (true) {
35
+ try {
36
+ return yield evmClient.publicClient.getTransactionReceipt({
37
+ hash: txHash,
38
+ });
39
+ }
40
+ catch (err) {
41
+ const msg = getErrorMessage(err);
42
+ const retryable = EVM_RECEIPT_NOT_FOUND_RE.test(msg);
43
+ if (!retryable || Date.now() - start > timeoutMs) {
44
+ throw err;
45
+ }
46
+ yield new Promise((r) => setTimeout(r, EVM_RECEIPT_POLL_MS));
47
+ }
48
+ }
49
+ });
50
+ }
18
51
  function waitForEvmConfirmationsWithCountdown(ctx, evmClient, txHash, confirmations, timeoutMs) {
19
52
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
20
53
  if (confirmations <= 0) {
@@ -23,9 +56,7 @@ function waitForEvmConfirmationsWithCountdown(ctx, evmClient, txHash, confirmati
23
56
  }
24
57
  (0, context_2.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_106_03, confirmations);
25
58
  const start = Date.now();
26
- const receipt = yield evmClient.publicClient.waitForTransactionReceipt({
27
- hash: txHash,
28
- });
59
+ const receipt = yield waitForEvmReceipt(evmClient, txHash, timeoutMs);
29
60
  const targetBlock = receipt.blockNumber + BigInt(confirmations);
30
61
  let lastEmitted = 0;
31
62
  // eslint-disable-next-line no-constant-condition
@@ -112,9 +143,7 @@ function waitForLockerFeeConfirmation(ctx, txHashBytes) {
112
143
  case enums_1.VM.EVM: {
113
144
  const evmClient = (0, context_1.getOriginEvmClient)(ctx);
114
145
  const txHash = (0, viem_1.bytesToHex)(txHashBytes);
115
- const receipt = yield evmClient.publicClient.waitForTransactionReceipt({
116
- hash: txHash,
117
- });
146
+ const receipt = yield waitForEvmReceipt(evmClient, txHash, timeout);
118
147
  if (fastConfirmations <= 1)
119
148
  return;
120
149
  const targetBlock = receipt.blockNumber + BigInt(fastConfirmations);
@@ -20,6 +20,9 @@ const svm_client_1 = require("../../vm-client/svm-client");
20
20
  const context_2 = require("./context");
21
21
  const gas_calculator_1 = require("./gas-calculator");
22
22
  const gateway_client_1 = require("./gateway-client");
23
+ const gas_usd_sizer_1 = require("./gas-usd-sizer");
24
+ const price_fetch_1 = require("../../price-fetch/price-fetch");
25
+ const utils_1 = require("../../utils");
23
26
  const helpers_1 = require("./helpers");
24
27
  const payload_builder_1 = require("./payload-builder");
25
28
  const uea_manager_1 = require("./uea-manager");
@@ -100,7 +103,11 @@ function executeFundsOnlyEvm(ctx, execute, eventBuffer, transformFn, chain, rpcU
100
103
  const gasPrice = yield ctx.pushClient.getGasPrice();
101
104
  const requiredGasFee = gasEstimate * gasPrice;
102
105
  const requiredFunds = requiredGasFee + ((_c = execute.value) !== null && _c !== void 0 ? _c : BigInt(0));
103
- let nativeAmount = yield (0, gas_calculator_1.calculateNativeAmountForDeposit)(ctx, chain, requiredFunds, ueaBalanceForGas);
106
+ let nativeAmount = yield (0, gas_calculator_1.calculateNativeAmountForDeposit)(ctx, chain, requiredFunds, ueaBalanceForGas,
107
+ // Defer the 103-03-04 ("Prepaid Deposit Estimated") hook — we emit the full
108
+ // 103-03 sizing sequence below, after 103-01/103-02 (UEA resolution), to
109
+ // match the funds-payload path ordering.
110
+ { emitDepositEstimated: false });
104
111
  // For EVM fee-locking, fixed-rate PC→USD sizing can underfund when the
105
112
  // live WPC/native pool prices PC above the SDK's fallback price. Size the
106
113
  // native deposit against the same Push Chain pool used by the gateway.
@@ -121,6 +128,41 @@ function executeFundsOnlyEvm(ctx, execute, eventBuffer, transformFn, chain, rpcU
121
128
  (0, context_2.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_103_01);
122
129
  (0, context_2.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_103_02, ueaAddress, deployed);
123
130
  (0, context_2.printLog)(ctx, `UEA resolved: ${ueaAddress}, deployed: ${deployed}`);
131
+ // SDK 5.2 R1 deposit-sizing progress (parent + Case A/B/C + terminal),
132
+ // emitted here — after UEA resolution — to match the funds-payload path's
133
+ // ordering. The terminal 103-03-04 hook was suppressed inside
134
+ // calculateNativeAmountForDeposit above so it lands in this position instead
135
+ // of before 103-01/103-02. Purely cosmetic: a pool/price read failure here
136
+ // must never block the bridge, so we fall back to the terminal hook alone.
137
+ try {
138
+ const r1Sizing = yield (0, gas_usd_sizer_1.sizeR1PushGas)(ctx, {
139
+ pushGasFeeWei: requiredGasFee,
140
+ originChain: chain,
141
+ });
142
+ const nativeTokenUsdPrice = yield new price_fetch_1.PriceFetch(ctx.rpcUrls).getPrice(chain);
143
+ const nativeDecimals = chain_1.CHAIN_INFO[chain].vm === enums_1.VM.SVM ? 9 : 18;
144
+ const oneNativeUnit = utils_1.Utils.helpers.parseUnits('1', nativeDecimals);
145
+ const plannedPCDeposit = nativeTokenUsdPrice === BigInt(0)
146
+ ? BigInt(0)
147
+ : (r1Sizing.paddedDepositUsd * oneNativeUnit +
148
+ (nativeTokenUsdPrice - BigInt(1))) /
149
+ nativeTokenUsdPrice +
150
+ BigInt(1);
151
+ const extraDepositPC = plannedPCDeposit > requiredGasFee
152
+ ? plannedPCDeposit - requiredGasFee
153
+ : BigInt(0);
154
+ (0, context_2.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_103_03, chain, r1Sizing.paddedDepositUsd);
155
+ const r1SizingHookId = {
156
+ A: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_103_03_01,
157
+ B: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_103_03_02,
158
+ C: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_103_03_03,
159
+ }[r1Sizing.category];
160
+ (0, context_2.fireProgressHook)(ctx, r1SizingHookId, chain, requiredGasFee, extraDepositPC, r1Sizing.paddedDepositUsd);
161
+ (0, context_2.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_103_03_04, nativeAmount, r1Sizing.paddedDepositUsd, chain);
162
+ }
163
+ catch (_d) {
164
+ (0, context_2.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_103_03_04, nativeAmount, BigInt(0), chain);
165
+ }
124
166
  (0, context_2.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_106_01, execute.funds.amount, execute.funds.token.decimals, symbol);
125
167
  // Approve gateway to pull tokens if ERC-20
126
168
  if (execute.funds.token.mechanism === 'approve') {
@@ -10,4 +10,5 @@ import { ProgressEvent } from '../../progress-hook/progress-hook.types';
10
10
  import type { ExecuteParams, UniversalTxResponse } from '../orchestrator.types';
11
11
  import type { OrchestratorContext } from './context';
12
12
  import { type ResponseBuilderCallbacks } from './response-builder';
13
+ export declare function resolveRequiredFunds(requiredGasFee: bigint, executeValue: bigint, requiredFundsOverride?: bigint): bigint;
13
14
  export declare function executeStandardPayload(ctx: OrchestratorContext, execute: ExecuteParams, eventBuffer: ProgressEvent[], getResponseCallbacks: () => ResponseBuilderCallbacks): Promise<UniversalTxResponse>;
@@ -8,6 +8,7 @@
8
8
  * - Signed verification path for deployed UEA with funds
9
9
  */
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.resolveRequiredFunds = resolveRequiredFunds;
11
12
  exports.executeStandardPayload = executeStandardPayload;
12
13
  const tslib_1 = require("tslib");
13
14
  const bs58_1 = require("../../internal/bs58");
@@ -76,6 +77,12 @@ function encodePayloadForOriginIfNeeded(ctx, payload, feeLockingRequired) {
76
77
  }
77
78
  return encodePayloadForOrigin(ctx, payload);
78
79
  }
80
+ function resolveRequiredFunds(requiredGasFee, executeValue, requiredFundsOverride) {
81
+ const baseRequiredFunds = requiredGasFee + executeValue;
82
+ return requiredFundsOverride && requiredFundsOverride > baseRequiredFunds
83
+ ? requiredFundsOverride
84
+ : baseRequiredFunds;
85
+ }
79
86
  function executeStandardPayload(ctx, execute, eventBuffer, getResponseCallbacks) {
80
87
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
81
88
  var _a, _b, _c;
@@ -122,7 +129,7 @@ function executeStandardPayload(ctx, execute, eventBuffer, getResponseCallbacks)
122
129
  const gasEstimate = execute.gasLimit || BigInt(1e7);
123
130
  const gasPrice = yield ctx.pushClient.getGasPrice();
124
131
  const requiredGasFee = gasEstimate * gasPrice;
125
- const requiredFunds = requiredGasFee + execute.value;
132
+ const requiredFunds = resolveRequiredFunds(requiredGasFee, execute.value, execute._requiredFundsOverride);
126
133
  // Fetch UEA Details (or use pre-fetched status if available)
127
134
  const UEA = (0, uea_manager_1.computeUEAOffchain)(ctx);
128
135
  let isUEADeployed;
@@ -33,7 +33,9 @@ export declare function queryRescueGasFee(ctx: OrchestratorContext, prc20Token:
33
33
  nativeValueForGas: bigint;
34
34
  sizing?: GasSizingDecision;
35
35
  }>;
36
- export declare function calculateNativeAmountForDeposit(ctx: OrchestratorContext, chain: CHAIN, requiredFunds: bigint, ueaBalance: bigint): Promise<bigint>;
36
+ export declare function calculateNativeAmountForDeposit(ctx: OrchestratorContext, chain: CHAIN, requiredFunds: bigint, ueaBalance: bigint, opts?: {
37
+ emitDepositEstimated?: boolean;
38
+ }): Promise<bigint>;
37
39
  export declare function calculateGasAmountFromAmountOutMinETH(ctx: OrchestratorContext, gasTokenAddress: `0x${string}`, amountOutMinETH: bigint | string, quoteExactOutputFn: (amountOut: bigint, opts: {
38
40
  from: PayableToken | undefined;
39
41
  to: MoveableToken | undefined;
@@ -449,7 +449,12 @@ function queryRescueGasFee(ctx, prc20Token, destinationChain) {
449
449
  // ============================================================================
450
450
  // Native Amount Calculation for Deposit
451
451
  // ============================================================================
452
- function calculateNativeAmountForDeposit(ctx, chain, requiredFunds, ueaBalance) {
452
+ function calculateNativeAmountForDeposit(ctx, chain, requiredFunds, ueaBalance,
453
+ // When false, suppress the SEND-TX-103-03-04 ("Prepaid Deposit Estimated")
454
+ // progress hook so the caller can emit the full 103-03 sizing sequence
455
+ // (parent + Case A/B/C + terminal) AFTER 103-01/103-02, matching the
456
+ // funds-payload path ordering. The deposit math is unaffected.
457
+ opts) {
453
458
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
454
459
  var _a, _b;
455
460
  (0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_102_01);
@@ -497,7 +502,9 @@ function calculateNativeAmountForDeposit(ctx, chain, requiredFunds, ueaBalance)
497
502
  let nativeAmount = (depositUsd * oneNativeUnit + (nativeTokenUsdPrice - BigInt(1))) /
498
503
  nativeTokenUsdPrice;
499
504
  nativeAmount = nativeAmount + BigInt(1);
500
- (0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_103_03_04, nativeAmount, depositUsd, chain);
505
+ if ((opts === null || opts === void 0 ? void 0 : opts.emitDepositEstimated) !== false) {
506
+ (0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_103_03_04, nativeAmount, depositUsd, chain);
507
+ }
501
508
  return nativeAmount;
502
509
  });
503
510
  }
@@ -68,6 +68,7 @@ export declare function waitForAllOutboundTxsV2(ctx: OrchestratorContext, pushCh
68
68
  pollingIntervalMs: number;
69
69
  timeout: number;
70
70
  _resolvedSubTxId?: string;
71
+ _outboundIndexByHop?: Map<number, number>;
71
72
  progressHook?: (event: {
72
73
  hopIndex: number;
73
74
  route: TransactionRouteType;
@@ -180,7 +180,8 @@ function waitForOutboundTx(ctx_1, pushChainTxHash_1) {
180
180
  const pollStart = Date.now();
181
181
  (0, context_1.printLog)(ctx, `[waitForOutboundTx] Poll #${pollCount} | Elapsed: ${pollStart - startTime}ms / ${timeout}ms`);
182
182
  if (!cachedUniversalSubTxId) {
183
- cachedUniversalSubTxId = (_a = (yield (0, outbound_tracker_1.extractUniversalSubTxIdFromTx)(ctx, pushChainTxHash))) !== null && _a !== void 0 ? _a : undefined;
183
+ cachedUniversalSubTxId =
184
+ (_a = (yield (0, outbound_tracker_1.extractUniversalSubTxIdFromTx)(ctx, pushChainTxHash))) !== null && _a !== void 0 ? _a : undefined;
184
185
  if (!cachedUniversalSubTxId) {
185
186
  cachedUniversalSubTxId = (0, outbound_tracker_1.computeUniversalTxId)(ctx.pushNetwork, pushChainTxHash);
186
187
  }
@@ -228,13 +229,15 @@ function waitForOutboundTx(ctx_1, pushChainTxHash_1) {
228
229
  let matchingOutboundIndex = 0;
229
230
  for (const ob of outbounds) {
230
231
  // If a destination chain filter is set, skip outbound entries that don't match.
231
- if (_expectedDestinationChain && ob.destinationChain !== _expectedDestinationChain) {
232
+ if (_expectedDestinationChain &&
233
+ ob.destinationChain !== _expectedDestinationChain) {
232
234
  (0, context_1.printLog)(ctx, `[waitForOutboundTx] Poll #${pollCount} | outbound chain '${ob.destinationChain}' does not match expected '${_expectedDestinationChain}', skipping`);
233
235
  continue;
234
236
  }
235
237
  const currentOutboundIndex = matchingOutboundIndex;
236
238
  matchingOutboundIndex += 1;
237
- if (_outboundIndex !== undefined && currentOutboundIndex !== _outboundIndex) {
239
+ if (_outboundIndex !== undefined &&
240
+ currentOutboundIndex !== _outboundIndex) {
238
241
  (0, context_1.printLog)(ctx, `[waitForOutboundTx] Poll #${pollCount} | outbound index ${currentOutboundIndex} for '${ob.destinationChain}' does not match expected index ${_outboundIndex}, skipping`);
239
242
  continue;
240
243
  }
@@ -262,7 +265,10 @@ function waitForOutboundTx(ctx_1, pushChainTxHash_1) {
262
265
  const verdict = yield verifyExternalEvmReceipt(ctx, chain, ob.observedTx.txHash);
263
266
  if (verdict === 'reverted') {
264
267
  (0, context_1.printLog)(ctx, `[waitForOutboundTx] Source-chain RPC reports REVERTED for ${ob.destinationChain} tx ${ob.observedTx.txHash} (cosmos still ${(_o = types_1.UniversalTxStatus[statusNum]) !== null && _o !== void 0 ? _o : statusNum})`);
265
- progressHook === null || progressHook === void 0 ? void 0 : progressHook({ status: 'failed', elapsed: Date.now() - startTime });
268
+ progressHook === null || progressHook === void 0 ? void 0 : progressHook({
269
+ status: 'failed',
270
+ elapsed: Date.now() - startTime,
271
+ });
266
272
  throw new OutboundFailedError(`Outbound to ${ob.destinationChain} reverted on source-chain RPC (tx: ${ob.observedTx.txHash}). Push Chain TX: ${pushChainTxHash}.`, pushChainTxHash, ob.destinationChain, ob.observedTx.txHash);
267
273
  }
268
274
  if (verdict !== 'success') {
@@ -277,12 +283,19 @@ function waitForOutboundTx(ctx_1, pushChainTxHash_1) {
277
283
  const displayTxHash = (_s = (0, helpers_1.toExternalTxHashDisplay)(chain, ob.observedTx.txHash)) !== null && _s !== void 0 ? _s : ob.observedTx.txHash;
278
284
  let explorerUrl = '';
279
285
  if (isSvm) {
280
- const cluster = chain === enums_1.CHAIN.SOLANA_DEVNET ? '?cluster=devnet'
281
- : chain === enums_1.CHAIN.SOLANA_TESTNET ? '?cluster=testnet' : '';
282
- explorerUrl = explorerBaseUrl ? `${explorerBaseUrl}/tx/${displayTxHash}${cluster}` : '';
286
+ const cluster = chain === enums_1.CHAIN.SOLANA_DEVNET
287
+ ? '?cluster=devnet'
288
+ : chain === enums_1.CHAIN.SOLANA_TESTNET
289
+ ? '?cluster=testnet'
290
+ : '';
291
+ explorerUrl = explorerBaseUrl
292
+ ? `${explorerBaseUrl}/tx/${displayTxHash}${cluster}`
293
+ : '';
283
294
  }
284
295
  else {
285
- explorerUrl = explorerBaseUrl ? `${explorerBaseUrl}/tx/${displayTxHash}` : '';
296
+ explorerUrl = explorerBaseUrl
297
+ ? `${explorerBaseUrl}/tx/${displayTxHash}`
298
+ : '';
286
299
  }
287
300
  // OutboundTxDetails carries the raw `0x`-hex form so internal
288
301
  // consumers (R3 inbound tracker in response-builder.ts:786 →
@@ -298,7 +311,10 @@ function waitForOutboundTx(ctx_1, pushChainTxHash_1) {
298
311
  assetAddr: ob.externalAssetAddr,
299
312
  };
300
313
  (0, context_1.printLog)(ctx, `[waitForOutboundTx] FOUND on poll #${pollCount} | elapsed: ${Date.now() - startTime}ms | externalTxHash: ${displayTxHash}`);
301
- progressHook === null || progressHook === void 0 ? void 0 : progressHook({ status: 'found', elapsed: Date.now() - startTime });
314
+ progressHook === null || progressHook === void 0 ? void 0 : progressHook({
315
+ status: 'found',
316
+ elapsed: Date.now() - startTime,
317
+ });
302
318
  return details;
303
319
  }
304
320
  }
@@ -333,18 +349,24 @@ function waitForOutboundTx(ctx_1, pushChainTxHash_1) {
333
349
  */
334
350
  function waitForAllOutboundTxsV2(ctx, pushChainTxHash, outboundHops, options) {
335
351
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
336
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
337
- const { initialWaitMs, pollingIntervalMs, timeout, progressHook, _resolvedSubTxId, } = options;
352
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
353
+ const { initialWaitMs, pollingIntervalMs, timeout, progressHook, _resolvedSubTxId, _outboundIndexByHop, } = options;
338
354
  const startTime = Date.now();
339
- // Build a map: CAIP-2 namespace -> hop(s) for matching outbound entries
355
+ // Build maps for matching outbound entries. When the cascade composer merged
356
+ // multiple logical hops into one outbound request, `_outboundIndexByHop` maps
357
+ // those hops to the same outbound ordinal.
340
358
  const chainToHops = new Map();
341
359
  for (const hop of outboundHops) {
342
360
  const chainInfo = chain_1.CHAIN_INFO[hop.executionChain];
343
361
  if (chainInfo) {
344
362
  const namespace = `${chain_1.VM_NAMESPACE[chainInfo.vm]}:${chainInfo.chainId}`;
345
- const existing = chainToHops.get(namespace) || [];
363
+ const existingByOrdinal = chainToHops.get(namespace) || new Map();
364
+ const fallbackOrdinal = [...existingByOrdinal.values()].reduce((count, hopsForOrdinal) => count + hopsForOrdinal.length, 0);
365
+ const outboundOrdinal = (_a = _outboundIndexByHop === null || _outboundIndexByHop === void 0 ? void 0 : _outboundIndexByHop.get(hop.hopIndex)) !== null && _a !== void 0 ? _a : fallbackOrdinal;
366
+ const existing = existingByOrdinal.get(outboundOrdinal) || [];
346
367
  existing.push(hop);
347
- chainToHops.set(namespace, existing);
368
+ existingByOrdinal.set(outboundOrdinal, existing);
369
+ chainToHops.set(namespace, existingByOrdinal);
348
370
  }
349
371
  }
350
372
  const expectedChains = [...chainToHops.keys()];
@@ -387,7 +409,9 @@ function waitForAllOutboundTxsV2(ctx, pushChainTxHash, outboundHops, options) {
387
409
  // Resolve query ID on first poll
388
410
  if (!cachedQueryId) {
389
411
  const allSubTxIds = yield (0, outbound_tracker_1.extractAllUniversalSubTxIds)(ctx, pushChainTxHash);
390
- const subTxId = allSubTxIds.length > 0 ? allSubTxIds[0] : (0, outbound_tracker_1.computeUniversalTxId)(ctx.pushNetwork, pushChainTxHash);
412
+ const subTxId = allSubTxIds.length > 0
413
+ ? allSubTxIds[0]
414
+ : (0, outbound_tracker_1.computeUniversalTxId)(ctx.pushNetwork, pushChainTxHash);
391
415
  cachedQueryId = subTxId.startsWith('0x') ? subTxId.slice(2) : subTxId;
392
416
  (0, context_1.printLog)(ctx, `[waitForAllOutboundTxsV2] Resolved queryId: ${cachedQueryId}`);
393
417
  }
@@ -396,41 +420,45 @@ function waitForAllOutboundTxsV2(ctx, pushChainTxHash, outboundHops, options) {
396
420
  consecutiveErrors = 0; // Reset on successful RPC call
397
421
  const utx = v2Response === null || v2Response === void 0 ? void 0 : v2Response.universalTx;
398
422
  const statusNum = utx === null || utx === void 0 ? void 0 : utx.universalStatus;
399
- const statusName = (_a = types_1.UniversalTxStatus[statusNum]) !== null && _a !== void 0 ? _a : statusNum;
400
- (0, context_1.printLog)(ctx, `[waitForAllOutboundTxsV2] Poll #${pollCount} | status: ${statusNum} (${statusName}) | outboundTx count: ${(_c = (_b = utx === null || utx === void 0 ? void 0 : utx.outboundTx) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0}`);
401
- if ((_d = utx === null || utx === void 0 ? void 0 : utx.outboundTx) === null || _d === void 0 ? void 0 : _d.length) {
423
+ const statusName = (_b = types_1.UniversalTxStatus[statusNum]) !== null && _b !== void 0 ? _b : statusNum;
424
+ (0, context_1.printLog)(ctx, `[waitForAllOutboundTxsV2] Poll #${pollCount} | status: ${statusNum} (${statusName}) | outboundTx count: ${(_d = (_c = utx === null || utx === void 0 ? void 0 : utx.outboundTx) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0}`);
425
+ if ((_e = utx === null || utx === void 0 ? void 0 : utx.outboundTx) === null || _e === void 0 ? void 0 : _e.length) {
402
426
  const outboundOrdinalByChain = new Map();
403
427
  for (const ob of utx.outboundTx) {
404
428
  const destChain = ob.destinationChain;
405
- const hopsForChain = chainToHops.get(destChain);
406
- if (!hopsForChain)
429
+ const hopsByOrdinal = chainToHops.get(destChain);
430
+ if (!hopsByOrdinal)
407
431
  continue;
408
- const outboundOrdinal = (_e = outboundOrdinalByChain.get(destChain)) !== null && _e !== void 0 ? _e : 0;
432
+ const outboundOrdinal = (_f = outboundOrdinalByChain.get(destChain)) !== null && _f !== void 0 ? _f : 0;
409
433
  outboundOrdinalByChain.set(destChain, outboundOrdinal + 1);
410
- const targetHop = hopsForChain[outboundOrdinal];
411
- if (!targetHop ||
412
- targetHop.status === 'confirmed' ||
413
- targetHop.status === 'failed') {
434
+ const targetHops = hopsByOrdinal.get(outboundOrdinal) || [];
435
+ const pendingTargetHops = targetHops.filter((hop) => hop.status !== 'confirmed' && hop.status !== 'failed');
436
+ if (pendingTargetHops.length === 0)
414
437
  continue;
415
- }
416
- const externalTxHash = (_f = ob.observedTx) === null || _f === void 0 ? void 0 : _f.txHash;
438
+ const primaryTargetHop = pendingTargetHops[0];
439
+ const externalTxHash = (_g = ob.observedTx) === null || _g === void 0 ? void 0 : _g.txHash;
417
440
  const hasObservedTxHash = !!externalTxHash && externalTxHash !== 'EMPTY';
418
441
  const observedFailure = hasObservedTxHash &&
419
- ((_g = ob.observedTx) === null || _g === void 0 ? void 0 : _g.success) === false &&
420
- Boolean(((_h = ob.observedTx) === null || _h === void 0 ? void 0 : _h.errorMsg) ||
442
+ ((_h = ob.observedTx) === null || _h === void 0 ? void 0 : _h.success) === false &&
443
+ Boolean(((_j = ob.observedTx) === null || _j === void 0 ? void 0 : _j.errorMsg) ||
421
444
  ob.abortReason ||
422
445
  ob.outboundStatus >= types_2.OutboundStatus.REVERTED);
423
446
  // Fail fast on per-outbound REVERTED or observed destination failure.
424
- if (ob.outboundStatus === types_2.OutboundStatus.REVERTED || observedFailure) {
425
- targetHop.status = 'failed';
426
- (0, context_1.printLog)(ctx, `[waitForAllOutboundTxsV2] Outbound to ${destChain} REVERTED | outboundIndex ${outboundOrdinal} | hop ${targetHop.hopIndex} | error: ${ob.abortReason || ((_j = ob.observedTx) === null || _j === void 0 ? void 0 : _j.errorMsg) || 'Unknown'}`);
427
- progressHook === null || progressHook === void 0 ? void 0 : progressHook({
428
- hopIndex: targetHop.hopIndex,
429
- route: targetHop.route,
430
- chain: targetHop.executionChain,
431
- status: 'failed',
432
- });
433
- return { success: false, failedAt: targetHop.hopIndex };
447
+ if (ob.outboundStatus === types_2.OutboundStatus.REVERTED ||
448
+ observedFailure) {
449
+ for (const targetHop of pendingTargetHops) {
450
+ targetHop.status = 'failed';
451
+ progressHook === null || progressHook === void 0 ? void 0 : progressHook({
452
+ hopIndex: targetHop.hopIndex,
453
+ route: targetHop.route,
454
+ chain: targetHop.executionChain,
455
+ status: 'failed',
456
+ });
457
+ }
458
+ (0, context_1.printLog)(ctx, `[waitForAllOutboundTxsV2] Outbound to ${destChain} REVERTED | outboundIndex ${outboundOrdinal} | hops ${pendingTargetHops
459
+ .map((hop) => hop.hopIndex)
460
+ .join(', ')} | error: ${ob.abortReason || ((_k = ob.observedTx) === null || _k === void 0 ? void 0 : _k.errorMsg) || 'Unknown'}`);
461
+ return { success: false, failedAt: primaryTargetHop.hopIndex };
434
462
  }
435
463
  // Check for observed tx hash. Cosmos may lag the status transition,
436
464
  // so EVM destinations are verified against the destination receipt.
@@ -442,15 +470,19 @@ function waitForAllOutboundTxsV2(ctx, pushChainTxHash, outboundHops, options) {
442
470
  if (!isCosmosObserved) {
443
471
  const verdict = yield verifyExternalEvmReceipt(ctx, chain, externalTxHash);
444
472
  if (verdict === 'reverted') {
445
- targetHop.status = 'failed';
446
- (0, context_1.printLog)(ctx, `[waitForAllOutboundTxsV2] Source-chain RPC reports REVERTED for ${destChain} tx ${externalTxHash} | outboundIndex ${outboundOrdinal} | hop ${targetHop.hopIndex}`);
447
- progressHook === null || progressHook === void 0 ? void 0 : progressHook({
448
- hopIndex: targetHop.hopIndex,
449
- route: targetHop.route,
450
- chain: targetHop.executionChain,
451
- status: 'failed',
452
- });
453
- return { success: false, failedAt: targetHop.hopIndex };
473
+ for (const targetHop of pendingTargetHops) {
474
+ targetHop.status = 'failed';
475
+ progressHook === null || progressHook === void 0 ? void 0 : progressHook({
476
+ hopIndex: targetHop.hopIndex,
477
+ route: targetHop.route,
478
+ chain: targetHop.executionChain,
479
+ status: 'failed',
480
+ });
481
+ }
482
+ (0, context_1.printLog)(ctx, `[waitForAllOutboundTxsV2] Source-chain RPC reports REVERTED for ${destChain} tx ${externalTxHash} | outboundIndex ${outboundOrdinal} | hops ${pendingTargetHops
483
+ .map((hop) => hop.hopIndex)
484
+ .join(', ')}`);
485
+ return { success: false, failedAt: primaryTargetHop.hopIndex };
454
486
  }
455
487
  if (verdict !== 'success') {
456
488
  continue;
@@ -462,38 +494,49 @@ function waitForAllOutboundTxsV2(ctx, pushChainTxHash, outboundHops, options) {
462
494
  // inside this function; it's not exposed to consumers from the
463
495
  // cascade path (cascade.ts doesn't call findChildUtxIdFromExternalTx
464
496
  // for the cascade outbound hash).
465
- const displayTxHash = (_k = (0, helpers_1.toExternalTxHashDisplay)(chain !== null && chain !== void 0 ? chain : undefined, externalTxHash)) !== null && _k !== void 0 ? _k : externalTxHash;
497
+ const displayTxHash = (_l = (0, helpers_1.toExternalTxHashDisplay)(chain !== null && chain !== void 0 ? chain : undefined, externalTxHash)) !== null && _l !== void 0 ? _l : externalTxHash;
466
498
  let explorerUrl = '';
467
499
  if (chain && externalTxHash) {
468
- const explorerBaseUrl = (_l = chain_1.CHAIN_INFO[chain]) === null || _l === void 0 ? void 0 : _l.explorerUrl;
469
- const isSvm = ((_m = chain_1.CHAIN_INFO[chain]) === null || _m === void 0 ? void 0 : _m.vm) === enums_1.VM.SVM;
500
+ const explorerBaseUrl = (_m = chain_1.CHAIN_INFO[chain]) === null || _m === void 0 ? void 0 : _m.explorerUrl;
501
+ const isSvm = ((_o = chain_1.CHAIN_INFO[chain]) === null || _o === void 0 ? void 0 : _o.vm) === enums_1.VM.SVM;
470
502
  if (isSvm) {
471
- const cluster = chain === enums_1.CHAIN.SOLANA_DEVNET ? '?cluster=devnet'
472
- : chain === enums_1.CHAIN.SOLANA_TESTNET ? '?cluster=testnet' : '';
473
- explorerUrl = explorerBaseUrl ? `${explorerBaseUrl}/tx/${displayTxHash}${cluster}` : '';
503
+ const cluster = chain === enums_1.CHAIN.SOLANA_DEVNET
504
+ ? '?cluster=devnet'
505
+ : chain === enums_1.CHAIN.SOLANA_TESTNET
506
+ ? '?cluster=testnet'
507
+ : '';
508
+ explorerUrl = explorerBaseUrl
509
+ ? `${explorerBaseUrl}/tx/${displayTxHash}${cluster}`
510
+ : '';
474
511
  }
475
512
  else {
476
- explorerUrl = explorerBaseUrl ? `${explorerBaseUrl}/tx/${displayTxHash}` : '';
513
+ explorerUrl = explorerBaseUrl
514
+ ? `${explorerBaseUrl}/tx/${displayTxHash}`
515
+ : '';
477
516
  }
478
517
  }
479
- targetHop.status = 'confirmed';
480
- targetHop.txHash = displayTxHash;
481
- targetHop.outboundDetails = {
482
- externalTxHash: displayTxHash,
483
- destinationChain: chain || targetHop.executionChain,
484
- explorerUrl,
485
- recipient: ob.recipient,
486
- amount: ob.amount,
487
- assetAddr: ob.externalAssetAddr,
488
- };
489
- (0, context_1.printLog)(ctx, `[waitForAllOutboundTxsV2] FOUND outbound for ${destChain} | outboundIndex ${outboundOrdinal} | hop ${targetHop.hopIndex} | externalTxHash: ${displayTxHash}`);
490
- progressHook === null || progressHook === void 0 ? void 0 : progressHook({
491
- hopIndex: targetHop.hopIndex,
492
- route: targetHop.route,
493
- chain: targetHop.executionChain,
494
- status: 'confirmed',
495
- txHash: displayTxHash,
496
- });
518
+ for (const targetHop of pendingTargetHops) {
519
+ targetHop.status = 'confirmed';
520
+ targetHop.txHash = displayTxHash;
521
+ targetHop.outboundDetails = {
522
+ externalTxHash: displayTxHash,
523
+ destinationChain: chain || targetHop.executionChain,
524
+ explorerUrl,
525
+ recipient: ob.recipient,
526
+ amount: ob.amount,
527
+ assetAddr: ob.externalAssetAddr,
528
+ };
529
+ progressHook === null || progressHook === void 0 ? void 0 : progressHook({
530
+ hopIndex: targetHop.hopIndex,
531
+ route: targetHop.route,
532
+ chain: targetHop.executionChain,
533
+ status: 'confirmed',
534
+ txHash: displayTxHash,
535
+ });
536
+ }
537
+ (0, context_1.printLog)(ctx, `[waitForAllOutboundTxsV2] FOUND outbound for ${destChain} | outboundIndex ${outboundOrdinal} | hops ${pendingTargetHops
538
+ .map((hop) => hop.hopIndex)
539
+ .join(', ')} | externalTxHash: ${displayTxHash}`);
497
540
  }
498
541
  }
499
542
  }
@@ -508,7 +551,7 @@ function waitForAllOutboundTxsV2(ctx, pushChainTxHash, outboundHops, options) {
508
551
  if (statusNum === types_1.UniversalTxStatus.PC_EXECUTED_SUCCESS) {
509
552
  const stillUnresolved = outboundHops.filter((h) => h.status !== 'confirmed');
510
553
  if (stillUnresolved.length > 0) {
511
- const hasPendingOutbound = (_o = utx === null || utx === void 0 ? void 0 : utx.outboundTx) === null || _o === void 0 ? void 0 : _o.some((ob) => {
554
+ const hasPendingOutbound = (_p = utx === null || utx === void 0 ? void 0 : utx.outboundTx) === null || _p === void 0 ? void 0 : _p.some((ob) => {
512
555
  var _a;
513
556
  return ob.outboundStatus === types_2.OutboundStatus.PENDING &&
514
557
  (!((_a = ob.observedTx) === null || _a === void 0 ? void 0 : _a.txHash) || ob.observedTx.txHash === 'EMPTY');
@@ -563,14 +563,32 @@ function transformToUniversalTxResponse(ctx_1, tx_1) {
563
563
  }
564
564
  }
565
565
  const origin = `${chain_1.VM_NAMESPACE[vm]}:${chainId}:${originAddress}`;
566
- // Create signature from transaction r, s, v values
566
+ // Create signature from transaction r, s, v values.
567
+ //
568
+ // Push Chain's derived (module-injected) inbound txs are reported over the EVM
569
+ // JSON-RPC as **legacy `type 0x0`**, which omits the `yParity` field that viem
570
+ // only populates for typed (EIP-1559/2930) txs — so `tx.yParity` is `undefined`.
571
+ // Per the chain team (the hash inconsistencies were fixed chain-side, but
572
+ // derived txs remain legacy 0x0 by design), the SDK must accept `0x0` and
573
+ // surface a numeric `yParity`. Derive it from `v`: 0/1 → as-is, 27/28 → v-27,
574
+ // EIP-155 → (v-35)%2. (For the zeroed-signature derived txs, v=0 → yParity=0.)
567
575
  let signature;
568
576
  try {
577
+ const vNum = typeof tx.v === 'bigint' ? Number(tx.v) : tx.v || 0;
578
+ let yParity = tx.yParity;
579
+ if (yParity === undefined) {
580
+ if (vNum === 0 || vNum === 1)
581
+ yParity = vNum;
582
+ else if (vNum === 27 || vNum === 28)
583
+ yParity = vNum - 27;
584
+ else
585
+ yParity = (vNum - 35) % 2;
586
+ }
569
587
  signature = {
570
588
  r: tx.r || '0x0',
571
589
  s: tx.s || '0x0',
572
- v: typeof tx.v === 'bigint' ? Number(tx.v) : tx.v || 0,
573
- yParity: tx.yParity,
590
+ v: vNum,
591
+ yParity,
574
592
  };
575
593
  }
576
594
  catch (_d) {
@@ -631,7 +649,7 @@ function transformToUniversalTxResponse(ctx_1, tx_1) {
631
649
  accessList: Array.isArray(tx.accessList) ? [...tx.accessList] : [],
632
650
  // 6. Utilities
633
651
  wait: (waitOptions) => tslib_1.__awaiter(this, void 0, void 0, function* () {
634
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
652
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
635
653
  // For trackTransaction replays the tx is historical — cosmos already
636
654
  // has the full graph indexed, so the 20s/30s initial-wait buffers
637
655
  // designed for live sends are pure deadweight. Default them to 0 when
@@ -776,7 +794,15 @@ function transformToUniversalTxResponse(ctx_1, tx_1) {
776
794
  // Resolve the terminal hash for the whole journey: inbound (R3) >
777
795
  // external outbound (R2) > the Push tx itself (R1). Single-tx analogue
778
796
  // of CascadeCompletionResult.finalTxHash.
779
- baseReceipt = Object.assign(Object.assign({}, baseReceipt), { finalTxHash: (_l = (_k = baseReceipt.pushInboundTxHash) !== null && _k !== void 0 ? _k : baseReceipt.externalTxHash) !== null && _l !== void 0 ? _l : baseReceipt.hash });
797
+ baseReceipt = Object.assign(Object.assign({}, baseReceipt), { finalTxHash: (_l = (_k = baseReceipt.pushInboundTxHash) !== null && _k !== void 0 ? _k : baseReceipt.externalTxHash) !== null && _l !== void 0 ? _l : baseReceipt.hash,
798
+ // Ensure `externalChain` is populated whenever an external/inbound leg
799
+ // surfaced an `externalTxHash`. The outbound-success branch already sets
800
+ // it to the destination chain (so this `??` never overrides it); this
801
+ // only fills the inbound/R3 and failed/timeout paths, where it should be
802
+ // the route's external chain (= the response's `chain`).
803
+ externalChain: (_m = baseReceipt.externalChain) !== null && _m !== void 0 ? _m : (baseReceipt.externalTxHash
804
+ ? universalTxResponse.chain
805
+ : baseReceipt.externalChain) });
780
806
  return baseReceipt;
781
807
  }),
782
808
  // Internal: register a wait-phase progressHook without replaying
@@ -19,6 +19,13 @@ import { MoveableToken } from '../../constants/tokens';
19
19
  import { TransactionRoute } from '../route-detector';
20
20
  import type { ChainTarget, ExecuteParams, UniversalExecuteParams, UniversalOutboundTxRequest, UniversalTxRequest, UniversalTxResponse } from '../orchestrator.types';
21
21
  import type { PUSH_NETWORK } from '../../constants/enums';
22
+ export declare function getRequiredNativeBalanceForOutbound(nativeValueForGas: bigint, gasReserve: bigint): bigint;
23
+ export declare function shouldSkipFeeLockingForOutbound(opts: {
24
+ isUEADeployed: boolean;
25
+ balance: bigint;
26
+ nativeValueForGas: bigint;
27
+ gasReserve: bigint;
28
+ }): boolean;
22
29
  export declare function resolveR2DestinationFundsToken(token: MoveableToken, targetChain: CHAIN, pushNetwork: PUSH_NETWORK): MoveableToken;
23
30
  type ExecuteFn = (params: ExecuteParams | UniversalExecuteParams) => Promise<UniversalTxResponse>;
24
31
  /**