@pushchain/core 5.1.4-alpha.8 → 5.1.4

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushchain/core",
3
- "version": "5.1.4-alpha.8",
3
+ "version": "5.1.4",
4
4
  "homepage": "https://push.org",
5
5
  "keywords": [
6
6
  "pushchain",
@@ -28,6 +28,9 @@ const confirmation_1 = require("./confirmation");
28
28
  const gateway_client_1 = require("./gateway-client");
29
29
  const response_builder_1 = require("./response-builder");
30
30
  const push_chain_tx_1 = require("./push-chain-tx");
31
+ const gas_calculator_1 = require("./gas-calculator");
32
+ const helpers_2 = require("./helpers");
33
+ const price_fetch_1 = require("../../price-fetch/price-fetch");
31
34
  function executeStandardPayload(ctx, execute, eventBuffer, getResponseCallbacks) {
32
35
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
33
36
  var _a, _b;
@@ -198,7 +201,57 @@ function executeStandardPayload(ctx, execute, eventBuffer, getResponseCallbacks)
198
201
  const fixedPushAmount = utils_1.Utils.helpers.parseUnits('0.001', 18);
199
202
  const lockAmount = funds < requiredFunds ? fundDifference : fixedPushAmount;
200
203
  const lockAmountInUSD = ctx.pushClient.pushToUSDC(lockAmount);
201
- const feeLockTxHashBytes = yield (0, gateway_client_1.lockFee)(ctx, lockAmountInUSD, universalPayload, req);
204
+ // Apply minimum deposit override (e.g., Route 2 needs enough UPC for outbound swap)
205
+ const effectiveLockAmountInUSD = execute._minimumDepositUsd && execute._minimumDepositUsd > lockAmountInUSD
206
+ ? execute._minimumDepositUsd
207
+ : lockAmountInUSD;
208
+ // Pre-flight: predict the actual $PC the UEA will receive after the
209
+ // on-chain pETH → WPC swap and validate it covers requiredFunds.
210
+ // Uses the same Uniswap V3 quoter the chain uses (execute_inbound_gas.go).
211
+ // Mirrors the pattern in route-handlers.ts (Route 2/3).
212
+ const { vm: originVm } = chain_1.CHAIN_INFO[chain];
213
+ if (originVm === enums_1.VM.EVM) {
214
+ try {
215
+ // Replicate lockFee's USD → native conversion (with its $1–$10 clamp)
216
+ const oneUsd = utils_1.Utils.helpers.parseUnits('1', 8);
217
+ const tenUsd = utils_1.Utils.helpers.parseUnits('10', 8);
218
+ let depositUsd = effectiveLockAmountInUSD < oneUsd ? oneUsd : effectiveLockAmountInUSD;
219
+ if (depositUsd > tenUsd)
220
+ depositUsd = tenUsd;
221
+ const ethPrice = yield new price_fetch_1.PriceFetch(ctx.rpcUrls).getPrice(chain);
222
+ if (ethPrice > BigInt(0)) {
223
+ const nativeAmountETH = (depositUsd * BigInt(1e18) + (ethPrice - BigInt(1))) / ethPrice + BigInt(1);
224
+ const originPrc20 = (0, helpers_2.getNativePRC20ForChain)(chain, ctx.pushNetwork);
225
+ const predictedUPC = yield (0, gas_calculator_1.estimateDepositFromLockedNative)(ctx, nativeAmountETH, originPrc20);
226
+ if (predictedUPC > BigInt(0)) {
227
+ // 10% slippage margin (same as route-handlers.ts)
228
+ const safeUPC = (predictedUPC * BigInt(90)) / BigInt(100);
229
+ const totalAvailable = safeUPC + funds;
230
+ (0, context_1.printLog)(ctx, `Fee-lock pre-flight: deposit ${depositUsd.toString()} USD → ` +
231
+ `~${nativeAmountETH.toString()} wei ETH → ~${predictedUPC.toString()} UPC ` +
232
+ `(safe: ${safeUPC.toString()}), existing: ${funds.toString()}, ` +
233
+ `total: ${totalAvailable.toString()}, required: ${requiredFunds.toString()}`);
234
+ if (totalAvailable < requiredFunds) {
235
+ const shortfall = requiredFunds - totalAvailable;
236
+ throw new Error(`Insufficient deposit: the fee-lock will deposit ~${(Number(safeUPC) / 1e18).toFixed(4)} $PC ` +
237
+ `but the transaction requires ~${(Number(requiredFunds) / 1e18).toFixed(4)} $PC ` +
238
+ `(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.`);
241
+ }
242
+ }
243
+ }
244
+ }
245
+ catch (err) {
246
+ // If the error is our own insufficient-deposit error, rethrow it
247
+ if (err instanceof Error && err.message.startsWith('Insufficient deposit')) {
248
+ throw err;
249
+ }
250
+ // Otherwise quoter failed — log and continue (same fallback as route-handlers)
251
+ (0, context_1.printLog)(ctx, `Fee-lock pre-flight quote failed, proceeding without validation: ${err instanceof Error ? err.message : String(err)}`);
252
+ }
253
+ }
254
+ const feeLockTxHashBytes = yield (0, gateway_client_1.lockFee)(ctx, effectiveLockAmountInUSD, universalPayload, req);
202
255
  feeLockTxHash = (0, viem_1.bytesToHex)(feeLockTxHashBytes);
203
256
  verificationData = (0, viem_1.bytesToHex)(feeLockTxHashBytes);
204
257
  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
@@ -195,10 +195,24 @@ function extractPcTxAndTransform(ctx, pushChainUniversalTx, gatewayTxHash, event
195
195
  const lastPcTransaction = pushChainUniversalTx.pcTx.at(-1);
196
196
  (0, context_1.printLog)(ctx, `${label} — pcTx: ` + JSON.stringify(pushChainUniversalTx.pcTx.map((p) => ({ txHash: p.txHash, status: p.status, errorMsg: p.errorMsg })), null, 2));
197
197
  (0, context_1.printLog)(ctx, `${label} — using lastPcTransaction: ` + JSON.stringify(lastPcTransaction, null, 2));
198
- if (!(lastPcTransaction === null || lastPcTransaction === void 0 ? void 0 : lastPcTransaction.txHash)) {
199
- const failedPcTx = pushChainUniversalTx.pcTx.find((pcTx) => pcTx.status === 'FAILED' && pcTx.errorMsg);
198
+ if (!(lastPcTransaction === null || lastPcTransaction === void 0 ? void 0 : lastPcTransaction.txHash) || lastPcTransaction.status === 'FAILED') {
199
+ const failedPcTx = (lastPcTransaction === null || lastPcTransaction === void 0 ? void 0 : lastPcTransaction.status) === 'FAILED'
200
+ ? lastPcTransaction
201
+ : pushChainUniversalTx.pcTx.find((pcTx) => pcTx.status === 'FAILED' && pcTx.errorMsg);
200
202
  const errorDetails = (failedPcTx === null || failedPcTx === void 0 ? void 0 : failedPcTx.errorMsg) ? `: ${failedPcTx.errorMsg}` : '';
201
- throw new Error(`No transaction hash found in Push Chain response for gateway tx: ${gatewayTxHash}${errorDetails}`);
203
+ // Parse known UEA error selectors for actionable hints
204
+ let hint = '';
205
+ if (failedPcTx === null || failedPcTx === void 0 ? void 0 : failedPcTx.errorMsg) {
206
+ if (failedPcTx.errorMsg.includes('0xacfdb444')) {
207
+ hint = ' [ExecutionFailed: the subcall to the target contract reverted. ' +
208
+ 'Check that the target address can receive native tokens (has receive/fallback) ' +
209
+ 'and that calldata matches a valid function signature.]';
210
+ }
211
+ else if (failedPcTx.errorMsg.includes('0x179a867c')) {
212
+ hint = ' [ExpiredDeadline: the transaction deadline has passed.]';
213
+ }
214
+ }
215
+ throw new Error(`Push Chain transaction failed for gateway tx: ${gatewayTxHash}${errorDetails}${hint}`);
202
216
  }
203
217
  const tx = yield ctx.pushClient.getTransaction(lastPcTransaction.txHash);
204
218
  return transformFn(tx, eventBuffer);
@@ -150,7 +150,7 @@ function trackTransaction(ctx, txHash, options, callbacks) {
150
150
  throw new Error('trackTransaction requires ResponseBuilderCallbacks');
151
151
  }
152
152
  const { chain = (0, helpers_1.getPushChainForNetwork)(ctx.pushNetwork), progressHook, waitForCompletion = true, advanced = {}, } = options !== null && options !== void 0 ? options : {};
153
- const { timeout = 300000, rpcUrls = {} } = advanced;
153
+ const { timeout = 300000, pollingIntervalMs = 1000, rpcUrls = {} } = advanced;
154
154
  // Event buffer for replay via response.progressHook()
155
155
  const eventBuffer = [];
156
156
  // Helper to emit progress events
@@ -190,7 +190,7 @@ function trackTransaction(ctx, txHash, options, callbacks) {
190
190
  throw new Error(`Timeout: transaction ${txHash} not confirmed within ${timeout}ms`);
191
191
  }
192
192
  // Brief delay before retry
193
- yield new Promise((r) => setTimeout(r, 1000));
193
+ yield new Promise((r) => setTimeout(r, pollingIntervalMs));
194
194
  }
195
195
  }
196
196
  // Try to get UniversalTx data for richer progress reconstruction
@@ -256,7 +256,7 @@ function trackTransaction(ctx, txHash, options, callbacks) {
256
256
  function transformToUniversalTxResponse(ctx_1, tx_1) {
257
257
  return tslib_1.__awaiter(this, arguments, void 0, function* (ctx, tx, eventBuffer = [], callbacks) {
258
258
  const chain = ctx.universalSigner.account.chain;
259
- const { vm, chainId } = chain_1.CHAIN_INFO[chain];
259
+ let { vm, chainId } = chain_1.CHAIN_INFO[chain];
260
260
  let from;
261
261
  let to;
262
262
  let value;
@@ -269,6 +269,13 @@ function transformToUniversalTxResponse(ctx_1, tx_1) {
269
269
  throw new Error('UEA origin account is null');
270
270
  }
271
271
  originAddress = ueaOrigin.account.address;
272
+ // Use the resolved origin chain for vm/chainId so that the `origin`
273
+ // field reflects the transaction's actual origin, not the tracker's.
274
+ const resolvedChainInfo = chain_1.CHAIN_INFO[ueaOrigin.account.chain];
275
+ if (resolvedChainInfo) {
276
+ vm = resolvedChainInfo.vm;
277
+ chainId = resolvedChainInfo.chainId;
278
+ }
272
279
  from = (0, viem_1.getAddress)(tx.to);
273
280
  let decoded;
274
281
  if (tx.input !== '0x') {
@@ -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");
@@ -277,46 +279,48 @@ function executeUoaToCea(ctx, params, executeFn) {
277
279
  throw new Error(`Failed to query outbound gas fee: ${err}`);
278
280
  }
279
281
  }
280
- // Estimate the actual WPC needed for the swap using on-chain pool price.
281
- // The static 1Mx multiplier from queryOutboundGasFee doesn't reflect the real
282
- // WPC/gasToken exchange rate. estimateNativeValueForSwap reads the Uniswap V3
283
- // pool's slot0, computes the true cost, and adds a 2x buffer.
284
- // This matches the SVM path (executeUoaToCeaSvm) which already uses this.
285
- // Excess msg.value is refunded by the contract's swapAndBurnGas.
286
- if (universalCoreAddress && gasFee > BigInt(0)) {
287
- const estimated = yield (0, gas_calculator_1.estimateNativeValueForSwap)(ctx, universalCoreAddress, gasToken, gasFee, ueaBalance);
288
- if (estimated > nativeValueForGas) {
289
- (0, context_1.printLog)(ctx, `executeUoaToCea — pool-price estimate: ${estimated.toString()} ` +
290
- `(was ${nativeValueForGas.toString()}), UEA balance: ${ueaBalance.toString()}`);
291
- nativeValueForGas = estimated;
292
- }
293
- }
294
- // Balance-aware cap: avoid draining the entire UEA balance for gas.
282
+ // Balance-aware adjustment: MAXIMIZE nativeValueForGas within available balance.
295
283
  // The contract's swapAndBurnGas does exactOutputSingle and refunds excess PC,
296
- // so overshooting within balance is safe.
284
+ // so overshooting within balance is safe. Undershooting causes the swap to revert.
297
285
  const EVM_NATIVE_VALUE_TARGET = BigInt(200e18); // 200 UPC
298
286
  const EVM_GAS_RESERVE = BigInt(3e18); // 3 UPC for tx overhead
299
- 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
+ }
300
311
  let adjustedValue;
301
- if (currentBalance > EVM_NATIVE_VALUE_TARGET + EVM_GAS_RESERVE) {
302
- // Enough balance: cap at 200 UPC (pool estimate may be lower)
303
- adjustedValue = nativeValueForGas < EVM_NATIVE_VALUE_TARGET
304
- ? nativeValueForGas
305
- : EVM_NATIVE_VALUE_TARGET;
312
+ if (effectiveBalance > EVM_NATIVE_VALUE_TARGET + EVM_GAS_RESERVE) {
313
+ adjustedValue = EVM_NATIVE_VALUE_TARGET;
306
314
  }
307
- else if (currentBalance > EVM_GAS_RESERVE) {
308
- // Low balance: use what's available minus reserve
309
- const available = currentBalance - EVM_GAS_RESERVE;
310
- adjustedValue = nativeValueForGas < available ? nativeValueForGas : available;
315
+ else if (effectiveBalance > EVM_GAS_RESERVE) {
316
+ adjustedValue = effectiveBalance - EVM_GAS_RESERVE;
311
317
  }
312
318
  else {
313
- // Very low/zero balance (fresh wallet). The pool-price estimate (now in
314
- // nativeValueForGas) is accurate. Fee-locking will deposit enough UPC
315
- // because requiredFunds includes this value, and lockFee floors at $1.
316
319
  adjustedValue = nativeValueForGas;
317
320
  }
318
321
  if (adjustedValue !== nativeValueForGas) {
319
- (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()})`);
320
324
  nativeValueForGas = adjustedValue;
321
325
  }
322
326
  // Build outbound multicalls (approve burn + sendUniversalTxOutbound with native value)
@@ -360,17 +364,11 @@ function executeUoaToCea(ctx, params, executeFn) {
360
364
  // This handles fee-locking on origin chain and executes the multicall from UEA context
361
365
  // Sum native values from multicall entries for proper fee calculation
362
366
  const multicallNativeValue = pushChainMulticalls.reduce((sum, mc) => { var _a; return sum + ((_a = mc.value) !== null && _a !== void 0 ? _a : BigInt(0)); }, BigInt(0));
363
- const executeParams = {
364
- to: ueaAddress, // multicall executes from UEA
365
- value: multicallNativeValue, // ensures correct requiredFunds calculation
366
- data: pushChainMulticalls, // array triggers multicall mode
367
- _ueaStatus: {
367
+ const executeParams = Object.assign({ to: ueaAddress, value: multicallNativeValue, data: pushChainMulticalls, _ueaStatus: {
368
368
  isDeployed: isUEADeployed,
369
369
  nonce: ueaNonce,
370
370
  balance: ueaBalance,
371
- },
372
- _skipFeeLocking: isUEADeployed, // skip fee-locking only if UEA is already deployed
373
- };
371
+ }, _skipFeeLocking: isUEADeployed }, (!isUEADeployed ? { _minimumDepositUsd: ROUTE2_MINIMUM_DEPOSIT_USD } : {}));
374
372
  const response = yield executeFn(executeParams);
375
373
  // Add chain info to response
376
374
  response.chain = targetChain;
@@ -567,7 +565,7 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
567
565
  */
568
566
  function executeCeaToPush(ctx, params, executeFn) {
569
567
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
570
- var _a, _b, _c, _d, _e, _f, _g;
568
+ var _a, _b, _c, _d, _e, _f;
571
569
  // 1. Validate and extract source chain
572
570
  if (!((_a = params.from) === null || _a === void 0 ? void 0 : _a.chain)) {
573
571
  throw new Error('Route 3 (CEA -> Push) requires from.chain to specify the source CEA chain');
@@ -702,27 +700,24 @@ function executeCeaToPush(ctx, params, executeFn) {
702
700
  // CEA will self-execute this multicall (to=CEA, value=0)
703
701
  const ceaPayload = (0, payload_builders_1.buildCeaMulticallPayload)(ceaMulticalls);
704
702
  (0, context_1.printLog)(ctx, `executeCeaToPush — CEA payload (first 100 chars): ${ceaPayload.slice(0, 100)}...`);
705
- // 8. Determine PRC-20 token for the outbound burn on Push Chain.
706
- // For ERC20 flows (params.funds with token), use the token's PRC-20 (e.g. pUSDT)
707
- // so the Vault deposits ERC20 to CEA. For native flows, use the chain's native PRC-20 (e.g. pBNB).
708
- let prc20Token;
709
- if (((_f = params.funds) === null || _f === void 0 ? void 0 : _f.amount) && params.funds.token) {
710
- const token = params.funds.token;
711
- prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
712
- }
713
- else {
714
- prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
715
- }
716
- // burnAmount = PRC20 to burn on Push Chain (NOT the bridge amount).
717
- // Vault deposits burnAmount to CEA. CEA uses burnAmount + pre-existing balance for the bridge.
718
- const burnAmount = amount;
703
+ // 8. Determine PRC-20 token for the outbound relay.
704
+ // Route 3: ALWAYS use native PRC-20 for chain namespace lookup + gas fees.
705
+ // The CEA uses its OWN pre-existing balance on the external chain to bridge
706
+ // tokens back — no PRC-20 burn is needed on Push Chain.
707
+ // burnAmount = 0 makes this a payload-only outbound relay.
708
+ //
709
+ // Previously this burned pUSDT/pToken equal to the bridge amount, which failed
710
+ // because the UEA never holds pUSDT — the whole point of Route 3 is to get
711
+ // tokens FROM the CEA back to Push Chain, not to round-trip them.
712
+ const prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
713
+ const burnAmount = BigInt(0);
719
714
  (0, context_1.printLog)(ctx, `executeCeaToPush — prc20Token: ${prc20Token}, burnAmount: ${burnAmount.toString()}`);
720
715
  // 9. Build outbound request (same structure as Route 2)
721
716
  // target = CEA address (for self-execution), value = 0 in payload
722
717
  const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, // target: CEA address (to=CEA for self-execution)
723
718
  prc20Token, // token: native PRC-20 for source chain (for namespace lookup)
724
- burnAmount, // amount: 1 wei (precompile workaround)
725
- (_g = params.gasLimit) !== null && _g !== void 0 ? _g : BigInt(0), ceaPayload, // payload: ABI-encoded CEA multicall
719
+ burnAmount, // amount: 0 (payload-only, CEA uses its own balance)
720
+ (_f = params.gasLimit) !== null && _f !== void 0 ? _f : BigInt(0), ceaPayload, // payload: ABI-encoded CEA multicall
726
721
  ueaAddress // revertRecipient: UEA
727
722
  );
728
723
  (0, context_1.printLog)(ctx, `executeCeaToPush — outbound request: ${JSON.stringify({
@@ -759,9 +754,43 @@ function executeCeaToPush(ctx, params, executeFn) {
759
754
  const currentBalance = ueaBalance;
760
755
  // Cosmos-EVM tx overhead costs ~1 PC per operation; 3 PC covers approve(s) + buffer.
761
756
  const OUTBOUND_GAS_RESERVE_R3 = BigInt(3e18);
762
- if (currentBalance > OUTBOUND_GAS_RESERVE_R3 && currentBalance - OUTBOUND_GAS_RESERVE_R3 > nativeValueForGas) {
763
- const adjustedValue = currentBalance - OUTBOUND_GAS_RESERVE_R3;
764
- (0, context_1.printLog)(ctx, `executeCeaToPush — adjusting nativeValueForGas from ${nativeValueForGas.toString()} to ${adjustedValue.toString()} (UEA balance: ${currentBalance.toString()})`);
757
+ const ROUTE3_MINIMUM_DEPOSIT_USD = utils_1.Utils.helpers.parseUnits('10', 8); // $10
758
+ // For fresh wallets: predict post-fee-locking UPC balance using the same
759
+ // Uniswap V3 quoter the chain uses (pETH → WPC swap), matching Route 2 logic.
760
+ let effectiveBalance = currentBalance;
761
+ if (!isUEADeployed && effectiveBalance <= OUTBOUND_GAS_RESERVE_R3) {
762
+ const signerChain = ctx.universalSigner.account.chain;
763
+ const ethPrice = yield new price_fetch_1.PriceFetch(ctx.rpcUrls).getPrice(signerChain);
764
+ const nativeAmountETH = ethPrice > BigInt(0)
765
+ ? (ROUTE3_MINIMUM_DEPOSIT_USD * BigInt(1e18) + (ethPrice - BigInt(1))) / ethPrice + BigInt(1)
766
+ : BigInt(0);
767
+ if (nativeAmountETH > BigInt(0)) {
768
+ const originPrc20 = (0, helpers_1.getNativePRC20ForChain)(signerChain, ctx.pushNetwork);
769
+ const predictedUPC = yield (0, gas_calculator_1.estimateDepositFromLockedNative)(ctx, nativeAmountETH, originPrc20);
770
+ if (predictedUPC > BigInt(0)) {
771
+ effectiveBalance = (predictedUPC * BigInt(90)) / BigInt(100);
772
+ (0, context_1.printLog)(ctx, `executeCeaToPush — fresh wallet: Uniswap quote predicts ${predictedUPC.toString()} UPC ` +
773
+ `from ${nativeAmountETH.toString()} wei pETH, using ${effectiveBalance.toString()} (90%)`);
774
+ }
775
+ }
776
+ }
777
+ // Balance-aware adjustment matching Route 2 logic:
778
+ // - If balance exceeds target + reserve: use target (200 UPC)
779
+ // - If balance exceeds reserve: use balance - reserve (maximize within budget)
780
+ // - Otherwise: keep original nativeValueForGas (fallback)
781
+ const EVM_NATIVE_VALUE_TARGET_R3 = BigInt(200e18); // 200 UPC
782
+ let adjustedValue;
783
+ if (effectiveBalance > EVM_NATIVE_VALUE_TARGET_R3 + OUTBOUND_GAS_RESERVE_R3) {
784
+ adjustedValue = EVM_NATIVE_VALUE_TARGET_R3;
785
+ }
786
+ else if (effectiveBalance > OUTBOUND_GAS_RESERVE_R3) {
787
+ adjustedValue = effectiveBalance - OUTBOUND_GAS_RESERVE_R3;
788
+ }
789
+ else {
790
+ adjustedValue = nativeValueForGas;
791
+ }
792
+ if (adjustedValue !== nativeValueForGas) {
793
+ (0, context_1.printLog)(ctx, `executeCeaToPush — adjusting nativeValueForGas from ${nativeValueForGas.toString()} to ${adjustedValue.toString()} (effective balance: ${effectiveBalance.toString()})`);
765
794
  nativeValueForGas = adjustedValue;
766
795
  }
767
796
  // 12. Build Push Chain multicalls (approvals + sendUniversalTxOutbound)
@@ -778,17 +807,11 @@ function executeCeaToPush(ctx, params, executeFn) {
778
807
  // Sum native values from multicall entries for proper fee calculation
779
808
  const multicallNativeValue = pushChainMulticalls.reduce((sum, mc) => { var _a; return sum + ((_a = mc.value) !== null && _a !== void 0 ? _a : BigInt(0)); }, BigInt(0));
780
809
  // 13. Execute through the normal execute() flow
781
- const executeParams = {
782
- to: ueaAddress,
783
- value: multicallNativeValue, // ensures correct requiredFunds calculation
784
- data: pushChainMulticalls,
785
- _ueaStatus: {
810
+ const executeParams = Object.assign({ to: ueaAddress, value: multicallNativeValue, data: pushChainMulticalls, _ueaStatus: {
786
811
  isDeployed: isUEADeployed,
787
812
  nonce: ueaNonce,
788
813
  balance: ueaBalance,
789
- },
790
- _skipFeeLocking: true, // outbound executes on Push Chain, no external fee locking
791
- };
814
+ }, _skipFeeLocking: isUEADeployed }, (!isUEADeployed ? { _minimumDepositUsd: ROUTE3_MINIMUM_DEPOSIT_USD } : {}));
792
815
  const response = yield executeFn(executeParams);
793
816
  // Add Route 3 context to response
794
817
  response.chain = sourceChain;
@@ -826,7 +849,6 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
826
849
  // Determine drain amount and token
827
850
  let drainAmount = BigInt(0);
828
851
  let tokenMintHex;
829
- let prc20Token;
830
852
  if (((_a = params.funds) === null || _a === void 0 ? void 0 : _a.amount) && params.funds.amount > BigInt(0)) {
831
853
  // SPL token drain
832
854
  drainAmount = params.funds.amount;
@@ -835,22 +857,15 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
835
857
  // Convert SPL mint address to 32-byte hex
836
858
  const mintPk = new web3_js_1.PublicKey(token.address);
837
859
  tokenMintHex = ('0x' + Buffer.from(mintPk.toBytes()).toString('hex'));
838
- prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
839
- }
840
- else {
841
- prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
842
860
  }
843
861
  }
844
862
  else if (params.value && params.value > BigInt(0)) {
845
863
  // Native SOL drain
846
864
  drainAmount = params.value;
847
- prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
848
- }
849
- else {
850
- // Payload-only Route 3 SVM: no funds to drain, just execute data on Push Chain
851
- drainAmount = BigInt(0);
852
- prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
853
865
  }
866
+ // Route 3 SVM: ALWAYS use native PRC-20 for chain namespace lookup + gas fees.
867
+ // CEA uses its own pre-existing balance — no PRC-20 burn needed on Push Chain.
868
+ const prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
854
869
  // Build the SVM CPI payload (send_universal_tx_to_uea wrapped in execute)
855
870
  // If params.data is provided, pass it as extraPayload for Push Chain execution
856
871
  let extraPayload;
@@ -922,17 +937,11 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
922
937
  // Sum native values from multicall entries for proper fee calculation
923
938
  const multicallNativeValue = pushChainMulticalls.reduce((sum, mc) => { var _a; return sum + ((_a = mc.value) !== null && _a !== void 0 ? _a : BigInt(0)); }, BigInt(0));
924
939
  // Execute through the normal execute() flow
925
- const executeParams = {
926
- to: ueaAddress,
927
- value: multicallNativeValue, // ensures correct requiredFunds calculation
928
- data: pushChainMulticalls,
929
- _ueaStatus: {
940
+ const executeParams = Object.assign({ to: ueaAddress, value: multicallNativeValue, data: pushChainMulticalls, _ueaStatus: {
930
941
  isDeployed: isUEADeployed,
931
942
  nonce: ueaNonce,
932
943
  balance: ueaBalance,
933
- },
934
- _skipFeeLocking: true, // outbound executes on Push Chain, no external fee locking
935
- };
944
+ }, _skipFeeLocking: isUEADeployed }, (!isUEADeployed ? { _minimumDepositUsd: utils_1.Utils.helpers.parseUnits('10', 8) } : {}));
936
945
  const response = yield executeFn(executeParams);
937
946
  // Add Route 3 SVM context to response
938
947
  response.chain = sourceChain;
@@ -1080,28 +1089,19 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1080
1089
  const gatewayProgramHex = ('0x' + Buffer.from(programPk.toBytes()).toString('hex'));
1081
1090
  let drainAmount = BigInt(0);
1082
1091
  let tokenMintHex;
1083
- let prc20Token;
1084
1092
  if (((_j = params.funds) === null || _j === void 0 ? void 0 : _j.amount) && params.funds.amount > BigInt(0)) {
1085
1093
  drainAmount = params.funds.amount;
1086
1094
  const token = params.funds.token;
1087
1095
  if (token && token.address) {
1088
1096
  const mintPk = new web3_js_1.PublicKey(token.address);
1089
1097
  tokenMintHex = ('0x' + Buffer.from(mintPk.toBytes()).toString('hex'));
1090
- prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
1091
- }
1092
- else {
1093
- prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
1094
1098
  }
1095
1099
  }
1096
1100
  else if (params.value && params.value > BigInt(0)) {
1097
1101
  drainAmount = params.value;
1098
- prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
1099
- }
1100
- else {
1101
- // Payload-only Route 3 SVM: no funds to drain, just execute data on Push Chain
1102
- drainAmount = BigInt(0);
1103
- prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
1104
1102
  }
1103
+ // Route 3 SVM: ALWAYS use native PRC-20, CEA uses its own balance.
1104
+ const prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
1105
1105
  // Derive CEA PDA as revert recipient
1106
1106
  const ueaBytes2 = Buffer.from(ueaAddress.slice(2), 'hex');
1107
1107
  const [ceaPda2] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('push_identity'), ueaBytes2], programPk);
@@ -1189,9 +1189,9 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1189
1189
  ceaMulticalls.push(sendCall);
1190
1190
  const ceaPayload = (0, payload_builders_1.buildCeaMulticallPayload)(ceaMulticalls);
1191
1191
  const prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
1192
- // burnAmount = actual amount needed by CEA. Vault deposits this as msg.value.
1193
- // Fallback to BigInt(1) for payload-only outbound (precompile rejects amount=0).
1194
- const burnAmount = amount > BigInt(0) ? amount : BigInt(1);
1192
+ // Route 3: CEA uses its own pre-existing balance — no PRC-20 burn needed.
1193
+ // burnAmount = 0 makes this a payload-only outbound relay.
1194
+ const burnAmount = BigInt(0);
1195
1195
  const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, prc20Token, burnAmount, (_o = params.gasLimit) !== null && _o !== void 0 ? _o : BigInt(0), ceaPayload, ueaAddress);
1196
1196
  return { payload: ceaPayload, gatewayRequest: outboundReq };
1197
1197
  }
@@ -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