@pushchain/core 5.1.4 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushchain/core",
3
- "version": "5.1.4",
3
+ "version": "5.1.5-alpha.0",
4
4
  "homepage": "https://push.org",
5
5
  "keywords": [
6
6
  "pushchain",
@@ -1,7 +1,7 @@
1
1
  import { PushChain } from './push-chain/push-chain';
2
2
  export type { ConversionQuote } from './constants/tokens';
3
3
  export type { UniversalSigner, UniversalAccount, UniversalSignerSkeleton, DerivedExecutorAccount, ResolvedAccount, ResolvedControllerAccounts, AccountType, AccountRole, } from './universal/universal.types';
4
- export type { ChainTarget, ChainSource, UniversalTo, TransactionRouteType, UniversalExecuteParams, UniversalOutboundTxRequest, PreparedUniversalTx, CascadedTxResponse, CascadeHopInfo, CascadeTrackOptions, CascadeProgressEvent, CascadeCompletionResult, MultiChainTxResponse, OutboundTxDetails, WaitForOutboundOptions, SvmGatewayAccountMeta, SvmExecutePayloadFields, SvmExecuteParams, AccountStatus, UEAStatus, RescueFundsParams, } from './orchestrator/orchestrator.types';
4
+ export type { ChainTarget, ChainSource, UniversalTo, TransactionRouteType, UniversalExecuteParams, UniversalOutboundTxRequest, PreparedUniversalTx, CascadedTxResponse, CascadeHopInfo, CascadeTrackOptions, CascadeProgressEvent, CascadeCompletionResult, MultiChainTxResponse, OutboundTxDetails, WaitForOutboundOptions, SvmGatewayAccountMeta, SvmExecutePayloadFields, AccountStatus, UEAStatus, RescueFundsParams, } from './orchestrator/orchestrator.types';
5
5
  export { parseUEAVersion } from './orchestrator/orchestrator.types';
6
6
  export { TransactionRoute, detectRoute, isChainTarget } from './orchestrator/route-detector';
7
7
  export { getCEAAddress, getPushAccountForCEA, isCEA, chainSupportsCEA, chainSupportsOutbound, getCEAFactoryAddress, getAllCEAAddresses, } from './orchestrator/cea-utils';
@@ -27,6 +27,7 @@ const payload_builder_1 = require("./payload-builder");
27
27
  const uea_manager_1 = require("./uea-manager");
28
28
  const gas_calculator_1 = require("./gas-calculator");
29
29
  const route_handlers_1 = require("./route-handlers");
30
+ const build_payload_1 = require("../svm-idl/build-payload");
30
31
  // ============================================================================
31
32
  // prepareTransaction
32
33
  // ============================================================================
@@ -110,18 +111,12 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
110
111
  const targetChain = target.chain;
111
112
  // Branch: SVM vs EVM
112
113
  if ((0, payload_builders_1.isSvmChain)(targetChain)) {
113
- // SVM path: no CEA lookup, build SVM payload
114
- const hasSvmExecute = !!params.svmExecute;
115
- let svmPayload = '0x';
116
- if (hasSvmExecute) {
117
- const exec = params.svmExecute;
118
- svmPayload = (0, payload_builders_1.encodeSvmExecutePayload)({
119
- targetProgram: exec.targetProgram,
120
- accounts: exec.accounts,
121
- ixData: exec.ixData,
122
- instructionId: 2,
123
- });
124
- }
114
+ // SVM path: no CEA lookup, build SVM payload via IDL resolver
115
+ const { svmPayload, hasExecute: hasSvmExecute } = (0, build_payload_1.buildSvmPayloadFromParams)({
116
+ data: params.data,
117
+ to: target,
118
+ senderUea: ueaAddress,
119
+ });
125
120
  let prc20Token = selectors_1.ZERO_ADDRESS;
126
121
  let burnAmount = BigInt(0);
127
122
  if ((_b = params.funds) === null || _b === void 0 ? void 0 : _b.amount) {
@@ -432,7 +427,7 @@ function getSegmentType(route) {
432
427
  // composeCascade
433
428
  // ============================================================================
434
429
  function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
435
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
430
+ var _a, _b, _c, _d, _e, _f, _g;
436
431
  let accumulatedPushMulticalls = [];
437
432
  const gatewayPcAddress = (0, helpers_1.getUniversalGatewayPCAddress)();
438
433
  // Compute per-outbound nativeValueForGas from UEA balance
@@ -465,10 +460,9 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
465
460
  if (isSvmSegment) {
466
461
  // SVM: use the pre-built SVM payload from the hop descriptor
467
462
  outboundPayload = (_a = firstHop.svmPayload) !== null && _a !== void 0 ? _a : '0x';
468
- // For SVM, target is the recipient/program from the hop params
463
+ // For SVM, to.address IS the target (program for execute, recipient for withdraw)
469
464
  const svmTarget = firstHop.params.to;
470
- targetForOutbound =
471
- (_c = (_b = firstHop.params.svmExecute) === null || _b === void 0 ? void 0 : _b.targetProgram) !== null && _c !== void 0 ? _c : svmTarget.address;
465
+ targetForOutbound = svmTarget.address;
472
466
  }
473
467
  else if (isMigration) {
474
468
  // Migration: use raw 4-byte MIGRATION_SELECTOR, no multicall wrapping
@@ -482,7 +476,7 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
482
476
  targetForOutbound = (firstHop === null || firstHop === void 0 ? void 0 : firstHop.ceaAddress) || ueaAddress;
483
477
  }
484
478
  // Build outbound request
485
- const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetForOutbound, segment.prc20Token || selectors_1.ZERO_ADDRESS, segment.totalBurnAmount || BigInt(0), (_d = segment.gasLimit) !== null && _d !== void 0 ? _d : BigInt(0), outboundPayload, ueaAddress);
479
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetForOutbound, segment.prc20Token || selectors_1.ZERO_ADDRESS, segment.totalBurnAmount || BigInt(0), (_b = segment.gasLimit) !== null && _b !== void 0 ? _b : BigInt(0), outboundPayload, ueaAddress);
486
480
  // Build approval + outbound multicalls
487
481
  const segGasFee = segment.gasFee || BigInt(0);
488
482
  const outboundMulticalls = (0, payload_builders_1.buildOutboundApprovalAndCall)({
@@ -507,7 +501,7 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
507
501
  // Build push multicalls from this hop's own data (e.g., counter.increment())
508
502
  // This is the Route 3 hop's payload that executes on Push Chain after inbound.
509
503
  const hop0 = segment.hops[0];
510
- if ((_e = hop0 === null || hop0 === void 0 ? void 0 : hop0.params) === null || _e === void 0 ? void 0 : _e.data) {
504
+ if ((_c = hop0 === null || hop0 === void 0 ? void 0 : hop0.params) === null || _c === void 0 ? void 0 : _c.data) {
511
505
  const hopPushMulticalls = (0, payload_builders_1.buildExecuteMulticall)({
512
506
  execute: {
513
507
  to: hop0.params.to,
@@ -543,7 +537,7 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
543
537
  let drainAmount = BigInt(0);
544
538
  let tokenMintHex;
545
539
  const params = hop.params;
546
- if (((_f = params.funds) === null || _f === void 0 ? void 0 : _f.amount) && params.funds.amount > BigInt(0)) {
540
+ if (((_d = params.funds) === null || _d === void 0 ? void 0 : _d.amount) && params.funds.amount > BigInt(0)) {
547
541
  drainAmount = params.funds.amount;
548
542
  const token = params.funds.token;
549
543
  if (token && token.address) {
@@ -572,7 +566,7 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
572
566
  });
573
567
  const svmBurnAmount = segment.totalBurnAmount || BigInt(0);
574
568
  const outboundReq = (0, payload_builders_1.buildOutboundRequest)(gatewayProgramHex, segment.prc20Token ||
575
- (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), svmBurnAmount, (_g = segment.gasLimit) !== null && _g !== void 0 ? _g : BigInt(0), svmPayload, ueaAddress);
569
+ (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), svmBurnAmount, (_e = segment.gasLimit) !== null && _e !== void 0 ? _e : BigInt(0), svmPayload, ueaAddress);
576
570
  const inboundGasFee = segment.gasFee || BigInt(0);
577
571
  const outboundMulticalls = (0, payload_builders_1.buildOutboundApprovalAndCall)({
578
572
  prc20Token: segment.prc20Token ||
@@ -598,7 +592,7 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
598
592
  let tokenAddress = selectors_1.ZERO_ADDRESS;
599
593
  let amount = BigInt(0);
600
594
  const params = hop.params;
601
- if ((_h = params.funds) === null || _h === void 0 ? void 0 : _h.amount) {
595
+ if ((_f = params.funds) === null || _f === void 0 ? void 0 : _f.amount) {
602
596
  const token = params.funds.token;
603
597
  if (token) {
604
598
  if (token.mechanism === 'native') {
@@ -645,7 +639,7 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
645
639
  // Wrap CEA multicall in outbound from Push Chain
646
640
  const ceaPayload = (0, payload_builders_1.buildCeaMulticallPayload)(ceaMulticalls);
647
641
  const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, segment.prc20Token ||
648
- (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), segment.totalBurnAmount || BigInt(0), (_j = segment.gasLimit) !== null && _j !== void 0 ? _j : BigInt(0), ceaPayload, ueaAddress);
642
+ (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), segment.totalBurnAmount || BigInt(0), (_g = segment.gasLimit) !== null && _g !== void 0 ? _g : BigInt(0), ceaPayload, ueaAddress);
649
643
  const inboundGasFee = segment.gasFee || BigInt(0);
650
644
  const outboundMulticalls = (0, payload_builders_1.buildOutboundApprovalAndCall)({
651
645
  prc20Token: segment.prc20Token ||
@@ -74,15 +74,15 @@ function executeFundsWithPayload(ctx, execute, eventBuffer, getResponseCallbacks
74
74
  const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
75
75
  const ueaBalance = yield ctx.pushClient.getBalance(ueaAddress);
76
76
  (0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_03_02, ueaAddress, deployed);
77
- // Determine USD to deposit via gateway (8 decimals) with caps: min=$1, max=$10
77
+ // Determine USD to deposit via gateway (8 decimals) with caps: min=$1, max=$1000
78
78
  const oneUsd = utils_1.Utils.helpers.parseUnits('1', 8);
79
- const tenUsd = utils_1.Utils.helpers.parseUnits('10', 8);
79
+ const maxUsd = utils_1.Utils.helpers.parseUnits('1000', 8);
80
80
  const deficit = requiredFunds > ueaBalance ? requiredFunds - ueaBalance : BigInt(0);
81
81
  let depositUsd = deficit > BigInt(0) ? ctx.pushClient.pushToUSDC(deficit) : oneUsd;
82
82
  if (depositUsd < oneUsd)
83
83
  depositUsd = oneUsd;
84
- if (depositUsd > tenUsd)
85
- throw new Error('Deposit value exceeds max $10 worth of native token');
84
+ if (depositUsd > maxUsd)
85
+ throw new Error('Deposit value exceeds max $1000 worth of native token');
86
86
  (0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_02_02, depositUsd);
87
87
  // If SVM, clamp depositUsd to on-chain Config caps
88
88
  if (chain_1.CHAIN_INFO[chain].vm === enums_1.VM.SVM) {
@@ -200,7 +200,34 @@ function executeStandardPayload(ctx, execute, eventBuffer, getResponseCallbacks)
200
200
  const fundDifference = requiredFunds - funds;
201
201
  const fixedPushAmount = utils_1.Utils.helpers.parseUnits('0.001', 18);
202
202
  const lockAmount = funds < requiredFunds ? fundDifference : fixedPushAmount;
203
- const lockAmountInUSD = ctx.pushClient.pushToUSDC(lockAmount);
203
+ // Size the USD deposit against the REAL Uniswap pool rate (not the fixed
204
+ // $0.10/PC SDK rate) for EVM origins. The pool typically prices PC much
205
+ // higher than $0.10, so pushToUSDC under-provisions and the resulting
206
+ // deposit gets clamped up to the $1 minimum, producing near-zero WPC.
207
+ // When the quoted USD exceeds the $1000 cap the later clamp kicks in
208
+ // (pool limit surfaces via the pre-flight capacity message).
209
+ // Falls back to pushToUSDC if the quoter is unavailable.
210
+ const { vm: sizingOriginVm } = chain_1.CHAIN_INFO[chain];
211
+ let lockAmountInUSD = ctx.pushClient.pushToUSDC(lockAmount);
212
+ if (sizingOriginVm === enums_1.VM.EVM && lockAmount > BigInt(0)) {
213
+ // Target lockAmount × 1.2 so that the 10% slippage margin applied by
214
+ // the pre-flight check (safeUPC = predictedUPC × 0.9) leaves enough
215
+ // headroom even when Uniswap V3 exactOutput↔exactInput quotes aren't
216
+ // perfectly symmetric (tick-math rounding).
217
+ const targetWPC = (lockAmount * BigInt(12)) / BigInt(10);
218
+ const originPrc20ForSizing = (0, helpers_2.getNativePRC20ForChain)(chain, ctx.pushNetwork);
219
+ const requiredNative = yield (0, gas_calculator_1.estimateNativeForDesiredDeposit)(ctx, targetWPC, originPrc20ForSizing);
220
+ if (requiredNative > BigInt(0)) {
221
+ const ethPriceForSizing = yield new price_fetch_1.PriceFetch(ctx.rpcUrls).getPrice(chain);
222
+ if (ethPriceForSizing > BigInt(0)) {
223
+ // requiredNative (18-dec wei) × ethPrice (8-dec USD/ETH) / 1e18 = USD in 8-dec
224
+ const poolBasedUsd = (requiredNative * ethPriceForSizing) / BigInt(1e18);
225
+ (0, context_1.printLog)(ctx, `Fee-lock sizing: pushToUSDC=${lockAmountInUSD.toString()} (fixed $0.10/PC), ` +
226
+ `poolBased=${poolBasedUsd.toString()} (pool quote for ${targetWPC.toString()} WPC) — using poolBased`);
227
+ lockAmountInUSD = poolBasedUsd;
228
+ }
229
+ }
230
+ }
204
231
  // Apply minimum deposit override (e.g., Route 2 needs enough UPC for outbound swap)
205
232
  const effectiveLockAmountInUSD = execute._minimumDepositUsd && execute._minimumDepositUsd > lockAmountInUSD
206
233
  ? execute._minimumDepositUsd
@@ -212,12 +239,12 @@ function executeStandardPayload(ctx, execute, eventBuffer, getResponseCallbacks)
212
239
  const { vm: originVm } = chain_1.CHAIN_INFO[chain];
213
240
  if (originVm === enums_1.VM.EVM) {
214
241
  try {
215
- // Replicate lockFee's USD → native conversion (with its $1–$10 clamp)
242
+ // Replicate lockFee's USD → native conversion (with its $1–$1000 clamp)
216
243
  const oneUsd = utils_1.Utils.helpers.parseUnits('1', 8);
217
- const tenUsd = utils_1.Utils.helpers.parseUnits('10', 8);
244
+ const maxUsd = utils_1.Utils.helpers.parseUnits('1000', 8);
218
245
  let depositUsd = effectiveLockAmountInUSD < oneUsd ? oneUsd : effectiveLockAmountInUSD;
219
- if (depositUsd > tenUsd)
220
- depositUsd = tenUsd;
246
+ if (depositUsd > maxUsd)
247
+ depositUsd = maxUsd;
221
248
  const ethPrice = yield new price_fetch_1.PriceFetch(ctx.rpcUrls).getPrice(chain);
222
249
  if (ethPrice > BigInt(0)) {
223
250
  const nativeAmountETH = (depositUsd * BigInt(1e18) + (ethPrice - BigInt(1))) / ethPrice + BigInt(1);
@@ -233,11 +260,32 @@ function executeStandardPayload(ctx, execute, eventBuffer, getResponseCallbacks)
233
260
  `total: ${totalAvailable.toString()}, required: ${requiredFunds.toString()}`);
234
261
  if (totalAvailable < requiredFunds) {
235
262
  const shortfall = requiredFunds - totalAvailable;
263
+ // On failure only: probe pool capacity at the max cap so the
264
+ // caller gets a readable "max transferable right now" number.
265
+ // One extra quoter call — not run on the success path.
266
+ let maxTransferableNote = '';
267
+ if (depositUsd < maxUsd) {
268
+ const maxNativeAmountETH = (maxUsd * BigInt(1e18) + (ethPrice - BigInt(1))) / ethPrice + BigInt(1);
269
+ const maxPredictedUPC = yield (0, gas_calculator_1.estimateDepositFromLockedNative)(ctx, maxNativeAmountETH, originPrc20);
270
+ if (maxPredictedUPC > BigInt(0)) {
271
+ const maxSafeUPC = (maxPredictedUPC * BigInt(90)) / BigInt(100);
272
+ const gasReserve = utils_1.Utils.helpers.parseUnits('0.01', 18);
273
+ const maxFromDeposit = maxSafeUPC > gasReserve ? maxSafeUPC - gasReserve : BigInt(0);
274
+ const maxTransferable = funds + maxFromDeposit;
275
+ maxTransferableNote =
276
+ `Max transferable right now (at $1000 cap, pool-limited): ` +
277
+ `~${(Number(maxTransferable) / 1e18).toFixed(4)} $PC ` +
278
+ `(UEA balance ${(Number(funds) / 1e18).toFixed(4)} + ` +
279
+ `pool-safe deposit ${(Number(maxFromDeposit) / 1e18).toFixed(4)} − gas).`;
280
+ (0, context_1.printLog)(ctx, `Fee-lock capacity: ${maxTransferableNote}`);
281
+ }
282
+ }
236
283
  throw new Error(`Insufficient deposit: the fee-lock will deposit ~${(Number(safeUPC) / 1e18).toFixed(4)} $PC ` +
237
284
  `but the transaction requires ~${(Number(requiredFunds) / 1e18).toFixed(4)} $PC ` +
238
285
  `(shortfall: ~${(Number(shortfall) / 1e18).toFixed(4)} $PC). ` +
239
- `The fee-lock deposit is capped at $10 USD. Reduce the transfer value or ` +
240
- `pre-fund the UEA (${UEA}) on Push Chain.`);
286
+ `The fee-lock deposit is capped at $1000 USD. ` +
287
+ (maxTransferableNote ? maxTransferableNote + ' ' : '') +
288
+ `Reduce the transfer value or pre-fund the UEA (${UEA}) on Push Chain.`);
241
289
  }
242
290
  }
243
291
  }
@@ -45,3 +45,13 @@ export declare function calculateGasAmountFromAmountOutMinETH(ctx: OrchestratorC
45
45
  * Returns 0 on any failure (caller should fall back to other logic).
46
46
  */
47
47
  export declare function estimateDepositFromLockedNative(ctx: OrchestratorContext, nativeAmountForLocker: bigint, prc20Token: `0x${string}`): Promise<bigint>;
48
+ /**
49
+ * Inverse of estimateDepositFromLockedNative: given a desired WPC output,
50
+ * asks the Push Chain Uniswap V3 pool how much native PRC-20 (pETH) input is
51
+ * required to produce exactly that output via QuoterV2.quoteExactOutputSingle.
52
+ *
53
+ * Used to size fee-lock deposits against the real on-chain pool rate instead
54
+ * of the fixed $0.10/PC SDK rate. Returns 0 on any failure (caller should
55
+ * fall back to the fixed-rate sizing).
56
+ */
57
+ export declare function estimateNativeForDesiredDeposit(ctx: OrchestratorContext, desiredWpc: bigint, prc20Token: `0x${string}`): Promise<bigint>;
@@ -11,6 +11,7 @@ exports.queryRescueGasFee = queryRescueGasFee;
11
11
  exports.calculateNativeAmountForDeposit = calculateNativeAmountForDeposit;
12
12
  exports.calculateGasAmountFromAmountOutMinETH = calculateGasAmountFromAmountOutMinETH;
13
13
  exports.estimateDepositFromLockedNative = estimateDepositFromLockedNative;
14
+ exports.estimateNativeForDesiredDeposit = estimateNativeForDesiredDeposit;
14
15
  const tslib_1 = require("tslib");
15
16
  const web3_js_1 = require("@solana/web3.js");
16
17
  const viem_1 = require("viem");
@@ -336,13 +337,13 @@ function calculateNativeAmountForDeposit(ctx, chain, requiredFunds, ueaBalance)
336
337
  var _a, _b;
337
338
  (0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_02_01);
338
339
  const oneUsd = utils_1.Utils.helpers.parseUnits('1', 8);
339
- const tenUsd = utils_1.Utils.helpers.parseUnits('10', 8);
340
+ const maxUsd = utils_1.Utils.helpers.parseUnits('1000', 8);
340
341
  const deficit = requiredFunds > ueaBalance ? requiredFunds - ueaBalance : BigInt(0);
341
342
  let depositUsd = deficit > BigInt(0) ? ctx.pushClient.pushToUSDC(deficit) : oneUsd;
342
343
  if (depositUsd < oneUsd)
343
344
  depositUsd = oneUsd;
344
- if (depositUsd > tenUsd)
345
- throw new Error('Deposit value exceeds max $10 worth of native token');
345
+ if (depositUsd > maxUsd)
346
+ throw new Error('Deposit value exceeds max $1000 worth of native token');
346
347
  if (chain_1.CHAIN_INFO[chain].vm === enums_1.VM.SVM) {
347
348
  const svmClient = new svm_client_1.SvmClient({
348
349
  rpcUrls: ctx.rpcUrls[enums_1.CHAIN.SOLANA_DEVNET] ||
@@ -515,4 +516,95 @@ function estimateDepositFromLockedNative(ctx, nativeAmountForLocker, prc20Token)
515
516
  }
516
517
  });
517
518
  }
519
+ /**
520
+ * Inverse of estimateDepositFromLockedNative: given a desired WPC output,
521
+ * asks the Push Chain Uniswap V3 pool how much native PRC-20 (pETH) input is
522
+ * required to produce exactly that output via QuoterV2.quoteExactOutputSingle.
523
+ *
524
+ * Used to size fee-lock deposits against the real on-chain pool rate instead
525
+ * of the fixed $0.10/PC SDK rate. Returns 0 on any failure (caller should
526
+ * fall back to the fixed-rate sizing).
527
+ */
528
+ function estimateNativeForDesiredDeposit(ctx, desiredWpc, prc20Token) {
529
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
530
+ var _a, _b;
531
+ try {
532
+ if (desiredWpc <= BigInt(0))
533
+ return BigInt(0);
534
+ const pushChain = (0, helpers_1.getPushChainForNetwork)(ctx.pushNetwork);
535
+ const quoterAddress = (_b = (_a = chain_1.CHAIN_INFO[pushChain]) === null || _a === void 0 ? void 0 : _a.dex) === null || _b === void 0 ? void 0 : _b.uniV3QuoterV2;
536
+ if (!quoterAddress || quoterAddress.startsWith('0xTBD'))
537
+ return BigInt(0);
538
+ const gatewayPcAddress = (0, helpers_1.getUniversalGatewayPCAddress)();
539
+ const universalCoreAddress = yield ctx.pushClient.readContract({
540
+ address: gatewayPcAddress,
541
+ abi: abi_1.UNIVERSAL_GATEWAY_PC,
542
+ functionName: 'UNIVERSAL_CORE',
543
+ args: [],
544
+ });
545
+ const [wpcAddress, feeTier] = yield Promise.all([
546
+ ctx.pushClient.readContract({
547
+ address: universalCoreAddress,
548
+ abi: UNIVERSAL_CORE_SWAP_HELPERS_ABI,
549
+ functionName: 'WPC',
550
+ args: [],
551
+ }),
552
+ ctx.pushClient.readContract({
553
+ address: universalCoreAddress,
554
+ abi: UNIVERSAL_CORE_SWAP_HELPERS_ABI,
555
+ functionName: 'defaultFeeTier',
556
+ args: [prc20Token],
557
+ }),
558
+ ]);
559
+ if (!feeTier || !wpcAddress)
560
+ return BigInt(0);
561
+ const QUOTER_ABI = [
562
+ {
563
+ type: 'function',
564
+ name: 'quoteExactOutputSingle',
565
+ inputs: [
566
+ {
567
+ type: 'tuple',
568
+ components: [
569
+ { name: 'tokenIn', type: 'address' },
570
+ { name: 'tokenOut', type: 'address' },
571
+ { name: 'amount', type: 'uint256' },
572
+ { name: 'fee', type: 'uint24' },
573
+ { name: 'sqrtPriceLimitX96', type: 'uint160' },
574
+ ],
575
+ },
576
+ ],
577
+ outputs: [
578
+ { name: 'amountIn', type: 'uint256' },
579
+ { name: 'sqrtPriceX96After', type: 'uint160' },
580
+ { name: 'initializedTicksCrossed', type: 'uint32' },
581
+ { name: 'gasEstimate', type: 'uint256' },
582
+ ],
583
+ stateMutability: 'view',
584
+ },
585
+ ];
586
+ const result = yield ctx.pushClient.readContract({
587
+ address: quoterAddress,
588
+ abi: QUOTER_ABI,
589
+ functionName: 'quoteExactOutputSingle',
590
+ args: [
591
+ {
592
+ tokenIn: prc20Token,
593
+ tokenOut: wpcAddress,
594
+ amount: desiredWpc,
595
+ fee: feeTier,
596
+ sqrtPriceLimitX96: BigInt(0),
597
+ },
598
+ ],
599
+ });
600
+ const amountIn = result[0];
601
+ (0, context_1.printLog)(ctx, `estimateNativeForDesiredDeposit — quote: ${desiredWpc.toString()} WPC ← ${amountIn.toString()} pETH (fee tier: ${feeTier})`);
602
+ return amountIn;
603
+ }
604
+ catch (err) {
605
+ (0, context_1.printLog)(ctx, `estimateNativeForDesiredDeposit — failed: ${err instanceof Error ? err.message : String(err)}`);
606
+ return BigInt(0);
607
+ }
608
+ });
609
+ }
518
610
  //# sourceMappingURL=gas-calculator.js.map
@@ -96,10 +96,10 @@ req) {
96
96
  const nativeTokenUsdPrice = yield new price_fetch_1.PriceFetch(ctx.rpcUrls).getPrice(chain);
97
97
  const nativeDecimals = 18;
98
98
  const oneUsd = utils_1.Utils.helpers.parseUnits('1', 8);
99
- const tenUsd = utils_1.Utils.helpers.parseUnits('10', 8);
99
+ const maxUsd = utils_1.Utils.helpers.parseUnits('1000', 8);
100
100
  let depositUsd = amount < oneUsd ? oneUsd : amount;
101
- if (depositUsd > tenUsd)
102
- depositUsd = tenUsd;
101
+ if (depositUsd > maxUsd)
102
+ depositUsd = maxUsd;
103
103
  let nativeAmount = (depositUsd * BigInt(Math.pow(10, nativeDecimals)) +
104
104
  (nativeTokenUsdPrice - BigInt(1))) /
105
105
  nativeTokenUsdPrice;
@@ -114,10 +114,10 @@ req) {
114
114
  ]);
115
115
  const nativeDecimals = 9;
116
116
  const oneUsd = utils_1.Utils.helpers.parseUnits('1', 8);
117
- const tenUsd = utils_1.Utils.helpers.parseUnits('10', 8);
117
+ const maxUsd = utils_1.Utils.helpers.parseUnits('1000', 8);
118
118
  let depositUsd = amount < oneUsd ? oneUsd : amount;
119
- if (depositUsd > tenUsd)
120
- depositUsd = tenUsd;
119
+ if (depositUsd > maxUsd)
120
+ depositUsd = maxUsd;
121
121
  let nativeAmount = (depositUsd * BigInt(Math.pow(10, nativeDecimals)) +
122
122
  (nativeTokenUsdPrice - BigInt(1))) /
123
123
  nativeTokenUsdPrice;
@@ -35,6 +35,7 @@ const utils_1 = require("../../utils");
35
35
  const price_fetch_1 = require("../../price-fetch/price-fetch");
36
36
  const route_detector_1 = require("../route-detector");
37
37
  const cea_utils_1 = require("../cea-utils");
38
+ const build_payload_1 = require("../svm-idl/build-payload");
38
39
  const payload_builders_1 = require("../payload-builders");
39
40
  const selectors_1 = require("../../constants/selectors");
40
41
  const erc20_evm_1 = require("../../constants/abi/erc20.evm");
@@ -393,22 +394,15 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
393
394
  const targetChain = target.chain;
394
395
  const targetAddress = target.address; // 0x-prefixed, 32 bytes
395
396
  const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
396
- 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
+ });
397
402
  (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — target: ${targetAddress}, chain: ${targetChain}, ` +
398
403
  `hasSvmExecute: ${hasSvmExecute}, value: ${(_b = (_a = params.value) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : '0'}`);
399
- // --- Build SVM payload ---
400
- let svmPayload = '0x'; // empty for withdraw (SOL/SPL)
401
404
  if (hasSvmExecute) {
402
- // Execute case: encode the CPI payload
403
- const exec = params.svmExecute;
404
- svmPayload = (0, payload_builders_1.encodeSvmExecutePayload)({
405
- targetProgram: exec.targetProgram,
406
- accounts: exec.accounts,
407
- ixData: exec.ixData,
408
- instructionId: 2,
409
- });
410
- (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — encoded execute payload: ${(svmPayload.length - 2) / 2} bytes, ` +
411
- `${exec.accounts.length} accounts`);
405
+ (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — encoded execute payload: ${(svmPayload.length - 2) / 2} bytes`);
412
406
  }
413
407
  // --- Determine PRC-20 token and burn amount ---
414
408
  let prc20Token = selectors_1.ZERO_ADDRESS;
@@ -439,13 +433,8 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
439
433
  burnAmount = BigInt(0);
440
434
  (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — EXECUTE-only: using PRC-20 ${prc20Token} with zero burn amount`);
441
435
  }
442
- // --- Determine target bytes ---
443
- // For withdraw: target is the Solana recipient pubkey
444
- // For execute: target is the target Solana program
445
- const targetBytes = hasSvmExecute
446
- ? params.svmExecute.targetProgram
447
- : targetAddress;
448
436
  // --- Build outbound request ---
437
+ // targetBytes: for execute = program id (resolver output); for withdraw = to.address
449
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
450
439
  );
451
440
  (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — outbound request: ${JSON.stringify({
@@ -972,7 +961,7 @@ function executeCeaToCea(ctx, params, executeFn) {
972
961
  */
973
962
  function buildPayloadForRoute(ctx, params, route, nonce) {
974
963
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
975
- 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;
976
965
  const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
977
966
  switch (route) {
978
967
  case route_detector_1.TransactionRoute.UOA_TO_PUSH: {
@@ -995,20 +984,15 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
995
984
  const target = params.to;
996
985
  // Branch: SVM vs EVM
997
986
  if ((0, payload_builders_1.isSvmChain)(target.chain)) {
998
- // SVM: build SVM payload (binary or empty for withdraw)
999
- let payload = '0x';
1000
- if (params.svmExecute) {
1001
- payload = (0, payload_builders_1.encodeSvmExecutePayload)({
1002
- targetProgram: params.svmExecute.targetProgram,
1003
- accounts: params.svmExecute.accounts,
1004
- ixData: params.svmExecute.ixData,
1005
- instructionId: 2,
1006
- });
1007
- }
1008
- 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
+ });
1009
993
  let prc20Token = selectors_1.ZERO_ADDRESS;
1010
994
  let burnAmount = BigInt(0);
1011
- 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) {
1012
996
  const token = params.funds.token;
1013
997
  if (token) {
1014
998
  prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
@@ -1019,15 +1003,15 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1019
1003
  prc20Token = (0, helpers_1.getNativePRC20ForChain)(target.chain, ctx.pushNetwork);
1020
1004
  burnAmount = params.value;
1021
1005
  }
1022
- else if (params.svmExecute) {
1006
+ else if (hasExecute) {
1023
1007
  prc20Token = (0, helpers_1.getNativePRC20ForChain)(target.chain, ctx.pushNetwork);
1024
1008
  burnAmount = BigInt(1);
1025
1009
  }
1026
- 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);
1027
1011
  return { payload, gatewayRequest: outboundReq };
1028
1012
  }
1029
1013
  // EVM path: Resolve CEA address first (needed for self-transfer check)
1030
- 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]);
1031
1015
  // Build CEA outbound payload
1032
1016
  const multicalls = [];
1033
1017
  if (params.data) {
@@ -1060,7 +1044,7 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1060
1044
  const payload = (0, payload_builders_1.buildCeaMulticallPayload)(multicalls);
1061
1045
  let prc20Token = selectors_1.ZERO_ADDRESS;
1062
1046
  let burnAmount = BigInt(0);
1063
- 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) {
1064
1048
  const token = params.funds.token;
1065
1049
  if (token) {
1066
1050
  prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
@@ -1068,13 +1052,13 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1068
1052
  }
1069
1053
  }
1070
1054
  const targetBytes = ceaAddress;
1071
- 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);
1072
1056
  return { payload, gatewayRequest: outboundReq };
1073
1057
  }
1074
1058
  case route_detector_1.TransactionRoute.CEA_TO_PUSH: {
1075
1059
  // Route 3: CEA -> Push Chain
1076
1060
  // Build CEA multicall (approve + sendUniversalTxFromCEA) and wrap in outbound
1077
- 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)) {
1078
1062
  throw new Error('Route 3 (CEA -> Push) requires from.chain');
1079
1063
  }
1080
1064
  const sourceChain = params.from.chain;
@@ -1089,7 +1073,7 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1089
1073
  const gatewayProgramHex = ('0x' + Buffer.from(programPk.toBytes()).toString('hex'));
1090
1074
  let drainAmount = BigInt(0);
1091
1075
  let tokenMintHex;
1092
- 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)) {
1093
1077
  drainAmount = params.funds.amount;
1094
1078
  const token = params.funds.token;
1095
1079
  if (token && token.address) {
@@ -1114,10 +1098,10 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1114
1098
  });
1115
1099
  // burnAmount = 1 (minimum for precompile; drain amount lives inside the ixData)
1116
1100
  const burnAmount = BigInt(1);
1117
- 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);
1118
1102
  return { payload: svmPayload, gatewayRequest: outboundReq };
1119
1103
  }
1120
- 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]);
1121
1105
  // CEA auto-deploys on-chain: Vault.finalizeUniversalTx calls CEAFactory.deployCEA()
1122
1106
  // if CEA doesn't exist yet. No SDK-side blocking needed.
1123
1107
  const gatewayAddr = chain_1.UNIVERSAL_GATEWAY_ADDRESSES[sourceChain];
@@ -1128,7 +1112,7 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1128
1112
  const ceaMulticalls = [];
1129
1113
  let tokenAddress = selectors_1.ZERO_ADDRESS;
1130
1114
  let amount = BigInt(0);
1131
- 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) {
1132
1116
  const token = params.funds.token;
1133
1117
  if (token) {
1134
1118
  if (token.mechanism === 'native') {
@@ -1192,7 +1176,7 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1192
1176
  // Route 3: CEA uses its own pre-existing balance — no PRC-20 burn needed.
1193
1177
  // burnAmount = 0 makes this a payload-only outbound relay.
1194
1178
  const burnAmount = BigInt(0);
1195
- const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, prc20Token, burnAmount, (_o = params.gasLimit) !== null && _o !== void 0 ? _o : BigInt(0), ceaPayload, ueaAddress);
1179
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, prc20Token, burnAmount, (_l = params.gasLimit) !== null && _l !== void 0 ? _l : BigInt(0), ceaPayload, ueaAddress);
1196
1180
  return { payload: ceaPayload, gatewayRequest: outboundReq };
1197
1181
  }
1198
1182
  default:
@@ -110,7 +110,7 @@ export type ExecuteParams = {
110
110
  * Internal: Minimum fee-locking deposit in USD (8 decimals).
111
111
  * Overrides the default $1 floor when the caller needs a larger deposit
112
112
  * (e.g., Route 2 needs enough UPC for the outbound swap).
113
- * Still capped at $10 by lockFee.
113
+ * Still capped at $1000 by lockFee.
114
114
  * @internal
115
115
  */
116
116
  _minimumDepositUsd?: bigint;
@@ -291,16 +291,9 @@ export type UniversalExecuteParams = Omit<ExecuteParams, 'to'> & {
291
291
  * - ChainTarget: External chain target (Routes 2, 3, 4)
292
292
  */
293
293
  to: UniversalTo;
294
- /**
295
- * SVM-specific: CPI execution parameters for Solana targets (Route 2).
296
- * When present, the outbound transaction will execute a Cross-Program Invocation
297
- * on the specified Solana program with the given accounts and instruction data.
298
- * Only applicable when `to.chain` is a Solana chain.
299
- */
300
- svmExecute?: SvmExecuteParams;
301
294
  /**
302
295
  * When true, sends MIGRATION_SELECTOR as raw CEA payload (no multicall wrapping).
303
- * Used for CEA contract upgrades. Incompatible with value/funds/data/svmExecute.
296
+ * Used for CEA contract upgrades. Incompatible with value/funds/data.
304
297
  * Only applicable for Route 2 (UOA_TO_CEA) on EVM chains.
305
298
  */
306
299
  migration?: boolean;
@@ -329,18 +322,6 @@ export interface SvmExecutePayloadFields {
329
322
  /** Instruction ID: 2 = execute (default) */
330
323
  instructionId?: number;
331
324
  }
332
- /**
333
- * User-facing parameters for CPI execution on a Solana program via Route 2.
334
- * Passed as `svmExecute` in UniversalExecuteParams when targeting SVM chains.
335
- */
336
- export interface SvmExecuteParams {
337
- /** Target Solana program to CPI into (32 bytes, 0x-prefixed hex) */
338
- targetProgram: `0x${string}`;
339
- /** Accounts required for the CPI call */
340
- accounts: SvmGatewayAccountMeta[];
341
- /** Raw instruction data for the target program */
342
- ixData: Uint8Array;
343
- }
344
325
  /**
345
326
  * Request structure for sendUniversalTxOutbound on Push Chain
346
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;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.encodeAnchorIxData = encodeAnchorIxData;
4
+ exports.isAnchorIdl = isAnchorIdl;
5
+ const anchor_1 = require("@coral-xyz/anchor");
6
+ const web3_js_1 = require("@solana/web3.js");
7
+ const buffer_1 = require("buffer");
8
+ function encodeAnchorIxData(idl, functionName, positionalArgs = []) {
9
+ var _a, _b;
10
+ const ix = findInstruction(idl, functionName);
11
+ if (!ix) {
12
+ throw new Error(`Instruction '${functionName}' not found in IDL '${(_b = (_a = idl.metadata) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : 'unknown'}'`);
13
+ }
14
+ if (positionalArgs.length !== ix.args.length) {
15
+ throw new Error(`Arg count mismatch for '${ix.name}': IDL expects ${ix.args.length}, got ${positionalArgs.length}`);
16
+ }
17
+ const named = {};
18
+ for (let i = 0; i < ix.args.length; i++) {
19
+ named[ix.args[i].name] = normalizeArg(positionalArgs[i], ix.args[i]);
20
+ }
21
+ const coder = new anchor_1.BorshInstructionCoder(idl);
22
+ return new Uint8Array(coder.encode(ix.name, named));
23
+ }
24
+ function findInstruction(idl, name) {
25
+ const snake = name.replace(/([A-Z])/g, (_, c) => '_' + c.toLowerCase());
26
+ const camel = name.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
27
+ return idl.instructions.find((i) => i.name === name || i.name === snake || i.name === camel);
28
+ }
29
+ function normalizeArg(value, field) {
30
+ if (typeof value === 'bigint') {
31
+ return new anchor_1.BN(value.toString());
32
+ }
33
+ if (field.type === 'pubkey' &&
34
+ typeof value === 'string' &&
35
+ value.startsWith('0x') &&
36
+ value.length === 66) {
37
+ return new web3_js_1.PublicKey(buffer_1.Buffer.from(value.slice(2), 'hex'));
38
+ }
39
+ return value;
40
+ }
41
+ function isAnchorIdl(value) {
42
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
43
+ return false;
44
+ const maybe = value;
45
+ return Array.isArray(maybe['instructions']) && typeof maybe['address'] === 'string';
46
+ }
47
+ //# sourceMappingURL=ix-encoder.js.map
@@ -0,0 +1,10 @@
1
+ import type { Idl } from '@coral-xyz/anchor';
2
+ type AddressKey = `0x${string}`;
3
+ export declare function registerIdl(programAddress: string, idl: unknown): void;
4
+ export declare function getIdl(programAddress: string): Idl | undefined;
5
+ export declare function getRegisteredIdls(): Array<{
6
+ address: AddressKey;
7
+ idl: Idl;
8
+ }>;
9
+ export declare function clearRegistry(): void;
10
+ export {};
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerIdl = registerIdl;
4
+ exports.getIdl = getIdl;
5
+ exports.getRegisteredIdls = getRegisteredIdls;
6
+ exports.clearRegistry = clearRegistry;
7
+ const ix_encoder_1 = require("./ix-encoder");
8
+ const store = new Map();
9
+ function normalizeKey(address) {
10
+ if (typeof address !== 'string' || !address.startsWith('0x')) {
11
+ throw new Error(`registerIdl: programAddress must be 0x-prefixed 32-byte hex, got '${address}'`);
12
+ }
13
+ const body = address.slice(2);
14
+ if (body.length !== 64 || !/^[0-9a-fA-F]+$/.test(body)) {
15
+ throw new Error(`registerIdl: programAddress must be 32 bytes (66 chars incl. 0x), got length ${address.length}`);
16
+ }
17
+ return ('0x' + body.toLowerCase());
18
+ }
19
+ function registerIdl(programAddress, idl) {
20
+ const key = normalizeKey(programAddress);
21
+ if (!(0, ix_encoder_1.isAnchorIdl)(idl)) {
22
+ throw new Error(`registerIdl: idl for ${programAddress} is not a recognized Anchor IDL (missing 'instructions' or 'address')`);
23
+ }
24
+ store.set(key, idl);
25
+ }
26
+ function getIdl(programAddress) {
27
+ const key = normalizeKey(programAddress);
28
+ return store.get(key);
29
+ }
30
+ function getRegisteredIdls() {
31
+ return Array.from(store.entries()).map(([address, idl]) => ({ address, idl }));
32
+ }
33
+ function clearRegistry() {
34
+ store.clear();
35
+ }
36
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1,14 @@
1
+ import { CHAIN } from '../../constants/enums';
2
+ import type { SvmGatewayAccountMeta } from '../orchestrator.types';
3
+ export interface ResolveInput {
4
+ programAddress: `0x${string}`;
5
+ data: Uint8Array;
6
+ senderUea: `0x${string}`;
7
+ targetChain: CHAIN;
8
+ }
9
+ export interface ResolvedSvmCall {
10
+ targetProgram: `0x${string}`;
11
+ accounts: SvmGatewayAccountMeta[];
12
+ ixData: Uint8Array;
13
+ }
14
+ export declare function resolveSvmCall(input: ResolveInput): ResolvedSvmCall;
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveSvmCall = resolveSvmCall;
4
+ const web3_js_1 = require("@solana/web3.js");
5
+ const buffer_1 = require("buffer");
6
+ const account_1 = require("../../universal/account/account");
7
+ const registry_1 = require("./registry");
8
+ const CEA_AUTHORITY_NAMES = new Set([
9
+ 'cea_authority',
10
+ 'ceaAuthority',
11
+ 'cea',
12
+ 'cea_signer',
13
+ 'ceaSigner',
14
+ ]);
15
+ function resolveSvmCall(input) {
16
+ var _a, _b;
17
+ const idl = (0, registry_1.getIdl)(input.programAddress);
18
+ if (!idl) {
19
+ throw new Error(`resolveSvmCall: no IDL registered for ${input.programAddress}. ` +
20
+ `Call PushChain.utils.svm.registerIdl(${input.programAddress}, idl) first.`);
21
+ }
22
+ if (input.data.length < 8) {
23
+ throw new Error('resolveSvmCall: data must be at least 8 bytes (Anchor discriminator)');
24
+ }
25
+ const disc = input.data.slice(0, 8);
26
+ const ix = idl.instructions.find((i) => buffer_1.Buffer.from(i.discriminator).equals(buffer_1.Buffer.from(disc)));
27
+ if (!ix) {
28
+ throw new Error(`resolveSvmCall: no instruction in IDL '${(_b = (_a = idl.metadata) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : idl.address}' matches discriminator [${Array.from(disc).join(',')}]`);
29
+ }
30
+ const programPk = new web3_js_1.PublicKey(buffer_1.Buffer.from(input.programAddress.slice(2), 'hex'));
31
+ const { address: ceaPdaHex } = (0, account_1.deriveSvmCeaPda)(input.senderUea, input.targetChain, true);
32
+ const accounts = [];
33
+ for (const item of ix.accounts) {
34
+ if ('accounts' in item) {
35
+ throw new Error(`resolveSvmCall: nested account group '${item.name}' in '${ix.name}' not supported`);
36
+ }
37
+ const flat = item;
38
+ const pubkey = resolveAccountPubkey(flat, programPk, ceaPdaHex, ix.name);
39
+ accounts.push({ pubkey, isWritable: Boolean(flat.writable) });
40
+ }
41
+ return {
42
+ targetProgram: input.programAddress,
43
+ accounts,
44
+ ixData: input.data,
45
+ };
46
+ }
47
+ function resolveAccountPubkey(spec, programPk, ceaPdaHex, ixName) {
48
+ if (spec.address) {
49
+ return pkToHex(new web3_js_1.PublicKey(spec.address));
50
+ }
51
+ if (spec.pda) {
52
+ const seeds = resolveConstSeeds(spec.pda.seeds, spec.name, ixName);
53
+ const programForPda = spec.pda.program
54
+ ? new web3_js_1.PublicKey(resolveConstSeedValue(spec.pda.program, spec.name, ixName))
55
+ : programPk;
56
+ const [pda] = web3_js_1.PublicKey.findProgramAddressSync(seeds, programForPda);
57
+ return pkToHex(pda);
58
+ }
59
+ if (CEA_AUTHORITY_NAMES.has(spec.name)) {
60
+ return ceaPdaHex;
61
+ }
62
+ throw new Error(`resolveSvmCall: cannot resolve account '${spec.name}' in '${ixName}'. ` +
63
+ `Add 'address' or 'pda.seeds' to the IDL, or rename to a CEA convention (cea_authority).`);
64
+ }
65
+ function resolveConstSeeds(seeds, accountName, ixName) {
66
+ return seeds.map((seed, idx) => {
67
+ if (seed.kind !== 'const') {
68
+ throw new Error(`resolveSvmCall: non-const PDA seed (kind='${seed.kind}') on account '${accountName}' in '${ixName}' not supported yet`);
69
+ }
70
+ if (!Array.isArray(seed.value)) {
71
+ throw new Error(`resolveSvmCall: const seed at index ${idx} on '${accountName}' is malformed`);
72
+ }
73
+ return buffer_1.Buffer.from(seed.value);
74
+ });
75
+ }
76
+ function resolveConstSeedValue(seed, accountName, ixName) {
77
+ if (seed.kind !== 'const' || !Array.isArray(seed.value)) {
78
+ throw new Error(`resolveSvmCall: only const program-seed supported on '${accountName}' in '${ixName}'`);
79
+ }
80
+ return buffer_1.Buffer.from(seed.value);
81
+ }
82
+ function pkToHex(pk) {
83
+ return ('0x' + buffer_1.Buffer.from(pk.toBytes()).toString('hex'));
84
+ }
85
+ //# sourceMappingURL=resolve.js.map
@@ -396,15 +396,25 @@ class PushChain {
396
396
  */
397
397
  validatedUniversalSigner, (_c = options === null || options === void 0 ? void 0 : options.network) !== null && _c !== void 0 ? _c : enums_1.PUSH_NETWORK.TESTNET_DONUT, (_d = options === null || options === void 0 ? void 0 : options.rpcUrls) !== null && _d !== void 0 ? _d : {}, (_e = options === null || options === void 0 ? void 0 : options.printTraces) !== null && _e !== void 0 ? _e : false, options === null || options === void 0 ? void 0 : options.progressHook);
398
398
  const instance = new _a(orchestrator, validatedUniversalSigner, blockExplorers, isReadOnly);
399
- // Background fetch account status (non-blocking, 30s timeout)
400
- // Stored on instance so consumers can await it if needed: await client.accountStatusReady
399
+ // Background fetch account status (non-blocking, 30s timeout).
400
+ // The timer must be cleared once the race settles — otherwise the
401
+ // unref'd setTimeout keeps the Node event loop alive for the full
402
+ // 30s after the caller's work is done, making scripts appear "stuck".
401
403
  const ACCOUNT_STATUS_TIMEOUT = 30000;
404
+ let accountStatusTimer;
402
405
  instance.accountStatusReady = Promise.race([
403
406
  // eslint-disable-next-line @typescript-eslint/no-empty-function
404
407
  instance.getAccountStatus().then(() => { }),
405
- new Promise((_, reject) => setTimeout(() => reject(new Error('Account status fetch timed out')), ACCOUNT_STATUS_TIMEOUT)),
406
- ]).catch(() => {
408
+ new Promise((_, reject) => {
409
+ accountStatusTimer = setTimeout(() => reject(new Error('Account status fetch timed out')), ACCOUNT_STATUS_TIMEOUT);
410
+ }),
411
+ ])
412
+ .catch(() => {
407
413
  // Silently ignore — lazy check in execute() will retry if needed
414
+ })
415
+ .finally(() => {
416
+ if (accountStatusTimer)
417
+ clearTimeout(accountStatusTimer);
408
418
  });
409
419
  // Let execute() await the background fetch instead of re-fetching
410
420
  orchestrator.accountStatusReadyPromise = instance.accountStatusReady;
@@ -68,6 +68,19 @@ export declare function convertExecutorToOrigin(executorAddress: string, options
68
68
  /** @internal Skip deprecation warning for SDK-internal calls */
69
69
  _internal?: boolean;
70
70
  }): Promise<OriginAccountInfo>;
71
+ /**
72
+ * Derive the SVM CEA (PDA) for a UEA address on a Solana-based chain.
73
+ *
74
+ * Solana doesn't use a CEAFactory contract — instead, the CEA is a
75
+ * Program Derived Address (PDA) with seeds: ["push_identity", ueaBytes]
76
+ * and the SVM gateway program as the program ID.
77
+ *
78
+ * @param ueaAddress - The UEA (or Push EOA) hex address (20 bytes).
79
+ * @param targetChain - The SVM target chain.
80
+ * @param skipNetworkCheck - Whether to skip deployment status check.
81
+ * @returns The derived 32-byte Solana PDA address (as 0x-prefixed hex) and deployment status.
82
+ */
83
+ export declare function deriveSvmCeaPda(ueaAddress: `0x${string}`, targetChain: CHAIN, skipNetworkCheck: boolean): DerivedExecutorAccount;
71
84
  /**
72
85
  * Derive the executor account (UEA or CEA) for a given account.
73
86
  *
@@ -6,6 +6,7 @@ exports.fromChainAgnostic = fromChainAgnostic;
6
6
  exports.convertOriginToExecutor = convertOriginToExecutor;
7
7
  exports.convertExecutorToOriginAccount = convertExecutorToOriginAccount;
8
8
  exports.convertExecutorToOrigin = convertExecutorToOrigin;
9
+ exports.deriveSvmCeaPda = deriveSvmCeaPda;
9
10
  exports.deriveExecutorAccount = deriveExecutorAccount;
10
11
  exports.resolveControllerAccount = resolveControllerAccount;
11
12
  const tslib_1 = require("tslib");
@@ -3,6 +3,7 @@ import { construct, toUniversal as toUniversalSigner, toUniversalFromKeypair } f
3
3
  import { CHAIN, PUSH_NETWORK } from './constants/enums';
4
4
  import { type MoveableToken } from './constants/tokens';
5
5
  import type { PushChain } from './push-chain/push-chain';
6
+ import { registerIdl as registerSvmIdl, getRegisteredIdls as getRegisteredSvmIdls, getIdl as getSvmIdl } from './orchestrator/svm-idl/registry';
6
7
  /**
7
8
  * @dev - THESE UTILS ARE EXPORTED TO SDK CONSUMER
8
9
  * @dev - Make sure each exported fn has good comments to help out sdk consumer
@@ -32,6 +33,24 @@ export declare class Utils {
32
33
  deriveExecutorAccount: typeof deriveExecutorAccount;
33
34
  resolveControllerAccount: typeof resolveControllerAccount;
34
35
  };
36
+ /**
37
+ * SVM (Solana) utilities for registering program Anchor IDLs.
38
+ *
39
+ * Register your program's Anchor IDL once per process; `prepareTransaction`
40
+ * then resolves accounts, PDAs, and signers from the IDL so you only need
41
+ * to pass `data` (discriminator + Borsh args), the same shape used for EVM.
42
+ *
43
+ * @example
44
+ * PushChain.utils.svm.registerIdl(
45
+ * PROGRAM_ADDRESS_HEX,
46
+ * testCounterIdl
47
+ * );
48
+ */
49
+ static svm: {
50
+ registerIdl: typeof registerSvmIdl;
51
+ getRegisteredIdls: typeof getRegisteredSvmIdls;
52
+ getIdl: typeof getSvmIdl;
53
+ };
35
54
  static signer: {
36
55
  /**
37
56
  * Converts various signer types (viem, ethers v6, Solana) into a UniversalSigner.
@@ -98,7 +117,7 @@ export declare class Utils {
98
117
  */
99
118
  getChainName: (chainName: string) => string | undefined;
100
119
  encodeTxData({ abi, functionName, args, }: {
101
- abi: any[];
120
+ abi: any;
102
121
  functionName: string;
103
122
  args?: any[];
104
123
  }): `0x${string}`;
package/src/lib/utils.js CHANGED
@@ -7,6 +7,9 @@ const enums_1 = require("./constants/enums");
7
7
  const tokens_1 = require("./constants/tokens");
8
8
  const chain_1 = require("./constants/chain");
9
9
  const viem_1 = require("viem");
10
+ const buffer_1 = require("buffer");
11
+ const ix_encoder_1 = require("./orchestrator/svm-idl/ix-encoder");
12
+ const registry_1 = require("./orchestrator/svm-idl/registry");
10
13
  /**
11
14
  * @dev - THESE UTILS ARE EXPORTED TO SDK CONSUMER
12
15
  * @dev - Make sure each exported fn has good comments to help out sdk consumer
@@ -74,6 +77,24 @@ Utils.account = {
74
77
  deriveExecutorAccount: account_1.deriveExecutorAccount,
75
78
  resolveControllerAccount: account_1.resolveControllerAccount,
76
79
  };
80
+ /**
81
+ * SVM (Solana) utilities for registering program Anchor IDLs.
82
+ *
83
+ * Register your program's Anchor IDL once per process; `prepareTransaction`
84
+ * then resolves accounts, PDAs, and signers from the IDL so you only need
85
+ * to pass `data` (discriminator + Borsh args), the same shape used for EVM.
86
+ *
87
+ * @example
88
+ * PushChain.utils.svm.registerIdl(
89
+ * PROGRAM_ADDRESS_HEX,
90
+ * testCounterIdl
91
+ * );
92
+ */
93
+ Utils.svm = {
94
+ registerIdl: registry_1.registerIdl,
95
+ getRegisteredIdls: registry_1.getRegisteredIdls,
96
+ getIdl: registry_1.getIdl,
97
+ };
77
98
  Utils.signer = {
78
99
  /**
79
100
  * Converts various signer types (viem, ethers v6, Solana) into a UniversalSigner.
@@ -207,6 +228,13 @@ Utils.helpers = {
207
228
  };
208
229
  })(),
209
230
  encodeTxData({ abi, functionName, args = [], }) {
231
+ if ((0, ix_encoder_1.isAnchorIdl)(abi)) {
232
+ if (!Array.isArray(args)) {
233
+ throw new Error('Arguments must be an array');
234
+ }
235
+ const bytes = (0, ix_encoder_1.encodeAnchorIxData)(abi, functionName, args);
236
+ return `0x${buffer_1.Buffer.from(bytes).toString('hex')}`;
237
+ }
210
238
  // Validate inputs
211
239
  if (!Array.isArray(abi)) {
212
240
  throw new Error('ABI must be an array');