@pushchain/core 5.1.18 → 6.0.0

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 (45) hide show
  1. package/package.json +1 -1
  2. package/src/lib/constants/abi/cea.evm.d.ts +52 -0
  3. package/src/lib/constants/abi/cea.evm.js +66 -1
  4. package/src/lib/constants/abi/prc20.evm.d.ts +117 -11
  5. package/src/lib/constants/abi/prc20.evm.js +38 -11
  6. package/src/lib/constants/abi/uea.evm.js +41 -0
  7. package/src/lib/constants/abi/universalGatewayPC.evm.d.ts +117 -1
  8. package/src/lib/constants/abi/universalGatewayPC.evm.js +40 -1
  9. package/src/lib/constants/abi/universalGatewayV0.json +1 -1
  10. package/src/lib/constants/chain.js +14 -14
  11. package/src/lib/constants/tokens.js +1 -0
  12. package/src/lib/formatters.d.ts +6 -0
  13. package/src/lib/formatters.js +14 -0
  14. package/src/lib/index.d.ts +2 -0
  15. package/src/lib/index.js +7 -1
  16. package/src/lib/orchestrator/internals/cascade.js +139 -81
  17. package/src/lib/orchestrator/internals/errors.js +1 -1
  18. package/src/lib/orchestrator/internals/execute-funds-only.js +3 -2
  19. package/src/lib/orchestrator/internals/execute-funds-payload.js +2 -1
  20. package/src/lib/orchestrator/internals/execute-standard.js +4 -4
  21. package/src/lib/orchestrator/internals/gas-calculator.d.ts +1 -0
  22. package/src/lib/orchestrator/internals/gas-calculator.js +13 -11
  23. package/src/lib/orchestrator/internals/helpers.js +1 -0
  24. package/src/lib/orchestrator/internals/inbound-tracker.js +4 -2
  25. package/src/lib/orchestrator/internals/outbound-sync.js +73 -60
  26. package/src/lib/orchestrator/internals/pc-usd-oracle.d.ts +1 -1
  27. package/src/lib/orchestrator/internals/pc-usd-oracle.js +4 -3
  28. package/src/lib/orchestrator/internals/push-chain-tx.js +5 -1
  29. package/src/lib/orchestrator/internals/route-handlers.js +155 -185
  30. package/src/lib/orchestrator/internals/signing.js +7 -2
  31. package/src/lib/orchestrator/internals/svm-helpers.js +3 -3
  32. package/src/lib/orchestrator/internals/svm-rent.d.ts +42 -9
  33. package/src/lib/orchestrator/internals/svm-rent.js +148 -14
  34. package/src/lib/orchestrator/internals/tx-transformer.js +5 -2
  35. package/src/lib/orchestrator/max-pc-for-gas.d.ts +60 -0
  36. package/src/lib/orchestrator/max-pc-for-gas.js +66 -0
  37. package/src/lib/orchestrator/orchestrator.d.ts +1 -0
  38. package/src/lib/orchestrator/orchestrator.types.d.ts +31 -9
  39. package/src/lib/orchestrator/payload-builders.d.ts +7 -6
  40. package/src/lib/orchestrator/payload-builders.js +18 -6
  41. package/src/lib/progress-hook/progress-hook.js +26 -18
  42. package/src/lib/universal-tx-detector/events.d.ts +3 -1
  43. package/src/lib/universal-tx-detector/events.js +3 -1
  44. package/src/lib/universal-tx-detector/svm-events.d.ts +1 -1
  45. package/src/lib/universal-tx-detector/svm-events.js +1 -1
@@ -137,13 +137,14 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
137
137
  params,
138
138
  route: routeStr,
139
139
  gasLimit,
140
+ maxPCForGas: (_c = params.maxPCForGas) !== null && _c !== void 0 ? _c : BigInt(0),
140
141
  ueaAddress,
141
142
  revertRecipient: ueaAddress,
142
143
  };
143
144
  switch (route) {
144
145
  case route_detector_1.TransactionRoute.UOA_TO_PUSH: {
145
146
  // Route 1: Build Push Chain multicalls
146
- const seedAmount = (_c = params.value) !== null && _c !== void 0 ? _c : BigInt(0);
147
+ const seedAmount = (_d = params.value) !== null && _d !== void 0 ? _d : BigInt(0);
147
148
  if (isValueOnlyNativeSeedToOwnUea(params, ueaAddress)) {
148
149
  return Object.assign(Object.assign({}, baseDescriptor), { pushMulticalls: [], nativeSeedOnly: true, nativeSeedAmount: seedAmount });
149
150
  }
@@ -168,7 +169,7 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
168
169
  });
169
170
  let prc20Token = selectors_1.ZERO_ADDRESS;
170
171
  let burnAmount = BigInt(0);
171
- if ((_d = params.funds) === null || _d === void 0 ? void 0 : _d.amount) {
172
+ if ((_e = params.funds) === null || _e === void 0 ? void 0 : _e.amount) {
172
173
  const token = params.funds.token;
173
174
  if (token) {
174
175
  prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token).address;
@@ -190,35 +191,46 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
190
191
  }
191
192
  let gasToken = selectors_1.ZERO_ADDRESS;
192
193
  let gasFee = BigInt(0);
194
+ let gasPrice = BigInt(0);
193
195
  let sizing;
194
- // Conditional CEA-ATA rent bump (SPL-only). Mirrors single-route
195
- // executeUoaToCeaSvm path. See svm-rent.ts for rationale.
196
- const splMintBase58 = burnAmount > BigInt(0)
197
- ? (_f = (_e = params.funds) === null || _e === void 0 ? void 0 : _e.token) === null || _f === void 0 ? void 0 : _f.address
196
+ // SVM finalize budget: compare the quoted gasFee in lamports against
197
+ // the gateway finalize minimum, then re-query with a bumped gasLimit
198
+ // if the default quote is too low.
199
+ const svmFundsToken = burnAmount > BigInt(0)
200
+ ? (_f = params.funds) === null || _f === void 0 ? void 0 : _f.token
198
201
  : undefined;
199
- let effectiveGasLimit = yield (0, svm_rent_1.maybeBumpForCeaAtaRent)({
200
- ctx,
201
- ueaAddress,
202
- targetChain,
203
- splMintBase58,
204
- burnAmount,
205
- effectiveGasLimit: gasLimit,
206
- });
202
+ const splMintBase58 = (svmFundsToken === null || svmFundsToken === void 0 ? void 0 : svmFundsToken.mechanism) === 'approve'
203
+ ? svmFundsToken.address
204
+ : undefined;
205
+ let effectiveGasLimit = gasLimit;
207
206
  if (prc20Token !== selectors_1.ZERO_ADDRESS) {
208
- const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, effectiveGasLimit, targetChain);
207
+ let result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, effectiveGasLimit, targetChain);
208
+ result = yield (0, svm_rent_1.ensureSvmFinalizeGasBudgetQuote)({
209
+ ctx,
210
+ ueaAddress,
211
+ targetChain,
212
+ prc20Token,
213
+ quote: result,
214
+ splMintBase58,
215
+ burnAmount,
216
+ pathTag: 'buildHopDescriptor',
217
+ });
209
218
  gasToken = result.gasToken;
210
219
  gasFee = result.gasFee;
220
+ gasPrice = result.gasPrice;
211
221
  sizing = result.sizing;
212
- if (!params.gasLimit && result.gasPrice > BigInt(0)) {
213
- effectiveGasLimit = result.gasFee / result.gasPrice;
214
- (0, context_1.printLog)(ctx, `buildHopDescriptor — SVM derived effectiveGasLimit: ${effectiveGasLimit} (gasFee=${result.gasFee} / gasPrice=${result.gasPrice})`);
222
+ effectiveGasLimit = result.gasLimitUsed;
223
+ if (!params.gasLimit) {
224
+ (0, context_1.printLog)(ctx, `buildHopDescriptor — SVM resolved effectiveGasLimit: ${effectiveGasLimit} ` +
225
+ `(gasFee=${result.gasFee}, gasPrice=${result.gasPrice})`);
215
226
  }
216
227
  }
217
228
  return Object.assign(Object.assign({}, baseDescriptor), { targetChain, isSvmTarget: true, svmPayload,
218
229
  prc20Token,
219
230
  burnAmount,
220
231
  gasToken,
221
- gasFee, gasLimit: effectiveGasLimit, sizing });
232
+ gasFee,
233
+ gasPrice, gasLimit: effectiveGasLimit, sizing });
222
234
  }
223
235
  // EVM path: Resolve CEA address + build CEA multicalls
224
236
  const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, targetChain, (_g = ctx.rpcUrls[targetChain]) === null || _g === void 0 ? void 0 : _g[0]);
@@ -228,18 +240,21 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
228
240
  const burnAmount = BigInt(0); // Migration is logic-only — no funds. CEA rejects msg.value != 0.
229
241
  let gasToken = selectors_1.ZERO_ADDRESS;
230
242
  let gasFee = BigInt(0);
243
+ let gasPrice = BigInt(0);
231
244
  let sizing;
232
245
  if (prc20Token !== selectors_1.ZERO_ADDRESS) {
233
246
  const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, gasLimit, targetChain);
234
247
  gasToken = result.gasToken;
235
248
  gasFee = result.gasFee;
249
+ gasPrice = result.gasPrice;
236
250
  sizing = result.sizing;
237
251
  }
238
252
  return Object.assign(Object.assign({}, baseDescriptor), { targetChain,
239
253
  ceaAddress, ceaMulticalls: [], prc20Token,
240
254
  burnAmount,
241
255
  gasToken,
242
- gasFee, isMigration: true, sizing });
256
+ gasFee,
257
+ gasPrice, isMigration: true, sizing });
243
258
  }
244
259
  // Build CEA multicalls
245
260
  const ceaMulticalls = [];
@@ -342,11 +357,13 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
342
357
  // Query gas fee
343
358
  let gasToken = selectors_1.ZERO_ADDRESS;
344
359
  let gasFee = BigInt(0);
360
+ let gasPrice = BigInt(0);
345
361
  let sizing;
346
362
  if (prc20Token !== selectors_1.ZERO_ADDRESS) {
347
363
  const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, gasLimit, targetChain);
348
364
  gasToken = result.gasToken;
349
365
  gasFee = result.gasFee;
366
+ gasPrice = result.gasPrice;
350
367
  sizing = result.sizing;
351
368
  }
352
369
  return Object.assign(Object.assign({}, baseDescriptor), { targetChain,
@@ -356,6 +373,7 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
356
373
  burnAmount,
357
374
  gasToken,
358
375
  gasFee,
376
+ gasPrice,
359
377
  sizing });
360
378
  }
361
379
  case route_detector_1.TransactionRoute.CEA_TO_PUSH: {
@@ -401,19 +419,33 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
401
419
  }
402
420
  let gasToken = selectors_1.ZERO_ADDRESS;
403
421
  let gasFee = BigInt(0);
422
+ let gasPrice = BigInt(0);
404
423
  let sizing;
424
+ let effectiveGasLimit = gasLimit;
405
425
  if (prc20Token !== selectors_1.ZERO_ADDRESS) {
406
- const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, gasLimit, sourceChain);
426
+ let result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, effectiveGasLimit, sourceChain);
427
+ result = yield (0, svm_rent_1.ensureSvmFinalizeGasBudgetQuote)({
428
+ ctx,
429
+ ueaAddress,
430
+ targetChain: sourceChain,
431
+ prc20Token,
432
+ quote: result,
433
+ splMintBase58: undefined,
434
+ burnAmount: BigInt(0),
435
+ pathTag: 'buildHopDescriptor',
436
+ });
407
437
  gasToken = result.gasToken;
408
438
  gasFee = result.gasFee;
439
+ gasPrice = result.gasPrice;
409
440
  sizing = result.sizing;
441
+ effectiveGasLimit = result.gasLimitUsed;
410
442
  }
411
443
  return Object.assign(Object.assign({}, baseDescriptor), { sourceChain, ceaAddress: ceaPdaHex, isSvmTarget: true, prc20Token,
412
444
  // Route 3 is payload-only on Push: the drain amount lives inside the
413
445
  // SVM payload. Burning here would require the UEA to pre-hold pSOL.
414
446
  burnAmount: BigInt(0), gasToken,
415
447
  gasFee,
416
- sizing });
448
+ gasPrice, gasLimit: effectiveGasLimit, sizing });
417
449
  }
418
450
  const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, sourceChain, (_p = ctx.rpcUrls[sourceChain]) === null || _p === void 0 ? void 0 : _p[0]);
419
451
  // CEA auto-deploys on-chain: Vault.finalizeUniversalTx calls CEAFactory.deployCEA()
@@ -443,11 +475,13 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
443
475
  const prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
444
476
  let gasToken = selectors_1.ZERO_ADDRESS;
445
477
  let gasFee = BigInt(0);
478
+ let gasPrice = BigInt(0);
446
479
  let sizing;
447
480
  if (prc20Token !== selectors_1.ZERO_ADDRESS) {
448
481
  const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, gasLimit, sourceChain);
449
482
  gasToken = result.gasToken;
450
483
  gasFee = result.gasFee;
484
+ gasPrice = result.gasPrice;
451
485
  sizing = result.sizing;
452
486
  }
453
487
  return Object.assign(Object.assign({}, baseDescriptor), { sourceChain,
@@ -458,6 +492,7 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
458
492
  // token it is trying to receive from the external CEA.
459
493
  burnAmount: BigInt(0), gasToken,
460
494
  gasFee,
495
+ gasPrice,
461
496
  sizing });
462
497
  }
463
498
  default:
@@ -512,6 +547,8 @@ function classifyIntoSegments(hops) {
512
547
  // Accumulate gas fees
513
548
  currentSegment.gasFee =
514
549
  (currentSegment.gasFee || BigInt(0)) + (hop.gasFee || BigInt(0));
550
+ currentSegment.gasPrice = mergeGasPrice(currentSegment.gasPrice, hop.gasPrice);
551
+ currentSegment.maxPCForGas = mergeMaxPCForGas(currentSegment.maxPCForGas, hop.maxPCForGas);
515
552
  }
516
553
  else if (segType === 'INBOUND_FROM_CEA') {
517
554
  // Sum burn amounts so the single sendUniversalTxToUEA carries the
@@ -524,6 +561,8 @@ function classifyIntoSegments(hops) {
524
561
  }
525
562
  currentSegment.gasFee =
526
563
  (currentSegment.gasFee || BigInt(0)) + (hop.gasFee || BigInt(0));
564
+ currentSegment.gasPrice = mergeGasPrice(currentSegment.gasPrice, hop.gasPrice);
565
+ currentSegment.maxPCForGas = mergeMaxPCForGas(currentSegment.maxPCForGas, hop.maxPCForGas);
527
566
  }
528
567
  else if (segType === 'PUSH_EXECUTION') {
529
568
  currentSegment.mergedPushMulticalls = [
@@ -561,7 +600,9 @@ function classifyIntoSegments(hops) {
561
600
  prc20Token: hop.prc20Token,
562
601
  gasToken: hop.gasToken,
563
602
  gasFee: hop.gasFee,
603
+ gasPrice: hop.gasPrice,
564
604
  gasLimit: hop.gasLimit,
605
+ maxPCForGas: hop.maxPCForGas,
565
606
  sizing: hop.sizing,
566
607
  };
567
608
  segments.push(currentSegment);
@@ -573,6 +614,18 @@ function classifyIntoSegments(hops) {
573
614
  function categoryRank(c) {
574
615
  return c === 'C' ? 2 : c === 'B' ? 1 : 0;
575
616
  }
617
+ function mergeMaxPCForGas(current, next) {
618
+ const a = current !== null && current !== void 0 ? current : BigInt(0);
619
+ const b = next !== null && next !== void 0 ? next : BigInt(0);
620
+ if (a === BigInt(0) || b === BigInt(0))
621
+ return BigInt(0);
622
+ return a + b;
623
+ }
624
+ function mergeGasPrice(current, next) {
625
+ const a = current !== null && current !== void 0 ? current : BigInt(0);
626
+ const b = next !== null && next !== void 0 ? next : BigInt(0);
627
+ return a > b ? a : b;
628
+ }
576
629
  // ============================================================================
577
630
  // getSegmentType
578
631
  // ============================================================================
@@ -615,6 +668,34 @@ function isNativeSeedOnlySegment(segment) {
615
668
  function segmentNeedsOutboundGas(segment) {
616
669
  return segment.type !== 'PUSH_EXECUTION' && !isNativeSeedOnlySegment(segment);
617
670
  }
671
+ function buildRootOutboundIndexByHop(segments, hops) {
672
+ var _a;
673
+ const hopIndexByDescriptor = new Map();
674
+ hops.forEach((hop, index) => hopIndexByDescriptor.set(hop, index));
675
+ const nextIndexByChain = new Map();
676
+ const result = new Map();
677
+ for (const segment of segments) {
678
+ let chain;
679
+ if (segment.type === 'OUTBOUND_TO_CEA') {
680
+ chain = segment.targetChain;
681
+ }
682
+ else if (segment.type === 'INBOUND_FROM_CEA' &&
683
+ !isNativeSeedOnlySegment(segment)) {
684
+ chain = segment.sourceChain;
685
+ }
686
+ if (!chain)
687
+ continue;
688
+ const outboundIndex = (_a = nextIndexByChain.get(chain)) !== null && _a !== void 0 ? _a : 0;
689
+ for (const hop of segment.hops) {
690
+ const hopIndex = hopIndexByDescriptor.get(hop);
691
+ if (hopIndex !== undefined) {
692
+ result.set(hopIndex, outboundIndex);
693
+ }
694
+ }
695
+ nextIndexByChain.set(chain, outboundIndex + 1);
696
+ }
697
+ return result;
698
+ }
618
699
  function sumNativeSeedAmount(hops) {
619
700
  return hops.reduce((sum, hop) => { var _a; return sum + ((_a = hop.nativeSeedAmount) !== null && _a !== void 0 ? _a : BigInt(0)); }, BigInt(0));
620
701
  }
@@ -674,7 +755,7 @@ function getInboundFundingForBurnToken(segments, beforeSegmentIndex, burnToken,
674
755
  }
675
756
  function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
676
757
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
677
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
758
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
678
759
  let accumulatedPushMulticalls = [];
679
760
  let requiredNativeValue = BigInt(0);
680
761
  const gatewayPcAddress = (0, helpers_1.getUniversalGatewayPCAddress)();
@@ -729,7 +810,7 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce)
729
810
  const universalCoreAddress = yield ctx.pushClient.readContract({
730
811
  address: gatewayPcAddress,
731
812
  abi: abi_1.UNIVERSAL_GATEWAY_PC,
732
- functionName: 'UNIVERSAL_CORE',
813
+ functionName: 'universalCore',
733
814
  args: [],
734
815
  });
735
816
  // Compute per-SVM budget = ueaBalance − (PC needed for EVM outbounds) − 1 PC safety
@@ -838,7 +919,10 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce)
838
919
  // EVM: build CEA payload from merged multicalls
839
920
  outboundPayload = (0, payload_builders_1.buildCeaMulticallPayload)(segment.mergedCeaMulticalls || []);
840
921
  // Get CEA address from the first hop
841
- targetForOutbound = (firstHop === null || firstHop === void 0 ? void 0 : firstHop.ceaAddress) || ueaAddress;
922
+ targetForOutbound = outboundPayload === '0x'
923
+ ? selectors_1.ZERO_ADDRESS
924
+ : (firstHop === null || firstHop === void 0 ? void 0 : firstHop.ceaAddress) || ueaAddress;
925
+ (0, payload_builders_1.assertCeaFundsParkingInvariant)(targetForOutbound, outboundPayload);
842
926
  }
843
927
  // Per-segment PRC-20 burn-balance pre-check (cascade R2-style segments
844
928
  // only — INBOUND_FROM_CEA segments are payload-only with totalBurnAmount=0
@@ -891,7 +975,7 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce)
891
975
  }
892
976
  }
893
977
  // Build outbound request
894
- const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetForOutbound, segment.prc20Token || selectors_1.ZERO_ADDRESS, segment.totalBurnAmount || BigInt(0), (_e = segment.gasLimit) !== null && _e !== void 0 ? _e : BigInt(0), outboundPayload, ueaAddress);
978
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetForOutbound, segment.prc20Token || selectors_1.ZERO_ADDRESS, segment.totalBurnAmount || BigInt(0), (_e = segment.gasLimit) !== null && _e !== void 0 ? _e : BigInt(0), outboundPayload, ueaAddress, (_f = segment.maxPCForGas) !== null && _f !== void 0 ? _f : BigInt(0), (_g = segment.gasPrice) !== null && _g !== void 0 ? _g : BigInt(0));
895
979
  // Build approval + outbound multicalls
896
980
  const segGasFee = segment.gasFee || BigInt(0);
897
981
  const svmOverride = svmNativeValueBySegment.get(segment);
@@ -908,7 +992,7 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce)
908
992
  else {
909
993
  evmFallbackValue = segGasFee * BigInt(1000000);
910
994
  }
911
- let nativeValueForGas = (_f = svmOverride !== null && svmOverride !== void 0 ? svmOverride : perOutboundNativeValue) !== null && _f !== void 0 ? _f : evmFallbackValue;
995
+ let nativeValueForGas = (_h = svmOverride !== null && svmOverride !== void 0 ? svmOverride : perOutboundNativeValue) !== null && _h !== void 0 ? _h : evmFallbackValue;
912
996
  // SDK 5.2 Case C: when the segment's sizing is category C, compose
913
997
  // R2/OUTBOUND_TO_CEA segments no longer apply Case A/B/C sizing
914
998
  // (scoped to R1 + R3 only). segment.sizing may still be populated
@@ -949,7 +1033,7 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce)
949
1033
  // UEA multicall so no nested outbound is needed.
950
1034
  const mergedHopMulticalls = [];
951
1035
  for (const h of segment.hops) {
952
- if (((_g = h === null || h === void 0 ? void 0 : h.params) === null || _g === void 0 ? void 0 : _g.data) || ((_h = h === null || h === void 0 ? void 0 : h.params) === null || _h === void 0 ? void 0 : _h.value)) {
1036
+ if (((_j = h === null || h === void 0 ? void 0 : h.params) === null || _j === void 0 ? void 0 : _j.data) || ((_k = h === null || h === void 0 ? void 0 : h.params) === null || _k === void 0 ? void 0 : _k.value)) {
953
1037
  const hopCalls = (0, payload_builders_1.buildExecuteMulticall)({
954
1038
  execute: {
955
1039
  to: h.params.to,
@@ -993,7 +1077,7 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce)
993
1077
  let drainAmount = BigInt(0);
994
1078
  let tokenMintHex;
995
1079
  const params = hop.params;
996
- if (((_j = params.funds) === null || _j === void 0 ? void 0 : _j.amount) && params.funds.amount > BigInt(0)) {
1080
+ if (((_l = params.funds) === null || _l === void 0 ? void 0 : _l.amount) && params.funds.amount > BigInt(0)) {
997
1081
  drainAmount = params.funds.amount;
998
1082
  const token = params.funds.token;
999
1083
  if (token && token.address) {
@@ -1022,7 +1106,7 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce)
1022
1106
  });
1023
1107
  const svmBurnAmount = segment.totalBurnAmount || BigInt(0);
1024
1108
  const outboundReq = (0, payload_builders_1.buildOutboundRequest)(gatewayProgramHex, segment.prc20Token ||
1025
- (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), svmBurnAmount, (_k = segment.gasLimit) !== null && _k !== void 0 ? _k : BigInt(0), svmPayload, ueaAddress);
1109
+ (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), svmBurnAmount, (_m = segment.gasLimit) !== null && _m !== void 0 ? _m : BigInt(0), svmPayload, ueaAddress, (_o = segment.maxPCForGas) !== null && _o !== void 0 ? _o : BigInt(0), (_p = segment.gasPrice) !== null && _p !== void 0 ? _p : BigInt(0));
1026
1110
  const inboundGasFee = segment.gasFee || BigInt(0);
1027
1111
  let svmInboundFallback;
1028
1112
  if (effectiveUeaBalance > BigInt(0)) {
@@ -1036,7 +1120,7 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce)
1036
1120
  // SDK 5.2 R3-style Case C bump: when the inbound segment's sizing
1037
1121
  // is C, top up the swap budget by the overflow. No bridge-swap —
1038
1122
  // R3 has no destination funds delivery.
1039
- if (((_l = segment.sizing) === null || _l === void 0 ? void 0 : _l.category) === 'C' &&
1123
+ if (((_q = segment.sizing) === null || _q === void 0 ? void 0 : _q.category) === 'C' &&
1040
1124
  segment.sizing.overflowNativePc > BigInt(0)) {
1041
1125
  const bumped = svmInboundNativeValue + segment.sizing.overflowNativePc;
1042
1126
  (0, context_1.printLog)(ctx, `composeCascade — INBOUND_FROM_CEA (SVM) Case C: bumping nativeValueForGas from ${svmInboundNativeValue} to ${bumped} (overflow=${segment.sizing.overflowNativePc})`);
@@ -1056,7 +1140,8 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce)
1056
1140
  accumulatedPushMulticalls = [...outboundMulticalls];
1057
1141
  break;
1058
1142
  }
1059
- // EVM path: Build CEA multicall: [approve?, sendUniversalTxFromCEA(payload)]
1143
+ // EVM path: Build CEA multicall: sendUniversalTxToUEA(payload).
1144
+ // CEA handles ERC20 gateway approval internally.
1060
1145
  const ceaMulticalls = [];
1061
1146
  // Add primary hop's own CEA operations if any
1062
1147
  // (e.g., approve + swap before bridging back)
@@ -1072,7 +1157,7 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce)
1072
1157
  let amount = BigInt(0);
1073
1158
  for (const mergedHop of segment.hops) {
1074
1159
  const hParams = mergedHop.params;
1075
- if ((_m = hParams.funds) === null || _m === void 0 ? void 0 : _m.amount) {
1160
+ if ((_r = hParams.funds) === null || _r === void 0 ? void 0 : _r.amount) {
1076
1161
  const token = hParams.funds.token;
1077
1162
  if (token) {
1078
1163
  if (token.mechanism === 'native') {
@@ -1086,32 +1171,6 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce)
1086
1171
  }
1087
1172
  tokenAddress = addr;
1088
1173
  amount += hParams.funds.amount;
1089
- // CEA approves gateway once with the cumulative allowance.
1090
- const gatewayAddr = chain_1.UNIVERSAL_GATEWAY_ADDRESSES[sourceChain];
1091
- if (!gatewayAddr) {
1092
- throw new Error(`No UniversalGateway address configured for chain ${sourceChain}`);
1093
- }
1094
- // Reset allowance to 0 first for USDT-style tokens
1095
- const approveZeroData = (0, viem_1.encodeFunctionData)({
1096
- abi: erc20_evm_1.ERC20_EVM,
1097
- functionName: 'approve',
1098
- args: [gatewayAddr, BigInt(0)],
1099
- });
1100
- ceaMulticalls.push({
1101
- to: tokenAddress,
1102
- value: BigInt(0),
1103
- data: approveZeroData,
1104
- });
1105
- const approveData = (0, viem_1.encodeFunctionData)({
1106
- abi: erc20_evm_1.ERC20_EVM,
1107
- functionName: 'approve',
1108
- args: [gatewayAddr, hParams.funds.amount],
1109
- });
1110
- ceaMulticalls.push({
1111
- to: tokenAddress,
1112
- value: BigInt(0),
1113
- data: approveData,
1114
- });
1115
1174
  }
1116
1175
  }
1117
1176
  }
@@ -1134,7 +1193,7 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce)
1134
1193
  ? segment.gasLimit
1135
1194
  : selectors_1.DEFAULT_CEA_TO_PUSH_GAS_LIMIT;
1136
1195
  const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, segment.prc20Token ||
1137
- (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), segment.totalBurnAmount || BigInt(0), effectiveGasLimit, ceaPayload, ueaAddress);
1196
+ (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), segment.totalBurnAmount || BigInt(0), effectiveGasLimit, ceaPayload, ueaAddress, (_s = segment.maxPCForGas) !== null && _s !== void 0 ? _s : BigInt(0), (_t = segment.gasPrice) !== null && _t !== void 0 ? _t : BigInt(0));
1138
1197
  const inboundGasFee = segment.gasFee || BigInt(0);
1139
1198
  let evmInboundFallback;
1140
1199
  if (effectiveUeaBalance > BigInt(0)) {
@@ -1148,7 +1207,7 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce)
1148
1207
  // SDK 5.2 R3-style Case C bump: when the inbound segment's sizing
1149
1208
  // is C, top up the swap budget by the overflow. No bridge-swap —
1150
1209
  // R3 has no destination funds delivery.
1151
- if (((_o = segment.sizing) === null || _o === void 0 ? void 0 : _o.category) === 'C' &&
1210
+ if (((_u = segment.sizing) === null || _u === void 0 ? void 0 : _u.category) === 'C' &&
1152
1211
  segment.sizing.overflowNativePc > BigInt(0)) {
1153
1212
  const bumped = evmInboundNativeValue + segment.sizing.overflowNativePc;
1154
1213
  (0, context_1.printLog)(ctx, `composeCascade — INBOUND_FROM_CEA (EVM) Case C: bumping nativeValueForGas from ${evmInboundNativeValue} to ${bumped} (overflow=${segment.sizing.overflowNativePc})`);
@@ -1319,7 +1378,7 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks, defaultEventHook) {
1319
1378
  hops: hopInfos,
1320
1379
  hopCount: hops.length,
1321
1380
  waitForAll: (opts) => tslib_1.__awaiter(this, void 0, void 0, function* () {
1322
- var _a, _b, _c, _d, _e, _f, _g;
1381
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1323
1382
  const { pollingIntervalMs = 10000, timeout = 300000, progressHook: cascadeProgressHook, eventHook = defaultEventHook, } = opts || {};
1324
1383
  const startTime = Date.now();
1325
1384
  // Per-hop progress emission. Fans out cascade markers to BOTH the
@@ -1398,8 +1457,9 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks, defaultEventHook) {
1398
1457
  yield response.wait();
1399
1458
  const ceaToPushIndex = hopInfos.findIndex((h, index) => h.route === 'CEA_TO_PUSH' && !isNativeSeedOnlyHop(hops[index]));
1400
1459
  const isAfterCeaToPush = (index) => ceaToPushIndex >= 0 && index > ceaToPushIndex;
1401
- const trackOutboundHops = (outboundHops, pushTxHash, resolvedSubTxId) => tslib_1.__awaiter(this, void 0, void 0, function* () {
1402
- var _a, _b, _c;
1460
+ const rootOutboundIndexByHop = buildRootOutboundIndexByHop(segments, hops);
1461
+ const trackOutboundHops = (outboundHops, pushTxHash, resolvedSubTxId, outboundIndexByHop) => tslib_1.__awaiter(this, void 0, void 0, function* () {
1462
+ var _a, _b, _c, _d;
1403
1463
  if (outboundHops.length === 0)
1404
1464
  return null;
1405
1465
  if (outboundHops.length === 1) {
@@ -1434,13 +1494,11 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks, defaultEventHook) {
1434
1494
  });
1435
1495
  let lastEmittedStatus;
1436
1496
  try {
1437
- const outboundDetails = yield callbacks.waitForOutboundTxFn(pushTxHash, {
1438
- initialWaitMs: Math.min(60000, remainingTimeout),
1439
- pollingIntervalMs,
1440
- timeout: remainingTimeout,
1441
- _resolvedSubTxId: resolvedSubTxId,
1442
- _expectedDestinationChain: hop.executionChain,
1443
- progressHook: (event) => {
1497
+ const outboundDetails = yield callbacks.waitForOutboundTxFn(pushTxHash, Object.assign(Object.assign({ initialWaitMs: Math.min(60000, remainingTimeout), pollingIntervalMs, timeout: remainingTimeout, _resolvedSubTxId: resolvedSubTxId, _expectedDestinationChain: hop.executionChain }, (outboundIndexByHop
1498
+ ? {
1499
+ _outboundIndex: (_a = outboundIndexByHop.get(hop.hopIndex)) !== null && _a !== void 0 ? _a : 0,
1500
+ }
1501
+ : {})), { progressHook: (event) => {
1444
1502
  cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
1445
1503
  hopIndex: hop.hopIndex,
1446
1504
  route: hop.route,
@@ -1453,14 +1511,13 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks, defaultEventHook) {
1453
1511
  lastEmittedStatus = 'polling';
1454
1512
  emitHopEvent(hopHooks.polling(hop.executionChain, event.elapsed));
1455
1513
  }
1456
- },
1457
- });
1514
+ } }));
1458
1515
  // `outboundDetails.externalTxHash` from waitForOutboundTx
1459
1516
  // is the raw `0x`-hex form (internal canonical). For the
1460
1517
  // user-facing `hop.txHash`, `hop.outboundDetails`, and
1461
1518
  // cascade progress event, normalize to base58 for SVM so
1462
1519
  // consumers can paste it straight into a Solana explorer.
1463
- const displayTxHash = (_a = (0, helpers_1.toExternalTxHashDisplay)(outboundDetails.destinationChain, outboundDetails.externalTxHash)) !== null && _a !== void 0 ? _a : outboundDetails.externalTxHash;
1520
+ const displayTxHash = (_b = (0, helpers_1.toExternalTxHashDisplay)(outboundDetails.destinationChain, outboundDetails.externalTxHash)) !== null && _b !== void 0 ? _b : outboundDetails.externalTxHash;
1464
1521
  const userFacingOutboundDetails = Object.assign(Object.assign({}, outboundDetails), { externalTxHash: displayTxHash });
1465
1522
  hop.status = 'confirmed';
1466
1523
  hop.txHash = displayTxHash;
@@ -1549,12 +1606,12 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks, defaultEventHook) {
1549
1606
  },
1550
1607
  });
1551
1608
  if (!allOutboundDetails.success) {
1552
- const failedIdx = (_b = allOutboundDetails.failedAt) !== null && _b !== void 0 ? _b : 0;
1609
+ const failedIdx = (_c = allOutboundDetails.failedAt) !== null && _c !== void 0 ? _c : 0;
1553
1610
  const failedHop = outboundHops.find((h) => h.hopIndex === failedIdx);
1554
1611
  const failMsg = failedHop
1555
1612
  ? `Outbound failed for hop ${failedIdx} on ${failedHop.executionChain}`
1556
1613
  : `Outbound cascade failed at hop ${failedIdx}`;
1557
- emitHopEvent((0, progress_route_hooks_1.pickWaitHooks)(failedHop === null || failedHop === void 0 ? void 0 : failedHop.route).failed((_c = failedHop === null || failedHop === void 0 ? void 0 : failedHop.executionChain) !== null && _c !== void 0 ? _c : '', failMsg));
1614
+ emitHopEvent((0, progress_route_hooks_1.pickWaitHooks)(failedHop === null || failedHop === void 0 ? void 0 : failedHop.route).failed((_d = failedHop === null || failedHop === void 0 ? void 0 : failedHop.executionChain) !== null && _d !== void 0 ? _d : '', failMsg));
1558
1615
  emitCascadeFailed(failedIdx, failMsg);
1559
1616
  return {
1560
1617
  success: false,
@@ -1603,7 +1660,7 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks, defaultEventHook) {
1603
1660
  // 2. Track direct outbound hops (Route 2: UOA_TO_CEA) that belong
1604
1661
  // to the root Push UTX.
1605
1662
  const rootOutboundHops = hopInfos.filter((h) => h.route === 'UOA_TO_CEA' && !isAfterCeaToPush(h.hopIndex));
1606
- const rootOutboundFailure = yield trackOutboundHops(rootOutboundHops, response.hash);
1663
+ const rootOutboundFailure = yield trackOutboundHops(rootOutboundHops, response.hash, undefined, rootOutboundIndexByHop);
1607
1664
  if (rootOutboundFailure)
1608
1665
  return rootOutboundFailure;
1609
1666
  let childInbound;
@@ -1638,6 +1695,7 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks, defaultEventHook) {
1638
1695
  pollingIntervalMs,
1639
1696
  timeout: remainingTimeout,
1640
1697
  _expectedDestinationChain: inboundHop.executionChain,
1698
+ _outboundIndex: (_d = rootOutboundIndexByHop.get(inboundHop.hopIndex)) !== null && _d !== void 0 ? _d : 0,
1641
1699
  progressHook: (event) => {
1642
1700
  cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
1643
1701
  hopIndex: inboundHop.hopIndex,
@@ -1666,7 +1724,7 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks, defaultEventHook) {
1666
1724
  emitCascadeFailed(inboundHop.hopIndex, errMsg);
1667
1725
  inboundHop.status = 'failed';
1668
1726
  const failedExternalTxHash = err === null || err === void 0 ? void 0 : err.externalTxHash;
1669
- const failedDisplayTxHash = (_d = (0, helpers_1.toExternalTxHashDisplay)(inboundHop.executionChain, failedExternalTxHash)) !== null && _d !== void 0 ? _d : failedExternalTxHash;
1727
+ const failedDisplayTxHash = (_e = (0, helpers_1.toExternalTxHashDisplay)(inboundHop.executionChain, failedExternalTxHash)) !== null && _e !== void 0 ? _e : failedExternalTxHash;
1670
1728
  if (failedDisplayTxHash)
1671
1729
  inboundHop.txHash = failedDisplayTxHash;
1672
1730
  cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
@@ -1683,7 +1741,7 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks, defaultEventHook) {
1683
1741
  failedAt: inboundHop.hopIndex,
1684
1742
  };
1685
1743
  }
1686
- const displayExternalTxHash = (_e = (0, helpers_1.toExternalTxHashDisplay)(sourceOutboundDetails.destinationChain, sourceOutboundDetails.externalTxHash)) !== null && _e !== void 0 ? _e : sourceOutboundDetails.externalTxHash;
1744
+ const displayExternalTxHash = (_f = (0, helpers_1.toExternalTxHashDisplay)(sourceOutboundDetails.destinationChain, sourceOutboundDetails.externalTxHash)) !== null && _f !== void 0 ? _f : sourceOutboundDetails.externalTxHash;
1687
1745
  inboundHop.outboundDetails = Object.assign(Object.assign({}, sourceOutboundDetails), { externalTxHash: displayExternalTxHash });
1688
1746
  inboundHop.txHash = displayExternalTxHash;
1689
1747
  emitHopEvent(hopHooks.success(inboundHop.outboundDetails));
@@ -1691,7 +1749,7 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks, defaultEventHook) {
1691
1749
  let inboundLastStatus;
1692
1750
  const inboundRemainingTimeout = timeout - (Date.now() - startTime);
1693
1751
  try {
1694
- childInbound = yield ((_f = callbacks.waitForInboundPushTxFn) !== null && _f !== void 0 ? _f : ((externalTxHash, sourceChain, opts) => (0, inbound_tracker_1.waitForInboundPushTx)(ctx, externalTxHash, sourceChain, opts)))(sourceOutboundDetails.externalTxHash, inboundHop.executionChain, {
1752
+ childInbound = yield ((_g = callbacks.waitForInboundPushTxFn) !== null && _g !== void 0 ? _g : ((externalTxHash, sourceChain, opts) => (0, inbound_tracker_1.waitForInboundPushTx)(ctx, externalTxHash, sourceChain, opts)))(sourceOutboundDetails.externalTxHash, inboundHop.executionChain, {
1695
1753
  initialWaitMs: Math.min(60000, inboundRemainingTimeout),
1696
1754
  pollingIntervalMs,
1697
1755
  timeout: inboundRemainingTimeout,
@@ -1729,7 +1787,7 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks, defaultEventHook) {
1729
1787
  };
1730
1788
  }
1731
1789
  if (childInbound.status !== 'confirmed') {
1732
- const failMsg = (_g = childInbound.errorMessage) !== null && _g !== void 0 ? _g : 'inbound execution failed on Push Chain';
1790
+ const failMsg = (_h = childInbound.errorMessage) !== null && _h !== void 0 ? _h : 'inbound execution failed on Push Chain';
1733
1791
  inboundHop.status = 'failed';
1734
1792
  inboundHop.txHash =
1735
1793
  childInbound.pushTxHash || displayExternalTxHash;
@@ -29,7 +29,7 @@ const formatters_1 = require("../../formatters");
29
29
  */
30
30
  class PushChainExecutionError extends Error {
31
31
  constructor(message, opts = {}) {
32
- super(message);
32
+ super((0, formatters_1.normalizePcInsufficientFundsError)(message));
33
33
  this.code = 'PUSH_CHAIN_EXECUTION_FAILED';
34
34
  this.name = 'PushChainExecutionError';
35
35
  this.gatewayTxHash = opts.gatewayTxHash;
@@ -29,6 +29,7 @@ const push_chain_tx_1 = require("./push-chain-tx");
29
29
  const svm_helpers_1 = require("./svm-helpers");
30
30
  const helpers_2 = require("./helpers");
31
31
  const response_builder_2 = require("./response-builder");
32
+ const formatters_1 = require("../../formatters");
32
33
  function executeFundsOnly(ctx, execute, eventBuffer, getResponseCallbacks) {
33
34
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
34
35
  const transformFn = (tx, buf = []) => (0, response_builder_2.transformToUniversalTxResponse)(ctx, tx, buf, getResponseCallbacks());
@@ -139,7 +140,7 @@ function executeFundsOnlyEvm(ctx, execute, eventBuffer, transformFn, chain, rpcU
139
140
  }
140
141
  catch (err) {
141
142
  if (!(err instanceof push_chain_tx_1.PushChainExecutionError)) {
142
- const errMsg = err instanceof Error ? err.message : String(err);
143
+ const errMsg = (0, formatters_1.normalizePcInsufficientFundsError)(err instanceof Error ? err.message : String(err));
143
144
  (0, context_2.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_199_02, errMsg);
144
145
  throw new push_chain_tx_1.PushChainExecutionError(errMsg, { gatewayTxHash: txHash });
145
146
  }
@@ -230,7 +231,7 @@ function executeFundsOnlySvm(ctx, execute, eventBuffer, transformFn, chain, rpcU
230
231
  }
231
232
  catch (err) {
232
233
  if (!(err instanceof push_chain_tx_1.PushChainExecutionError)) {
233
- const errMsg = err instanceof Error ? err.message : String(err);
234
+ const errMsg = (0, formatters_1.normalizePcInsufficientFundsError)(err instanceof Error ? err.message : String(err));
234
235
  (0, context_2.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_199_02, errMsg);
235
236
  throw new push_chain_tx_1.PushChainExecutionError(errMsg, { gatewayTxHash: txSignature });
236
237
  }
@@ -32,6 +32,7 @@ const push_chain_tx_1 = require("./push-chain-tx");
32
32
  const svm_bridge_1 = require("./svm-bridge");
33
33
  const signing_1 = require("./signing");
34
34
  const quote_1 = require("./quote");
35
+ const formatters_1 = require("../../formatters");
35
36
  function executeFundsWithPayload(ctx, execute, eventBuffer, getResponseCallbacks) {
36
37
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
37
38
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
@@ -240,7 +241,7 @@ function executeFundsWithPayload(ctx, execute, eventBuffer, getResponseCallbacks
240
241
  }
241
242
  catch (err) {
242
243
  if (!(err instanceof push_chain_tx_1.PushChainExecutionError)) {
243
- const errMsg = err instanceof Error ? err.message : String(err);
244
+ const errMsg = (0, formatters_1.normalizePcInsufficientFundsError)(err instanceof Error ? err.message : String(err));
244
245
  (0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_199_02, errMsg);
245
246
  throw new push_chain_tx_1.PushChainExecutionError(errMsg, { gatewayTxHash: txHash });
246
247
  }