@pushchain/core 5.1.4 → 5.1.5-alpha.1

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 (28) hide show
  1. package/package.json +1 -1
  2. package/src/lib/index.d.ts +1 -1
  3. package/src/lib/orchestrator/internals/cascade.js +68 -25
  4. package/src/lib/orchestrator/internals/execute-funds-payload.js +4 -4
  5. package/src/lib/orchestrator/internals/execute-standard.js +55 -7
  6. package/src/lib/orchestrator/internals/gas-calculator.d.ts +10 -0
  7. package/src/lib/orchestrator/internals/gas-calculator.js +95 -3
  8. package/src/lib/orchestrator/internals/gateway-client.js +6 -6
  9. package/src/lib/orchestrator/internals/response-builder.js +24 -1
  10. package/src/lib/orchestrator/internals/route-handlers.js +27 -43
  11. package/src/lib/orchestrator/orchestrator.types.d.ts +13 -23
  12. package/src/lib/orchestrator/route-detector.js +0 -3
  13. package/src/lib/orchestrator/svm-idl/build-payload.d.ts +11 -0
  14. package/src/lib/orchestrator/svm-idl/build-payload.js +36 -0
  15. package/src/lib/orchestrator/svm-idl/ix-encoder.d.ts +3 -0
  16. package/src/lib/orchestrator/svm-idl/ix-encoder.js +47 -0
  17. package/src/lib/orchestrator/svm-idl/registry.d.ts +10 -0
  18. package/src/lib/orchestrator/svm-idl/registry.js +36 -0
  19. package/src/lib/orchestrator/svm-idl/resolve.d.ts +14 -0
  20. package/src/lib/orchestrator/svm-idl/resolve.js +85 -0
  21. package/src/lib/progress-hook/progress-hook.js +40 -0
  22. package/src/lib/progress-hook/progress-hook.types.d.ts +5 -0
  23. package/src/lib/progress-hook/progress-hook.types.js +6 -0
  24. package/src/lib/push-chain/push-chain.js +14 -4
  25. package/src/lib/universal/account/account.d.ts +13 -0
  26. package/src/lib/universal/account/account.js +1 -0
  27. package/src/lib/utils.d.ts +20 -1
  28. package/src/lib/utils.js +28 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushchain/core",
3
- "version": "5.1.4",
3
+ "version": "5.1.5-alpha.1",
4
4
  "homepage": "https://push.org",
5
5
  "keywords": [
6
6
  "pushchain",
@@ -1,7 +1,7 @@
1
1
  import { PushChain } from './push-chain/push-chain';
2
2
  export type { ConversionQuote } from './constants/tokens';
3
3
  export type { UniversalSigner, UniversalAccount, UniversalSignerSkeleton, DerivedExecutorAccount, ResolvedAccount, ResolvedControllerAccounts, AccountType, AccountRole, } from './universal/universal.types';
4
- export type { ChainTarget, ChainSource, UniversalTo, TransactionRouteType, UniversalExecuteParams, UniversalOutboundTxRequest, PreparedUniversalTx, CascadedTxResponse, CascadeHopInfo, CascadeTrackOptions, CascadeProgressEvent, CascadeCompletionResult, MultiChainTxResponse, OutboundTxDetails, WaitForOutboundOptions, SvmGatewayAccountMeta, SvmExecutePayloadFields, SvmExecuteParams, AccountStatus, UEAStatus, RescueFundsParams, } from './orchestrator/orchestrator.types';
4
+ export type { ChainTarget, ChainSource, UniversalTo, TransactionRouteType, UniversalExecuteParams, UniversalOutboundTxRequest, PreparedUniversalTx, CascadedTxResponse, CascadeHopInfo, CascadeTrackOptions, CascadeProgressEvent, CascadeCompletionResult, MultiChainTxResponse, OutboundTxDetails, WaitForOutboundOptions, SvmGatewayAccountMeta, SvmExecutePayloadFields, AccountStatus, UEAStatus, RescueFundsParams, } from './orchestrator/orchestrator.types';
5
5
  export { parseUEAVersion } from './orchestrator/orchestrator.types';
6
6
  export { TransactionRoute, detectRoute, isChainTarget } from './orchestrator/route-detector';
7
7
  export { getCEAAddress, getPushAccountForCEA, isCEA, chainSupportsCEA, chainSupportsOutbound, getCEAFactoryAddress, getAllCEAAddresses, } from './orchestrator/cea-utils';
@@ -27,6 +27,9 @@ const payload_builder_1 = require("./payload-builder");
27
27
  const uea_manager_1 = require("./uea-manager");
28
28
  const gas_calculator_1 = require("./gas-calculator");
29
29
  const route_handlers_1 = require("./route-handlers");
30
+ const build_payload_1 = require("../svm-idl/build-payload");
31
+ const progress_hook_types_1 = require("../../progress-hook/progress-hook.types");
32
+ const progress_hook_1 = tslib_1.__importDefault(require("../../progress-hook/progress-hook"));
30
33
  // ============================================================================
31
34
  // prepareTransaction
32
35
  // ============================================================================
@@ -110,18 +113,12 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
110
113
  const targetChain = target.chain;
111
114
  // Branch: SVM vs EVM
112
115
  if ((0, payload_builders_1.isSvmChain)(targetChain)) {
113
- // SVM path: no CEA lookup, build SVM payload
114
- const hasSvmExecute = !!params.svmExecute;
115
- let svmPayload = '0x';
116
- if (hasSvmExecute) {
117
- const exec = params.svmExecute;
118
- svmPayload = (0, payload_builders_1.encodeSvmExecutePayload)({
119
- targetProgram: exec.targetProgram,
120
- accounts: exec.accounts,
121
- ixData: exec.ixData,
122
- instructionId: 2,
123
- });
124
- }
116
+ // SVM path: no CEA lookup, build SVM payload via IDL resolver
117
+ const { svmPayload, hasExecute: hasSvmExecute } = (0, build_payload_1.buildSvmPayloadFromParams)({
118
+ data: params.data,
119
+ to: target,
120
+ senderUea: ueaAddress,
121
+ });
125
122
  let prc20Token = selectors_1.ZERO_ADDRESS;
126
123
  let burnAmount = BigInt(0);
127
124
  if ((_b = params.funds) === null || _b === void 0 ? void 0 : _b.amount) {
@@ -432,7 +429,7 @@ function getSegmentType(route) {
432
429
  // composeCascade
433
430
  // ============================================================================
434
431
  function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
435
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
432
+ var _a, _b, _c, _d, _e, _f, _g;
436
433
  let accumulatedPushMulticalls = [];
437
434
  const gatewayPcAddress = (0, helpers_1.getUniversalGatewayPCAddress)();
438
435
  // Compute per-outbound nativeValueForGas from UEA balance
@@ -465,10 +462,9 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
465
462
  if (isSvmSegment) {
466
463
  // SVM: use the pre-built SVM payload from the hop descriptor
467
464
  outboundPayload = (_a = firstHop.svmPayload) !== null && _a !== void 0 ? _a : '0x';
468
- // For SVM, target is the recipient/program from the hop params
465
+ // For SVM, to.address IS the target (program for execute, recipient for withdraw)
469
466
  const svmTarget = firstHop.params.to;
470
- targetForOutbound =
471
- (_c = (_b = firstHop.params.svmExecute) === null || _b === void 0 ? void 0 : _b.targetProgram) !== null && _c !== void 0 ? _c : svmTarget.address;
467
+ targetForOutbound = svmTarget.address;
472
468
  }
473
469
  else if (isMigration) {
474
470
  // Migration: use raw 4-byte MIGRATION_SELECTOR, no multicall wrapping
@@ -482,7 +478,7 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
482
478
  targetForOutbound = (firstHop === null || firstHop === void 0 ? void 0 : firstHop.ceaAddress) || ueaAddress;
483
479
  }
484
480
  // Build outbound request
485
- const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetForOutbound, segment.prc20Token || selectors_1.ZERO_ADDRESS, segment.totalBurnAmount || BigInt(0), (_d = segment.gasLimit) !== null && _d !== void 0 ? _d : BigInt(0), outboundPayload, ueaAddress);
481
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetForOutbound, segment.prc20Token || selectors_1.ZERO_ADDRESS, segment.totalBurnAmount || BigInt(0), (_b = segment.gasLimit) !== null && _b !== void 0 ? _b : BigInt(0), outboundPayload, ueaAddress);
486
482
  // Build approval + outbound multicalls
487
483
  const segGasFee = segment.gasFee || BigInt(0);
488
484
  const outboundMulticalls = (0, payload_builders_1.buildOutboundApprovalAndCall)({
@@ -507,7 +503,7 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
507
503
  // Build push multicalls from this hop's own data (e.g., counter.increment())
508
504
  // This is the Route 3 hop's payload that executes on Push Chain after inbound.
509
505
  const hop0 = segment.hops[0];
510
- if ((_e = hop0 === null || hop0 === void 0 ? void 0 : hop0.params) === null || _e === void 0 ? void 0 : _e.data) {
506
+ if ((_c = hop0 === null || hop0 === void 0 ? void 0 : hop0.params) === null || _c === void 0 ? void 0 : _c.data) {
511
507
  const hopPushMulticalls = (0, payload_builders_1.buildExecuteMulticall)({
512
508
  execute: {
513
509
  to: hop0.params.to,
@@ -543,7 +539,7 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
543
539
  let drainAmount = BigInt(0);
544
540
  let tokenMintHex;
545
541
  const params = hop.params;
546
- if (((_f = params.funds) === null || _f === void 0 ? void 0 : _f.amount) && params.funds.amount > BigInt(0)) {
542
+ if (((_d = params.funds) === null || _d === void 0 ? void 0 : _d.amount) && params.funds.amount > BigInt(0)) {
547
543
  drainAmount = params.funds.amount;
548
544
  const token = params.funds.token;
549
545
  if (token && token.address) {
@@ -572,7 +568,7 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
572
568
  });
573
569
  const svmBurnAmount = segment.totalBurnAmount || BigInt(0);
574
570
  const outboundReq = (0, payload_builders_1.buildOutboundRequest)(gatewayProgramHex, segment.prc20Token ||
575
- (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), svmBurnAmount, (_g = segment.gasLimit) !== null && _g !== void 0 ? _g : BigInt(0), svmPayload, ueaAddress);
571
+ (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), svmBurnAmount, (_e = segment.gasLimit) !== null && _e !== void 0 ? _e : BigInt(0), svmPayload, ueaAddress);
576
572
  const inboundGasFee = segment.gasFee || BigInt(0);
577
573
  const outboundMulticalls = (0, payload_builders_1.buildOutboundApprovalAndCall)({
578
574
  prc20Token: segment.prc20Token ||
@@ -598,7 +594,7 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
598
594
  let tokenAddress = selectors_1.ZERO_ADDRESS;
599
595
  let amount = BigInt(0);
600
596
  const params = hop.params;
601
- if ((_h = params.funds) === null || _h === void 0 ? void 0 : _h.amount) {
597
+ if ((_f = params.funds) === null || _f === void 0 ? void 0 : _f.amount) {
602
598
  const token = params.funds.token;
603
599
  if (token) {
604
600
  if (token.mechanism === 'native') {
@@ -645,7 +641,7 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
645
641
  // Wrap CEA multicall in outbound from Push Chain
646
642
  const ceaPayload = (0, payload_builders_1.buildCeaMulticallPayload)(ceaMulticalls);
647
643
  const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, segment.prc20Token ||
648
- (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), segment.totalBurnAmount || BigInt(0), (_j = segment.gasLimit) !== null && _j !== void 0 ? _j : BigInt(0), ceaPayload, ueaAddress);
644
+ (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), segment.totalBurnAmount || BigInt(0), (_g = segment.gasLimit) !== null && _g !== void 0 ? _g : BigInt(0), ceaPayload, ueaAddress);
649
645
  const inboundGasFee = segment.gasFee || BigInt(0);
650
646
  const outboundMulticalls = (0, payload_builders_1.buildOutboundApprovalAndCall)({
651
647
  prc20Token: segment.prc20Token ||
@@ -781,8 +777,8 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks) {
781
777
  hops: hopInfos,
782
778
  hopCount: hops.length,
783
779
  waitForAll: (opts) => tslib_1.__awaiter(this, void 0, void 0, function* () {
784
- var _a;
785
- const { pollingIntervalMs = 10000, timeout = 300000, progressHook: cascadeProgressHook, } = opts || {};
780
+ var _a, _b;
781
+ const { pollingIntervalMs = 10000, timeout = 300000, progressHook: cascadeProgressHook, eventHook, } = opts || {};
786
782
  const startTime = Date.now();
787
783
  try {
788
784
  // 1. Wait for initial Push Chain tx confirmation
@@ -847,12 +843,14 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks) {
847
843
  status: 'timeout',
848
844
  elapsed: Date.now() - startTime,
849
845
  });
846
+ eventHook === null || eventHook === void 0 ? void 0 : eventHook(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_04](Date.now() - startTime));
850
847
  return {
851
848
  success: false,
852
849
  hops: hopInfos,
853
850
  failedAt: hop.hopIndex,
854
851
  };
855
852
  }
853
+ eventHook === null || eventHook === void 0 ? void 0 : eventHook(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_08_01](hop.executionChain));
856
854
  cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
857
855
  hopIndex: hop.hopIndex,
858
856
  route: hop.route,
@@ -860,6 +858,7 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks) {
860
858
  status: 'polling',
861
859
  elapsed: Date.now() - startTime,
862
860
  });
861
+ let lastEmittedStatus;
863
862
  try {
864
863
  const outboundDetails = yield callbacks.waitForOutboundTxFn(response.hash, {
865
864
  initialWaitMs: Math.min(60000, remainingTimeout),
@@ -873,11 +872,17 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks) {
873
872
  status: event.status,
874
873
  elapsed: Date.now() - startTime,
875
874
  });
875
+ if (event.status === 'polling' &&
876
+ lastEmittedStatus !== 'polling') {
877
+ lastEmittedStatus = 'polling';
878
+ eventHook === null || eventHook === void 0 ? void 0 : eventHook(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_08_02](event.elapsed));
879
+ }
876
880
  },
877
881
  });
878
882
  hop.status = 'confirmed';
879
883
  hop.txHash = outboundDetails.externalTxHash;
880
884
  hop.outboundDetails = outboundDetails;
885
+ eventHook === null || eventHook === void 0 ? void 0 : eventHook(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_03](outboundDetails));
881
886
  cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
882
887
  hopIndex: hop.hopIndex,
883
888
  route: hop.route,
@@ -888,6 +893,11 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks) {
888
893
  });
889
894
  }
890
895
  catch (err) {
896
+ const errMsg = err instanceof Error ? err.message : String(err);
897
+ const isTimeout = errMsg.startsWith('Timeout waiting for outbound transaction');
898
+ eventHook === null || eventHook === void 0 ? void 0 : eventHook(isTimeout
899
+ ? progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_04](remainingTimeout)
900
+ : progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_05](errMsg));
891
901
  hop.status = 'failed';
892
902
  cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
893
903
  hopIndex: hop.hopIndex,
@@ -905,6 +915,13 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks) {
905
915
  }
906
916
  else {
907
917
  // Multiple outbound hops: use V2 API which returns outboundTx[]
918
+ // Emit "Awaiting External Chain" once per outbound hop before
919
+ // polling begins.
920
+ for (const outHop of outboundHops) {
921
+ eventHook === null || eventHook === void 0 ? void 0 : eventHook(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_08_01](outHop.executionChain));
922
+ }
923
+ const perHopLastStatus = new Map();
924
+ const confirmedHops = new Set();
908
925
  const allOutboundDetails = yield callbacks.waitForAllOutboundTxsFn(response.hash, outboundHops, {
909
926
  initialWaitMs: Math.min(60000, timeout - (Date.now() - startTime)),
910
927
  pollingIntervalMs,
@@ -918,13 +935,39 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks) {
918
935
  txHash: event.txHash,
919
936
  elapsed: Date.now() - startTime,
920
937
  });
938
+ const prev = perHopLastStatus.get(event.hopIndex);
939
+ if (event.status === 'polling' &&
940
+ prev !== 'polling') {
941
+ perHopLastStatus.set(event.hopIndex, 'polling');
942
+ eventHook === null || eventHook === void 0 ? void 0 : eventHook(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_08_02](Date.now() - startTime));
943
+ }
944
+ if (event.status === 'confirmed' &&
945
+ event.txHash &&
946
+ !confirmedHops.has(event.hopIndex)) {
947
+ // waitForAllOutboundTxsV2 populates hop.outboundDetails
948
+ // on the matched hop before emitting 'confirmed' (see
949
+ // outbound-sync.ts:348-355). Auto-promotion paths
950
+ // without a txHash are skipped by the `event.txHash`
951
+ // guard above, so we only reach here with full details.
952
+ const matchedHop = outboundHops.find((h) => h.hopIndex === event.hopIndex);
953
+ if (matchedHop === null || matchedHop === void 0 ? void 0 : matchedHop.outboundDetails) {
954
+ confirmedHops.add(event.hopIndex);
955
+ eventHook === null || eventHook === void 0 ? void 0 : eventHook(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_03](matchedHop.outboundDetails));
956
+ }
957
+ }
921
958
  },
922
959
  });
923
960
  if (!allOutboundDetails.success) {
961
+ const failedIdx = (_b = allOutboundDetails.failedAt) !== null && _b !== void 0 ? _b : 0;
962
+ const failedHop = outboundHops.find((h) => h.hopIndex === failedIdx);
963
+ const failMsg = failedHop
964
+ ? `Outbound failed for hop ${failedIdx} on ${failedHop.executionChain}`
965
+ : `Outbound cascade failed at hop ${failedIdx}`;
966
+ eventHook === null || eventHook === void 0 ? void 0 : eventHook(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_05](failMsg));
924
967
  return {
925
968
  success: false,
926
969
  hops: hopInfos,
927
- failedAt: allOutboundDetails.failedAt,
970
+ failedAt: failedIdx,
928
971
  };
929
972
  }
930
973
  }
@@ -74,15 +74,15 @@ function executeFundsWithPayload(ctx, execute, eventBuffer, getResponseCallbacks
74
74
  const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
75
75
  const ueaBalance = yield ctx.pushClient.getBalance(ueaAddress);
76
76
  (0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_03_02, ueaAddress, deployed);
77
- // Determine USD to deposit via gateway (8 decimals) with caps: min=$1, max=$10
77
+ // Determine USD to deposit via gateway (8 decimals) with caps: min=$1, max=$1000
78
78
  const oneUsd = utils_1.Utils.helpers.parseUnits('1', 8);
79
- const tenUsd = utils_1.Utils.helpers.parseUnits('10', 8);
79
+ const maxUsd = utils_1.Utils.helpers.parseUnits('1000', 8);
80
80
  const deficit = requiredFunds > ueaBalance ? requiredFunds - ueaBalance : BigInt(0);
81
81
  let depositUsd = deficit > BigInt(0) ? ctx.pushClient.pushToUSDC(deficit) : oneUsd;
82
82
  if (depositUsd < oneUsd)
83
83
  depositUsd = oneUsd;
84
- if (depositUsd > tenUsd)
85
- throw new Error('Deposit value exceeds max $10 worth of native token');
84
+ if (depositUsd > maxUsd)
85
+ throw new Error('Deposit value exceeds max $1000 worth of native token');
86
86
  (0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_02_02, depositUsd);
87
87
  // If SVM, clamp depositUsd to on-chain Config caps
88
88
  if (chain_1.CHAIN_INFO[chain].vm === enums_1.VM.SVM) {
@@ -200,7 +200,34 @@ function executeStandardPayload(ctx, execute, eventBuffer, getResponseCallbacks)
200
200
  const fundDifference = requiredFunds - funds;
201
201
  const fixedPushAmount = utils_1.Utils.helpers.parseUnits('0.001', 18);
202
202
  const lockAmount = funds < requiredFunds ? fundDifference : fixedPushAmount;
203
- const lockAmountInUSD = ctx.pushClient.pushToUSDC(lockAmount);
203
+ // Size the USD deposit against the REAL Uniswap pool rate (not the fixed
204
+ // $0.10/PC SDK rate) for EVM origins. The pool typically prices PC much
205
+ // higher than $0.10, so pushToUSDC under-provisions and the resulting
206
+ // deposit gets clamped up to the $1 minimum, producing near-zero WPC.
207
+ // When the quoted USD exceeds the $1000 cap the later clamp kicks in
208
+ // (pool limit surfaces via the pre-flight capacity message).
209
+ // Falls back to pushToUSDC if the quoter is unavailable.
210
+ const { vm: sizingOriginVm } = chain_1.CHAIN_INFO[chain];
211
+ let lockAmountInUSD = ctx.pushClient.pushToUSDC(lockAmount);
212
+ if (sizingOriginVm === enums_1.VM.EVM && lockAmount > BigInt(0)) {
213
+ // Target lockAmount × 1.2 so that the 10% slippage margin applied by
214
+ // the pre-flight check (safeUPC = predictedUPC × 0.9) leaves enough
215
+ // headroom even when Uniswap V3 exactOutput↔exactInput quotes aren't
216
+ // perfectly symmetric (tick-math rounding).
217
+ const targetWPC = (lockAmount * BigInt(12)) / BigInt(10);
218
+ const originPrc20ForSizing = (0, helpers_2.getNativePRC20ForChain)(chain, ctx.pushNetwork);
219
+ const requiredNative = yield (0, gas_calculator_1.estimateNativeForDesiredDeposit)(ctx, targetWPC, originPrc20ForSizing);
220
+ if (requiredNative > BigInt(0)) {
221
+ const ethPriceForSizing = yield new price_fetch_1.PriceFetch(ctx.rpcUrls).getPrice(chain);
222
+ if (ethPriceForSizing > BigInt(0)) {
223
+ // requiredNative (18-dec wei) × ethPrice (8-dec USD/ETH) / 1e18 = USD in 8-dec
224
+ const poolBasedUsd = (requiredNative * ethPriceForSizing) / BigInt(1e18);
225
+ (0, context_1.printLog)(ctx, `Fee-lock sizing: pushToUSDC=${lockAmountInUSD.toString()} (fixed $0.10/PC), ` +
226
+ `poolBased=${poolBasedUsd.toString()} (pool quote for ${targetWPC.toString()} WPC) — using poolBased`);
227
+ lockAmountInUSD = poolBasedUsd;
228
+ }
229
+ }
230
+ }
204
231
  // Apply minimum deposit override (e.g., Route 2 needs enough UPC for outbound swap)
205
232
  const effectiveLockAmountInUSD = execute._minimumDepositUsd && execute._minimumDepositUsd > lockAmountInUSD
206
233
  ? execute._minimumDepositUsd
@@ -212,12 +239,12 @@ function executeStandardPayload(ctx, execute, eventBuffer, getResponseCallbacks)
212
239
  const { vm: originVm } = chain_1.CHAIN_INFO[chain];
213
240
  if (originVm === enums_1.VM.EVM) {
214
241
  try {
215
- // Replicate lockFee's USD → native conversion (with its $1–$10 clamp)
242
+ // Replicate lockFee's USD → native conversion (with its $1–$1000 clamp)
216
243
  const oneUsd = utils_1.Utils.helpers.parseUnits('1', 8);
217
- const tenUsd = utils_1.Utils.helpers.parseUnits('10', 8);
244
+ const maxUsd = utils_1.Utils.helpers.parseUnits('1000', 8);
218
245
  let depositUsd = effectiveLockAmountInUSD < oneUsd ? oneUsd : effectiveLockAmountInUSD;
219
- if (depositUsd > tenUsd)
220
- depositUsd = tenUsd;
246
+ if (depositUsd > maxUsd)
247
+ depositUsd = maxUsd;
221
248
  const ethPrice = yield new price_fetch_1.PriceFetch(ctx.rpcUrls).getPrice(chain);
222
249
  if (ethPrice > BigInt(0)) {
223
250
  const nativeAmountETH = (depositUsd * BigInt(1e18) + (ethPrice - BigInt(1))) / ethPrice + BigInt(1);
@@ -233,11 +260,32 @@ function executeStandardPayload(ctx, execute, eventBuffer, getResponseCallbacks)
233
260
  `total: ${totalAvailable.toString()}, required: ${requiredFunds.toString()}`);
234
261
  if (totalAvailable < requiredFunds) {
235
262
  const shortfall = requiredFunds - totalAvailable;
263
+ // On failure only: probe pool capacity at the max cap so the
264
+ // caller gets a readable "max transferable right now" number.
265
+ // One extra quoter call — not run on the success path.
266
+ let maxTransferableNote = '';
267
+ if (depositUsd < maxUsd) {
268
+ const maxNativeAmountETH = (maxUsd * BigInt(1e18) + (ethPrice - BigInt(1))) / ethPrice + BigInt(1);
269
+ const maxPredictedUPC = yield (0, gas_calculator_1.estimateDepositFromLockedNative)(ctx, maxNativeAmountETH, originPrc20);
270
+ if (maxPredictedUPC > BigInt(0)) {
271
+ const maxSafeUPC = (maxPredictedUPC * BigInt(90)) / BigInt(100);
272
+ const gasReserve = utils_1.Utils.helpers.parseUnits('0.01', 18);
273
+ const maxFromDeposit = maxSafeUPC > gasReserve ? maxSafeUPC - gasReserve : BigInt(0);
274
+ const maxTransferable = funds + maxFromDeposit;
275
+ maxTransferableNote =
276
+ `Max transferable right now (at $1000 cap, pool-limited): ` +
277
+ `~${(Number(maxTransferable) / 1e18).toFixed(4)} $PC ` +
278
+ `(UEA balance ${(Number(funds) / 1e18).toFixed(4)} + ` +
279
+ `pool-safe deposit ${(Number(maxFromDeposit) / 1e18).toFixed(4)} − gas).`;
280
+ (0, context_1.printLog)(ctx, `Fee-lock capacity: ${maxTransferableNote}`);
281
+ }
282
+ }
236
283
  throw new Error(`Insufficient deposit: the fee-lock will deposit ~${(Number(safeUPC) / 1e18).toFixed(4)} $PC ` +
237
284
  `but the transaction requires ~${(Number(requiredFunds) / 1e18).toFixed(4)} $PC ` +
238
285
  `(shortfall: ~${(Number(shortfall) / 1e18).toFixed(4)} $PC). ` +
239
- `The fee-lock deposit is capped at $10 USD. Reduce the transfer value or ` +
240
- `pre-fund the UEA (${UEA}) on Push Chain.`);
286
+ `The fee-lock deposit is capped at $1000 USD. ` +
287
+ (maxTransferableNote ? maxTransferableNote + ' ' : '') +
288
+ `Reduce the transfer value or pre-fund the UEA (${UEA}) on Push Chain.`);
241
289
  }
242
290
  }
243
291
  }
@@ -45,3 +45,13 @@ export declare function calculateGasAmountFromAmountOutMinETH(ctx: OrchestratorC
45
45
  * Returns 0 on any failure (caller should fall back to other logic).
46
46
  */
47
47
  export declare function estimateDepositFromLockedNative(ctx: OrchestratorContext, nativeAmountForLocker: bigint, prc20Token: `0x${string}`): Promise<bigint>;
48
+ /**
49
+ * Inverse of estimateDepositFromLockedNative: given a desired WPC output,
50
+ * asks the Push Chain Uniswap V3 pool how much native PRC-20 (pETH) input is
51
+ * required to produce exactly that output via QuoterV2.quoteExactOutputSingle.
52
+ *
53
+ * Used to size fee-lock deposits against the real on-chain pool rate instead
54
+ * of the fixed $0.10/PC SDK rate. Returns 0 on any failure (caller should
55
+ * fall back to the fixed-rate sizing).
56
+ */
57
+ export declare function estimateNativeForDesiredDeposit(ctx: OrchestratorContext, desiredWpc: bigint, prc20Token: `0x${string}`): Promise<bigint>;
@@ -11,6 +11,7 @@ exports.queryRescueGasFee = queryRescueGasFee;
11
11
  exports.calculateNativeAmountForDeposit = calculateNativeAmountForDeposit;
12
12
  exports.calculateGasAmountFromAmountOutMinETH = calculateGasAmountFromAmountOutMinETH;
13
13
  exports.estimateDepositFromLockedNative = estimateDepositFromLockedNative;
14
+ exports.estimateNativeForDesiredDeposit = estimateNativeForDesiredDeposit;
14
15
  const tslib_1 = require("tslib");
15
16
  const web3_js_1 = require("@solana/web3.js");
16
17
  const viem_1 = require("viem");
@@ -336,13 +337,13 @@ function calculateNativeAmountForDeposit(ctx, chain, requiredFunds, ueaBalance)
336
337
  var _a, _b;
337
338
  (0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_02_01);
338
339
  const oneUsd = utils_1.Utils.helpers.parseUnits('1', 8);
339
- const tenUsd = utils_1.Utils.helpers.parseUnits('10', 8);
340
+ const maxUsd = utils_1.Utils.helpers.parseUnits('1000', 8);
340
341
  const deficit = requiredFunds > ueaBalance ? requiredFunds - ueaBalance : BigInt(0);
341
342
  let depositUsd = deficit > BigInt(0) ? ctx.pushClient.pushToUSDC(deficit) : oneUsd;
342
343
  if (depositUsd < oneUsd)
343
344
  depositUsd = oneUsd;
344
- if (depositUsd > tenUsd)
345
- throw new Error('Deposit value exceeds max $10 worth of native token');
345
+ if (depositUsd > maxUsd)
346
+ throw new Error('Deposit value exceeds max $1000 worth of native token');
346
347
  if (chain_1.CHAIN_INFO[chain].vm === enums_1.VM.SVM) {
347
348
  const svmClient = new svm_client_1.SvmClient({
348
349
  rpcUrls: ctx.rpcUrls[enums_1.CHAIN.SOLANA_DEVNET] ||
@@ -515,4 +516,95 @@ function estimateDepositFromLockedNative(ctx, nativeAmountForLocker, prc20Token)
515
516
  }
516
517
  });
517
518
  }
519
+ /**
520
+ * Inverse of estimateDepositFromLockedNative: given a desired WPC output,
521
+ * asks the Push Chain Uniswap V3 pool how much native PRC-20 (pETH) input is
522
+ * required to produce exactly that output via QuoterV2.quoteExactOutputSingle.
523
+ *
524
+ * Used to size fee-lock deposits against the real on-chain pool rate instead
525
+ * of the fixed $0.10/PC SDK rate. Returns 0 on any failure (caller should
526
+ * fall back to the fixed-rate sizing).
527
+ */
528
+ function estimateNativeForDesiredDeposit(ctx, desiredWpc, prc20Token) {
529
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
530
+ var _a, _b;
531
+ try {
532
+ if (desiredWpc <= BigInt(0))
533
+ return BigInt(0);
534
+ const pushChain = (0, helpers_1.getPushChainForNetwork)(ctx.pushNetwork);
535
+ const quoterAddress = (_b = (_a = chain_1.CHAIN_INFO[pushChain]) === null || _a === void 0 ? void 0 : _a.dex) === null || _b === void 0 ? void 0 : _b.uniV3QuoterV2;
536
+ if (!quoterAddress || quoterAddress.startsWith('0xTBD'))
537
+ return BigInt(0);
538
+ const gatewayPcAddress = (0, helpers_1.getUniversalGatewayPCAddress)();
539
+ const universalCoreAddress = yield ctx.pushClient.readContract({
540
+ address: gatewayPcAddress,
541
+ abi: abi_1.UNIVERSAL_GATEWAY_PC,
542
+ functionName: 'UNIVERSAL_CORE',
543
+ args: [],
544
+ });
545
+ const [wpcAddress, feeTier] = yield Promise.all([
546
+ ctx.pushClient.readContract({
547
+ address: universalCoreAddress,
548
+ abi: UNIVERSAL_CORE_SWAP_HELPERS_ABI,
549
+ functionName: 'WPC',
550
+ args: [],
551
+ }),
552
+ ctx.pushClient.readContract({
553
+ address: universalCoreAddress,
554
+ abi: UNIVERSAL_CORE_SWAP_HELPERS_ABI,
555
+ functionName: 'defaultFeeTier',
556
+ args: [prc20Token],
557
+ }),
558
+ ]);
559
+ if (!feeTier || !wpcAddress)
560
+ return BigInt(0);
561
+ const QUOTER_ABI = [
562
+ {
563
+ type: 'function',
564
+ name: 'quoteExactOutputSingle',
565
+ inputs: [
566
+ {
567
+ type: 'tuple',
568
+ components: [
569
+ { name: 'tokenIn', type: 'address' },
570
+ { name: 'tokenOut', type: 'address' },
571
+ { name: 'amount', type: 'uint256' },
572
+ { name: 'fee', type: 'uint24' },
573
+ { name: 'sqrtPriceLimitX96', type: 'uint160' },
574
+ ],
575
+ },
576
+ ],
577
+ outputs: [
578
+ { name: 'amountIn', type: 'uint256' },
579
+ { name: 'sqrtPriceX96After', type: 'uint160' },
580
+ { name: 'initializedTicksCrossed', type: 'uint32' },
581
+ { name: 'gasEstimate', type: 'uint256' },
582
+ ],
583
+ stateMutability: 'view',
584
+ },
585
+ ];
586
+ const result = yield ctx.pushClient.readContract({
587
+ address: quoterAddress,
588
+ abi: QUOTER_ABI,
589
+ functionName: 'quoteExactOutputSingle',
590
+ args: [
591
+ {
592
+ tokenIn: prc20Token,
593
+ tokenOut: wpcAddress,
594
+ amount: desiredWpc,
595
+ fee: feeTier,
596
+ sqrtPriceLimitX96: BigInt(0),
597
+ },
598
+ ],
599
+ });
600
+ const amountIn = result[0];
601
+ (0, context_1.printLog)(ctx, `estimateNativeForDesiredDeposit — quote: ${desiredWpc.toString()} WPC ← ${amountIn.toString()} pETH (fee tier: ${feeTier})`);
602
+ return amountIn;
603
+ }
604
+ catch (err) {
605
+ (0, context_1.printLog)(ctx, `estimateNativeForDesiredDeposit — failed: ${err instanceof Error ? err.message : String(err)}`);
606
+ return BigInt(0);
607
+ }
608
+ });
609
+ }
518
610
  //# sourceMappingURL=gas-calculator.js.map
@@ -96,10 +96,10 @@ req) {
96
96
  const nativeTokenUsdPrice = yield new price_fetch_1.PriceFetch(ctx.rpcUrls).getPrice(chain);
97
97
  const nativeDecimals = 18;
98
98
  const oneUsd = utils_1.Utils.helpers.parseUnits('1', 8);
99
- const tenUsd = utils_1.Utils.helpers.parseUnits('10', 8);
99
+ const maxUsd = utils_1.Utils.helpers.parseUnits('1000', 8);
100
100
  let depositUsd = amount < oneUsd ? oneUsd : amount;
101
- if (depositUsd > tenUsd)
102
- depositUsd = tenUsd;
101
+ if (depositUsd > maxUsd)
102
+ depositUsd = maxUsd;
103
103
  let nativeAmount = (depositUsd * BigInt(Math.pow(10, nativeDecimals)) +
104
104
  (nativeTokenUsdPrice - BigInt(1))) /
105
105
  nativeTokenUsdPrice;
@@ -114,10 +114,10 @@ req) {
114
114
  ]);
115
115
  const nativeDecimals = 9;
116
116
  const oneUsd = utils_1.Utils.helpers.parseUnits('1', 8);
117
- const tenUsd = utils_1.Utils.helpers.parseUnits('10', 8);
117
+ const maxUsd = utils_1.Utils.helpers.parseUnits('1000', 8);
118
118
  let depositUsd = amount < oneUsd ? oneUsd : amount;
119
- if (depositUsd > tenUsd)
120
- depositUsd = tenUsd;
119
+ if (depositUsd > maxUsd)
120
+ depositUsd = maxUsd;
121
121
  let nativeAmount = (depositUsd * BigInt(Math.pow(10, nativeDecimals)) +
122
122
  (nativeTokenUsdPrice - BigInt(1))) /
123
123
  nativeTokenUsdPrice;
@@ -16,6 +16,8 @@ const viem_1 = require("viem");
16
16
  const uea_evm_1 = require("../../constants/abi/uea.evm");
17
17
  const chain_1 = require("../../constants/chain");
18
18
  const enums_1 = require("../../constants/enums");
19
+ const progress_hook_types_1 = require("../../progress-hook/progress-hook.types");
20
+ const progress_hook_1 = tslib_1.__importDefault(require("../../progress-hook/progress-hook"));
19
21
  const account_1 = require("../../universal/account/account");
20
22
  const push_client_1 = require("../../push-client/push-client");
21
23
  const route_detector_1 = require("../route-detector");
@@ -472,19 +474,40 @@ function transformToUniversalTxResponse(ctx_1, tx_1) {
472
474
  ? (0, route_detector_1.getRouteInfo)(universalTxResponse.route)
473
475
  : undefined;
474
476
  if (routeInfo === null || routeInfo === void 0 ? void 0 : routeInfo.isOutbound) {
477
+ // Response-level only — NOT routed through ctx.progressHook because
478
+ // the default ui-kit toast lacks icon/hide-timer handling for these
479
+ // events. A follow-up ui-kit PR will add handling and can route
480
+ // through both hooks.
481
+ registeredProgressHook === null || registeredProgressHook === void 0 ? void 0 : registeredProgressHook(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_08_01]());
482
+ let lastEmittedStatus;
483
+ const outboundTranslator = (event) => {
484
+ if (!registeredProgressHook)
485
+ return;
486
+ if (event.status === 'polling' && lastEmittedStatus !== 'polling') {
487
+ lastEmittedStatus = 'polling';
488
+ registeredProgressHook(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_08_02](event.elapsed));
489
+ }
490
+ };
475
491
  try {
476
492
  const outboundDetails = yield callbacks.waitForOutboundTx(tx.hash, {
477
493
  initialWaitMs: callbacks.outboundConstants.initialWaitMs,
478
494
  pollingIntervalMs: callbacks.outboundConstants.pollingIntervalMs,
479
495
  timeout: callbacks.outboundConstants.maxTimeoutMs,
496
+ progressHook: outboundTranslator,
480
497
  });
498
+ registeredProgressHook === null || registeredProgressHook === void 0 ? void 0 : registeredProgressHook(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_03](outboundDetails));
481
499
  // Merge external chain details into receipt
482
500
  baseReceipt = Object.assign(Object.assign({}, baseReceipt), { externalTxHash: outboundDetails.externalTxHash, externalChain: outboundDetails.destinationChain, externalExplorerUrl: outboundDetails.explorerUrl, externalRecipient: outboundDetails.recipient, externalAmount: outboundDetails.amount, externalAssetAddr: outboundDetails.assetAddr });
483
501
  }
484
502
  catch (error) {
503
+ const errMsg = error instanceof Error ? error.message : String(error);
504
+ const isTimeout = errMsg.startsWith('Timeout waiting for outbound transaction');
505
+ registeredProgressHook === null || registeredProgressHook === void 0 ? void 0 : registeredProgressHook(isTimeout
506
+ ? progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_04](callbacks.outboundConstants.maxTimeoutMs)
507
+ : progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_05](errMsg));
485
508
  // Outbound polling timed out - return partial receipt (don't throw)
486
509
  // Push Chain tx succeeded, external tracking can be retried later
487
- callbacks.printLog(`[wait] External chain tracking failed: ${error instanceof Error ? error.message : String(error)}`);
510
+ callbacks.printLog(`[wait] External chain tracking failed: ${errMsg}`);
488
511
  }
489
512
  }
490
513
  return baseReceipt;