@pushchain/core 5.1.4-alpha.6 → 5.1.4-alpha.8

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.6",
3
+ "version": "5.1.4-alpha.8",
4
4
  "homepage": "https://push.org",
5
5
  "keywords": [
6
6
  "pushchain",
@@ -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);
@@ -220,8 +232,13 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
220
232
  burnAmount = params.value;
221
233
  }
222
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.
223
240
  prc20Token = (0, helpers_1.getNativePRC20ForChain)(targetChain, ctx.pushNetwork);
224
- burnAmount = BigInt(1); // Minimum for precompile
241
+ burnAmount = BigInt(0);
225
242
  }
226
243
  // Query gas fee
227
244
  let gasToken = selectors_1.ZERO_ADDRESS;
@@ -283,10 +300,12 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
283
300
  gasToken = result.gasToken;
284
301
  gasFee = result.gasFee;
285
302
  }
286
- 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,
287
304
  gasFee });
288
305
  }
289
- 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.
290
309
  // Determine token/amount for the inbound
291
310
  let tokenAddress = selectors_1.ZERO_ADDRESS;
292
311
  let amount = BigInt(0);
@@ -319,7 +338,7 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
319
338
  }
320
339
  return Object.assign(Object.assign({}, baseDescriptor), { sourceChain,
321
340
  ceaAddress,
322
- prc20Token, burnAmount: amount > BigInt(0) ? amount : BigInt(1), gasToken,
341
+ prc20Token, burnAmount: amount, gasToken,
323
342
  gasFee });
324
343
  }
325
344
  default:
@@ -551,15 +570,15 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
551
570
  ? new Uint8Array(Buffer.from(intermediatePayload.slice(2), 'hex'))
552
571
  : undefined,
553
572
  });
554
- const burnAmount = BigInt(1);
573
+ const svmBurnAmount = segment.totalBurnAmount || BigInt(0);
555
574
  const outboundReq = (0, payload_builders_1.buildOutboundRequest)(gatewayProgramHex, segment.prc20Token ||
556
- (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);
557
576
  const inboundGasFee = segment.gasFee || BigInt(0);
558
577
  const outboundMulticalls = (0, payload_builders_1.buildOutboundApprovalAndCall)({
559
578
  prc20Token: segment.prc20Token ||
560
579
  (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork),
561
580
  gasToken: segment.gasToken || selectors_1.ZERO_ADDRESS,
562
- burnAmount,
581
+ burnAmount: svmBurnAmount,
563
582
  gasFee: inboundGasFee,
564
583
  nativeValueForGas: perOutboundNativeValue !== null && perOutboundNativeValue !== void 0 ? perOutboundNativeValue : inboundGasFee * BigInt(1000),
565
584
  gatewayPcAddress,
@@ -626,13 +645,13 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
626
645
  // Wrap CEA multicall in outbound from Push Chain
627
646
  const ceaPayload = (0, payload_builders_1.buildCeaMulticallPayload)(ceaMulticalls);
628
647
  const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, segment.prc20Token ||
629
- (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);
630
649
  const inboundGasFee = segment.gasFee || BigInt(0);
631
650
  const outboundMulticalls = (0, payload_builders_1.buildOutboundApprovalAndCall)({
632
651
  prc20Token: segment.prc20Token ||
633
652
  (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork),
634
653
  gasToken: segment.gasToken || selectors_1.ZERO_ADDRESS,
635
- burnAmount: segment.totalBurnAmount || BigInt(1),
654
+ burnAmount: segment.totalBurnAmount || BigInt(0),
636
655
  gasFee: inboundGasFee,
637
656
  nativeValueForGas: perOutboundNativeValue !== null && perOutboundNativeValue !== void 0 ? perOutboundNativeValue : inboundGasFee * BigInt(1000),
638
657
  gatewayPcAddress,
@@ -263,44 +263,56 @@ function executeUoaToCea(ctx, params, executeFn) {
263
263
  let gasFee = BigInt(0);
264
264
  let nativeValueForGas = BigInt(0);
265
265
  let gasToken = selectors_1.ZERO_ADDRESS;
266
+ let universalCoreAddress;
266
267
  if (prc20Token !== selectors_1.ZERO_ADDRESS) {
267
268
  try {
268
269
  const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, outboundReq.gasLimit);
269
270
  gasFee = result.gasFee;
270
271
  gasToken = result.gasToken;
271
272
  nativeValueForGas = result.nativeValueForGas;
273
+ universalCoreAddress = result.universalCoreAddress;
272
274
  (0, context_1.printLog)(ctx, `executeUoaToCea — queried gas fee: ${gasFee.toString()}, gasToken: ${gasToken}, nativeValueForGas: ${nativeValueForGas.toString()}`);
273
275
  }
274
276
  catch (err) {
275
277
  throw new Error(`Failed to query outbound gas fee: ${err}`);
276
278
  }
277
279
  }
278
- // Adjust nativeValueForGas: the 1Mx multiplier from queryOutboundGasFee produces
279
- // a value far too low for the actual WPC/gasToken swap price. Set it to 200 UPC
280
- // (capped by balance) — enough for the swap, but avoids draining thin pools.
281
- // The contract's swapAndBurnGas does exactOutputSingle and refunds excess PC.
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.
295
+ // The contract's swapAndBurnGas does exactOutputSingle and refunds excess PC,
296
+ // so overshooting within balance is safe.
282
297
  const EVM_NATIVE_VALUE_TARGET = BigInt(200e18); // 200 UPC
283
298
  const EVM_GAS_RESERVE = BigInt(3e18); // 3 UPC for tx overhead
284
- // Reuse ueaBalance fetched earlier (line 383) — no tx sent between, balance is stable
285
299
  const currentBalance = ueaBalance;
286
- // Balance-aware adjustment — same logic whether UEA is deployed or not.
287
- // The old !isUEADeployed branch blindly used 200 UPC assuming fee-locking
288
- // would deposit enough, but the actual deposit depends on origin-chain ETH
289
- // locked — not a guaranteed 200 UPC. This caused executeUniversalTx to
290
- // revert when UEA balance < 200 UPC. The contract's swapAndBurnGas does
291
- // exactOutputSingle and refunds excess PC, so overshooting within balance
292
- // is safe.
293
300
  let adjustedValue;
294
301
  if (currentBalance > EVM_NATIVE_VALUE_TARGET + EVM_GAS_RESERVE) {
295
- // Enough balance: use 200 UPC target
296
- adjustedValue = EVM_NATIVE_VALUE_TARGET;
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;
297
306
  }
298
307
  else if (currentBalance > EVM_GAS_RESERVE) {
299
308
  // Low balance: use what's available minus reserve
300
- adjustedValue = currentBalance - EVM_GAS_RESERVE;
309
+ const available = currentBalance - EVM_GAS_RESERVE;
310
+ adjustedValue = nativeValueForGas < available ? nativeValueForGas : available;
301
311
  }
302
312
  else {
303
- // Very low balance: use original query value as-is
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.
304
316
  adjustedValue = nativeValueForGas;
305
317
  }
306
318
  if (adjustedValue !== nativeValueForGas) {
@@ -576,10 +588,8 @@ function executeCeaToPush(ctx, params, executeFn) {
576
588
  // 4. Get CEA address on source chain
577
589
  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]);
578
590
  (0, context_1.printLog)(ctx, `executeCeaToPush — sourceChain: ${sourceChain}, CEA: ${ceaAddress}, deployed: ${ceaDeployed}`);
579
- if (!ceaDeployed) {
580
- throw new Error(`CEA not deployed on ${sourceChain}. ` +
581
- `Deploy CEA first using Route 2 (UOA -> CEA) before using Route 3.`);
582
- }
591
+ // CEA auto-deploys on-chain: Vault.finalizeUniversalTx calls CEAFactory.deployCEA()
592
+ // if CEA doesn't exist yet. No SDK-side blocking needed.
583
593
  // 5. Get UniversalGateway address on source chain
584
594
  const gatewayAddress = chain_1.UNIVERSAL_GATEWAY_ADDRESSES[sourceChain];
585
595
  if (!gatewayAddress) {
@@ -1108,6 +1118,8 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1108
1118
  return { payload: svmPayload, gatewayRequest: outboundReq };
1109
1119
  }
1110
1120
  const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, sourceChain, (_l = ctx.rpcUrls[sourceChain]) === null || _l === void 0 ? void 0 : _l[0]);
1121
+ // CEA auto-deploys on-chain: Vault.finalizeUniversalTx calls CEAFactory.deployCEA()
1122
+ // if CEA doesn't exist yet. No SDK-side blocking needed.
1111
1123
  const gatewayAddr = chain_1.UNIVERSAL_GATEWAY_ADDRESSES[sourceChain];
1112
1124
  if (!gatewayAddr) {
1113
1125
  throw new Error(`No UniversalGateway address configured for chain ${sourceChain}`);
@@ -10,6 +10,7 @@ exports.deriveExecutorAccount = deriveExecutorAccount;
10
10
  exports.resolveControllerAccount = resolveControllerAccount;
11
11
  const tslib_1 = require("tslib");
12
12
  const viem_1 = require("viem");
13
+ const web3_js_1 = require("@solana/web3.js");
13
14
  const chain_1 = require("../../constants/chain");
14
15
  const enums_1 = require("../../constants/enums");
15
16
  const bs58_1 = require("../../internal/bs58");
@@ -430,6 +431,30 @@ function getChainName(chain) {
430
431
  const entry = Object.entries(enums_1.CHAIN).find(([, val]) => val === chain);
431
432
  return entry ? entry[0] : chain;
432
433
  }
434
+ /**
435
+ * Derive the SVM CEA (PDA) for a UEA address on a Solana-based chain.
436
+ *
437
+ * Solana doesn't use a CEAFactory contract — instead, the CEA is a
438
+ * Program Derived Address (PDA) with seeds: ["push_identity", ueaBytes]
439
+ * and the SVM gateway program as the program ID.
440
+ *
441
+ * @param ueaAddress - The UEA (or Push EOA) hex address (20 bytes).
442
+ * @param targetChain - The SVM target chain.
443
+ * @param skipNetworkCheck - Whether to skip deployment status check.
444
+ * @returns The derived 32-byte Solana PDA address (as 0x-prefixed hex) and deployment status.
445
+ */
446
+ function deriveSvmCeaPda(ueaAddress, targetChain, skipNetworkCheck) {
447
+ const gatewayAddress = chain_1.CHAIN_INFO[targetChain].lockerContract;
448
+ if (!gatewayAddress) {
449
+ throw new Error(`SVM gateway program not configured for chain ${targetChain}`);
450
+ }
451
+ const gatewayProgramId = new web3_js_1.PublicKey(gatewayAddress);
452
+ const ueaBytes = Buffer.from(ueaAddress.slice(2), 'hex'); // 20 bytes
453
+ const [ceaPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('push_identity'), ueaBytes], gatewayProgramId);
454
+ const ceaPdaHex = ('0x' +
455
+ Buffer.from(ceaPda.toBytes()).toString('hex'));
456
+ return { address: ceaPdaHex, deployed: skipNetworkCheck ? null : null };
457
+ }
433
458
  /**
434
459
  * Derive the executor account (UEA or CEA) for a given account.
435
460
  *
@@ -490,6 +515,10 @@ function deriveExecutorAccount(account, options) {
490
515
  }
491
516
  // If target chain is external, derive CEA from the computed UEA
492
517
  if (targetChain && !isPushChain(targetChain)) {
518
+ // SVM chains use PDA derivation, not CEAFactory
519
+ if (chain_1.CHAIN_INFO[targetChain].vm === enums_1.VM.SVM) {
520
+ return deriveSvmCeaPda(ueaAddress, targetChain, skipNetworkCheck);
521
+ }
493
522
  const { cea, isDeployed } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, targetChain);
494
523
  return { address: cea, deployed: skipNetworkCheck ? null : isDeployed };
495
524
  }
@@ -512,6 +541,10 @@ function deriveExecutorAccount(account, options) {
512
541
  // --- Case 2: Input is on Push Chain ---
513
542
  // If target chain is external, derive CEA
514
543
  if (targetChain && !isPushChain(targetChain)) {
544
+ // SVM chains use PDA derivation, not CEAFactory
545
+ if (chain_1.CHAIN_INFO[targetChain].vm === enums_1.VM.SVM) {
546
+ return deriveSvmCeaPda(address, targetChain, skipNetworkCheck);
547
+ }
515
548
  const { cea, isDeployed } = yield (0, cea_utils_1.getCEAAddress)(address, targetChain);
516
549
  return { address: cea, deployed: skipNetworkCheck ? null : isDeployed };
517
550
  }