@pushchain/core 6.0.18 → 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.
- package/CHANGELOG.md +8 -0
- package/package.json +1 -1
- package/src/lib/constants/chain.d.ts +16 -0
- package/src/lib/constants/chain.js +32 -4
- package/src/lib/formatters.d.ts +1 -0
- package/src/lib/formatters.js +66 -1
- package/src/lib/orchestrator/internals/cascade.js +64 -46
- package/src/lib/orchestrator/internals/confirmation.js +8 -8
- package/src/lib/orchestrator/internals/error-decoder.d.ts +1 -0
- package/src/lib/orchestrator/internals/error-decoder.js +10 -0
- package/src/lib/orchestrator/internals/errors.js +1 -1
- package/src/lib/orchestrator/internals/execute-funds-only.js +8 -6
- package/src/lib/orchestrator/internals/execute-funds-payload.js +6 -4
- package/src/lib/orchestrator/internals/execute-standard.js +10 -8
- package/src/lib/orchestrator/internals/gateway-client.js +34 -1
- package/src/lib/orchestrator/internals/inbound-tracker.js +4 -3
- package/src/lib/orchestrator/internals/progress-route-hooks.d.ts +8 -4
- package/src/lib/orchestrator/internals/progress-route-hooks.js +8 -8
- package/src/lib/orchestrator/internals/push-chain-tx.js +71 -15
- package/src/lib/orchestrator/internals/response-builder.js +25 -16
- package/src/lib/orchestrator/internals/tx-transformer.js +11 -6
- package/src/lib/orchestrator/orchestrator.js +3 -17
- package/src/lib/orchestrator/orchestrator.types.d.ts +7 -0
- package/src/lib/progress-hook/progress-hook.js +37 -40
- package/src/lib/push-client/push-client.d.ts +39 -0
- package/src/lib/push-client/push-client.js +130 -12
- package/src/lib/universal/signer/signer.d.ts +1 -1
- package/src/lib/universal/signer/signer.js +83 -3
- package/src/lib/universal/universal.types.d.ts +37 -0
- package/src/lib/vm-client/evm-client.d.ts +43 -0
- 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
|
|
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
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
})
|
|
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
|
-
|
|
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
|
-
})
|
|
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
|
-
|
|
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
|
-
})
|
|
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
|
*
|