@pushchain/core 5.1.4-alpha.1 → 5.1.4-alpha.10

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 (30) hide show
  1. package/package.json +4 -4
  2. package/src/lib/constants/abi/factoryV1.d.ts +59 -76
  3. package/src/lib/constants/abi/factoryV1.js +0 -517
  4. package/src/lib/constants/abi/feeLocker.evm.d.ts +15 -50
  5. package/src/lib/constants/abi/feeLocker.evm.js +0 -385
  6. package/src/lib/constants/abi/index.d.ts +1 -1
  7. package/src/lib/constants/abi/index.js +1 -2
  8. package/src/lib/constants/abi/prc20.evm.d.ts +0 -53
  9. package/src/lib/constants/abi/prc20.evm.js +1 -42
  10. package/src/lib/constants/abi/uea.svm.d.ts +21 -99
  11. package/src/lib/constants/abi/uea.svm.js +0 -299
  12. package/src/lib/constants/abi/universalGatewayV0.evm.d.ts +45 -104
  13. package/src/lib/constants/abi/universalGatewayV0.evm.js +0 -2149
  14. package/src/lib/index.d.ts +1 -1
  15. package/src/lib/orchestrator/internals/cascade.d.ts +4 -2
  16. package/src/lib/orchestrator/internals/cascade.js +34 -18
  17. package/src/lib/orchestrator/internals/execute-standard.js +5 -1
  18. package/src/lib/orchestrator/internals/gas-calculator.d.ts +9 -0
  19. package/src/lib/orchestrator/internals/gas-calculator.js +94 -0
  20. package/src/lib/orchestrator/internals/route-handlers.js +47 -38
  21. package/src/lib/orchestrator/orchestrator.d.ts +5 -3
  22. package/src/lib/orchestrator/orchestrator.js +2 -12
  23. package/src/lib/orchestrator/orchestrator.types.d.ts +8 -15
  24. package/src/lib/progress-hook/progress-hook.js +1 -1
  25. package/src/lib/push-chain/push-chain.d.ts +5 -6
  26. package/src/lib/push-chain/push-chain.js +4 -3
  27. package/src/lib/universal/account/account.d.ts +16 -8
  28. package/src/lib/universal/account/account.js +118 -73
  29. package/src/lib/universal/signer/signer.js +1 -1
  30. package/src/lib/universal/universal.types.d.ts +2 -2
@@ -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, CascadedTransactionBuilder, 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, SvmExecuteParams, 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';
@@ -5,7 +5,7 @@
5
5
  * getSegmentType, composeCascade, createCascadedBuilder, createChainedBuilder.
6
6
  */
7
7
  import { TransactionRoute } from '../route-detector';
8
- import type { HopDescriptor, CascadeSegment, CascadeSegmentType, PreparedUniversalTx, CascadedTransactionBuilder, CascadeHopInfo, UniversalTxResponse, UniversalExecuteParams, MultiCall, WaitForOutboundOptions, OutboundTxDetails } from '../orchestrator.types';
8
+ import type { HopDescriptor, CascadeSegment, CascadeSegmentType, PreparedUniversalTx, CascadedTxResponse, CascadeHopInfo, UniversalTxResponse, UniversalExecuteParams, MultiCall, WaitForOutboundOptions, OutboundTxDetails } from '../orchestrator.types';
9
9
  import type { OrchestratorContext } from './context';
10
10
  export interface CascadeCallbacks {
11
11
  executeFn: (params: any) => Promise<UniversalTxResponse>;
@@ -20,4 +20,6 @@ export declare function buildHopDescriptor(ctx: OrchestratorContext, params: Uni
20
20
  export declare function classifyIntoSegments(hops: HopDescriptor[]): CascadeSegment[];
21
21
  export declare function getSegmentType(route: string): CascadeSegmentType;
22
22
  export declare function composeCascade(ctx: OrchestratorContext, segments: CascadeSegment[], ueaAddress: `0x${string}`, ueaBalance?: bigint, ueaNonce?: bigint): MultiCall[];
23
- export declare function createCascadedBuilder(ctx: OrchestratorContext, preparedTxs: PreparedUniversalTx[], callbacks: CascadeCallbacks): CascadedTransactionBuilder;
23
+ export declare function createCascadedBuilder(ctx: OrchestratorContext, preparedTxs: PreparedUniversalTx[], callbacks: CascadeCallbacks): {
24
+ send: () => Promise<CascadedTxResponse>;
25
+ };
@@ -37,16 +37,28 @@ function prepareTransaction(ctx, params, callbacks) {
37
37
  clientChain: ctx.universalSigner.account.chain,
38
38
  });
39
39
  const route = (0, route_detector_1.detectRoute)(params);
40
- // Skip getCode if accountStatusCache already confirmed deployment
40
+ // Push native EOA: block Route 1 — use sendTransaction() for direct Push calls
41
+ const signerChain = ctx.universalSigner.account.chain;
42
+ const isPushNativeEOA = (0, helpers_1.isPushChain)(signerChain);
43
+ if (isPushNativeEOA && route === route_detector_1.TransactionRoute.UOA_TO_PUSH) {
44
+ throw new Error('Push native accounts cannot use prepareTransaction for Push Chain transactions. ' +
45
+ 'Use sendTransaction() instead for direct Push Chain calls.');
46
+ }
47
+ // Fetch UEA nonce (skip for Push native EOA — uses EVM nonce, not UEA contract nonce)
41
48
  let nonce;
42
- const deployedHintCascade = (_b = (_a = ctx.accountStatusCache) === null || _a === void 0 ? void 0 : _a.uea) === null || _b === void 0 ? void 0 : _b.deployed;
43
49
  const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
44
- if (deployedHintCascade) {
45
- nonce = yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress);
50
+ if (isPushNativeEOA) {
51
+ nonce = BigInt(0);
46
52
  }
47
53
  else {
48
- const status = yield (0, uea_manager_1.getUeaStatusAndNonce)(ctx);
49
- nonce = status.nonce;
54
+ const deployedHintCascade = (_b = (_a = ctx.accountStatusCache) === null || _a === void 0 ? void 0 : _a.uea) === null || _b === void 0 ? void 0 : _b.deployed;
55
+ if (deployedHintCascade) {
56
+ nonce = yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress);
57
+ }
58
+ else {
59
+ const status = yield (0, uea_manager_1.getUeaStatusAndNonce)(ctx);
60
+ nonce = status.nonce;
61
+ }
50
62
  }
51
63
  // Build the payload based on route
52
64
  const { payload, gatewayRequest } = yield (0, route_handlers_1.buildPayloadForRoute)(ctx, params, route, nonce);
@@ -62,8 +74,6 @@ function prepareTransaction(ctx, params, callbacks) {
62
74
  nonce,
63
75
  deadline,
64
76
  _hop: hop,
65
- thenOn: (nextTx) => createCascadedBuilder(ctx, [prepared, nextTx], callbacks),
66
- send: () => callbacks.executeFn(params),
67
77
  };
68
78
  return prepared;
69
79
  });
@@ -222,8 +232,13 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
222
232
  burnAmount = params.value;
223
233
  }
224
234
  else if (params.data) {
235
+ // PAYLOAD-only (no value transfer): still need native PRC-20 token for
236
+ // chain namespace lookup + gas fees, but burn amount must be zero.
237
+ // Matches the direct executeUoaToCea path (route-handlers.ts).
238
+ // Using burnAmount=1 would trigger transferFrom on the PRC-20 token,
239
+ // which reverts when the UEA has no PRC-20 balance for that chain.
225
240
  prc20Token = (0, helpers_1.getNativePRC20ForChain)(targetChain, ctx.pushNetwork);
226
- burnAmount = BigInt(1); // Minimum for precompile
241
+ burnAmount = BigInt(0);
227
242
  }
228
243
  // Query gas fee
229
244
  let gasToken = selectors_1.ZERO_ADDRESS;
@@ -285,10 +300,12 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
285
300
  gasToken = result.gasToken;
286
301
  gasFee = result.gasFee;
287
302
  }
288
- return Object.assign(Object.assign({}, baseDescriptor), { sourceChain, ceaAddress: ceaPdaHex, isSvmTarget: true, prc20Token, burnAmount: amount > BigInt(0) ? amount : BigInt(1), gasToken,
303
+ return Object.assign(Object.assign({}, baseDescriptor), { sourceChain, ceaAddress: ceaPdaHex, isSvmTarget: true, prc20Token, burnAmount: amount, gasToken,
289
304
  gasFee });
290
305
  }
291
- const { cea: ceaAddress, isDeployed } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, sourceChain, (_h = ctx.rpcUrls[sourceChain]) === null || _h === void 0 ? void 0 : _h[0]);
306
+ const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, sourceChain, (_h = ctx.rpcUrls[sourceChain]) === null || _h === void 0 ? void 0 : _h[0]);
307
+ // CEA auto-deploys on-chain: Vault.finalizeUniversalTx calls CEAFactory.deployCEA()
308
+ // if CEA doesn't exist yet. No SDK-side blocking needed.
292
309
  // Determine token/amount for the inbound
293
310
  let tokenAddress = selectors_1.ZERO_ADDRESS;
294
311
  let amount = BigInt(0);
@@ -321,7 +338,7 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
321
338
  }
322
339
  return Object.assign(Object.assign({}, baseDescriptor), { sourceChain,
323
340
  ceaAddress,
324
- prc20Token, burnAmount: amount > BigInt(0) ? amount : BigInt(1), gasToken,
341
+ prc20Token, burnAmount: amount, gasToken,
325
342
  gasFee });
326
343
  }
327
344
  default:
@@ -553,15 +570,15 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
553
570
  ? new Uint8Array(Buffer.from(intermediatePayload.slice(2), 'hex'))
554
571
  : undefined,
555
572
  });
556
- const burnAmount = BigInt(1);
573
+ const svmBurnAmount = segment.totalBurnAmount || BigInt(0);
557
574
  const outboundReq = (0, payload_builders_1.buildOutboundRequest)(gatewayProgramHex, segment.prc20Token ||
558
- (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), burnAmount, (_g = segment.gasLimit) !== null && _g !== void 0 ? _g : BigInt(0), svmPayload, ueaAddress);
575
+ (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), svmBurnAmount, (_g = segment.gasLimit) !== null && _g !== void 0 ? _g : BigInt(0), svmPayload, ueaAddress);
559
576
  const inboundGasFee = segment.gasFee || BigInt(0);
560
577
  const outboundMulticalls = (0, payload_builders_1.buildOutboundApprovalAndCall)({
561
578
  prc20Token: segment.prc20Token ||
562
579
  (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork),
563
580
  gasToken: segment.gasToken || selectors_1.ZERO_ADDRESS,
564
- burnAmount,
581
+ burnAmount: svmBurnAmount,
565
582
  gasFee: inboundGasFee,
566
583
  nativeValueForGas: perOutboundNativeValue !== null && perOutboundNativeValue !== void 0 ? perOutboundNativeValue : inboundGasFee * BigInt(1000),
567
584
  gatewayPcAddress,
@@ -628,13 +645,13 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
628
645
  // Wrap CEA multicall in outbound from Push Chain
629
646
  const ceaPayload = (0, payload_builders_1.buildCeaMulticallPayload)(ceaMulticalls);
630
647
  const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, segment.prc20Token ||
631
- (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), segment.totalBurnAmount || BigInt(1), (_j = segment.gasLimit) !== null && _j !== void 0 ? _j : BigInt(0), ceaPayload, ueaAddress);
648
+ (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), segment.totalBurnAmount || BigInt(0), (_j = segment.gasLimit) !== null && _j !== void 0 ? _j : BigInt(0), ceaPayload, ueaAddress);
632
649
  const inboundGasFee = segment.gasFee || BigInt(0);
633
650
  const outboundMulticalls = (0, payload_builders_1.buildOutboundApprovalAndCall)({
634
651
  prc20Token: segment.prc20Token ||
635
652
  (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork),
636
653
  gasToken: segment.gasToken || selectors_1.ZERO_ADDRESS,
637
- burnAmount: segment.totalBurnAmount || BigInt(1),
654
+ burnAmount: segment.totalBurnAmount || BigInt(0),
638
655
  gasFee: inboundGasFee,
639
656
  nativeValueForGas: perOutboundNativeValue !== null && perOutboundNativeValue !== void 0 ? perOutboundNativeValue : inboundGasFee * BigInt(1000),
640
657
  gatewayPcAddress,
@@ -653,7 +670,6 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
653
670
  // ============================================================================
654
671
  function createCascadedBuilder(ctx, preparedTxs, callbacks) {
655
672
  return {
656
- thenOn: (nextTx) => createCascadedBuilder(ctx, [...preparedTxs, nextTx], callbacks),
657
673
  send: () => tslib_1.__awaiter(this, void 0, void 0, function* () {
658
674
  const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
659
675
  // Extract HopDescriptors
@@ -198,7 +198,11 @@ function executeStandardPayload(ctx, execute, eventBuffer, getResponseCallbacks)
198
198
  const fixedPushAmount = utils_1.Utils.helpers.parseUnits('0.001', 18);
199
199
  const lockAmount = funds < requiredFunds ? fundDifference : fixedPushAmount;
200
200
  const lockAmountInUSD = ctx.pushClient.pushToUSDC(lockAmount);
201
- const feeLockTxHashBytes = yield (0, gateway_client_1.lockFee)(ctx, lockAmountInUSD, universalPayload, req);
201
+ // Apply minimum deposit override (e.g., Route 2 needs enough UPC for outbound swap)
202
+ const effectiveLockAmountInUSD = execute._minimumDepositUsd && execute._minimumDepositUsd > lockAmountInUSD
203
+ ? execute._minimumDepositUsd
204
+ : lockAmountInUSD;
205
+ const feeLockTxHashBytes = yield (0, gateway_client_1.lockFee)(ctx, effectiveLockAmountInUSD, universalPayload, req);
202
206
  feeLockTxHash = (0, viem_1.bytesToHex)(feeLockTxHashBytes);
203
207
  verificationData = (0, viem_1.bytesToHex)(feeLockTxHashBytes);
204
208
  const { vm } = chain_1.CHAIN_INFO[chain];
@@ -36,3 +36,12 @@ export declare function calculateGasAmountFromAmountOutMinETH(ctx: OrchestratorC
36
36
  }) => Promise<ConversionQuote>): Promise<{
37
37
  gasAmount: bigint;
38
38
  }>;
39
+ /**
40
+ * Predicts how much UPC (WPC) the chain will deposit to the UEA from a given
41
+ * amount of locked native token (e.g., ETH on Sepolia). The chain converts
42
+ * locked ETH → pETH (PRC-20) → WPC via Uniswap V3 swap on Push Chain.
43
+ *
44
+ * Queries the same Uniswap V3 QuoterV2 the chain uses in execute_inbound_gas.go.
45
+ * Returns 0 on any failure (caller should fall back to other logic).
46
+ */
47
+ export declare function estimateDepositFromLockedNative(ctx: OrchestratorContext, nativeAmountForLocker: bigint, prc20Token: `0x${string}`): Promise<bigint>;
@@ -10,6 +10,7 @@ exports.estimateNativeValueForSwap = estimateNativeValueForSwap;
10
10
  exports.queryRescueGasFee = queryRescueGasFee;
11
11
  exports.calculateNativeAmountForDeposit = calculateNativeAmountForDeposit;
12
12
  exports.calculateGasAmountFromAmountOutMinETH = calculateGasAmountFromAmountOutMinETH;
13
+ exports.estimateDepositFromLockedNative = estimateDepositFromLockedNative;
13
14
  const tslib_1 = require("tslib");
14
15
  const web3_js_1 = require("@solana/web3.js");
15
16
  const viem_1 = require("viem");
@@ -421,4 +422,97 @@ function calculateGasAmountFromAmountOutMinETH(ctx, gasTokenAddress, amountOutMi
421
422
  return { gasAmount };
422
423
  });
423
424
  }
425
+ // ============================================================================
426
+ // Estimate UPC deposit from locked ETH via Uniswap V3 quote
427
+ // ============================================================================
428
+ /**
429
+ * Predicts how much UPC (WPC) the chain will deposit to the UEA from a given
430
+ * amount of locked native token (e.g., ETH on Sepolia). The chain converts
431
+ * locked ETH → pETH (PRC-20) → WPC via Uniswap V3 swap on Push Chain.
432
+ *
433
+ * Queries the same Uniswap V3 QuoterV2 the chain uses in execute_inbound_gas.go.
434
+ * Returns 0 on any failure (caller should fall back to other logic).
435
+ */
436
+ function estimateDepositFromLockedNative(ctx, nativeAmountForLocker, prc20Token) {
437
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
438
+ var _a, _b;
439
+ try {
440
+ const pushChain = (0, helpers_1.getPushChainForNetwork)(ctx.pushNetwork);
441
+ 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;
442
+ if (!quoterAddress || quoterAddress.startsWith('0xTBD'))
443
+ return BigInt(0);
444
+ // Get UniversalCore → WPC address + fee tier (reuses existing ABI)
445
+ const gatewayPcAddress = (0, helpers_1.getUniversalGatewayPCAddress)();
446
+ const universalCoreAddress = yield ctx.pushClient.readContract({
447
+ address: gatewayPcAddress,
448
+ abi: abi_1.UNIVERSAL_GATEWAY_PC,
449
+ functionName: 'UNIVERSAL_CORE',
450
+ args: [],
451
+ });
452
+ const [wpcAddress, feeTier] = yield Promise.all([
453
+ ctx.pushClient.readContract({
454
+ address: universalCoreAddress,
455
+ abi: UNIVERSAL_CORE_SWAP_HELPERS_ABI,
456
+ functionName: 'WPC',
457
+ args: [],
458
+ }),
459
+ ctx.pushClient.readContract({
460
+ address: universalCoreAddress,
461
+ abi: UNIVERSAL_CORE_SWAP_HELPERS_ABI,
462
+ functionName: 'defaultFeeTier',
463
+ args: [prc20Token],
464
+ }),
465
+ ]);
466
+ if (!feeTier || !wpcAddress)
467
+ return BigInt(0);
468
+ // QuoterV2.quoteExactInputSingle — same call the chain relay makes
469
+ const QUOTER_ABI = [
470
+ {
471
+ type: 'function',
472
+ name: 'quoteExactInputSingle',
473
+ inputs: [
474
+ {
475
+ type: 'tuple',
476
+ components: [
477
+ { name: 'tokenIn', type: 'address' },
478
+ { name: 'tokenOut', type: 'address' },
479
+ { name: 'amountIn', type: 'uint256' },
480
+ { name: 'fee', type: 'uint24' },
481
+ { name: 'sqrtPriceLimitX96', type: 'uint160' },
482
+ ],
483
+ },
484
+ ],
485
+ outputs: [
486
+ { name: 'amountOut', type: 'uint256' },
487
+ { name: 'sqrtPriceX96After', type: 'uint160' },
488
+ { name: 'initializedTicksCrossed', type: 'uint32' },
489
+ { name: 'gasEstimate', type: 'uint256' },
490
+ ],
491
+ stateMutability: 'view',
492
+ },
493
+ ];
494
+ const result = yield ctx.pushClient.readContract({
495
+ address: quoterAddress,
496
+ abi: QUOTER_ABI,
497
+ functionName: 'quoteExactInputSingle',
498
+ args: [
499
+ {
500
+ tokenIn: prc20Token,
501
+ tokenOut: wpcAddress,
502
+ amountIn: nativeAmountForLocker,
503
+ fee: feeTier,
504
+ sqrtPriceLimitX96: BigInt(0),
505
+ },
506
+ ],
507
+ });
508
+ const amountOut = result[0];
509
+ (0, context_1.printLog)(ctx, `estimateDepositFromLockedNative — quote: ${nativeAmountForLocker.toString()} pETH → ${amountOut.toString()} WPC (fee tier: ${feeTier})`);
510
+ return amountOut;
511
+ }
512
+ catch (err) {
513
+ (0, context_1.printLog)(ctx, `estimateDepositFromLockedNative — failed: ${err instanceof Error ? err.message : String(err)}`);
514
+ return BigInt(0);
515
+ }
516
+ });
517
+ }
424
518
  //# sourceMappingURL=gas-calculator.js.map
@@ -31,6 +31,8 @@ const payload_builder_1 = require("./payload-builder");
31
31
  const uea_manager_1 = require("./uea-manager");
32
32
  const gas_calculator_1 = require("./gas-calculator");
33
33
  const chain_1 = require("../../constants/chain");
34
+ const utils_1 = require("../../utils");
35
+ const price_fetch_1 = require("../../price-fetch/price-fetch");
34
36
  const route_detector_1 = require("../route-detector");
35
37
  const cea_utils_1 = require("../cea-utils");
36
38
  const payload_builders_1 = require("../payload-builders");
@@ -255,11 +257,7 @@ function executeUoaToCea(ctx, params, executeFn) {
255
257
  ctx.pushClient.getBalance(ueaAddress),
256
258
  ]);
257
259
  const isUEADeployed = isNativePushEOA || ueaCode !== undefined;
258
- if (!isUEADeployed) {
259
- throw new Error('UEA is not deployed. Please send an inbound transaction to Push Chain first ' +
260
- '(e.g. sendTransaction with value) to deploy your Universal Execution Account before using outbound transfers.');
261
- }
262
- const ueaNonce = isNativePushEOA ? BigInt(0) : yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress);
260
+ const ueaNonce = (!isUEADeployed || isNativePushEOA) ? BigInt(0) : yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress);
263
261
  // Build the multicall that will execute ON Push Chain from UEA context
264
262
  // This includes: 1) approve PRC-20 (if needed), 2) call sendUniversalTxOutbound
265
263
  const pushChainMulticalls = [];
@@ -267,41 +265,62 @@ function executeUoaToCea(ctx, params, executeFn) {
267
265
  let gasFee = BigInt(0);
268
266
  let nativeValueForGas = BigInt(0);
269
267
  let gasToken = selectors_1.ZERO_ADDRESS;
268
+ let universalCoreAddress;
270
269
  if (prc20Token !== selectors_1.ZERO_ADDRESS) {
271
270
  try {
272
271
  const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, outboundReq.gasLimit);
273
272
  gasFee = result.gasFee;
274
273
  gasToken = result.gasToken;
275
274
  nativeValueForGas = result.nativeValueForGas;
275
+ universalCoreAddress = result.universalCoreAddress;
276
276
  (0, context_1.printLog)(ctx, `executeUoaToCea — queried gas fee: ${gasFee.toString()}, gasToken: ${gasToken}, nativeValueForGas: ${nativeValueForGas.toString()}`);
277
277
  }
278
278
  catch (err) {
279
279
  throw new Error(`Failed to query outbound gas fee: ${err}`);
280
280
  }
281
281
  }
282
- // Adjust nativeValueForGas: the 1Mx multiplier from queryOutboundGasFee produces
283
- // a value far too low for the actual WPC/gasToken swap price. Set it to 200 UPC
284
- // (capped by balance) — enough for the swap, but avoids draining thin pools.
285
- // The contract's swapAndBurnGas does exactOutputSingle and refunds excess PC.
282
+ // Balance-aware adjustment: MAXIMIZE nativeValueForGas within available balance.
283
+ // The contract's swapAndBurnGas does exactOutputSingle and refunds excess PC,
284
+ // so overshooting within balance is safe. Undershooting causes the swap to revert.
286
285
  const EVM_NATIVE_VALUE_TARGET = BigInt(200e18); // 200 UPC
287
286
  const EVM_GAS_RESERVE = BigInt(3e18); // 3 UPC for tx overhead
288
- // Reuse ueaBalance fetched earlier (line 383) — no tx sent between, balance is stable
289
- const currentBalance = ueaBalance;
287
+ const ROUTE2_MINIMUM_DEPOSIT_USD = utils_1.Utils.helpers.parseUnits('10', 8); // $10
288
+ // Determine effective balance for the adjustment.
289
+ // For deployed UEAs: use real balance.
290
+ // For fresh wallets: predict the post-fee-locking balance by querying the same
291
+ // Uniswap V3 quoter the chain uses (pETH → WPC swap in execute_inbound_gas.go).
292
+ let effectiveBalance = ueaBalance;
293
+ if (!isUEADeployed && effectiveBalance <= EVM_GAS_RESERVE) {
294
+ // Calculate the ETH amount lockFee will send (same formula as lockFee)
295
+ const ethPrice = yield new price_fetch_1.PriceFetch(ctx.rpcUrls).getPrice(signerChain);
296
+ const nativeAmountETH = ethPrice > BigInt(0)
297
+ ? (ROUTE2_MINIMUM_DEPOSIT_USD * BigInt(1e18) + (ethPrice - BigInt(1))) / ethPrice + BigInt(1)
298
+ : BigInt(0);
299
+ if (nativeAmountETH > BigInt(0)) {
300
+ // Query the real swap quote: how much WPC (UPC) for this pETH amount?
301
+ const originPrc20 = (0, helpers_1.getNativePRC20ForChain)(signerChain, ctx.pushNetwork);
302
+ const predictedUPC = yield (0, gas_calculator_1.estimateDepositFromLockedNative)(ctx, nativeAmountETH, originPrc20);
303
+ if (predictedUPC > BigInt(0)) {
304
+ // 10% safety margin for slippage between quote and actual execution
305
+ effectiveBalance = (predictedUPC * BigInt(90)) / BigInt(100);
306
+ (0, context_1.printLog)(ctx, `executeUoaToCea — fresh wallet: Uniswap quote predicts ${predictedUPC.toString()} UPC ` +
307
+ `from ${nativeAmountETH.toString()} wei pETH, using ${effectiveBalance.toString()} (90%)`);
308
+ }
309
+ }
310
+ }
290
311
  let adjustedValue;
291
- if (currentBalance > EVM_NATIVE_VALUE_TARGET + EVM_GAS_RESERVE) {
292
- // Enough balance: use 200 UPC target
312
+ if (effectiveBalance > EVM_NATIVE_VALUE_TARGET + EVM_GAS_RESERVE) {
293
313
  adjustedValue = EVM_NATIVE_VALUE_TARGET;
294
314
  }
295
- else if (currentBalance > EVM_GAS_RESERVE) {
296
- // Low balance: use what's available minus reserve
297
- adjustedValue = currentBalance - EVM_GAS_RESERVE;
315
+ else if (effectiveBalance > EVM_GAS_RESERVE) {
316
+ adjustedValue = effectiveBalance - EVM_GAS_RESERVE;
298
317
  }
299
318
  else {
300
- // Very low balance: use original query value as-is
301
319
  adjustedValue = nativeValueForGas;
302
320
  }
303
321
  if (adjustedValue !== nativeValueForGas) {
304
- (0, context_1.printLog)(ctx, `executeUoaToCea — adjusting nativeValueForGas from ${nativeValueForGas.toString()} to ${adjustedValue.toString()} (UEA balance: ${currentBalance.toString()})`);
322
+ (0, context_1.printLog)(ctx, `executeUoaToCea — adjusting nativeValueForGas from ${nativeValueForGas.toString()} ` +
323
+ `to ${adjustedValue.toString()} (effective balance: ${effectiveBalance.toString()})`);
305
324
  nativeValueForGas = adjustedValue;
306
325
  }
307
326
  // Build outbound multicalls (approve burn + sendUniversalTxOutbound with native value)
@@ -345,17 +364,11 @@ function executeUoaToCea(ctx, params, executeFn) {
345
364
  // This handles fee-locking on origin chain and executes the multicall from UEA context
346
365
  // Sum native values from multicall entries for proper fee calculation
347
366
  const multicallNativeValue = pushChainMulticalls.reduce((sum, mc) => { var _a; return sum + ((_a = mc.value) !== null && _a !== void 0 ? _a : BigInt(0)); }, BigInt(0));
348
- const executeParams = {
349
- to: ueaAddress, // multicall executes from UEA
350
- value: multicallNativeValue, // ensures correct requiredFunds calculation
351
- data: pushChainMulticalls, // array triggers multicall mode
352
- _ueaStatus: {
367
+ const executeParams = Object.assign({ to: ueaAddress, value: multicallNativeValue, data: pushChainMulticalls, _ueaStatus: {
353
368
  isDeployed: isUEADeployed,
354
369
  nonce: ueaNonce,
355
370
  balance: ueaBalance,
356
- },
357
- _skipFeeLocking: true, // outbound executes on Push Chain, no external fee locking
358
- };
371
+ }, _skipFeeLocking: isUEADeployed }, (!isUEADeployed ? { _minimumDepositUsd: ROUTE2_MINIMUM_DEPOSIT_USD } : {}));
359
372
  const response = yield executeFn(executeParams);
360
373
  // Add chain info to response
361
374
  response.chain = targetChain;
@@ -463,11 +476,7 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
463
476
  isUEADeployed = ueaCode !== undefined;
464
477
  ueaBalance = balance;
465
478
  }
466
- if (!isUEADeployed) {
467
- throw new Error('UEA is not deployed. Please send an inbound transaction to Push Chain first ' +
468
- '(e.g. sendTransaction with value) to deploy your Universal Execution Account before using outbound transfers.');
469
- }
470
- const ueaNonce = isNativePushEOASvm ? BigInt(0) : yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress);
479
+ const ueaNonce = (!isUEADeployed || isNativePushEOASvm) ? BigInt(0) : yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress);
471
480
  // --- Query gas fee (identical to EVM path) ---
472
481
  let gasFee = BigInt(0);
473
482
  let gasToken = selectors_1.ZERO_ADDRESS;
@@ -531,7 +540,7 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
531
540
  nonce: ueaNonce,
532
541
  balance: ueaBalance,
533
542
  },
534
- _skipFeeLocking: true, // outbound executes on Push Chain, no external fee locking
543
+ _skipFeeLocking: isUEADeployed, // skip fee-locking only if UEA is already deployed
535
544
  };
536
545
  const response = yield executeFn(executeParams);
537
546
  // Add chain info to response
@@ -577,10 +586,8 @@ function executeCeaToPush(ctx, params, executeFn) {
577
586
  // 4. Get CEA address on source chain
578
587
  const { cea: ceaAddress, isDeployed: ceaDeployed } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, sourceChain, (_b = ctx.rpcUrls[sourceChain]) === null || _b === void 0 ? void 0 : _b[0]);
579
588
  (0, context_1.printLog)(ctx, `executeCeaToPush — sourceChain: ${sourceChain}, CEA: ${ceaAddress}, deployed: ${ceaDeployed}`);
580
- if (!ceaDeployed) {
581
- throw new Error(`CEA not deployed on ${sourceChain}. ` +
582
- `Deploy CEA first using Route 2 (UOA -> CEA) before using Route 3.`);
583
- }
589
+ // CEA auto-deploys on-chain: Vault.finalizeUniversalTx calls CEAFactory.deployCEA()
590
+ // if CEA doesn't exist yet. No SDK-side blocking needed.
584
591
  // 5. Get UniversalGateway address on source chain
585
592
  const gatewayAddress = chain_1.UNIVERSAL_GATEWAY_ADDRESSES[sourceChain];
586
593
  if (!gatewayAddress) {
@@ -617,7 +624,7 @@ function executeCeaToPush(ctx, params, executeFn) {
617
624
  // external chain), but that approach is racy: the balance can change between the SDK
618
625
  // query and relay execution, causing sendUniversalTxToUEA to revert with
619
626
  // InsufficientBalance. Pre-existing CEA funds remain parked and can be swept separately.
620
- let bridgeAmount = amount;
627
+ const bridgeAmount = amount;
621
628
  // Note: CEA contract may reject amount=0 in sendUniversalTxToUEA.
622
629
  // Keeping bridgeAmount as-is (0) for payload-only to test precompile behavior.
623
630
  // For ERC20 tokens, add approve call for the bridge amount
@@ -943,7 +950,7 @@ function executeCeaToCea(ctx, params, executeFn) {
943
950
  // CEA -> CEA requires chaining Route 3 (CEA -> Push) and Route 2 (Push -> CEA)
944
951
  // This is a complex flow that requires coordination
945
952
  throw new Error('CEA -> CEA transactions are not yet fully implemented. ' +
946
- 'Use prepareTransaction().thenOn() to chain Route 3 -> Route 2 manually.');
953
+ 'Use prepareTransaction() and executeTransactions() to chain Route 3 -> Route 2 manually.');
947
954
  });
948
955
  }
949
956
  // ---------------------------------------------------------------------------
@@ -1109,6 +1116,8 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1109
1116
  return { payload: svmPayload, gatewayRequest: outboundReq };
1110
1117
  }
1111
1118
  const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, sourceChain, (_l = ctx.rpcUrls[sourceChain]) === null || _l === void 0 ? void 0 : _l[0]);
1119
+ // CEA auto-deploys on-chain: Vault.finalizeUniversalTx calls CEAFactory.deployCEA()
1120
+ // if CEA doesn't exist yet. No SDK-side blocking needed.
1112
1121
  const gatewayAddr = chain_1.UNIVERSAL_GATEWAY_ADDRESSES[sourceChain];
1113
1122
  if (!gatewayAddr) {
1114
1123
  throw new Error(`No UniversalGateway address configured for chain ${sourceChain}`);
@@ -4,7 +4,7 @@ import type { AccountStatus, UniversalTxResponse } from './orchestrator.types';
4
4
  import { ProgressEvent } from '../progress-hook/progress-hook.types';
5
5
  import { PushClient } from '../push-client/push-client';
6
6
  import type { UniversalAccount, UniversalSigner } from '../universal/universal.types';
7
- import { ExecuteParams, UniversalExecuteParams, PreparedUniversalTx, CascadedTransactionBuilder, RescueFundsParams } from './orchestrator.types';
7
+ import { ExecuteParams, UniversalExecuteParams, PreparedUniversalTx, CascadedTxResponse, RescueFundsParams } from './orchestrator.types';
8
8
  export declare class Orchestrator {
9
9
  private readonly universalSigner;
10
10
  private pushNetwork;
@@ -79,9 +79,11 @@ export declare class Orchestrator {
79
79
  * The cascade composes all hops bottom-to-top into a single Push Chain tx.
80
80
  *
81
81
  * @param preparedTxs - Array of prepared transactions
82
- * @returns CascadedTransactionBuilder
82
+ * @returns Object with send() method
83
83
  */
84
- createCascadedBuilder(preparedTxs: PreparedUniversalTx[]): CascadedTransactionBuilder;
84
+ createCascadedBuilder(preparedTxs: PreparedUniversalTx[]): {
85
+ send: () => Promise<CascadedTxResponse>;
86
+ };
85
87
  /**
86
88
  * Queries gas fee for an outbound transaction from the UniversalCore contract.
87
89
  */
@@ -129,7 +129,7 @@ class Orchestrator {
129
129
  */
130
130
  execute(params) {
131
131
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
132
- var _a, _b, _c, _d, _e;
132
+ var _a, _b, _c, _d;
133
133
  // Lazy UEA upgrade check
134
134
  try {
135
135
  if (!this.accountStatusCache || !this.accountStatusCache.uea.loaded) {
@@ -166,16 +166,6 @@ class Orchestrator {
166
166
  try {
167
167
  const rcb = () => this._getResponseCallbacks();
168
168
  if (execute.funds) {
169
- const hasData = execute.data && execute.data !== '0x';
170
- const isSVM = ((_e = chain_1.CHAIN_INFO[this.ctx.universalSigner.account.chain]) === null || _e === void 0 ? void 0 : _e.vm) === enums_1.VM.SVM;
171
- // SVM two-phase: bridge funds first, then execute payload separately
172
- // (Push Chain node doesn't support FUNDS_AND_PAYLOAD payload decoding for SVM yet)
173
- if (hasData && isSVM) {
174
- const fundsOnly = Object.assign(Object.assign({}, execute), { data: undefined });
175
- yield (0, internals_1.executeFundsOnly)(this.ctx, fundsOnly, eventBuffer, rcb);
176
- const dataOnly = Object.assign(Object.assign({}, execute), { funds: undefined });
177
- return yield (0, internals_1.executeStandardPayload)(this.ctx, dataOnly, eventBuffer, rcb);
178
- }
179
169
  return !hasData
180
170
  ? yield (0, internals_1.executeFundsOnly)(this.ctx, execute, eventBuffer, rcb)
181
171
  : yield (0, internals_1.executeFundsWithPayload)(this.ctx, execute, eventBuffer, rcb);
@@ -221,7 +211,7 @@ class Orchestrator {
221
211
  * The cascade composes all hops bottom-to-top into a single Push Chain tx.
222
212
  *
223
213
  * @param preparedTxs - Array of prepared transactions
224
- * @returns CascadedTransactionBuilder
214
+ * @returns Object with send() method
225
215
  */
226
216
  createCascadedBuilder(preparedTxs) {
227
217
  return (0, internals_1.createCascadedBuilder)(this.ctx, preparedTxs, this._getCascadeCallbacks());
@@ -106,6 +106,14 @@ export type ExecuteParams = {
106
106
  * @internal
107
107
  */
108
108
  _skipFeeLocking?: boolean;
109
+ /**
110
+ * Internal: Minimum fee-locking deposit in USD (8 decimals).
111
+ * Overrides the default $1 floor when the caller needs a larger deposit
112
+ * (e.g., Route 2 needs enough UPC for the outbound swap).
113
+ * Still capped at $10 by lockFee.
114
+ * @internal
115
+ */
116
+ _minimumDepositUsd?: bigint;
109
117
  };
110
118
  /**
111
119
  * New Universal Transaction Receipt interface with prioritized field ordering
@@ -465,10 +473,6 @@ export interface PreparedUniversalTx {
465
473
  deadline: bigint;
466
474
  /** Internal hop descriptor for cascade nesting @internal */
467
475
  _hop: HopDescriptor;
468
- /** Chain additional transactions after this one (cascade) */
469
- thenOn: (nextTx: PreparedUniversalTx) => CascadedTransactionBuilder;
470
- /** Execute this prepared transaction */
471
- send: () => Promise<UniversalTxResponse>;
472
476
  }
473
477
  /**
474
478
  * @deprecated Use CascadedTxResponse instead.
@@ -485,17 +489,6 @@ export interface MultiChainTxResponse {
485
489
  status: 'pending' | 'confirmed' | 'failed';
486
490
  }[];
487
491
  }
488
- /**
489
- * Builder for composing cascaded (nested) multi-chain transactions.
490
- * Each call to thenOn() adds another hop to the cascade.
491
- * send() composes all hops bottom-to-top and executes a single Push Chain tx.
492
- */
493
- export interface CascadedTransactionBuilder {
494
- /** Add another hop to the cascade */
495
- thenOn: (nextTx: PreparedUniversalTx) => CascadedTransactionBuilder;
496
- /** Compose all hops bottom-to-top and execute the single initial tx */
497
- send: () => Promise<CascadedTxResponse>;
498
- }
499
492
  /**
500
493
  * Response for cascaded multi-chain transactions.
501
494
  * Contains the initial tx and tracking info for all hops.
@@ -35,7 +35,7 @@ const RAW_HOOKS = {
35
35
  [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_03_01]: () => ({
36
36
  id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_03_01,
37
37
  title: 'Resolving UEA',
38
- message: 'Resolving Execution Account (UEA) – computing address, checking deployment status and balance',
38
+ message: 'Resolving Executor Account (UEA) – computing address, checking deployment status and balance',
39
39
  response: null,
40
40
  level: 'INFO',
41
41
  }),
@@ -6,7 +6,7 @@ import { TypedData, TypedDataDomain } from 'viem';
6
6
  import { ProgressEvent } from '../progress-hook/progress-hook.types';
7
7
  import type { AccountStatus } from '../orchestrator/orchestrator.types';
8
8
  import { MoveableToken, PayableToken, ConversionQuote, MoveableTokenAccessor, PayableTokenAccessor } from '../constants/tokens';
9
- import type { PreparedUniversalTx, CascadedTransactionBuilder } from '../orchestrator/orchestrator.types';
9
+ import type { PreparedUniversalTx, CascadedTxResponse } from '../orchestrator/orchestrator.types';
10
10
  /**
11
11
  * @class PushChain
12
12
  *
@@ -71,15 +71,14 @@ export declare class PushChain {
71
71
  sendTransaction: Orchestrator['execute'];
72
72
  /**
73
73
  * Prepare a universal transaction without executing it.
74
- * Returns a PreparedUniversalTx that can be chained with thenOn() or sent.
74
+ * Returns a PreparedUniversalTx that can be passed to executeTransactions.
75
75
  */
76
76
  prepareTransaction: Orchestrator['prepareTransaction'];
77
77
  /**
78
- * Execute multiple transactions in sequence across chains.
79
- * Accepts PreparedUniversalTx (from prepareTransaction) and returns
80
- * a CascadedTransactionBuilder that supports .thenOn() for chaining.
78
+ * Execute one or more prepared transactions as a cascade across chains.
79
+ * Accepts an array of PreparedUniversalTx (from prepareTransaction).
81
80
  */
82
- executeTransactions: (firstTx: PreparedUniversalTx) => CascadedTransactionBuilder;
81
+ executeTransactions: (txs: PreparedUniversalTx[]) => Promise<CascadedTxResponse>;
83
82
  /**
84
83
  * Tracks a transaction by hash on Push Chain
85
84
  */
@@ -87,12 +87,12 @@ class PushChain {
87
87
  }
88
88
  return orchestrator.prepareTransaction.bind(orchestrator)(params);
89
89
  },
90
- executeTransactions: (firstTx) => {
90
+ executeTransactions: (txs) => tslib_1.__awaiter(this, void 0, void 0, function* () {
91
91
  if (this.isReadMode) {
92
92
  throw new Error('Read only mode cannot call executeTransactions function');
93
93
  }
94
- return orchestrator.createCascadedBuilder([firstTx]);
95
- },
94
+ return orchestrator.createCascadedBuilder(txs).send();
95
+ }),
96
96
  trackTransaction: (txHash, options) => {
97
97
  return orchestrator.trackTransaction.bind(orchestrator)(txHash, options);
98
98
  },
@@ -400,6 +400,7 @@ class PushChain {
400
400
  // Stored on instance so consumers can await it if needed: await client.accountStatusReady
401
401
  const ACCOUNT_STATUS_TIMEOUT = 30000;
402
402
  instance.accountStatusReady = Promise.race([
403
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
403
404
  instance.getAccountStatus().then(() => { }),
404
405
  new Promise((_, reject) => setTimeout(() => reject(new Error('Account status fetch timed out')), ACCOUNT_STATUS_TIMEOUT)),
405
406
  ]).catch(() => {