@pushchain/core 5.1.14 → 5.1.16

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 (26) hide show
  1. package/package.json +1 -1
  2. package/src/lib/orchestrator/internals/cascade.d.ts +16 -0
  3. package/src/lib/orchestrator/internals/cascade.js +205 -47
  4. package/src/lib/orchestrator/internals/error-decoder.d.ts +59 -0
  5. package/src/lib/orchestrator/internals/error-decoder.js +110 -0
  6. package/src/lib/orchestrator/internals/errors.d.ts +78 -0
  7. package/src/lib/orchestrator/internals/errors.js +83 -0
  8. package/src/lib/orchestrator/internals/outbound-sync.d.ts +2 -2
  9. package/src/lib/orchestrator/internals/outbound-sync.js +2 -2
  10. package/src/lib/orchestrator/internals/preflight.d.ts +80 -0
  11. package/src/lib/orchestrator/internals/preflight.js +145 -0
  12. package/src/lib/orchestrator/internals/push-chain-tx.d.ts +2 -18
  13. package/src/lib/orchestrator/internals/push-chain-tx.js +67 -40
  14. package/src/lib/orchestrator/internals/route-handlers.js +199 -114
  15. package/src/lib/orchestrator/internals/svm-rent.d.ts +46 -0
  16. package/src/lib/orchestrator/internals/svm-rent.js +100 -0
  17. package/src/lib/orchestrator/internals/tx-transformer.js +16 -1
  18. package/src/lib/orchestrator/orchestrator.js +34 -1
  19. package/src/lib/orchestrator/orchestrator.types.d.ts +27 -3
  20. package/src/lib/price-fetch/price-fetch.js +23 -5
  21. package/src/lib/progress-hook/progress-hook.js +130 -6
  22. package/src/lib/progress-hook/progress-hook.types.d.ts +8 -2
  23. package/src/lib/progress-hook/progress-hook.types.js +15 -0
  24. package/src/lib/universal/account/account.d.ts +1 -1
  25. package/src/lib/universal/account/account.js +1 -1
  26. package/src/lib/universal-tx-detector/child-inbounds.js +15 -0
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ /**
3
+ * Decoder for revert-error messages surfaced by Cosmos `tx.rawLog`.
4
+ *
5
+ * Two extraction paths:
6
+ * 1. 4-byte custom selector (e.g. `0xacfdb444:` or `0xf4d678b8000000…`) —
7
+ * looked up against `KNOWN_ERROR_SELECTORS` for a friendly name + hint.
8
+ * 2. `Error(string)` ABI-encoded revert (`0x08c379a0` selector + offset +
9
+ * length + data) — decoded into the original UTF-8 string.
10
+ *
11
+ * Unknown selectors fall through with `kind: 'unknown'` so callers can log
12
+ * them at INFO and grow the table from real reverts.
13
+ *
14
+ * The selector→name mappings below are EMPIRICAL: they were observed in
15
+ * production e2e logs and SDK test fixtures. `keccak256` of the obvious
16
+ * parameterless signatures (e.g. `keccak256("InsufficientBalance()")` =
17
+ * `0x01e1e567`) does NOT reproduce the listed selectors, so the deployed-
18
+ * source mapping is still unverified. Names and hints are worded as
19
+ * "likely cause" until the bytecode-source trace lands.
20
+ */
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.ERROR_STRING_SELECTOR = exports.KNOWN_ERROR_SELECTORS = void 0;
23
+ exports.decodeRevert = decodeRevert;
24
+ exports.KNOWN_ERROR_SELECTORS = {
25
+ '0xacfdb444': {
26
+ name: 'ExecutionFailed',
27
+ hint: 'Likely cause: subcall reverted (target contract or internal gas-swap precompile). ' +
28
+ 'Check that the target accepts native value (has receive/fallback) and that calldata matches a valid signature. ' +
29
+ 'Note: this selector also fires from internal SDK paths (e.g. fresh-wallet gas-swap) — see route2-fresh-wallet-gas-bug.spec.ts.',
30
+ provenance: 'Production e2e: e2e-2026-04-09T13-58-42-237Z.log:103 (R2 EVM target revert)',
31
+ },
32
+ '0x179a867c': {
33
+ name: 'ExpiredDeadline',
34
+ hint: 'Likely cause: transaction deadline passed before relay. Retry — the SDK will set a new deadline.',
35
+ provenance: 'Production e2e (associated with deadline-related reverts)',
36
+ },
37
+ '0xf4d678b8': {
38
+ name: 'InsufficientBalance',
39
+ hint: 'Likely cause: UEA does not hold enough of the burn token (PRC-20 mapped from the outbound `funds.token`). ' +
40
+ 'For Solana SPL outbounds, ensure UEA holds the corresponding pUSDT.sol / pUSDC.sol mint before retrying. ' +
41
+ 'Same selector may also surface from other balance-check reverters; verify burn-token balance first.',
42
+ provenance: 'Production e2e: e2e-2026-04-16T16-27-58-082Z.log:342 (R2 SPL fresh-UEA, no pUSDT.sol)',
43
+ },
44
+ '0x66f9d09e': {
45
+ name: 'GasLimitBelowBase',
46
+ hint: 'Likely cause: provided gasLimit is below the chain minimum. Omit `gasLimit` to use the per-chain default.',
47
+ provenance: 'Observed in dev (selector source unverified — see plan §9 #5)',
48
+ },
49
+ };
50
+ /** Selector for `Error(string)` (Solidity `require(false, "msg")`). */
51
+ exports.ERROR_STRING_SELECTOR = '0x08c379a0';
52
+ /** Hard cap on the decoded string length; protects against malformed data. */
53
+ const MAX_DECODED_STRING_LEN = 1024;
54
+ /**
55
+ * Extracts the first hex blob after `ret ` from a Cosmos tx error message,
56
+ * pulls the 4-byte selector, and looks it up. Returns `null` when no blob
57
+ * is found.
58
+ *
59
+ * **Assumption**: today's Cosmos `tx.rawLog` format contains a single
60
+ * `ret 0x…:` substring per failed message. If a future log format includes
61
+ * nested call traces with multiple `ret` substrings, this regex will pick
62
+ * the first selector — which may not be the most relevant one. Revisit
63
+ * if multi-revert log formats appear in production.
64
+ */
65
+ function decodeRevert(errorMsg) {
66
+ if (!errorMsg)
67
+ return null;
68
+ const m = errorMsg.match(/ret (0x[0-9a-fA-F]+)/);
69
+ if (!m)
70
+ return null;
71
+ const blob = m[1].toLowerCase();
72
+ if (blob.length < 10)
73
+ return null;
74
+ const sel = blob.slice(0, 10);
75
+ const entry = exports.KNOWN_ERROR_SELECTORS[sel];
76
+ if (entry) {
77
+ return {
78
+ kind: 'custom',
79
+ selector: sel,
80
+ name: entry.name,
81
+ hint: entry.hint,
82
+ provenance: entry.provenance,
83
+ };
84
+ }
85
+ if (sel === exports.ERROR_STRING_SELECTOR) {
86
+ try {
87
+ const data = blob.slice(10);
88
+ // Error(string) ABI: offset(32 bytes) + length(32 bytes) + data
89
+ // Skip the 32-byte offset (always 0x20).
90
+ const lengthHex = data.slice(64, 128);
91
+ if (lengthHex.length === 64) {
92
+ const length = parseInt(lengthHex, 16);
93
+ if (Number.isFinite(length) &&
94
+ length > 0 &&
95
+ length < MAX_DECODED_STRING_LEN) {
96
+ const strHex = data.slice(128, 128 + length * 2);
97
+ if (strHex.length === length * 2) {
98
+ const decoded = Buffer.from(strHex, 'hex').toString('utf8');
99
+ return { kind: 'string', selector: sel, decoded };
100
+ }
101
+ }
102
+ }
103
+ }
104
+ catch (_a) {
105
+ /* fall through to unknown */
106
+ }
107
+ }
108
+ return { kind: 'unknown', selector: sel };
109
+ }
110
+ //# sourceMappingURL=error-decoder.js.map
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Typed errors for orchestrator pre-flight checks.
3
+ *
4
+ * `PushChainExecutionError` lives here (instead of push-chain-tx.ts) so the
5
+ * pre-flight `InsufficientUEABalanceError` can extend it without creating a
6
+ * circular import (preflight.ts → errors.ts → push-chain-tx.ts → context.ts
7
+ * → … → route-handlers.ts → preflight.ts). push-chain-tx.ts re-exports it
8
+ * for backward-compat with existing `instanceof PushChainExecutionError`
9
+ * callers.
10
+ */
11
+ /**
12
+ * Structured decoded-error payload attached to terminal failure hooks
13
+ * (199-02 / 299-02 / 399-02). Mirrors the optional formatter parameter
14
+ * shape so the orchestrator's outer catch can lift it off the thrown
15
+ * error and pass it straight through.
16
+ */
17
+ export interface DecodedErrorPayload {
18
+ name?: string;
19
+ hint?: string;
20
+ selector?: string;
21
+ decoded?: string;
22
+ }
23
+ /**
24
+ * Typed error for Route 1 Push Chain tx failures. Thrown by
25
+ * `extractPcTxAndTransform` when the final pcTx commits with `status === 'FAILED'`,
26
+ * and surfaced as `SEND_TX_199_02` on the live stream. The readonly `code`
27
+ * discriminator lets callers classify via `instanceof PushChainExecutionError`
28
+ * instead of sniffing error messages.
29
+ *
30
+ * Carries the origin-chain gateway tx hash. There is no successful Push Chain
31
+ * tx to reference on this path — by definition the pcTx committed `FAILED`.
32
+ *
33
+ * `decodedError`: optional structured payload that flows through to terminal
34
+ * failure hook payloads (199-02 / 299-02 / 399-02 / 999-02). Set by the
35
+ * decoder wire-in (push-chain-tx.ts) and by InsufficientUEABalanceError so
36
+ * consumers don't have to parse the message string.
37
+ */
38
+ export declare class PushChainExecutionError extends Error {
39
+ readonly code: "PUSH_CHAIN_EXECUTION_FAILED";
40
+ readonly gatewayTxHash?: string;
41
+ readonly decodedError?: DecodedErrorPayload;
42
+ constructor(message: string, opts?: {
43
+ gatewayTxHash?: string;
44
+ decodedError?: DecodedErrorPayload;
45
+ });
46
+ }
47
+ export type PreflightPathTag = 'R2_EVM' | 'R2_SVM' | 'R3_EVM' | 'R3_SVM' | 'CASCADE';
48
+ export type PreflightShortfallReason = 'NATIVE' | 'PRC20';
49
+ export interface InsufficientUEABalanceErrorOpts {
50
+ required: bigint;
51
+ available: bigint;
52
+ shortfall: bigint;
53
+ ueaAddress: `0x${string}`;
54
+ pathTag: PreflightPathTag;
55
+ /** 'NATIVE' = UPC shortfall; 'PRC20' = burn-token shortfall. */
56
+ reason?: PreflightShortfallReason;
57
+ /** Burn token address (only set when reason === 'PRC20'). */
58
+ burnToken?: `0x${string}`;
59
+ /** Cascade-only: zero-based index of the failing segment. */
60
+ segmentIndex?: number;
61
+ }
62
+ /**
63
+ * Thrown by `runPreflight` when the UEA cannot cover the required outbound
64
+ * cost. No cosmos tx is submitted on this path — the existing silent-clamp
65
+ * behaviour was previously submitting under-funded swaps that reverted
66
+ * inside Uniswap with `Error("STF")`.
67
+ */
68
+ export declare class InsufficientUEABalanceError extends PushChainExecutionError {
69
+ readonly required: bigint;
70
+ readonly available: bigint;
71
+ readonly shortfall: bigint;
72
+ readonly ueaAddress: `0x${string}`;
73
+ readonly pathTag: PreflightPathTag;
74
+ readonly reason: PreflightShortfallReason;
75
+ readonly burnToken?: `0x${string}`;
76
+ readonly segmentIndex?: number;
77
+ constructor(opts: InsufficientUEABalanceErrorOpts);
78
+ }
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ /**
3
+ * Typed errors for orchestrator pre-flight checks.
4
+ *
5
+ * `PushChainExecutionError` lives here (instead of push-chain-tx.ts) so the
6
+ * pre-flight `InsufficientUEABalanceError` can extend it without creating a
7
+ * circular import (preflight.ts → errors.ts → push-chain-tx.ts → context.ts
8
+ * → … → route-handlers.ts → preflight.ts). push-chain-tx.ts re-exports it
9
+ * for backward-compat with existing `instanceof PushChainExecutionError`
10
+ * callers.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.InsufficientUEABalanceError = exports.PushChainExecutionError = void 0;
14
+ /**
15
+ * Typed error for Route 1 Push Chain tx failures. Thrown by
16
+ * `extractPcTxAndTransform` when the final pcTx commits with `status === 'FAILED'`,
17
+ * and surfaced as `SEND_TX_199_02` on the live stream. The readonly `code`
18
+ * discriminator lets callers classify via `instanceof PushChainExecutionError`
19
+ * instead of sniffing error messages.
20
+ *
21
+ * Carries the origin-chain gateway tx hash. There is no successful Push Chain
22
+ * tx to reference on this path — by definition the pcTx committed `FAILED`.
23
+ *
24
+ * `decodedError`: optional structured payload that flows through to terminal
25
+ * failure hook payloads (199-02 / 299-02 / 399-02 / 999-02). Set by the
26
+ * decoder wire-in (push-chain-tx.ts) and by InsufficientUEABalanceError so
27
+ * consumers don't have to parse the message string.
28
+ */
29
+ class PushChainExecutionError extends Error {
30
+ constructor(message, opts = {}) {
31
+ super(message);
32
+ this.code = 'PUSH_CHAIN_EXECUTION_FAILED';
33
+ this.name = 'PushChainExecutionError';
34
+ this.gatewayTxHash = opts.gatewayTxHash;
35
+ this.decodedError = opts.decodedError;
36
+ }
37
+ }
38
+ exports.PushChainExecutionError = PushChainExecutionError;
39
+ /**
40
+ * Thrown by `runPreflight` when the UEA cannot cover the required outbound
41
+ * cost. No cosmos tx is submitted on this path — the existing silent-clamp
42
+ * behaviour was previously submitting under-funded swaps that reverted
43
+ * inside Uniswap with `Error("STF")`.
44
+ */
45
+ class InsufficientUEABalanceError extends PushChainExecutionError {
46
+ constructor(opts) {
47
+ var _a;
48
+ const reason = (_a = opts.reason) !== null && _a !== void 0 ? _a : 'NATIVE';
49
+ const segLabel = opts.segmentIndex !== undefined ? `:seg${opts.segmentIndex}` : '';
50
+ const tokenSuffix = reason === 'PRC20' && opts.burnToken
51
+ ? ` of token ${opts.burnToken}`
52
+ : '';
53
+ const currencyLabel = reason === 'PRC20' ? 'units' : 'wei UPC';
54
+ const remediation = reason === 'PRC20'
55
+ ? `Bridge the burn token to UEA on Push Chain before retrying.`
56
+ : `Bridge >=${opts.shortfall} wei UPC to the UEA on Push Chain before retrying.`;
57
+ const message = `InsufficientUEABalance [${opts.pathTag}${segLabel}:${reason}]: ` +
58
+ `UEA ${opts.ueaAddress} has ${opts.available} ${currencyLabel}${tokenSuffix}; ` +
59
+ `outbound requires ${opts.required} ${currencyLabel}; shortfall ${opts.shortfall} ${currencyLabel}. ` +
60
+ remediation;
61
+ // Lift the structured info into the parent's decodedError so the
62
+ // orchestrator's outer catch can pass it straight through to the
63
+ // terminal failure hook payload (299-02 / 399-02 / 999-02) without
64
+ // parsing the message string.
65
+ super(message, {
66
+ decodedError: {
67
+ name: 'InsufficientUEABalance',
68
+ hint: remediation,
69
+ },
70
+ });
71
+ this.name = 'InsufficientUEABalanceError';
72
+ this.required = opts.required;
73
+ this.available = opts.available;
74
+ this.shortfall = opts.shortfall;
75
+ this.ueaAddress = opts.ueaAddress;
76
+ this.pathTag = opts.pathTag;
77
+ this.reason = reason;
78
+ this.burnToken = opts.burnToken;
79
+ this.segmentIndex = opts.segmentIndex;
80
+ }
81
+ }
82
+ exports.InsufficientUEABalanceError = InsufficientUEABalanceError;
83
+ //# sourceMappingURL=errors.js.map
@@ -15,8 +15,8 @@ export declare const OUTBOUND_POLL_INTERVAL_MS = 3000;
15
15
  export declare const OUTBOUND_MAX_TIMEOUT_MS = 180000;
16
16
  /** Maximum total timeout for the R3 inbound round-trip polling loop (ms). */
17
17
  export declare const INBOUND_MAX_TIMEOUT_MS = 300000;
18
- /** Initial wait before the first inbound poll (ms). Set to 0 — the outbound just completed, so the indexer may already have the child UTX. */
19
- export declare const INBOUND_INITIAL_WAIT_MS = 0;
18
+ /** Initial wait before the first inbound poll (ms). Validator observation + Push commit + indexer ingestion realistically take 15-25 s on testnet (esp. SVM source), so polling immediately just burns RPC on guaranteed-empty results. */
19
+ export declare const INBOUND_INITIAL_WAIT_MS = 15000;
20
20
  /**
21
21
  * Thrown by `waitForOutboundTx` when the polling loop exceeds the configured
22
22
  * timeout without observing an external tx hash. Callers can `instanceof`
@@ -30,8 +30,8 @@ exports.OUTBOUND_POLL_INTERVAL_MS = 3000; // 3s
30
30
  exports.OUTBOUND_MAX_TIMEOUT_MS = 180000; // 180s (3 min)
31
31
  /** Maximum total timeout for the R3 inbound round-trip polling loop (ms). */
32
32
  exports.INBOUND_MAX_TIMEOUT_MS = 300000; // 300s (5 min) — covers R3 inbound latency on testnet
33
- /** Initial wait before the first inbound poll (ms). Set to 0 — the outbound just completed, so the indexer may already have the child UTX. */
34
- exports.INBOUND_INITIAL_WAIT_MS = 0;
33
+ /** Initial wait before the first inbound poll (ms). Validator observation + Push commit + indexer ingestion realistically take 15-25 s on testnet (esp. SVM source), so polling immediately just burns RPC on guaranteed-empty results. */
34
+ exports.INBOUND_INITIAL_WAIT_MS = 15000;
35
35
  // ============================================================================
36
36
  // Typed errors
37
37
  // ============================================================================
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Shared pre-flight balance check for all six outbound paths
3
+ * (R2 EVM, R2 SVM, R3 EVM, R3 SVM, Cascade EVM, Cascade SVM).
4
+ *
5
+ * Today the route handlers silently clamp `nativeValueForGas` down to a
6
+ * value the UEA cannot fully cover, then submit anyway. The under-funded
7
+ * swap reverts inside Uniswap V3's `TransferHelper.safeTransferFrom`
8
+ * with `Error("STF")`, fees are burned, and the user sees an opaque
9
+ * library string with no balance signal.
10
+ *
11
+ * `runPreflight` replaces that branch: it checks the **pre-clamp**
12
+ * buffered pool quote against UEA balance (and optionally a PRC-20 burn
13
+ * balance) and either:
14
+ * - emits `SEND-TX-203-03` (INFO, sufficient) and lets the caller proceed; or
15
+ * - emits `SEND-TX-203-03` (ERROR, insufficient) + `SEND-TX-203-04` and
16
+ * throws `InsufficientUEABalanceError`.
17
+ *
18
+ * The helper is intentionally not "pure" — it emits hooks and may throw.
19
+ * Callers fetch all balances (so the helper itself does no I/O).
20
+ */
21
+ import type { OrchestratorContext } from './context';
22
+ import { type PreflightPathTag } from './errors';
23
+ export interface RunPreflightOpts {
24
+ ctx: OrchestratorContext;
25
+ ueaAddress: `0x${string}`;
26
+ /** Effective UEA balance in wei UPC (already includes fresh-wallet prediction). */
27
+ ueaBalance: bigint;
28
+ /** Buffered pool quote (pre-clamp) — what the SDK would actually attempt. */
29
+ requiredValue: bigint;
30
+ /** Reserve covering outer-tx gas + approve(s). */
31
+ gasReserve: bigint;
32
+ pathTag: PreflightPathTag;
33
+ /** Optional: PRC-20 burn token + amount + on-chain UEA balance. */
34
+ burnToken?: `0x${string}`;
35
+ burnAmount?: bigint;
36
+ prc20Balance?: bigint;
37
+ /** Cascade-only: identifies which segment failed. */
38
+ segmentIndex?: number;
39
+ /**
40
+ * Per-call opt-out (`params.options.allowUnderfundedSwap`). When `true`,
41
+ * pre-flight logs the shortfall but does NOT fire 203-04 or throw —
42
+ * caller can fall back to the legacy clamp-and-refund branch.
43
+ */
44
+ allowUnderfundedSwap?: boolean;
45
+ }
46
+ export type PreflightResult = {
47
+ ok: true;
48
+ /** What `nativeValueForGas` should be (== requiredValue when sufficient). */
49
+ adjustedValue: bigint;
50
+ } | {
51
+ ok: false;
52
+ /** Set when allowUnderfundedSwap=true: the legacy clamped value. */
53
+ legacyClampedValue: bigint;
54
+ };
55
+ /**
56
+ * Pre-flight check. PRC-20 balance is checked before the native check —
57
+ * a missing burn token fails before the gas-balance check, so the user
58
+ * sees the actionable PRC-20 shortfall instead of a stale UPC error.
59
+ *
60
+ * Returns a result object instead of throwing for the legacy opt-out
61
+ * path. The throw happens here (not in the caller) for the default
62
+ * `allowUnderfundedSwap=false` case so all 6 paths share one throw site.
63
+ */
64
+ export declare function runPreflight(opts: RunPreflightOpts): PreflightResult;
65
+ /**
66
+ * SVM-only telemetry threshold. Buffered pool quotes above this value
67
+ * fire `SEND-TX-203-05` (single-route) or `SEND-TX-003-05` (cascade).
68
+ * The threshold is for visibility, not truncation — pre-flight handles
69
+ * drain protection. v2 may add a hard cap once telemetry justifies a
70
+ * number.
71
+ */
72
+ export declare const SVM_NATIVE_VALUE_WARN_THRESHOLD: bigint;
73
+ /**
74
+ * Fire the SVM warn-threshold hook + log line if the buffered quote
75
+ * exceeds the threshold. Non-blocking; `nativeValueForGas` is not
76
+ * truncated by the caller. The hook ID switches to the cascade 0xx
77
+ * bucket (`003-05`) when `pathTag === 'CASCADE'` so cascade streams
78
+ * stay self-consistent.
79
+ */
80
+ export declare function maybeFireSvmWarnThreshold(ctx: OrchestratorContext, bufferedQuote: bigint, gasToken: `0x${string}`, pathTag: PreflightPathTag): void;
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ /**
3
+ * Shared pre-flight balance check for all six outbound paths
4
+ * (R2 EVM, R2 SVM, R3 EVM, R3 SVM, Cascade EVM, Cascade SVM).
5
+ *
6
+ * Today the route handlers silently clamp `nativeValueForGas` down to a
7
+ * value the UEA cannot fully cover, then submit anyway. The under-funded
8
+ * swap reverts inside Uniswap V3's `TransferHelper.safeTransferFrom`
9
+ * with `Error("STF")`, fees are burned, and the user sees an opaque
10
+ * library string with no balance signal.
11
+ *
12
+ * `runPreflight` replaces that branch: it checks the **pre-clamp**
13
+ * buffered pool quote against UEA balance (and optionally a PRC-20 burn
14
+ * balance) and either:
15
+ * - emits `SEND-TX-203-03` (INFO, sufficient) and lets the caller proceed; or
16
+ * - emits `SEND-TX-203-03` (ERROR, insufficient) + `SEND-TX-203-04` and
17
+ * throws `InsufficientUEABalanceError`.
18
+ *
19
+ * The helper is intentionally not "pure" — it emits hooks and may throw.
20
+ * Callers fetch all balances (so the helper itself does no I/O).
21
+ */
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.SVM_NATIVE_VALUE_WARN_THRESHOLD = void 0;
24
+ exports.runPreflight = runPreflight;
25
+ exports.maybeFireSvmWarnThreshold = maybeFireSvmWarnThreshold;
26
+ const context_1 = require("./context");
27
+ const progress_hook_types_1 = require("../../progress-hook/progress-hook.types");
28
+ const errors_1 = require("./errors");
29
+ /**
30
+ * Internal: compute the legacy clamp value (matches the pre-existing
31
+ * branches in route-handlers.ts:643-659 / :968-980 / :1396-1412).
32
+ * Returned only when `allowUnderfundedSwap=true`.
33
+ */
34
+ function legacyClamp(ueaBalance, requiredValue, gasReserve) {
35
+ if (ueaBalance >= requiredValue + gasReserve)
36
+ return requiredValue;
37
+ if (ueaBalance > gasReserve)
38
+ return ueaBalance - gasReserve;
39
+ if (ueaBalance > BigInt(0))
40
+ return ueaBalance < requiredValue ? ueaBalance : requiredValue;
41
+ return BigInt(0);
42
+ }
43
+ /**
44
+ * Pre-flight check. PRC-20 balance is checked before the native check —
45
+ * a missing burn token fails before the gas-balance check, so the user
46
+ * sees the actionable PRC-20 shortfall instead of a stale UPC error.
47
+ *
48
+ * Returns a result object instead of throwing for the legacy opt-out
49
+ * path. The throw happens here (not in the caller) for the default
50
+ * `allowUnderfundedSwap=false` case so all 6 paths share one throw site.
51
+ */
52
+ function runPreflight(opts) {
53
+ const { ctx, ueaAddress, ueaBalance, requiredValue, gasReserve, pathTag, burnToken, burnAmount, prc20Balance, segmentIndex, allowUnderfundedSwap, } = opts;
54
+ // Cascade context emits 003-xx (cascade 0xx bucket); single routes (R2/R3)
55
+ // emit 203-xx (R2 bucket). The two ID sets share an identical formatter
56
+ // signature, so the runtime branching is just an ID swap at each fire site.
57
+ const isCascade = pathTag === 'CASCADE';
58
+ const HOOK_PRE_INFO = isCascade
59
+ ? progress_hook_types_1.PROGRESS_HOOK.SEND_TX_003_03
60
+ : progress_hook_types_1.PROGRESS_HOOK.SEND_TX_203_03;
61
+ const HOOK_PRE_FAIL = isCascade
62
+ ? progress_hook_types_1.PROGRESS_HOOK.SEND_TX_003_04
63
+ : progress_hook_types_1.PROGRESS_HOOK.SEND_TX_203_04;
64
+ // 1. PRC-20 burn-balance check (skipped when burnAmount = 0 — R3 paths
65
+ // structurally hold zero of the source-chain native PRC-20; see plan §9 #4).
66
+ if (burnAmount && burnAmount > BigInt(0) && burnToken) {
67
+ const onHand = prc20Balance !== null && prc20Balance !== void 0 ? prc20Balance : BigInt(0);
68
+ const sufficient = onHand >= burnAmount;
69
+ (0, context_1.fireProgressHook)(ctx, HOOK_PRE_INFO, burnAmount, onHand, sufficient, ueaAddress, pathTag, { kind: 'PRC20', burnToken, segmentIndex });
70
+ if (!sufficient) {
71
+ const shortfall = burnAmount - onHand;
72
+ if (allowUnderfundedSwap) {
73
+ (0, context_1.printLog)(ctx, `[preflight ${pathTag}] PRC-20 shortfall ${shortfall} of ${burnToken}; ` +
74
+ `allowUnderfundedSwap=true — proceeding with legacy clamp behaviour, ` +
75
+ `swap is expected to revert with InsufficientBalance(0xf4d678b8) on-chain.`);
76
+ return {
77
+ ok: false,
78
+ legacyClampedValue: legacyClamp(ueaBalance, requiredValue, gasReserve),
79
+ };
80
+ }
81
+ (0, context_1.fireProgressHook)(ctx, HOOK_PRE_FAIL, burnAmount, onHand, shortfall, ueaAddress, pathTag, { kind: 'PRC20', burnToken, segmentIndex });
82
+ throw new errors_1.InsufficientUEABalanceError({
83
+ required: burnAmount,
84
+ available: onHand,
85
+ shortfall,
86
+ ueaAddress,
87
+ pathTag,
88
+ reason: 'PRC20',
89
+ burnToken,
90
+ segmentIndex,
91
+ });
92
+ }
93
+ }
94
+ // 2. Native (UPC) balance check.
95
+ const totalRequired = requiredValue + gasReserve;
96
+ const sufficient = ueaBalance >= totalRequired;
97
+ (0, context_1.fireProgressHook)(ctx, HOOK_PRE_INFO, totalRequired, ueaBalance, sufficient, ueaAddress, pathTag, { kind: 'NATIVE', segmentIndex });
98
+ if (sufficient) {
99
+ return { ok: true, adjustedValue: requiredValue };
100
+ }
101
+ const shortfall = totalRequired - ueaBalance;
102
+ if (allowUnderfundedSwap) {
103
+ const clamped = legacyClamp(ueaBalance, requiredValue, gasReserve);
104
+ (0, context_1.printLog)(ctx, `[preflight ${pathTag}] UPC shortfall ${shortfall} wei; ` +
105
+ `allowUnderfundedSwap=true — proceeding with legacy clamped value ${clamped}, ` +
106
+ `swap may revert inside Uniswap with Error("STF") if the clamp under-funds the pool quote.`);
107
+ return { ok: false, legacyClampedValue: clamped };
108
+ }
109
+ (0, context_1.fireProgressHook)(ctx, HOOK_PRE_FAIL, totalRequired, ueaBalance, shortfall, ueaAddress, pathTag, { kind: 'NATIVE', segmentIndex });
110
+ throw new errors_1.InsufficientUEABalanceError({
111
+ required: totalRequired,
112
+ available: ueaBalance,
113
+ shortfall,
114
+ ueaAddress,
115
+ pathTag,
116
+ reason: 'NATIVE',
117
+ segmentIndex,
118
+ });
119
+ }
120
+ /**
121
+ * SVM-only telemetry threshold. Buffered pool quotes above this value
122
+ * fire `SEND-TX-203-05` (single-route) or `SEND-TX-003-05` (cascade).
123
+ * The threshold is for visibility, not truncation — pre-flight handles
124
+ * drain protection. v2 may add a hard cap once telemetry justifies a
125
+ * number.
126
+ */
127
+ exports.SVM_NATIVE_VALUE_WARN_THRESHOLD = BigInt(5000) * BigInt(1e18);
128
+ /**
129
+ * Fire the SVM warn-threshold hook + log line if the buffered quote
130
+ * exceeds the threshold. Non-blocking; `nativeValueForGas` is not
131
+ * truncated by the caller. The hook ID switches to the cascade 0xx
132
+ * bucket (`003-05`) when `pathTag === 'CASCADE'` so cascade streams
133
+ * stay self-consistent.
134
+ */
135
+ function maybeFireSvmWarnThreshold(ctx, bufferedQuote, gasToken, pathTag) {
136
+ if (bufferedQuote <= exports.SVM_NATIVE_VALUE_WARN_THRESHOLD)
137
+ return;
138
+ const HOOK_WARN = pathTag === 'CASCADE'
139
+ ? progress_hook_types_1.PROGRESS_HOOK.SEND_TX_003_05
140
+ : progress_hook_types_1.PROGRESS_HOOK.SEND_TX_203_05;
141
+ (0, context_1.fireProgressHook)(ctx, HOOK_WARN, bufferedQuote, exports.SVM_NATIVE_VALUE_WARN_THRESHOLD, gasToken, pathTag);
142
+ (0, context_1.printLog)(ctx, `[preflight ${pathTag}] SVM warn-threshold tripped: bufferedQuote=${bufferedQuote} > ` +
143
+ `${exports.SVM_NATIVE_VALUE_WARN_THRESHOLD} for gasToken=${gasToken} — pool may be skewed`);
144
+ }
145
+ //# sourceMappingURL=preflight.js.map
@@ -10,23 +10,8 @@ import { UniversalPayload } from '../../generated/v1/tx';
10
10
  import type { TxResponse } from '../../vm-client/vm-client.types';
11
11
  import type { ExecuteParams, UniversalTxResponse } from '../orchestrator.types';
12
12
  import type { OrchestratorContext } from './context';
13
- /**
14
- * Typed error for Route 1 Push Chain tx failures. Thrown by
15
- * `extractPcTxAndTransform` when the final pcTx commits with `status === 'FAILED'`,
16
- * and surfaced as `SEND_TX_199_02` on the live stream. The readonly `code`
17
- * discriminator lets callers classify via `instanceof PushChainExecutionError`
18
- * instead of sniffing error messages.
19
- *
20
- * Carries the origin-chain gateway tx hash. There is no successful Push Chain
21
- * tx to reference on this path — by definition the pcTx committed `FAILED`.
22
- */
23
- export declare class PushChainExecutionError extends Error {
24
- readonly code: "PUSH_CHAIN_EXECUTION_FAILED";
25
- readonly gatewayTxHash?: string;
26
- constructor(message: string, opts?: {
27
- gatewayTxHash?: string;
28
- });
29
- }
13
+ import { PushChainExecutionError } from './errors';
14
+ export { PushChainExecutionError };
30
15
  type TransformToResponseFn = (tx: TxResponse, eventBuffer: ProgressEvent[]) => Promise<UniversalTxResponse>;
31
16
  /**
32
17
  * Sends an EVM transaction directly on Push Chain.
@@ -55,4 +40,3 @@ export declare function sendUniversalTx(ctx: OrchestratorContext, isUEADeployed:
55
40
  * and transforms to UniversalTxResponse.
56
41
  */
57
42
  export declare function extractPcTxAndTransform(ctx: OrchestratorContext, pushChainUniversalTx: UniversalTx | undefined, gatewayTxHash: string, eventBuffer: ProgressEvent[], label: string, transformFn: TransformToResponseFn): Promise<UniversalTxResponse>;
58
- export {};