@pushchain/core 6.0.17 → 6.0.19

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.
Files changed (31) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/package.json +1 -1
  3. package/src/lib/constants/chain.d.ts +16 -0
  4. package/src/lib/constants/chain.js +32 -4
  5. package/src/lib/formatters.d.ts +1 -0
  6. package/src/lib/formatters.js +66 -1
  7. package/src/lib/orchestrator/internals/cascade.js +65 -47
  8. package/src/lib/orchestrator/internals/confirmation.js +8 -8
  9. package/src/lib/orchestrator/internals/error-decoder.d.ts +1 -0
  10. package/src/lib/orchestrator/internals/error-decoder.js +10 -0
  11. package/src/lib/orchestrator/internals/errors.js +1 -1
  12. package/src/lib/orchestrator/internals/execute-funds-only.js +8 -6
  13. package/src/lib/orchestrator/internals/execute-funds-payload.js +7 -5
  14. package/src/lib/orchestrator/internals/execute-standard.js +10 -8
  15. package/src/lib/orchestrator/internals/gateway-client.js +34 -1
  16. package/src/lib/orchestrator/internals/inbound-tracker.js +4 -3
  17. package/src/lib/orchestrator/internals/progress-route-hooks.d.ts +8 -4
  18. package/src/lib/orchestrator/internals/progress-route-hooks.js +8 -8
  19. package/src/lib/orchestrator/internals/push-chain-tx.js +71 -15
  20. package/src/lib/orchestrator/internals/response-builder.js +25 -16
  21. package/src/lib/orchestrator/internals/tx-transformer.js +11 -6
  22. package/src/lib/orchestrator/orchestrator.js +3 -17
  23. package/src/lib/orchestrator/orchestrator.types.d.ts +7 -0
  24. package/src/lib/progress-hook/progress-hook.js +37 -40
  25. package/src/lib/push-client/push-client.d.ts +39 -0
  26. package/src/lib/push-client/push-client.js +130 -12
  27. package/src/lib/universal/signer/signer.d.ts +1 -1
  28. package/src/lib/universal/signer/signer.js +83 -3
  29. package/src/lib/universal/universal.types.d.ts +37 -0
  30. package/src/lib/vm-client/evm-client.d.ts +43 -0
  31. package/src/lib/vm-client/evm-client.js +154 -1
@@ -6,6 +6,7 @@ const chain_1 = require("../constants/chain");
6
6
  const enums_1 = require("../constants/enums");
7
7
  const progress_hook_types_1 = require("../progress-hook/progress-hook.types");
8
8
  const push_client_1 = require("../push-client/push-client");
9
+ const formatters_1 = require("../formatters");
9
10
  const route_detector_1 = require("./route-detector");
10
11
  const data_utils_1 = require("./data-utils");
11
12
  const internals_1 = require("./internals");
@@ -216,11 +217,7 @@ class Orchestrator {
216
217
  // check throws). Without this, the hook stream ends mid-flight and
217
218
  // consumers have no terminal close-out marker.
218
219
  if (!isRecursiveInnerCall && !this.ctx._routeTerminalEmitted) {
219
- const errMessage = err instanceof Error
220
- ? err.message
221
- : typeof err === 'string'
222
- ? err
223
- : 'Unknown error';
220
+ const errMessage = (0, formatters_1.normalizePublicErrorMessage)(err);
224
221
  // Lift any structured `decodedError` off the typed error so the
225
222
  // formatter's optional payload field gets populated.
226
223
  const decodedError = err instanceof Error && err.decodedError
@@ -261,18 +258,7 @@ class Orchestrator {
261
258
  return attachPerCallHookForWait(yield (0, internals_1.executeStandardPayload)(this.ctx, execute, eventBuffer, rcb));
262
259
  }
263
260
  catch (err) {
264
- const errMessage = err instanceof Error
265
- ? err.message
266
- : typeof err === 'string'
267
- ? err
268
- : (() => {
269
- try {
270
- return JSON.stringify(err);
271
- }
272
- catch (_a) {
273
- return 'Unknown error';
274
- }
275
- })();
261
+ const errMessage = (0, formatters_1.normalizePublicErrorMessage)(err);
276
262
  // Only the outermost frame emits the terminal error hook, and it picks
277
263
  // the route-correct ID. Recursive inner frames just re-throw so the
278
264
  // outer catch drives emission — avoids double-firing 199/299/399 events.
@@ -225,6 +225,13 @@ export interface UniversalTxResponse {
225
225
  _setProgressHookNoReplay?: (callback: (event: import('../progress-hook/progress-hook.types').ProgressEvent) => void) => void;
226
226
  type: string;
227
227
  typeVerbose: string;
228
+ /**
229
+ * Whether the transaction executed atomically (all-or-nothing).
230
+ * `true` for a single tx, an EIP-7702 batch, or a UEA `UEA_MULTICALL` batch;
231
+ * `false` only when a multicall fell back to the non-atomic sequential loop
232
+ * (separate per-call txs — an earlier call can commit while a later one reverts).
233
+ */
234
+ atomic: boolean;
228
235
  signature: Signature;
229
236
  raw?: {
230
237
  from: string;
@@ -203,25 +203,25 @@ const RAW_HOOKS_R1 = {
203
203
  },
204
204
  level: 'INFO',
205
205
  }),
206
- [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_106_03]: (required) => ({
206
+ [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_106_03]: (required, txHash) => ({
207
207
  id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_106_03,
208
208
  title: 'Awaiting Confirmations',
209
209
  message: `Waiting for ${required} confirmations`,
210
- response: { current: 0, required },
210
+ response: Object.assign({ current: 0, required }, (txHash ? { txHash } : {})),
211
211
  level: 'INFO',
212
212
  }),
213
- [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_106_03_01]: (current, required) => ({
213
+ [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_106_03_01]: (current, required, txHash) => ({
214
214
  id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_106_03_01,
215
215
  title: `Confirmation ${current}/${required} Received`,
216
216
  message: `${current}/${required} confirmations received`,
217
- response: { current, required },
217
+ response: Object.assign({ current, required }, (txHash ? { txHash } : {})),
218
218
  level: 'INFO',
219
219
  }),
220
- [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_106_03_02]: (current, required) => ({
220
+ [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_106_03_02]: (current, required, txHash) => ({
221
221
  id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_106_03_02,
222
222
  title: `Confirmation ${current}/${required} Received`,
223
223
  message: `${current}/${required} confirmations received`,
224
- response: { current, required },
224
+ response: Object.assign({ current, required }, (txHash ? { txHash } : {})),
225
225
  level: 'SUCCESS',
226
226
  }),
227
227
  [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_106_04]: (txHash) => ({
@@ -264,13 +264,13 @@ const RAW_HOOKS_R1 = {
264
264
  level: 'SUCCESS',
265
265
  };
266
266
  },
267
- [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_199_02]: (errMessage, decodedError) => {
267
+ [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_199_02]: (errMessage, decodedError, txHash) => {
268
268
  const normalizedErrMessage = (0, formatters_1.normalizePcInsufficientFundsError)(errMessage);
269
269
  return {
270
270
  id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_199_02,
271
271
  title: 'Push Chain Tx Failed',
272
272
  message: normalizedErrMessage,
273
- response: Object.assign({ error: normalizedErrMessage }, (decodedError ? { decodedError } : {})),
273
+ response: Object.assign(Object.assign({ error: normalizedErrMessage }, (txHash ? { txHash } : {})), (decodedError ? { decodedError } : {})),
274
274
  level: 'ERROR',
275
275
  };
276
276
  },
@@ -435,18 +435,18 @@ const RAW_HOOKS_R2 = {
435
435
  response: { chain: targetChain },
436
436
  level: 'INFO',
437
437
  }),
438
- [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_209_01]: (targetChain) => ({
438
+ [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_209_01]: (targetChain, pushTxHash) => ({
439
439
  id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_209_01,
440
440
  title: 'Awaiting Push Chain Relay',
441
441
  message: `Waiting for UGPC to relay execution to ${targetChain}`,
442
- response: { chain: targetChain },
442
+ response: Object.assign({ chain: targetChain }, (pushTxHash ? { pushTxHash } : {})),
443
443
  level: 'INFO',
444
444
  }),
445
- [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_209_02]: (targetChain, elapsedMs) => ({
445
+ [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_209_02]: (targetChain, elapsedMs, pushTxHash) => ({
446
446
  id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_209_02,
447
447
  title: `Syncing State with ${friendlyChain(targetChain)}`,
448
448
  message: `Polling ${targetChain} for CEA execution`,
449
- response: { chain: targetChain, elapsedMs },
449
+ response: Object.assign({ chain: targetChain, elapsedMs }, (pushTxHash ? { pushTxHash } : {})),
450
450
  level: 'INFO',
451
451
  }),
452
452
  [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_299_01]: (details) => ({
@@ -456,18 +456,18 @@ const RAW_HOOKS_R2 = {
456
456
  response: Object.assign({ txHash: details.externalTxHash }, details),
457
457
  level: 'SUCCESS',
458
458
  }),
459
- [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_299_02]: (targetChain, errorMessage, decodedError) => ({
459
+ [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_299_02]: (targetChain, errorMessage, decodedError, pushTxHash, txHash) => ({
460
460
  id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_299_02,
461
461
  title: `${friendlyChain(targetChain)} Tx Failed`,
462
462
  message: errorMessage,
463
- response: Object.assign({ error: errorMessage, chain: targetChain }, (decodedError ? { decodedError } : {})),
463
+ response: Object.assign(Object.assign(Object.assign({ error: errorMessage, chain: targetChain }, (pushTxHash ? { pushTxHash } : {})), (txHash ? { txHash } : {})), (decodedError ? { decodedError } : {})),
464
464
  level: 'ERROR',
465
465
  }),
466
- [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_299_03]: (targetChain, elapsedMs) => ({
466
+ [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_299_03]: (targetChain, elapsedMs, pushTxHash) => ({
467
467
  id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_299_03,
468
468
  title: `Syncing State with ${friendlyChain(targetChain)} Timeout`,
469
469
  message: `Timed out waiting for UGPC relay to ${targetChain}`,
470
- response: { error: 'relay timeout', chain: targetChain, elapsedMs },
470
+ response: Object.assign({ error: 'relay timeout', chain: targetChain, elapsedMs }, (pushTxHash ? { pushTxHash } : {})),
471
471
  level: 'ERROR',
472
472
  }),
473
473
  [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_299_99]: (targetChain, txHash) => ({
@@ -646,18 +646,18 @@ const RAW_HOOKS_R3 = {
646
646
  response: { chain: sourceChain },
647
647
  level: 'INFO',
648
648
  }),
649
- [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_309_01]: (sourceChain) => ({
649
+ [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_309_01]: (sourceChain, pushTxHash) => ({
650
650
  id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_309_01,
651
651
  title: `Awaiting ${friendlyChain(sourceChain)} Relay`,
652
652
  message: `Waiting for UGPC to relay to CEA on ${sourceChain}`,
653
- response: { chain: sourceChain },
653
+ response: Object.assign({ chain: sourceChain }, (pushTxHash ? { pushTxHash } : {})),
654
654
  level: 'INFO',
655
655
  }),
656
- [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_309_02]: (sourceChain, elapsedMs) => ({
656
+ [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_309_02]: (sourceChain, elapsedMs, pushTxHash) => ({
657
657
  id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_309_02,
658
658
  title: `Syncing State with ${friendlyChain(sourceChain)}`,
659
659
  message: `Polling ${sourceChain} for CEA execution`,
660
- response: { chain: sourceChain, elapsedMs },
660
+ response: Object.assign({ chain: sourceChain, elapsedMs }, (pushTxHash ? { pushTxHash } : {})),
661
661
  level: 'INFO',
662
662
  }),
663
663
  [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_309_03]: (sourceChain, txHash) => ({
@@ -667,18 +667,18 @@ const RAW_HOOKS_R3 = {
667
667
  response: { chain: sourceChain, txHash },
668
668
  level: 'INFO',
669
669
  }),
670
- [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_310_01]: (sourceChain) => ({
670
+ [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_310_01]: (sourceChain, pushTxHash) => ({
671
671
  id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_310_01,
672
672
  title: `${friendlyChain(sourceChain)} → Push Chain Inbound Tx Submitted`,
673
673
  message: `CEA initiated return — waiting for Push Chain inbound from ${sourceChain}`,
674
- response: { chain: sourceChain },
674
+ response: Object.assign({ chain: sourceChain }, (pushTxHash ? { pushTxHash } : {})),
675
675
  level: 'INFO',
676
676
  }),
677
- [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_310_02]: (sourceChain, elapsedMs) => ({
677
+ [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_310_02]: (sourceChain, elapsedMs, pushTxHash) => ({
678
678
  id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_310_02,
679
679
  title: 'Syncing State with Push Chain for Inbound Tx',
680
680
  message: `Polling Push Chain for inbound from ${sourceChain}`,
681
- response: { chain: sourceChain, elapsedMs },
681
+ response: Object.assign({ chain: sourceChain, elapsedMs }, (pushTxHash ? { pushTxHash } : {})),
682
682
  level: 'INFO',
683
683
  }),
684
684
  [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_399_01]: (sourceChain, txHash, receipt) => ({
@@ -695,7 +695,7 @@ const RAW_HOOKS_R3 = {
695
695
  // Phase is passed by the emission site so the title matches the leg that
696
696
  // actually failed. Default stays 'inbound' for callers that don't specify
697
697
  // (backwards-compat with fixtures + response-builder's inbound block).
698
- [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_399_02]: (errorMessage, phase = 'inbound', chain, decodedError) => {
698
+ [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_399_02]: (errorMessage, phase = 'inbound', chain, decodedError, pushTxHash, txHash) => {
699
699
  const normalizedErrorMessage = phase === 'push'
700
700
  ? (0, formatters_1.normalizePcInsufficientFundsError)(errorMessage)
701
701
  : errorMessage;
@@ -707,11 +707,11 @@ const RAW_HOOKS_R3 = {
707
707
  ? 'Push Chain Tx Failed'
708
708
  : 'Push Chain Inbound Tx Failed',
709
709
  message: normalizedErrorMessage,
710
- response: Object.assign({ error: normalizedErrorMessage, phase, chain: chain !== null && chain !== void 0 ? chain : null }, (decodedError ? { decodedError } : {})),
710
+ response: Object.assign(Object.assign(Object.assign({ error: normalizedErrorMessage, phase, chain: chain !== null && chain !== void 0 ? chain : null }, (pushTxHash ? { pushTxHash } : {})), (txHash ? { txHash } : {})), (decodedError ? { decodedError } : {})),
711
711
  level: 'ERROR',
712
712
  };
713
713
  },
714
- [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_399_03]: (sourceChain, elapsedMs, phase = 'inbound') => ({
714
+ [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_399_03]: (sourceChain, elapsedMs, phase = 'inbound', pushTxHash) => ({
715
715
  id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_399_03,
716
716
  title: 'Push Chain Inbound Tx Timeout',
717
717
  message: phase === 'outbound'
@@ -719,16 +719,11 @@ const RAW_HOOKS_R3 = {
719
719
  : phase === 'push'
720
720
  ? `Timed out waiting for Push Chain tx`
721
721
  : `Timed out waiting for inbound from ${sourceChain}`,
722
- response: {
723
- error: phase === 'outbound'
722
+ response: Object.assign({ error: phase === 'outbound'
724
723
  ? 'outbound timeout'
725
724
  : phase === 'push'
726
725
  ? 'push timeout'
727
- : 'inbound timeout',
728
- phase,
729
- chain: sourceChain,
730
- elapsedMs,
731
- },
726
+ : 'inbound timeout', phase, chain: sourceChain, elapsedMs }, (pushTxHash ? { pushTxHash } : {})),
732
727
  level: 'ERROR',
733
728
  }),
734
729
  [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_399_99]: (sourceChain, txHash) => ({
@@ -877,25 +872,27 @@ const RAW_HOOKS_MULTICHAIN = {
877
872
  response: { quoted, threshold, gasToken, pathTag },
878
873
  level: 'INFO',
879
874
  }),
880
- [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_999_01]: (hopCount) => ({
875
+ [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_999_01]: (hopCount, txHash) => ({
881
876
  id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_999_01,
882
877
  title: 'All Multichain Transactions Successful',
883
878
  message: `${hopCount}-hop transaction confirmed across all chains`,
884
- response: { hopCount },
879
+ response: Object.assign({ hopCount }, (txHash ? { txHash } : {})),
885
880
  level: 'SUCCESS',
886
881
  }),
887
- [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_999_02]: (failedAt, total, errorMessage) => ({
882
+ [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_999_02]: (failedAt, total, errorMessage, txHash, pushTxHash) => ({
888
883
  id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_999_02,
889
884
  title: 'Multichain Transactions Failed',
890
885
  message: `Multichain failed at hop ${failedAt} of ${total}: ${errorMessage}`,
891
- response: { failedAt, total, error: errorMessage },
886
+ response: Object.assign(Object.assign({ failedAt,
887
+ total, error: errorMessage }, (txHash ? { txHash } : {})), (pushTxHash ? { pushTxHash } : {})),
892
888
  level: 'ERROR',
893
889
  }),
894
- [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_999_03]: (failedAt, total) => ({
890
+ [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_999_03]: (failedAt, total, txHash, pushTxHash) => ({
895
891
  id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_999_03,
896
892
  title: 'Multichain Transactions Timeout',
897
893
  message: `Multichain timed out at hop ${failedAt} of ${total}`,
898
- response: { failedAt, total, error: 'multichain timeout' },
894
+ response: Object.assign(Object.assign({ failedAt,
895
+ total, error: 'multichain timeout' }, (txHash ? { txHash } : {})), (pushTxHash ? { pushTxHash } : {})),
899
896
  level: 'ERROR',
900
897
  }),
901
898
  };
@@ -1,3 +1,4 @@
1
+ import { TransactionReceipt } from 'viem';
1
2
  import { MsgDeployUEA, MsgExecutePayload, MsgMintPC, MsgMigrateUEA } from '../generated/v1/tx';
2
3
  import { Any } from 'cosmjs-types/google/protobuf/any';
3
4
  import { TxBody, TxRaw } from 'cosmjs-types/cosmos/tx/v1beta1/tx';
@@ -6,6 +7,7 @@ import { QueryGetUniversalTxResponse } from '../generated/uexecutor/v1/query';
6
7
  import { QueryGetUniversalTxResponseV2 } from '../generated/uexecutor/v2/query';
7
8
  import { EvmClient } from '../vm-client/evm-client';
8
9
  import { PushClientOptions } from './push-client.types';
10
+ import { TxResponse } from '../vm-client/vm-client.types';
9
11
  export declare class PushClient extends EvmClient {
10
12
  /** Gas limit for Cosmos transactions on Push Chain */
11
13
  private static readonly COSMOS_GAS_LIMIT;
@@ -15,6 +17,7 @@ export declare class PushClient extends EvmClient {
15
17
  lockerContract?: string;
16
18
  gatewayVersion?: "v0" | "v1";
17
19
  defaultRPC: string[];
20
+ archiveRPC?: string[];
18
21
  confirmations: number;
19
22
  fastConfirmations: number;
20
23
  timeout: number;
@@ -28,6 +31,7 @@ export declare class PushClient extends EvmClient {
28
31
  } & {
29
32
  denom: string;
30
33
  tendermintRpc: string[];
34
+ archiveTendermintRpc?: string[];
31
35
  prefix: string;
32
36
  factoryAddress: `0x${string}`;
33
37
  pushDecimals: bigint;
@@ -38,12 +42,35 @@ export declare class PushClient extends EvmClient {
38
42
  private readonly ephemeralKey;
39
43
  private readonly ephemeralAccount;
40
44
  private currentRpcIndex;
45
+ /**
46
+ * Archive (full-history) endpoints. History-sensitive reads fall back from
47
+ * the prune RPC to these on a pruned-history miss. Resolved from the chain's
48
+ * configured archive endpoints — empty/undefined for chains without one
49
+ * (mainnet/localnet), where the fallback is a no-op (prune-only behaviour).
50
+ */
51
+ private readonly archiveTendermintRpc;
52
+ private readonly archivePublicClient?;
41
53
  constructor(clientOptions: PushClientOptions);
42
54
  /**
43
55
  * Executes an operation with automatic fallback to next RPC endpoint on failure.
44
56
  * Follows the same pattern as SvmClient.executeWithFallback().
45
57
  */
46
58
  private executeWithRpcFallback;
59
+ /**
60
+ * Runs a history-sensitive Tendermint operation over the prune pool first and,
61
+ * only when prune can't serve it (the op threw on all prune attempts, or
62
+ * returned an `isEmpty` result), re-queries the archive pool. This is the
63
+ * explicit "prune miss → archive" fallback the existing per-URL machinery
64
+ * can't do on its own, because a pruned tx comes back as a *successful*
65
+ * empty/not-found rather than a transport error.
66
+ *
67
+ * When no archive is configured (mainnet/localnet, or `enableArchiveFallback`
68
+ * off) this is a pass-through: the original throw or empty result is returned
69
+ * unchanged — behaviour identical to before.
70
+ */
71
+ private executeWithArchiveFallback;
72
+ /** True when this client has a usable archive EVM endpoint configured. */
73
+ private get hasArchiveEvm();
47
74
  /**
48
75
  * Converts nPUSH (1e18) to USDC (1e8), fixed rate: 1 PUSH = 0.1 USDC
49
76
  */
@@ -89,4 +116,16 @@ export declare class PushClient extends EvmClient {
89
116
  * @throws If the tx isn't found.
90
117
  */
91
118
  getCosmosTx(txHash: string): Promise<DeliverTxResponse>;
119
+ /**
120
+ * EVM `getTransaction` with archive fallback. Prune first; on a not-found
121
+ * (pruned, or not yet indexed) and an archive EVM endpoint configured, retry
122
+ * the archive client. Without archive this is exactly `super.getTransaction`.
123
+ */
124
+ getTransaction(txHash: `0x${string}`): Promise<TxResponse>;
125
+ /**
126
+ * EVM `getTransactionReceipt` with archive fallback. Used by the inbound
127
+ * tracker to fetch a Push-leg receipt that may predate the prune window.
128
+ * Prune first; on not-found, retry the archive EVM client if configured.
129
+ */
130
+ getTransactionReceiptWithArchiveFallback(txHash: `0x${string}`): Promise<TransactionReceipt>;
92
131
  }
@@ -27,11 +27,23 @@ function pushNetworkToChain(network) {
27
27
  }
28
28
  class PushClient extends evm_client_1.EvmClient {
29
29
  constructor(clientOptions) {
30
- var _a;
30
+ var _a, _b, _c;
31
31
  const pushChainKey = pushNetworkToChain(clientOptions.network);
32
- super(Object.assign(Object.assign({}, clientOptions), { chain: (_a = clientOptions.chain) !== null && _a !== void 0 ? _a : (0, chain_1.getPushViemChain)(pushChainKey) }));
32
+ const chain = (_a = clientOptions.chain) !== null && _a !== void 0 ? _a : (0, chain_1.getPushViemChain)(pushChainKey);
33
+ super(Object.assign(Object.assign({}, clientOptions), { chain }));
33
34
  this.currentRpcIndex = 0;
34
35
  this.pushChainInfo = chain_1.PUSH_CHAIN_INFO[pushChainKey];
36
+ // Archive fallback is always set up; it's inert for chains without an
37
+ // archive endpoint (mainnet/localnet → empty lists), and otherwise lets
38
+ // history-sensitive reads recover a pruned tx from the archive node.
39
+ this.archiveTendermintRpc = (_b = this.pushChainInfo.archiveTendermintRpc) !== null && _b !== void 0 ? _b : [];
40
+ const archiveEvmUrls = (_c = chain_1.CHAIN_INFO[pushChainKey].archiveRPC) !== null && _c !== void 0 ? _c : [];
41
+ if (archiveEvmUrls.length > 0) {
42
+ this.archivePublicClient = (0, viem_1.createPublicClient)({
43
+ chain,
44
+ transport: (0, viem_1.fallback)(archiveEvmUrls.map((url) => (0, viem_1.http)(url, { retryCount: 5, retryDelay: 500 }))),
45
+ });
46
+ }
35
47
  this.ephemeralKey = (0, accounts_1.generatePrivateKey)();
36
48
  this.ephemeralAccount = (0, accounts_1.privateKeyToAccount)(this.ephemeralKey);
37
49
  }
@@ -40,8 +52,11 @@ class PushClient extends evm_client_1.EvmClient {
40
52
  * Follows the same pattern as SvmClient.executeWithFallback().
41
53
  */
42
54
  executeWithRpcFallback(operation_1) {
43
- return tslib_1.__awaiter(this, arguments, void 0, function* (operation, operationName = 'operation') {
44
- const rpcUrls = this.pushChainInfo.tendermintRpc;
55
+ return tslib_1.__awaiter(this, arguments, void 0, function* (operation, operationName = 'operation', rpcUrls = this.pushChainInfo.tendermintRpc,
56
+ // Sticky cursor (`currentRpcIndex`) is only meaningful for the primary
57
+ // prune pool; the archive pass passes `sticky=false` so a single archive
58
+ // URL can't pin the prune cursor.
59
+ sticky = true) {
45
60
  let lastError = null;
46
61
  // Cycle the RPC URLs, but also RETRY transient failures (5xx / network /
47
62
  // "HTTP request failed") with backoff — otherwise a single 502 on a
@@ -62,11 +77,12 @@ class PushClient extends evm_client_1.EvmClient {
62
77
  m.includes('fetch failed'));
63
78
  };
64
79
  for (let attempt = 0; attempt < totalAttempts; attempt++) {
65
- const rpcIndex = (this.currentRpcIndex + attempt) % rpcUrls.length;
80
+ const baseIndex = sticky ? this.currentRpcIndex : 0;
81
+ const rpcIndex = (baseIndex + attempt) % rpcUrls.length;
66
82
  const rpcUrl = rpcUrls[rpcIndex];
67
83
  try {
68
84
  const result = yield operation(rpcUrl);
69
- if (rpcIndex !== this.currentRpcIndex) {
85
+ if (sticky && rpcIndex !== this.currentRpcIndex) {
70
86
  this.currentRpcIndex = rpcIndex;
71
87
  }
72
88
  return result;
@@ -88,6 +104,47 @@ class PushClient extends evm_client_1.EvmClient {
88
104
  throw new Error(`All RPC endpoints failed for ${operationName}. Last error: ${lastError === null || lastError === void 0 ? void 0 : lastError.message}`);
89
105
  });
90
106
  }
107
+ /**
108
+ * Runs a history-sensitive Tendermint operation over the prune pool first and,
109
+ * only when prune can't serve it (the op threw on all prune attempts, or
110
+ * returned an `isEmpty` result), re-queries the archive pool. This is the
111
+ * explicit "prune miss → archive" fallback the existing per-URL machinery
112
+ * can't do on its own, because a pruned tx comes back as a *successful*
113
+ * empty/not-found rather than a transport error.
114
+ *
115
+ * When no archive is configured (mainnet/localnet, or `enableArchiveFallback`
116
+ * off) this is a pass-through: the original throw or empty result is returned
117
+ * unchanged — behaviour identical to before.
118
+ */
119
+ executeWithArchiveFallback(operation, operationName, isEmpty) {
120
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
121
+ let pruneResult;
122
+ let pruneError;
123
+ try {
124
+ pruneResult = yield this.executeWithRpcFallback(operation, operationName);
125
+ }
126
+ catch (error) {
127
+ pruneError = error;
128
+ }
129
+ const pruneMissed = pruneError !== undefined ||
130
+ (pruneResult !== undefined && isEmpty(pruneResult));
131
+ if (!pruneMissed) {
132
+ return pruneResult;
133
+ }
134
+ if (this.archiveTendermintRpc.length === 0) {
135
+ // No archive: preserve original behaviour (re-throw, or return empty).
136
+ if (pruneError !== undefined)
137
+ throw pruneError;
138
+ return pruneResult;
139
+ }
140
+ // Prune missed + archive available → re-query archive (non-sticky).
141
+ return this.executeWithRpcFallback(operation, `${operationName} (archive)`, this.archiveTendermintRpc, false);
142
+ });
143
+ }
144
+ /** True when this client has a usable archive EVM endpoint configured. */
145
+ get hasArchiveEvm() {
146
+ return this.archivePublicClient !== undefined;
147
+ }
91
148
  /**
92
149
  * Converts nPUSH (1e18) to USDC (1e8), fixed rate: 1 PUSH = 0.1 USDC
93
150
  */
@@ -224,14 +281,26 @@ class PushClient extends evm_client_1.EvmClient {
224
281
  */
225
282
  getUniversalTxByIdV2(id) {
226
283
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
227
- return this.executeWithRpcFallback((rpcUrl) => tslib_1.__awaiter(this, void 0, void 0, function* () {
284
+ const operation = (rpcUrl) => tslib_1.__awaiter(this, void 0, void 0, function* () {
228
285
  const tmClient = yield tendermint_rpc_1.Tendermint34Client.connect(rpcUrl);
229
286
  const queryClient = new stargate_1.QueryClient(tmClient);
230
287
  const rpc = (0, stargate_1.createProtobufRpcClient)(queryClient);
231
288
  const request = query_2.QueryGetUniversalTxRequestV2.fromPartial({ id });
232
289
  const responseBytes = yield rpc.request('uexecutor.v2.Query', 'GetUniversalTx', query_2.QueryGetUniversalTxRequestV2.encode(request).finish());
233
290
  return query_2.QueryGetUniversalTxResponseV2.decode(responseBytes);
234
- }), 'getUniversalTxByIdV2');
291
+ });
292
+ // This is a current-STATE query (abci_query), so prune is always
293
+ // authoritative — an empty `universalTx` is a real "doesn't exist", not a
294
+ // pruned-history miss. Only fall back to archive on a hard error (all prune
295
+ // attempts failed); never on an empty result (would double-load on polls).
296
+ try {
297
+ return yield this.executeWithRpcFallback(operation, 'getUniversalTxByIdV2');
298
+ }
299
+ catch (error) {
300
+ if (this.archiveTendermintRpc.length === 0)
301
+ throw error;
302
+ return this.executeWithRpcFallback(operation, 'getUniversalTxByIdV2 (archive)', this.archiveTendermintRpc, false);
303
+ }
235
304
  });
236
305
  }
237
306
  /**
@@ -241,11 +310,14 @@ class PushClient extends evm_client_1.EvmClient {
241
310
  */
242
311
  searchCosmosByQuery(query) {
243
312
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
244
- return this.executeWithRpcFallback((rpcUrl) => tslib_1.__awaiter(this, void 0, void 0, function* () {
313
+ const operation = (rpcUrl) => tslib_1.__awaiter(this, void 0, void 0, function* () {
245
314
  const client = yield stargate_1.StargateClient.connect(rpcUrl);
246
315
  const results = yield client.searchTx(query);
247
316
  return results.map((result) => JSON.parse(JSON.stringify(result, (_key, value) => typeof value === 'bigint' ? value.toString() : value)));
248
- }), 'searchCosmosByQuery');
317
+ });
318
+ // History-sensitive (tx_search): an empty result on prune may just mean the
319
+ // matching tx is outside the prune window → retry archive.
320
+ return this.executeWithArchiveFallback(operation, 'searchCosmosByQuery', (results) => results.length === 0);
249
321
  });
250
322
  }
251
323
  /**
@@ -256,7 +328,7 @@ class PushClient extends evm_client_1.EvmClient {
256
328
  */
257
329
  getCosmosTx(txHash) {
258
330
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
259
- return this.executeWithRpcFallback((rpcUrl) => tslib_1.__awaiter(this, void 0, void 0, function* () {
331
+ const operation = (rpcUrl) => tslib_1.__awaiter(this, void 0, void 0, function* () {
260
332
  const client = yield stargate_1.StargateClient.connect(rpcUrl);
261
333
  const query = `ethereum_tx.ethereumTxHash='${txHash}'`;
262
334
  const results = yield client.searchTx(query);
@@ -268,7 +340,53 @@ class PushClient extends evm_client_1.EvmClient {
268
340
  throw new Error(`No Cosmos-indexed tx for EVM hash ${txHash}`);
269
341
  }
270
342
  return Object.assign(Object.assign({}, convertedResults[0]), { transactionHash: txHash });
271
- }), 'getCosmosTx');
343
+ });
344
+ // History-sensitive (tx_search). The op throws on an empty result, so the
345
+ // pruned-history miss surfaces as a thrown error — `isEmpty` stays false and
346
+ // the wrapper retries archive via its caught-error path.
347
+ return this.executeWithArchiveFallback(operation, 'getCosmosTx', () => false);
348
+ });
349
+ }
350
+ /**
351
+ * EVM `getTransaction` with archive fallback. Prune first; on a not-found
352
+ * (pruned, or not yet indexed) and an archive EVM endpoint configured, retry
353
+ * the archive client. Without archive this is exactly `super.getTransaction`.
354
+ */
355
+ getTransaction(txHash) {
356
+ const _super = Object.create(null, {
357
+ getTransaction: { get: () => super.getTransaction }
358
+ });
359
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
360
+ try {
361
+ return yield _super.getTransaction.call(this, txHash);
362
+ }
363
+ catch (error) {
364
+ if (!this.hasArchiveEvm)
365
+ throw error;
366
+ const archiveClient = this.archivePublicClient;
367
+ const tx = yield archiveClient.getTransaction({ hash: txHash });
368
+ const wait = (...args_1) => tslib_1.__awaiter(this, [...args_1], void 0, function* (confirmations = 1) { return archiveClient.waitForTransactionReceipt({ hash: txHash, confirmations }); });
369
+ return Object.assign(Object.assign({}, tx), { wait });
370
+ }
371
+ });
372
+ }
373
+ /**
374
+ * EVM `getTransactionReceipt` with archive fallback. Used by the inbound
375
+ * tracker to fetch a Push-leg receipt that may predate the prune window.
376
+ * Prune first; on not-found, retry the archive EVM client if configured.
377
+ */
378
+ getTransactionReceiptWithArchiveFallback(txHash) {
379
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
380
+ try {
381
+ return yield this.publicClient.getTransactionReceipt({ hash: txHash });
382
+ }
383
+ catch (error) {
384
+ if (!this.hasArchiveEvm)
385
+ throw error;
386
+ return this.archivePublicClient.getTransactionReceipt({
387
+ hash: txHash,
388
+ });
389
+ }
272
390
  });
273
391
  }
274
392
  }
@@ -22,7 +22,7 @@ import { CHAIN, LIBRARY } from '../../constants/enums';
22
22
  * signAndSendTransaction: async (data) => signRawTx(data),
23
23
  * });
24
24
  */
25
- export declare function createUniversalSigner({ account, signMessage, signAndSendTransaction, signTypedData, }: UniversalSigner): UniversalSigner;
25
+ export declare function createUniversalSigner({ account, signMessage, signAndSendTransaction, signTypedData, signAuthorization, }: UniversalSigner): UniversalSigner;
26
26
  /**
27
27
  * Creates a UniversalSigner from either a viem, ethers, solana WalletClient or Account instance.
28
28
  *