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

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.
@@ -255,11 +255,7 @@ function executeUoaToCea(ctx, params, executeFn) {
255
255
  ctx.pushClient.getBalance(ueaAddress),
256
256
  ]);
257
257
  const isUEADeployed = isNativePushEOA || ueaCode !== undefined;
258
- if (!isUEADeployed) {
259
- throw new Error('UEA is not deployed. Please send an inbound transaction to Push Chain first ' +
260
- '(e.g. sendTransaction with value) to deploy your Universal Execution Account before using outbound transfers.');
261
- }
262
- const ueaNonce = isNativePushEOA ? BigInt(0) : yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress);
258
+ const ueaNonce = (!isUEADeployed || isNativePushEOA) ? BigInt(0) : yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress);
263
259
  // Build the multicall that will execute ON Push Chain from UEA context
264
260
  // This includes: 1) approve PRC-20 (if needed), 2) call sendUniversalTxOutbound
265
261
  const pushChainMulticalls = [];
@@ -288,7 +284,11 @@ function executeUoaToCea(ctx, params, executeFn) {
288
284
  // Reuse ueaBalance fetched earlier (line 383) — no tx sent between, balance is stable
289
285
  const currentBalance = ueaBalance;
290
286
  let adjustedValue;
291
- if (currentBalance > EVM_NATIVE_VALUE_TARGET + EVM_GAS_RESERVE) {
287
+ if (!isUEADeployed) {
288
+ // UEA not yet deployed — fee-locking will deposit funds; use target value, excess is refunded
289
+ adjustedValue = nativeValueForGas > BigInt(0) ? EVM_NATIVE_VALUE_TARGET : nativeValueForGas;
290
+ }
291
+ else if (currentBalance > EVM_NATIVE_VALUE_TARGET + EVM_GAS_RESERVE) {
292
292
  // Enough balance: use 200 UPC target
293
293
  adjustedValue = EVM_NATIVE_VALUE_TARGET;
294
294
  }
@@ -354,7 +354,7 @@ function executeUoaToCea(ctx, params, executeFn) {
354
354
  nonce: ueaNonce,
355
355
  balance: ueaBalance,
356
356
  },
357
- _skipFeeLocking: true, // outbound executes on Push Chain, no external fee locking
357
+ _skipFeeLocking: isUEADeployed, // skip fee-locking only if UEA is already deployed
358
358
  };
359
359
  const response = yield executeFn(executeParams);
360
360
  // Add chain info to response
@@ -463,11 +463,7 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
463
463
  isUEADeployed = ueaCode !== undefined;
464
464
  ueaBalance = balance;
465
465
  }
466
- if (!isUEADeployed) {
467
- throw new Error('UEA is not deployed. Please send an inbound transaction to Push Chain first ' +
468
- '(e.g. sendTransaction with value) to deploy your Universal Execution Account before using outbound transfers.');
469
- }
470
- const ueaNonce = isNativePushEOASvm ? BigInt(0) : yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress);
466
+ const ueaNonce = (!isUEADeployed || isNativePushEOASvm) ? BigInt(0) : yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress);
471
467
  // --- Query gas fee (identical to EVM path) ---
472
468
  let gasFee = BigInt(0);
473
469
  let gasToken = selectors_1.ZERO_ADDRESS;
@@ -531,7 +527,7 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
531
527
  nonce: ueaNonce,
532
528
  balance: ueaBalance,
533
529
  },
534
- _skipFeeLocking: true, // outbound executes on Push Chain, no external fee locking
530
+ _skipFeeLocking: isUEADeployed, // skip fee-locking only if UEA is already deployed
535
531
  };
536
532
  const response = yield executeFn(executeParams);
537
533
  // Add chain info to response
@@ -617,7 +613,7 @@ function executeCeaToPush(ctx, params, executeFn) {
617
613
  // external chain), but that approach is racy: the balance can change between the SDK
618
614
  // query and relay execution, causing sendUniversalTxToUEA to revert with
619
615
  // InsufficientBalance. Pre-existing CEA funds remain parked and can be swept separately.
620
- let bridgeAmount = amount;
616
+ const bridgeAmount = amount;
621
617
  // Note: CEA contract may reject amount=0 in sendUniversalTxToUEA.
622
618
  // Keeping bridgeAmount as-is (0) for payload-only to test precompile behavior.
623
619
  // For ERC20 tokens, add approve call for the bridge amount
@@ -129,7 +129,7 @@ class Orchestrator {
129
129
  */
130
130
  execute(params) {
131
131
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
132
- var _a, _b, _c, _d, _e;
132
+ var _a, _b, _c, _d;
133
133
  // Lazy UEA upgrade check
134
134
  try {
135
135
  if (!this.accountStatusCache || !this.accountStatusCache.uea.loaded) {
@@ -166,16 +166,6 @@ class Orchestrator {
166
166
  try {
167
167
  const rcb = () => this._getResponseCallbacks();
168
168
  if (execute.funds) {
169
- const hasData = execute.data && execute.data !== '0x';
170
- const isSVM = ((_e = chain_1.CHAIN_INFO[this.ctx.universalSigner.account.chain]) === null || _e === void 0 ? void 0 : _e.vm) === enums_1.VM.SVM;
171
- // SVM two-phase: bridge funds first, then execute payload separately
172
- // (Push Chain node doesn't support FUNDS_AND_PAYLOAD payload decoding for SVM yet)
173
- if (hasData && isSVM) {
174
- const fundsOnly = Object.assign(Object.assign({}, execute), { data: undefined });
175
- yield (0, internals_1.executeFundsOnly)(this.ctx, fundsOnly, eventBuffer, rcb);
176
- const dataOnly = Object.assign(Object.assign({}, execute), { funds: undefined });
177
- return yield (0, internals_1.executeStandardPayload)(this.ctx, dataOnly, eventBuffer, rcb);
178
- }
179
169
  return !hasData
180
170
  ? yield (0, internals_1.executeFundsOnly)(this.ctx, execute, eventBuffer, rcb)
181
171
  : yield (0, internals_1.executeFundsWithPayload)(this.ctx, execute, eventBuffer, rcb);
@@ -400,6 +400,7 @@ class PushChain {
400
400
  // Stored on instance so consumers can await it if needed: await client.accountStatusReady
401
401
  const ACCOUNT_STATUS_TIMEOUT = 30000;
402
402
  instance.accountStatusReady = Promise.race([
403
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
403
404
  instance.getAccountStatus().then(() => { }),
404
405
  new Promise((_, reject) => setTimeout(() => reject(new Error('Account status fetch timed out')), ACCOUNT_STATUS_TIMEOUT)),
405
406
  ]).catch(() => {
@@ -69,29 +69,37 @@ export declare function convertExecutorToOrigin(executorAddress: string, options
69
69
  _internal?: boolean;
70
70
  }): Promise<OriginAccountInfo>;
71
71
  /**
72
- * Derive the executor account (UEA or CEA) for a given origin account.
72
+ * Derive the executor account (UEA or CEA) for a given account.
73
73
  *
74
- * @param account - CAIP-10 formatted address string (e.g., "eip155:11155111:0xABC...")
75
- * @param options.chain - Target chain for CEA derivation. If omitted, derives UEA on Push Chain.
74
+ * - If account is on an external chain (Ethereum, Solana, etc.):
75
+ * Computes the UEA on Push Chain via the factory contract's computeUEA.
76
+ * If options.chain is an external chain, further derives the CEA on that chain.
77
+ *
78
+ * - If account is on Push Chain:
79
+ * Returns the address as-is (it's already a UEA or Push EOA).
80
+ * If options.chain is an external chain, derives the CEA on that chain.
81
+ *
82
+ * @param account - UniversalAccount with chain and address.
83
+ * @param options.chain - Target external chain for CEA derivation. If omitted, returns UEA.
76
84
  * @param options.skipNetworkCheck - If true, skip deployment check and return deployed: false.
77
85
  * @returns The derived executor account address and deployment status.
78
86
  */
79
- export declare function deriveExecutorAccount(account: string, options?: {
87
+ export declare function deriveExecutorAccount(account: UniversalAccount, options?: {
80
88
  chain?: CHAIN;
81
89
  skipNetworkCheck?: boolean;
82
90
  }): Promise<DerivedExecutorAccount>;
83
91
  /**
84
92
  * Resolve the controller (origin) accounts for a given executor address.
85
93
  *
86
- * Given a UEA or CEA address (as CAIP-10), resolves the full controller chain
94
+ * Given a UEA or CEA plain address, resolves the full controller chain
87
95
  * back to the origin account.
88
96
  *
89
- * @param account - CAIP-10 formatted address string of the executor account
90
- * @param options.chain - Supplementary chain context (chain info is in CAIP-10)
97
+ * @param address - Plain address string of the executor account (e.g., "0xABC...")
98
+ * @param options.chain - Chain context. If external chain, address is treated as CEA on that chain. If omitted or Push chain, address is treated as UEA on Push Chain.
91
99
  * @param options.skipNetworkCheck - If true, skip existence checks, set exists: false
92
100
  * @returns Resolved accounts in the controller chain
93
101
  */
94
- export declare function resolveControllerAccount(account: string, options?: {
102
+ export declare function resolveControllerAccount(address: string, options?: {
95
103
  chain?: CHAIN;
96
104
  skipNetworkCheck?: boolean;
97
105
  }): Promise<ResolvedControllerAccounts>;
@@ -431,120 +431,129 @@ function getChainName(chain) {
431
431
  return entry ? entry[0] : chain;
432
432
  }
433
433
  /**
434
- * Derive the executor account (UEA or CEA) for a given origin account.
434
+ * Derive the executor account (UEA or CEA) for a given account.
435
435
  *
436
- * @param account - CAIP-10 formatted address string (e.g., "eip155:11155111:0xABC...")
437
- * @param options.chain - Target chain for CEA derivation. If omitted, derives UEA on Push Chain.
436
+ * - If account is on an external chain (Ethereum, Solana, etc.):
437
+ * Computes the UEA on Push Chain via the factory contract's computeUEA.
438
+ * If options.chain is an external chain, further derives the CEA on that chain.
439
+ *
440
+ * - If account is on Push Chain:
441
+ * Returns the address as-is (it's already a UEA or Push EOA).
442
+ * If options.chain is an external chain, derives the CEA on that chain.
443
+ *
444
+ * @param account - UniversalAccount with chain and address.
445
+ * @param options.chain - Target external chain for CEA derivation. If omitted, returns UEA.
438
446
  * @param options.skipNetworkCheck - If true, skip deployment check and return deployed: false.
439
447
  * @returns The derived executor account address and deployment status.
440
448
  */
441
449
  function deriveExecutorAccount(account, options) {
442
450
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
443
451
  var _a;
444
- const { chain: originChain, address } = fromChainAgnostic(account);
445
452
  const skipNetworkCheck = (_a = options === null || options === void 0 ? void 0 : options.skipNetworkCheck) !== null && _a !== void 0 ? _a : false;
446
453
  const targetChain = options === null || options === void 0 ? void 0 : options.chain;
447
- // If target chain is external, derive CEA on that chain
448
- if (targetChain && !isPushChain(targetChain)) {
449
- // First get the UEA on Push Chain
454
+ const { chain, address } = account;
455
+ // --- Case 1: Input is on an external chain ---
456
+ if (!isPushChain(chain)) {
457
+ const { vm, chainId } = chain_1.CHAIN_INFO[chain];
458
+ const pushNetwork = getPushNetworkFromChain(chain);
459
+ // Check cache for previously computed UEA
460
+ const cachedUea = accountCache.get(cache_1.CacheKeys.ueaAddressOnchain(chain, address, pushNetwork, vm));
450
461
  let ueaAddress;
451
- if (isPushChain(originChain)) {
452
- ueaAddress = address;
462
+ if (cachedUea) {
463
+ ueaAddress = cachedUea;
453
464
  }
454
465
  else {
455
- const ueaResult = yield deriveExecutorAccount(account);
456
- ueaAddress = ueaResult.address;
466
+ // Compute UEA via factory contract
467
+ const pushChainKey = pushNetworkToChainKey(pushNetwork);
468
+ const pushClient = new push_client_1.PushClient({
469
+ rpcUrls: chain_1.CHAIN_INFO[pushChainKey].defaultRPC,
470
+ network: pushNetwork,
471
+ });
472
+ ueaAddress = yield pushClient.readContract({
473
+ address: pushClient.pushChainInfo.factoryAddress,
474
+ abi: factoryV1_1.FACTORY_V1,
475
+ functionName: 'computeUEA',
476
+ args: [
477
+ {
478
+ chainNamespace: chain_1.VM_NAMESPACE[vm],
479
+ chainId: chainId,
480
+ owner: vm === enums_1.VM.EVM
481
+ ? address
482
+ : vm === enums_1.VM.SVM
483
+ ? (0, viem_1.bytesToHex)(Uint8Array.from(bs58_1.bs58.decode(address)))
484
+ : address,
485
+ },
486
+ ],
487
+ });
488
+ // Cache the computed address
489
+ accountCache.set(cache_1.CacheKeys.ueaAddressOnchain(chain, address, pushNetwork, vm), ueaAddress);
457
490
  }
458
- // Get CEA on the target external chain
459
- const { cea, isDeployed } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, targetChain);
460
- return { address: cea, deployed: skipNetworkCheck ? false : isDeployed };
461
- }
462
- // If origin is Push chain, return the address directly
463
- if (isPushChain(originChain)) {
491
+ // If target chain is external, derive CEA from the computed UEA
492
+ if (targetChain && !isPushChain(targetChain)) {
493
+ const { cea, isDeployed } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, targetChain);
494
+ return { address: cea, deployed: skipNetworkCheck ? false : isDeployed };
495
+ }
496
+ // Return UEA with deployment check
464
497
  if (skipNetworkCheck) {
465
- return { address: address, deployed: false };
498
+ return { address: ueaAddress, deployed: false };
466
499
  }
467
- const pushNetwork = getPushNetworkFromChain(originChain);
468
500
  const pushChainKey = pushNetworkToChainKey(pushNetwork);
469
501
  const pushClient = new push_client_1.PushClient({
470
502
  rpcUrls: chain_1.CHAIN_INFO[pushChainKey].defaultRPC,
471
503
  network: pushNetwork,
472
504
  });
473
505
  const byteCode = yield pushClient.publicClient.getCode({
474
- address: address,
506
+ address: ueaAddress,
475
507
  });
476
- return { address: address, deployed: byteCode !== undefined };
508
+ const isDeployed = byteCode !== undefined;
509
+ accountCache.set(cache_1.CacheKeys.deploymentStatus(ueaAddress), isDeployed);
510
+ return { address: ueaAddress, deployed: isDeployed };
477
511
  }
478
- // Origin is external chain → compute UEA on Push Chain
479
- const { vm, chainId } = chain_1.CHAIN_INFO[originChain];
480
- const pushNetwork = getPushNetworkFromChain(originChain);
481
- // Check cache
482
- const cachedAddress = accountCache.get(cache_1.CacheKeys.ueaAddressOnchain(originChain, address, pushNetwork, vm));
483
- if (cachedAddress) {
484
- if (skipNetworkCheck) {
485
- return { address: cachedAddress, deployed: false };
486
- }
487
- const cachedDeploymentStatus = accountCache.get(cache_1.CacheKeys.deploymentStatus(cachedAddress));
488
- if (cachedDeploymentStatus !== null) {
489
- return {
490
- address: cachedAddress,
491
- deployed: cachedDeploymentStatus,
492
- };
493
- }
512
+ // --- Case 2: Input is on Push Chain ---
513
+ // If target chain is external, derive CEA
514
+ if (targetChain && !isPushChain(targetChain)) {
515
+ const { cea, isDeployed } = yield (0, cea_utils_1.getCEAAddress)(address, targetChain);
516
+ return { address: cea, deployed: skipNetworkCheck ? false : isDeployed };
494
517
  }
518
+ // Push address, no external target — return with deployment check
519
+ if (skipNetworkCheck) {
520
+ return { address: address, deployed: false };
521
+ }
522
+ const pushNetwork = targetChain && isPushChain(targetChain)
523
+ ? getPushNetworkFromChain(targetChain)
524
+ : getPushNetworkFromChain(chain);
495
525
  const pushChainKey = pushNetworkToChainKey(pushNetwork);
496
526
  const pushClient = new push_client_1.PushClient({
497
527
  rpcUrls: chain_1.CHAIN_INFO[pushChainKey].defaultRPC,
498
528
  network: pushNetwork,
499
529
  });
500
- const computedAddress = yield pushClient.readContract({
501
- address: pushClient.pushChainInfo.factoryAddress,
502
- abi: factoryV1_1.FACTORY_V1,
503
- functionName: 'computeUEA',
504
- args: [
505
- {
506
- chainNamespace: chain_1.VM_NAMESPACE[vm],
507
- chainId: chainId,
508
- owner: vm === enums_1.VM.EVM
509
- ? address
510
- : vm === enums_1.VM.SVM
511
- ? (0, viem_1.bytesToHex)(Uint8Array.from(bs58_1.bs58.decode(address)))
512
- : address,
513
- },
514
- ],
515
- });
516
- // Cache the computed address
517
- accountCache.set(cache_1.CacheKeys.ueaAddressOnchain(originChain, address, pushNetwork, vm), computedAddress);
518
- if (skipNetworkCheck) {
519
- return { address: computedAddress, deployed: false };
520
- }
521
530
  const byteCode = yield pushClient.publicClient.getCode({
522
- address: computedAddress,
531
+ address: address,
523
532
  });
524
- const isDeployed = byteCode !== undefined;
525
- // Cache the deployment status
526
- accountCache.set(cache_1.CacheKeys.deploymentStatus(computedAddress), isDeployed);
527
- return { address: computedAddress, deployed: isDeployed };
533
+ return {
534
+ address: address,
535
+ deployed: byteCode !== undefined,
536
+ };
528
537
  });
529
538
  }
530
539
  /**
531
540
  * Resolve the controller (origin) accounts for a given executor address.
532
541
  *
533
- * Given a UEA or CEA address (as CAIP-10), resolves the full controller chain
542
+ * Given a UEA or CEA plain address, resolves the full controller chain
534
543
  * back to the origin account.
535
544
  *
536
- * @param account - CAIP-10 formatted address string of the executor account
537
- * @param options.chain - Supplementary chain context (chain info is in CAIP-10)
545
+ * @param address - Plain address string of the executor account (e.g., "0xABC...")
546
+ * @param options.chain - Chain context. If external chain, address is treated as CEA on that chain. If omitted or Push chain, address is treated as UEA on Push Chain.
538
547
  * @param options.skipNetworkCheck - If true, skip existence checks, set exists: false
539
548
  * @returns Resolved accounts in the controller chain
540
549
  */
541
- function resolveControllerAccount(account, options) {
550
+ function resolveControllerAccount(address, options) {
542
551
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
543
552
  var _a;
544
- const { chain: inputChain, address } = fromChainAgnostic(account);
553
+ const inputChain = options === null || options === void 0 ? void 0 : options.chain;
545
554
  const skipNetworkCheck = (_a = options === null || options === void 0 ? void 0 : options.skipNetworkCheck) !== null && _a !== void 0 ? _a : false;
546
- // Case A: Input is on an external chain (CEA)
547
- if (!isPushChain(inputChain)) {
555
+ // Case A: External chain → address is a CEA on that chain
556
+ if (inputChain && !isPushChain(inputChain)) {
548
557
  // Look up UEA on Push Chain from CEA
549
558
  const pushAccountAddress = yield (0, cea_utils_1.getPushAccountForCEA)(address, inputChain);
550
559
  if (pushAccountAddress === '0x0000000000000000000000000000000000000000') {
@@ -597,9 +606,12 @@ function resolveControllerAccount(account, options) {
597
606
  }
598
607
  return { accounts };
599
608
  }
600
- // Case B: Input is on Push Chain (UEA or EOA)
601
- const pushNetwork = getPushNetworkFromChain(inputChain);
609
+ // Case B: No chain or Push chain → address is a UEA or EOA on Push Chain
610
+ const pushNetwork = inputChain && isPushChain(inputChain)
611
+ ? getPushNetworkFromChain(inputChain)
612
+ : enums_1.PUSH_NETWORK.TESTNET_DONUT;
602
613
  const pushChainKey = pushNetworkToChainKey(pushNetwork);
614
+ const pushChainId = pushChainKey;
603
615
  const RPC_URL = chain_1.PUSH_CHAIN_INFO[pushChainKey].defaultRPC[0];
604
616
  const FACTORY_ADDRESS = chain_1.PUSH_CHAIN_INFO[pushChainKey].factoryAddress;
605
617
  const client = (0, viem_1.createPublicClient)({ transport: (0, viem_1.http)(RPC_URL) });
@@ -639,8 +651,8 @@ function resolveControllerAccount(account, options) {
639
651
  return {
640
652
  accounts: [
641
653
  {
642
- chain: inputChain,
643
- chainName: getChainName(inputChain),
654
+ chain: pushChainId,
655
+ chainName: getChainName(pushChainId),
644
656
  address: address,
645
657
  type: 'uoa',
646
658
  exists: skipNetworkCheck ? false : true,