@pushchain/core 5.1.4-alpha.9 → 5.1.5-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -31,8 +31,11 @@ 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");
38
+ const build_payload_1 = require("../svm-idl/build-payload");
36
39
  const payload_builders_1 = require("../payload-builders");
37
40
  const selectors_1 = require("../../constants/selectors");
38
41
  const erc20_evm_1 = require("../../constants/abi/erc20.evm");
@@ -277,60 +280,48 @@ function executeUoaToCea(ctx, params, executeFn) {
277
280
  throw new Error(`Failed to query outbound gas fee: ${err}`);
278
281
  }
279
282
  }
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.
283
+ // Balance-aware adjustment: MAXIMIZE nativeValueForGas within available balance.
284
+ // The contract's swapAndBurnGas does exactOutputSingle and refunds excess PC,
285
+ // so overshooting within balance is safe. Undershooting causes the swap to revert.
286
286
  const EVM_NATIVE_VALUE_TARGET = BigInt(200e18); // 200 UPC
287
287
  const EVM_GAS_RESERVE = BigInt(3e18); // 3 UPC for tx overhead
288
- if (universalCoreAddress && gasFee > BigInt(0)) {
289
- // When UEA balance is 0, pass EVM_NATIVE_VALUE_TARGET as the balance hint
290
- // so the pool estimate isn't capped/fallback'd to zero.
291
- const balanceHint = ueaBalance > BigInt(0) ? ueaBalance : EVM_NATIVE_VALUE_TARGET;
292
- const estimated = yield (0, gas_calculator_1.estimateNativeValueForSwap)(ctx, universalCoreAddress, gasToken, gasFee, balanceHint);
293
- if (estimated > nativeValueForGas) {
294
- (0, context_1.printLog)(ctx, `executeUoaToCea — pool-price estimate: ${estimated.toString()} ` +
295
- `(was ${nativeValueForGas.toString()}), UEA balance: ${ueaBalance.toString()}`);
296
- nativeValueForGas = estimated;
288
+ const ROUTE2_MINIMUM_DEPOSIT_USD = utils_1.Utils.helpers.parseUnits('10', 8); // $10
289
+ // Determine effective balance for the adjustment.
290
+ // For deployed UEAs: use real balance.
291
+ // For fresh wallets: predict the post-fee-locking balance by querying the same
292
+ // Uniswap V3 quoter the chain uses (pETH → WPC swap in execute_inbound_gas.go).
293
+ let effectiveBalance = ueaBalance;
294
+ if (!isUEADeployed && effectiveBalance <= EVM_GAS_RESERVE) {
295
+ // Calculate the ETH amount lockFee will send (same formula as lockFee)
296
+ const ethPrice = yield new price_fetch_1.PriceFetch(ctx.rpcUrls).getPrice(signerChain);
297
+ const nativeAmountETH = ethPrice > BigInt(0)
298
+ ? (ROUTE2_MINIMUM_DEPOSIT_USD * BigInt(1e18) + (ethPrice - BigInt(1))) / ethPrice + BigInt(1)
299
+ : BigInt(0);
300
+ if (nativeAmountETH > BigInt(0)) {
301
+ // Query the real swap quote: how much WPC (UPC) for this pETH amount?
302
+ const originPrc20 = (0, helpers_1.getNativePRC20ForChain)(signerChain, ctx.pushNetwork);
303
+ const predictedUPC = yield (0, gas_calculator_1.estimateDepositFromLockedNative)(ctx, nativeAmountETH, originPrc20);
304
+ if (predictedUPC > BigInt(0)) {
305
+ // 10% safety margin for slippage between quote and actual execution
306
+ effectiveBalance = (predictedUPC * BigInt(90)) / BigInt(100);
307
+ (0, context_1.printLog)(ctx, `executeUoaToCea — fresh wallet: Uniswap quote predicts ${predictedUPC.toString()} UPC ` +
308
+ `from ${nativeAmountETH.toString()} wei pETH, using ${effectiveBalance.toString()} (90%)`);
309
+ }
297
310
  }
298
311
  }
299
- // Balance-aware adjustment: avoid draining the entire UEA balance for gas.
300
- // The contract's swapAndBurnGas does exactOutputSingle and refunds excess PC,
301
- // so overshooting within balance is safe.
302
- const currentBalance = ueaBalance;
303
312
  let adjustedValue;
304
- if (currentBalance > EVM_NATIVE_VALUE_TARGET + EVM_GAS_RESERVE) {
305
- // Enough balance: cap at 200 UPC (pool estimate may be lower)
306
- adjustedValue = nativeValueForGas < EVM_NATIVE_VALUE_TARGET
307
- ? nativeValueForGas
308
- : EVM_NATIVE_VALUE_TARGET;
313
+ if (effectiveBalance > EVM_NATIVE_VALUE_TARGET + EVM_GAS_RESERVE) {
314
+ adjustedValue = EVM_NATIVE_VALUE_TARGET;
309
315
  }
310
- else if (currentBalance > EVM_GAS_RESERVE) {
311
- // Low balance: use what's available minus reserve
312
- const available = currentBalance - EVM_GAS_RESERVE;
313
- adjustedValue = nativeValueForGas < available ? nativeValueForGas : available;
314
- }
315
- else if (!isUEADeployed) {
316
- // Fresh wallet: UEA not deployed, fee-locking WILL run and deposit funds.
317
- // Use EVM_NATIVE_VALUE_TARGET (200 UPC) — fee-locking auto-covers this
318
- // because requiredFunds includes nativeValueForGas, and lockFee converts
319
- // the UPC amount to USD and deposits the equivalent ETH.
320
- // The contract's swapAndBurnGas refunds any excess.
321
- // If pool estimate produced a usable value, prefer it (capped at 200 UPC).
322
- adjustedValue = nativeValueForGas > EVM_NATIVE_VALUE_TARGET
323
- ? EVM_NATIVE_VALUE_TARGET
324
- : nativeValueForGas > BigInt(0) ? nativeValueForGas : EVM_NATIVE_VALUE_TARGET;
325
- (0, context_1.printLog)(ctx, `executeUoaToCea — fresh wallet (UEA not deployed): using ${adjustedValue.toString()} for nativeValueForGas`);
316
+ else if (effectiveBalance > EVM_GAS_RESERVE) {
317
+ adjustedValue = effectiveBalance - EVM_GAS_RESERVE;
326
318
  }
327
319
  else {
328
- // Deployed UEA with near-zero balance: best-effort with pool/1M estimate.
329
- // User may need to add funds to UEA manually.
330
320
  adjustedValue = nativeValueForGas;
331
321
  }
332
322
  if (adjustedValue !== nativeValueForGas) {
333
- (0, context_1.printLog)(ctx, `executeUoaToCea — adjusting nativeValueForGas from ${nativeValueForGas.toString()} to ${adjustedValue.toString()} (UEA balance: ${currentBalance.toString()})`);
323
+ (0, context_1.printLog)(ctx, `executeUoaToCea — adjusting nativeValueForGas from ${nativeValueForGas.toString()} ` +
324
+ `to ${adjustedValue.toString()} (effective balance: ${effectiveBalance.toString()})`);
334
325
  nativeValueForGas = adjustedValue;
335
326
  }
336
327
  // Build outbound multicalls (approve burn + sendUniversalTxOutbound with native value)
@@ -374,17 +365,11 @@ function executeUoaToCea(ctx, params, executeFn) {
374
365
  // This handles fee-locking on origin chain and executes the multicall from UEA context
375
366
  // Sum native values from multicall entries for proper fee calculation
376
367
  const multicallNativeValue = pushChainMulticalls.reduce((sum, mc) => { var _a; return sum + ((_a = mc.value) !== null && _a !== void 0 ? _a : BigInt(0)); }, BigInt(0));
377
- const executeParams = {
378
- to: ueaAddress, // multicall executes from UEA
379
- value: multicallNativeValue, // ensures correct requiredFunds calculation
380
- data: pushChainMulticalls, // array triggers multicall mode
381
- _ueaStatus: {
368
+ const executeParams = Object.assign({ to: ueaAddress, value: multicallNativeValue, data: pushChainMulticalls, _ueaStatus: {
382
369
  isDeployed: isUEADeployed,
383
370
  nonce: ueaNonce,
384
371
  balance: ueaBalance,
385
- },
386
- _skipFeeLocking: isUEADeployed, // skip fee-locking only if UEA is already deployed
387
- };
372
+ }, _skipFeeLocking: isUEADeployed }, (!isUEADeployed ? { _minimumDepositUsd: ROUTE2_MINIMUM_DEPOSIT_USD } : {}));
388
373
  const response = yield executeFn(executeParams);
389
374
  // Add chain info to response
390
375
  response.chain = targetChain;
@@ -409,22 +394,15 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
409
394
  const targetChain = target.chain;
410
395
  const targetAddress = target.address; // 0x-prefixed, 32 bytes
411
396
  const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
412
- const hasSvmExecute = !!params.svmExecute;
397
+ const { svmPayload, targetBytes, hasExecute: hasSvmExecute, } = (0, build_payload_1.buildSvmPayloadFromParams)({
398
+ data: params.data,
399
+ to: target,
400
+ senderUea: ueaAddress,
401
+ });
413
402
  (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — target: ${targetAddress}, chain: ${targetChain}, ` +
414
403
  `hasSvmExecute: ${hasSvmExecute}, value: ${(_b = (_a = params.value) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : '0'}`);
415
- // --- Build SVM payload ---
416
- let svmPayload = '0x'; // empty for withdraw (SOL/SPL)
417
404
  if (hasSvmExecute) {
418
- // Execute case: encode the CPI payload
419
- const exec = params.svmExecute;
420
- svmPayload = (0, payload_builders_1.encodeSvmExecutePayload)({
421
- targetProgram: exec.targetProgram,
422
- accounts: exec.accounts,
423
- ixData: exec.ixData,
424
- instructionId: 2,
425
- });
426
- (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — encoded execute payload: ${(svmPayload.length - 2) / 2} bytes, ` +
427
- `${exec.accounts.length} accounts`);
405
+ (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — encoded execute payload: ${(svmPayload.length - 2) / 2} bytes`);
428
406
  }
429
407
  // --- Determine PRC-20 token and burn amount ---
430
408
  let prc20Token = selectors_1.ZERO_ADDRESS;
@@ -455,13 +433,8 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
455
433
  burnAmount = BigInt(0);
456
434
  (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — EXECUTE-only: using PRC-20 ${prc20Token} with zero burn amount`);
457
435
  }
458
- // --- Determine target bytes ---
459
- // For withdraw: target is the Solana recipient pubkey
460
- // For execute: target is the target Solana program
461
- const targetBytes = hasSvmExecute
462
- ? params.svmExecute.targetProgram
463
- : targetAddress;
464
436
  // --- Build outbound request ---
437
+ // targetBytes: for execute = program id (resolver output); for withdraw = to.address
465
438
  const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, (_d = params.gasLimit) !== null && _d !== void 0 ? _d : BigInt(0), svmPayload, ueaAddress // revert recipient is the UEA
466
439
  );
467
440
  (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — outbound request: ${JSON.stringify({
@@ -581,7 +554,7 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
581
554
  */
582
555
  function executeCeaToPush(ctx, params, executeFn) {
583
556
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
584
- var _a, _b, _c, _d, _e, _f, _g;
557
+ var _a, _b, _c, _d, _e, _f;
585
558
  // 1. Validate and extract source chain
586
559
  if (!((_a = params.from) === null || _a === void 0 ? void 0 : _a.chain)) {
587
560
  throw new Error('Route 3 (CEA -> Push) requires from.chain to specify the source CEA chain');
@@ -716,27 +689,24 @@ function executeCeaToPush(ctx, params, executeFn) {
716
689
  // CEA will self-execute this multicall (to=CEA, value=0)
717
690
  const ceaPayload = (0, payload_builders_1.buildCeaMulticallPayload)(ceaMulticalls);
718
691
  (0, context_1.printLog)(ctx, `executeCeaToPush — CEA payload (first 100 chars): ${ceaPayload.slice(0, 100)}...`);
719
- // 8. Determine PRC-20 token for the outbound burn on Push Chain.
720
- // For ERC20 flows (params.funds with token), use the token's PRC-20 (e.g. pUSDT)
721
- // so the Vault deposits ERC20 to CEA. For native flows, use the chain's native PRC-20 (e.g. pBNB).
722
- let prc20Token;
723
- if (((_f = params.funds) === null || _f === void 0 ? void 0 : _f.amount) && params.funds.token) {
724
- const token = params.funds.token;
725
- prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
726
- }
727
- else {
728
- prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
729
- }
730
- // burnAmount = PRC20 to burn on Push Chain (NOT the bridge amount).
731
- // Vault deposits burnAmount to CEA. CEA uses burnAmount + pre-existing balance for the bridge.
732
- const burnAmount = amount;
692
+ // 8. Determine PRC-20 token for the outbound relay.
693
+ // Route 3: ALWAYS use native PRC-20 for chain namespace lookup + gas fees.
694
+ // The CEA uses its OWN pre-existing balance on the external chain to bridge
695
+ // tokens back — no PRC-20 burn is needed on Push Chain.
696
+ // burnAmount = 0 makes this a payload-only outbound relay.
697
+ //
698
+ // Previously this burned pUSDT/pToken equal to the bridge amount, which failed
699
+ // because the UEA never holds pUSDT — the whole point of Route 3 is to get
700
+ // tokens FROM the CEA back to Push Chain, not to round-trip them.
701
+ const prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
702
+ const burnAmount = BigInt(0);
733
703
  (0, context_1.printLog)(ctx, `executeCeaToPush — prc20Token: ${prc20Token}, burnAmount: ${burnAmount.toString()}`);
734
704
  // 9. Build outbound request (same structure as Route 2)
735
705
  // target = CEA address (for self-execution), value = 0 in payload
736
706
  const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, // target: CEA address (to=CEA for self-execution)
737
707
  prc20Token, // token: native PRC-20 for source chain (for namespace lookup)
738
- burnAmount, // amount: 1 wei (precompile workaround)
739
- (_g = params.gasLimit) !== null && _g !== void 0 ? _g : BigInt(0), ceaPayload, // payload: ABI-encoded CEA multicall
708
+ burnAmount, // amount: 0 (payload-only, CEA uses its own balance)
709
+ (_f = params.gasLimit) !== null && _f !== void 0 ? _f : BigInt(0), ceaPayload, // payload: ABI-encoded CEA multicall
740
710
  ueaAddress // revertRecipient: UEA
741
711
  );
742
712
  (0, context_1.printLog)(ctx, `executeCeaToPush — outbound request: ${JSON.stringify({
@@ -773,9 +743,43 @@ function executeCeaToPush(ctx, params, executeFn) {
773
743
  const currentBalance = ueaBalance;
774
744
  // Cosmos-EVM tx overhead costs ~1 PC per operation; 3 PC covers approve(s) + buffer.
775
745
  const OUTBOUND_GAS_RESERVE_R3 = BigInt(3e18);
776
- if (currentBalance > OUTBOUND_GAS_RESERVE_R3 && currentBalance - OUTBOUND_GAS_RESERVE_R3 > nativeValueForGas) {
777
- const adjustedValue = currentBalance - OUTBOUND_GAS_RESERVE_R3;
778
- (0, context_1.printLog)(ctx, `executeCeaToPush — adjusting nativeValueForGas from ${nativeValueForGas.toString()} to ${adjustedValue.toString()} (UEA balance: ${currentBalance.toString()})`);
746
+ const ROUTE3_MINIMUM_DEPOSIT_USD = utils_1.Utils.helpers.parseUnits('10', 8); // $10
747
+ // For fresh wallets: predict post-fee-locking UPC balance using the same
748
+ // Uniswap V3 quoter the chain uses (pETH → WPC swap), matching Route 2 logic.
749
+ let effectiveBalance = currentBalance;
750
+ if (!isUEADeployed && effectiveBalance <= OUTBOUND_GAS_RESERVE_R3) {
751
+ const signerChain = ctx.universalSigner.account.chain;
752
+ const ethPrice = yield new price_fetch_1.PriceFetch(ctx.rpcUrls).getPrice(signerChain);
753
+ const nativeAmountETH = ethPrice > BigInt(0)
754
+ ? (ROUTE3_MINIMUM_DEPOSIT_USD * BigInt(1e18) + (ethPrice - BigInt(1))) / ethPrice + BigInt(1)
755
+ : BigInt(0);
756
+ if (nativeAmountETH > BigInt(0)) {
757
+ const originPrc20 = (0, helpers_1.getNativePRC20ForChain)(signerChain, ctx.pushNetwork);
758
+ const predictedUPC = yield (0, gas_calculator_1.estimateDepositFromLockedNative)(ctx, nativeAmountETH, originPrc20);
759
+ if (predictedUPC > BigInt(0)) {
760
+ effectiveBalance = (predictedUPC * BigInt(90)) / BigInt(100);
761
+ (0, context_1.printLog)(ctx, `executeCeaToPush — fresh wallet: Uniswap quote predicts ${predictedUPC.toString()} UPC ` +
762
+ `from ${nativeAmountETH.toString()} wei pETH, using ${effectiveBalance.toString()} (90%)`);
763
+ }
764
+ }
765
+ }
766
+ // Balance-aware adjustment matching Route 2 logic:
767
+ // - If balance exceeds target + reserve: use target (200 UPC)
768
+ // - If balance exceeds reserve: use balance - reserve (maximize within budget)
769
+ // - Otherwise: keep original nativeValueForGas (fallback)
770
+ const EVM_NATIVE_VALUE_TARGET_R3 = BigInt(200e18); // 200 UPC
771
+ let adjustedValue;
772
+ if (effectiveBalance > EVM_NATIVE_VALUE_TARGET_R3 + OUTBOUND_GAS_RESERVE_R3) {
773
+ adjustedValue = EVM_NATIVE_VALUE_TARGET_R3;
774
+ }
775
+ else if (effectiveBalance > OUTBOUND_GAS_RESERVE_R3) {
776
+ adjustedValue = effectiveBalance - OUTBOUND_GAS_RESERVE_R3;
777
+ }
778
+ else {
779
+ adjustedValue = nativeValueForGas;
780
+ }
781
+ if (adjustedValue !== nativeValueForGas) {
782
+ (0, context_1.printLog)(ctx, `executeCeaToPush — adjusting nativeValueForGas from ${nativeValueForGas.toString()} to ${adjustedValue.toString()} (effective balance: ${effectiveBalance.toString()})`);
779
783
  nativeValueForGas = adjustedValue;
780
784
  }
781
785
  // 12. Build Push Chain multicalls (approvals + sendUniversalTxOutbound)
@@ -792,17 +796,11 @@ function executeCeaToPush(ctx, params, executeFn) {
792
796
  // Sum native values from multicall entries for proper fee calculation
793
797
  const multicallNativeValue = pushChainMulticalls.reduce((sum, mc) => { var _a; return sum + ((_a = mc.value) !== null && _a !== void 0 ? _a : BigInt(0)); }, BigInt(0));
794
798
  // 13. Execute through the normal execute() flow
795
- const executeParams = {
796
- to: ueaAddress,
797
- value: multicallNativeValue, // ensures correct requiredFunds calculation
798
- data: pushChainMulticalls,
799
- _ueaStatus: {
799
+ const executeParams = Object.assign({ to: ueaAddress, value: multicallNativeValue, data: pushChainMulticalls, _ueaStatus: {
800
800
  isDeployed: isUEADeployed,
801
801
  nonce: ueaNonce,
802
802
  balance: ueaBalance,
803
- },
804
- _skipFeeLocking: true, // outbound executes on Push Chain, no external fee locking
805
- };
803
+ }, _skipFeeLocking: isUEADeployed }, (!isUEADeployed ? { _minimumDepositUsd: ROUTE3_MINIMUM_DEPOSIT_USD } : {}));
806
804
  const response = yield executeFn(executeParams);
807
805
  // Add Route 3 context to response
808
806
  response.chain = sourceChain;
@@ -840,7 +838,6 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
840
838
  // Determine drain amount and token
841
839
  let drainAmount = BigInt(0);
842
840
  let tokenMintHex;
843
- let prc20Token;
844
841
  if (((_a = params.funds) === null || _a === void 0 ? void 0 : _a.amount) && params.funds.amount > BigInt(0)) {
845
842
  // SPL token drain
846
843
  drainAmount = params.funds.amount;
@@ -849,22 +846,15 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
849
846
  // Convert SPL mint address to 32-byte hex
850
847
  const mintPk = new web3_js_1.PublicKey(token.address);
851
848
  tokenMintHex = ('0x' + Buffer.from(mintPk.toBytes()).toString('hex'));
852
- prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
853
- }
854
- else {
855
- prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
856
849
  }
857
850
  }
858
851
  else if (params.value && params.value > BigInt(0)) {
859
852
  // Native SOL drain
860
853
  drainAmount = params.value;
861
- prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
862
- }
863
- else {
864
- // Payload-only Route 3 SVM: no funds to drain, just execute data on Push Chain
865
- drainAmount = BigInt(0);
866
- prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
867
854
  }
855
+ // Route 3 SVM: ALWAYS use native PRC-20 for chain namespace lookup + gas fees.
856
+ // CEA uses its own pre-existing balance — no PRC-20 burn needed on Push Chain.
857
+ const prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
868
858
  // Build the SVM CPI payload (send_universal_tx_to_uea wrapped in execute)
869
859
  // If params.data is provided, pass it as extraPayload for Push Chain execution
870
860
  let extraPayload;
@@ -936,17 +926,11 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
936
926
  // Sum native values from multicall entries for proper fee calculation
937
927
  const multicallNativeValue = pushChainMulticalls.reduce((sum, mc) => { var _a; return sum + ((_a = mc.value) !== null && _a !== void 0 ? _a : BigInt(0)); }, BigInt(0));
938
928
  // Execute through the normal execute() flow
939
- const executeParams = {
940
- to: ueaAddress,
941
- value: multicallNativeValue, // ensures correct requiredFunds calculation
942
- data: pushChainMulticalls,
943
- _ueaStatus: {
929
+ const executeParams = Object.assign({ to: ueaAddress, value: multicallNativeValue, data: pushChainMulticalls, _ueaStatus: {
944
930
  isDeployed: isUEADeployed,
945
931
  nonce: ueaNonce,
946
932
  balance: ueaBalance,
947
- },
948
- _skipFeeLocking: true, // outbound executes on Push Chain, no external fee locking
949
- };
933
+ }, _skipFeeLocking: isUEADeployed }, (!isUEADeployed ? { _minimumDepositUsd: utils_1.Utils.helpers.parseUnits('10', 8) } : {}));
950
934
  const response = yield executeFn(executeParams);
951
935
  // Add Route 3 SVM context to response
952
936
  response.chain = sourceChain;
@@ -977,7 +961,7 @@ function executeCeaToCea(ctx, params, executeFn) {
977
961
  */
978
962
  function buildPayloadForRoute(ctx, params, route, nonce) {
979
963
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
980
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
964
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
981
965
  const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
982
966
  switch (route) {
983
967
  case route_detector_1.TransactionRoute.UOA_TO_PUSH: {
@@ -1000,20 +984,15 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1000
984
  const target = params.to;
1001
985
  // Branch: SVM vs EVM
1002
986
  if ((0, payload_builders_1.isSvmChain)(target.chain)) {
1003
- // SVM: build SVM payload (binary or empty for withdraw)
1004
- let payload = '0x';
1005
- if (params.svmExecute) {
1006
- payload = (0, payload_builders_1.encodeSvmExecutePayload)({
1007
- targetProgram: params.svmExecute.targetProgram,
1008
- accounts: params.svmExecute.accounts,
1009
- ixData: params.svmExecute.ixData,
1010
- instructionId: 2,
1011
- });
1012
- }
1013
- const targetBytes = (_b = (_a = params.svmExecute) === null || _a === void 0 ? void 0 : _a.targetProgram) !== null && _b !== void 0 ? _b : target.address;
987
+ // SVM: build SVM payload (binary or empty for withdraw) via IDL resolver
988
+ const { svmPayload: payload, targetBytes, hasExecute, } = (0, build_payload_1.buildSvmPayloadFromParams)({
989
+ data: params.data,
990
+ to: target,
991
+ senderUea: ueaAddress,
992
+ });
1014
993
  let prc20Token = selectors_1.ZERO_ADDRESS;
1015
994
  let burnAmount = BigInt(0);
1016
- if ((_c = params.funds) === null || _c === void 0 ? void 0 : _c.amount) {
995
+ if ((_a = params.funds) === null || _a === void 0 ? void 0 : _a.amount) {
1017
996
  const token = params.funds.token;
1018
997
  if (token) {
1019
998
  prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
@@ -1024,15 +1003,15 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1024
1003
  prc20Token = (0, helpers_1.getNativePRC20ForChain)(target.chain, ctx.pushNetwork);
1025
1004
  burnAmount = params.value;
1026
1005
  }
1027
- else if (params.svmExecute) {
1006
+ else if (hasExecute) {
1028
1007
  prc20Token = (0, helpers_1.getNativePRC20ForChain)(target.chain, ctx.pushNetwork);
1029
1008
  burnAmount = BigInt(1);
1030
1009
  }
1031
- const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, (_d = params.gasLimit) !== null && _d !== void 0 ? _d : BigInt(0), payload, ueaAddress);
1010
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, (_b = params.gasLimit) !== null && _b !== void 0 ? _b : BigInt(0), payload, ueaAddress);
1032
1011
  return { payload, gatewayRequest: outboundReq };
1033
1012
  }
1034
1013
  // EVM path: Resolve CEA address first (needed for self-transfer check)
1035
- const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, target.chain, (_e = ctx.rpcUrls[target.chain]) === null || _e === void 0 ? void 0 : _e[0]);
1014
+ const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, target.chain, (_c = ctx.rpcUrls[target.chain]) === null || _c === void 0 ? void 0 : _c[0]);
1036
1015
  // Build CEA outbound payload
1037
1016
  const multicalls = [];
1038
1017
  if (params.data) {
@@ -1065,7 +1044,7 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1065
1044
  const payload = (0, payload_builders_1.buildCeaMulticallPayload)(multicalls);
1066
1045
  let prc20Token = selectors_1.ZERO_ADDRESS;
1067
1046
  let burnAmount = BigInt(0);
1068
- if ((_f = params.funds) === null || _f === void 0 ? void 0 : _f.amount) {
1047
+ if ((_d = params.funds) === null || _d === void 0 ? void 0 : _d.amount) {
1069
1048
  const token = params.funds.token;
1070
1049
  if (token) {
1071
1050
  prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
@@ -1073,13 +1052,13 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1073
1052
  }
1074
1053
  }
1075
1054
  const targetBytes = ceaAddress;
1076
- const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, (_g = params.gasLimit) !== null && _g !== void 0 ? _g : BigInt(0), payload, ueaAddress);
1055
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, (_e = params.gasLimit) !== null && _e !== void 0 ? _e : BigInt(0), payload, ueaAddress);
1077
1056
  return { payload, gatewayRequest: outboundReq };
1078
1057
  }
1079
1058
  case route_detector_1.TransactionRoute.CEA_TO_PUSH: {
1080
1059
  // Route 3: CEA -> Push Chain
1081
1060
  // Build CEA multicall (approve + sendUniversalTxFromCEA) and wrap in outbound
1082
- if (!((_h = params.from) === null || _h === void 0 ? void 0 : _h.chain)) {
1061
+ if (!((_f = params.from) === null || _f === void 0 ? void 0 : _f.chain)) {
1083
1062
  throw new Error('Route 3 (CEA -> Push) requires from.chain');
1084
1063
  }
1085
1064
  const sourceChain = params.from.chain;
@@ -1094,28 +1073,19 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1094
1073
  const gatewayProgramHex = ('0x' + Buffer.from(programPk.toBytes()).toString('hex'));
1095
1074
  let drainAmount = BigInt(0);
1096
1075
  let tokenMintHex;
1097
- let prc20Token;
1098
- if (((_j = params.funds) === null || _j === void 0 ? void 0 : _j.amount) && params.funds.amount > BigInt(0)) {
1076
+ if (((_g = params.funds) === null || _g === void 0 ? void 0 : _g.amount) && params.funds.amount > BigInt(0)) {
1099
1077
  drainAmount = params.funds.amount;
1100
1078
  const token = params.funds.token;
1101
1079
  if (token && token.address) {
1102
1080
  const mintPk = new web3_js_1.PublicKey(token.address);
1103
1081
  tokenMintHex = ('0x' + Buffer.from(mintPk.toBytes()).toString('hex'));
1104
- prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
1105
- }
1106
- else {
1107
- prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
1108
1082
  }
1109
1083
  }
1110
1084
  else if (params.value && params.value > BigInt(0)) {
1111
1085
  drainAmount = params.value;
1112
- prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
1113
- }
1114
- else {
1115
- // Payload-only Route 3 SVM: no funds to drain, just execute data on Push Chain
1116
- drainAmount = BigInt(0);
1117
- prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
1118
1086
  }
1087
+ // Route 3 SVM: ALWAYS use native PRC-20, CEA uses its own balance.
1088
+ const prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
1119
1089
  // Derive CEA PDA as revert recipient
1120
1090
  const ueaBytes2 = Buffer.from(ueaAddress.slice(2), 'hex');
1121
1091
  const [ceaPda2] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('push_identity'), ueaBytes2], programPk);
@@ -1128,10 +1098,10 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1128
1098
  });
1129
1099
  // burnAmount = 1 (minimum for precompile; drain amount lives inside the ixData)
1130
1100
  const burnAmount = BigInt(1);
1131
- const outboundReq = (0, payload_builders_1.buildOutboundRequest)(gatewayProgramHex, prc20Token, burnAmount, (_k = params.gasLimit) !== null && _k !== void 0 ? _k : BigInt(0), svmPayload, ueaAddress);
1101
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(gatewayProgramHex, prc20Token, burnAmount, (_h = params.gasLimit) !== null && _h !== void 0 ? _h : BigInt(0), svmPayload, ueaAddress);
1132
1102
  return { payload: svmPayload, gatewayRequest: outboundReq };
1133
1103
  }
1134
- const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, sourceChain, (_l = ctx.rpcUrls[sourceChain]) === null || _l === void 0 ? void 0 : _l[0]);
1104
+ const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, sourceChain, (_j = ctx.rpcUrls[sourceChain]) === null || _j === void 0 ? void 0 : _j[0]);
1135
1105
  // CEA auto-deploys on-chain: Vault.finalizeUniversalTx calls CEAFactory.deployCEA()
1136
1106
  // if CEA doesn't exist yet. No SDK-side blocking needed.
1137
1107
  const gatewayAddr = chain_1.UNIVERSAL_GATEWAY_ADDRESSES[sourceChain];
@@ -1142,7 +1112,7 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1142
1112
  const ceaMulticalls = [];
1143
1113
  let tokenAddress = selectors_1.ZERO_ADDRESS;
1144
1114
  let amount = BigInt(0);
1145
- if ((_m = params.funds) === null || _m === void 0 ? void 0 : _m.amount) {
1115
+ if ((_k = params.funds) === null || _k === void 0 ? void 0 : _k.amount) {
1146
1116
  const token = params.funds.token;
1147
1117
  if (token) {
1148
1118
  if (token.mechanism === 'native') {
@@ -1203,10 +1173,10 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1203
1173
  ceaMulticalls.push(sendCall);
1204
1174
  const ceaPayload = (0, payload_builders_1.buildCeaMulticallPayload)(ceaMulticalls);
1205
1175
  const prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
1206
- // burnAmount = actual amount needed by CEA. Vault deposits this as msg.value.
1207
- // Fallback to BigInt(1) for payload-only outbound (precompile rejects amount=0).
1208
- const burnAmount = amount > BigInt(0) ? amount : BigInt(1);
1209
- const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, prc20Token, burnAmount, (_o = params.gasLimit) !== null && _o !== void 0 ? _o : BigInt(0), ceaPayload, ueaAddress);
1176
+ // Route 3: CEA uses its own pre-existing balance — no PRC-20 burn needed.
1177
+ // burnAmount = 0 makes this a payload-only outbound relay.
1178
+ const burnAmount = BigInt(0);
1179
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, prc20Token, burnAmount, (_l = params.gasLimit) !== null && _l !== void 0 ? _l : BigInt(0), ceaPayload, ueaAddress);
1210
1180
  return { payload: ceaPayload, gatewayRequest: outboundReq };
1211
1181
  }
1212
1182
  default:
@@ -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 $1000 by lockFee.
114
+ * @internal
115
+ */
116
+ _minimumDepositUsd?: bigint;
109
117
  };
110
118
  /**
111
119
  * New Universal Transaction Receipt interface with prioritized field ordering
@@ -283,16 +291,9 @@ export type UniversalExecuteParams = Omit<ExecuteParams, 'to'> & {
283
291
  * - ChainTarget: External chain target (Routes 2, 3, 4)
284
292
  */
285
293
  to: UniversalTo;
286
- /**
287
- * SVM-specific: CPI execution parameters for Solana targets (Route 2).
288
- * When present, the outbound transaction will execute a Cross-Program Invocation
289
- * on the specified Solana program with the given accounts and instruction data.
290
- * Only applicable when `to.chain` is a Solana chain.
291
- */
292
- svmExecute?: SvmExecuteParams;
293
294
  /**
294
295
  * When true, sends MIGRATION_SELECTOR as raw CEA payload (no multicall wrapping).
295
- * Used for CEA contract upgrades. Incompatible with value/funds/data/svmExecute.
296
+ * Used for CEA contract upgrades. Incompatible with value/funds/data.
296
297
  * Only applicable for Route 2 (UOA_TO_CEA) on EVM chains.
297
298
  */
298
299
  migration?: boolean;
@@ -321,18 +322,6 @@ export interface SvmExecutePayloadFields {
321
322
  /** Instruction ID: 2 = execute (default) */
322
323
  instructionId?: number;
323
324
  }
324
- /**
325
- * User-facing parameters for CPI execution on a Solana program via Route 2.
326
- * Passed as `svmExecute` in UniversalExecuteParams when targeting SVM chains.
327
- */
328
- export interface SvmExecuteParams {
329
- /** Target Solana program to CPI into (32 bytes, 0x-prefixed hex) */
330
- targetProgram: `0x${string}`;
331
- /** Accounts required for the CPI call */
332
- accounts: SvmGatewayAccountMeta[];
333
- /** Raw instruction data for the target program */
334
- ixData: Uint8Array;
335
- }
336
325
  /**
337
326
  * Request structure for sendUniversalTxOutbound on Push Chain
338
327
  * Used for Routes 2, 3, 4 (outbound from Push)
@@ -249,9 +249,6 @@ function validateRouteParams(params, context) {
249
249
  if (params.data) {
250
250
  throw new RouteValidationError('migration is incompatible with data');
251
251
  }
252
- if (params.svmExecute) {
253
- throw new RouteValidationError('migration is incompatible with svmExecute');
254
- }
255
252
  if (isChainTarget(params.to) && (0, payload_builders_1.isSvmChain)(params.to.chain)) {
256
253
  throw new RouteValidationError('migration is not supported on SVM chains');
257
254
  }
@@ -0,0 +1,11 @@
1
+ import type { ChainTarget } from '../orchestrator.types';
2
+ export interface SvmPayloadBundle {
3
+ svmPayload: `0x${string}`;
4
+ targetBytes: `0x${string}`;
5
+ hasExecute: boolean;
6
+ }
7
+ export declare function buildSvmPayloadFromParams(params: {
8
+ data?: `0x${string}` | unknown;
9
+ to: ChainTarget;
10
+ senderUea: `0x${string}`;
11
+ }): SvmPayloadBundle;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildSvmPayloadFromParams = buildSvmPayloadFromParams;
4
+ const viem_1 = require("viem");
5
+ const payload_builders_1 = require("../payload-builders");
6
+ const resolve_1 = require("./resolve");
7
+ function buildSvmPayloadFromParams(params) {
8
+ const hasExecute = typeof params.data === 'string' &&
9
+ params.data.startsWith('0x') &&
10
+ params.data.length > 2;
11
+ if (!hasExecute) {
12
+ return {
13
+ svmPayload: '0x',
14
+ targetBytes: params.to.address,
15
+ hasExecute: false,
16
+ };
17
+ }
18
+ const resolved = (0, resolve_1.resolveSvmCall)({
19
+ programAddress: params.to.address,
20
+ data: (0, viem_1.hexToBytes)(params.data),
21
+ senderUea: params.senderUea,
22
+ targetChain: params.to.chain,
23
+ });
24
+ const svmPayload = (0, payload_builders_1.encodeSvmExecutePayload)({
25
+ targetProgram: resolved.targetProgram,
26
+ accounts: resolved.accounts,
27
+ ixData: resolved.ixData,
28
+ instructionId: 2,
29
+ });
30
+ return {
31
+ svmPayload,
32
+ targetBytes: resolved.targetProgram,
33
+ hasExecute: true,
34
+ };
35
+ }
36
+ //# sourceMappingURL=build-payload.js.map
@@ -0,0 +1,3 @@
1
+ import { type Idl } from '@coral-xyz/anchor';
2
+ export declare function encodeAnchorIxData(idl: Idl, functionName: string, positionalArgs?: unknown[]): Uint8Array;
3
+ export declare function isAnchorIdl(value: unknown): value is Idl;