@pushchain/core 5.1.17 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/package.json +1 -1
  2. package/src/lib/constants/abi/cea.evm.d.ts +52 -0
  3. package/src/lib/constants/abi/cea.evm.js +66 -1
  4. package/src/lib/constants/abi/prc20.evm.d.ts +117 -11
  5. package/src/lib/constants/abi/prc20.evm.js +38 -11
  6. package/src/lib/constants/abi/uea.evm.js +41 -0
  7. package/src/lib/constants/abi/universalGatewayPC.evm.d.ts +117 -1
  8. package/src/lib/constants/abi/universalGatewayPC.evm.js +40 -1
  9. package/src/lib/constants/abi/universalGatewayV0.json +1 -1
  10. package/src/lib/constants/chain.js +14 -14
  11. package/src/lib/constants/index.d.ts +1 -1
  12. package/src/lib/constants/index.js +2 -1
  13. package/src/lib/constants/selectors.d.ts +8 -0
  14. package/src/lib/constants/selectors.js +9 -1
  15. package/src/lib/constants/tokens.js +1 -0
  16. package/src/lib/formatters.d.ts +7 -0
  17. package/src/lib/formatters.js +46 -0
  18. package/src/lib/index.d.ts +2 -0
  19. package/src/lib/index.js +7 -1
  20. package/src/lib/orchestrator/internals/cascade.d.ts +8 -1
  21. package/src/lib/orchestrator/internals/cascade.js +593 -192
  22. package/src/lib/orchestrator/internals/error-decoder.d.ts +2 -6
  23. package/src/lib/orchestrator/internals/error-decoder.js +10 -10
  24. package/src/lib/orchestrator/internals/errors.d.ts +1 -1
  25. package/src/lib/orchestrator/internals/errors.js +8 -5
  26. package/src/lib/orchestrator/internals/execute-funds-only.js +3 -2
  27. package/src/lib/orchestrator/internals/execute-funds-payload.js +2 -1
  28. package/src/lib/orchestrator/internals/execute-standard.js +11 -10
  29. package/src/lib/orchestrator/internals/gas-calculator.d.ts +1 -0
  30. package/src/lib/orchestrator/internals/gas-calculator.js +91 -22
  31. package/src/lib/orchestrator/internals/helpers.d.ts +2 -1
  32. package/src/lib/orchestrator/internals/helpers.js +14 -11
  33. package/src/lib/orchestrator/internals/inbound-tracker.js +4 -2
  34. package/src/lib/orchestrator/internals/outbound-sync.d.ts +1 -0
  35. package/src/lib/orchestrator/internals/outbound-sync.js +140 -71
  36. package/src/lib/orchestrator/internals/pc-usd-oracle.d.ts +1 -1
  37. package/src/lib/orchestrator/internals/pc-usd-oracle.js +4 -3
  38. package/src/lib/orchestrator/internals/preflight.d.ts +2 -16
  39. package/src/lib/orchestrator/internals/preflight.js +2 -36
  40. package/src/lib/orchestrator/internals/push-chain-tx.js +5 -1
  41. package/src/lib/orchestrator/internals/response-builder.js +23 -7
  42. package/src/lib/orchestrator/internals/route-handlers.js +182 -227
  43. package/src/lib/orchestrator/internals/signing.js +7 -2
  44. package/src/lib/orchestrator/internals/svm-helpers.js +3 -3
  45. package/src/lib/orchestrator/internals/svm-rent.d.ts +42 -9
  46. package/src/lib/orchestrator/internals/svm-rent.js +148 -14
  47. package/src/lib/orchestrator/internals/tx-transformer.js +5 -2
  48. package/src/lib/orchestrator/max-pc-for-gas.d.ts +60 -0
  49. package/src/lib/orchestrator/max-pc-for-gas.js +66 -0
  50. package/src/lib/orchestrator/orchestrator.d.ts +8 -1
  51. package/src/lib/orchestrator/orchestrator.js +40 -2
  52. package/src/lib/orchestrator/orchestrator.types.d.ts +35 -23
  53. package/src/lib/orchestrator/payload-builders.d.ts +7 -6
  54. package/src/lib/orchestrator/payload-builders.js +18 -6
  55. package/src/lib/progress-hook/progress-hook.js +39 -38
  56. package/src/lib/push-chain/push-chain.d.ts +11 -1
  57. package/src/lib/push-chain/push-chain.js +2 -2
  58. package/src/lib/universal-tx-detector/cascade.js +14 -4
  59. package/src/lib/universal-tx-detector/child-inbounds.js +9 -1
  60. package/src/lib/universal-tx-detector/detector.js +9 -3
  61. package/src/lib/universal-tx-detector/events.d.ts +3 -1
  62. package/src/lib/universal-tx-detector/events.js +3 -1
  63. package/src/lib/universal-tx-detector/normalize.d.ts +42 -0
  64. package/src/lib/universal-tx-detector/normalize.js +127 -0
  65. package/src/lib/universal-tx-detector/svm-events.d.ts +1 -1
  66. package/src/lib/universal-tx-detector/svm-events.js +1 -1
  67. package/src/lib/utils/external-tx-hash.d.ts +37 -0
  68. package/src/lib/utils/external-tx-hash.js +62 -0
@@ -10,12 +10,8 @@
10
10
  * Unknown selectors fall through with `kind: 'unknown'` so callers can log
11
11
  * them at INFO and grow the table from real reverts.
12
12
  *
13
- * The selector→name mappings below are EMPIRICAL: they were observed in
14
- * production e2e logs and SDK test fixtures. `keccak256` of the obvious
15
- * parameterless signatures (e.g. `keccak256("InsufficientBalance()")` =
16
- * `0x01e1e567`) does NOT reproduce the listed selectors, so the deployed-
17
- * source mapping is still unverified. Names and hints are worded as
18
- * "likely cause" until the bytecode-source trace lands.
13
+ * The selector→name mappings below come from source-verified custom errors
14
+ * where available, with production/e2e observations kept as provenance.
19
15
  */
20
16
  export interface KnownErrorSelector {
21
17
  /** Best-effort error name. Worded as a likely cause until source-traced. */
@@ -11,12 +11,8 @@
11
11
  * Unknown selectors fall through with `kind: 'unknown'` so callers can log
12
12
  * them at INFO and grow the table from real reverts.
13
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.
14
+ * The selector→name mappings below come from source-verified custom errors
15
+ * where available, with production/e2e observations kept as provenance.
20
16
  */
21
17
  Object.defineProperty(exports, "__esModule", { value: true });
22
18
  exports.ERROR_STRING_SELECTOR = exports.KNOWN_ERROR_SELECTORS = void 0;
@@ -36,10 +32,14 @@ exports.KNOWN_ERROR_SELECTORS = {
36
32
  },
37
33
  '0xf4d678b8': {
38
34
  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)',
35
+ hint: 'Likely cause: the executing account does not hold enough of the token being moved. ' +
36
+ 'For Push outbounds, verify the UEA PRC-20 burn-token balance. For CEA→Push routes, verify the source-chain CEA balance.',
37
+ provenance: 'CommonErrors/CEAErrors.InsufficientBalance(); production e2e: e2e-2026-04-16T16-27-58-082Z.log:342',
38
+ },
39
+ '0xb4fa3fb3': {
40
+ name: 'InvalidInput',
41
+ hint: 'Likely cause: contract-level input validation failed. For CEA self-calls, the multicall entry must use value=0 and the encoded sendUniversalTxToUEA amount/revertRecipient must be valid.',
42
+ provenance: 'CommonErrors/CEAErrors.InvalidInput(); observed while replaying failed Route 3 CEA self-call tx 0x6fc0635d76edae0659ff131fd4bbf3349e01946708ce56dc2668d878f34a6077',
43
43
  },
44
44
  '0x66f9d09e': {
45
45
  name: 'GasLimitBelowBase',
@@ -52,7 +52,7 @@ export interface InsufficientUEABalanceErrorOpts {
52
52
  shortfall: bigint;
53
53
  ueaAddress: `0x${string}`;
54
54
  pathTag: PreflightPathTag;
55
- /** 'NATIVE' = UPC shortfall; 'PRC20' = burn-token shortfall. */
55
+ /** 'NATIVE' = native PC shortfall; 'PRC20' = burn-token shortfall. */
56
56
  reason?: PreflightShortfallReason;
57
57
  /** Burn token address (only set when reason === 'PRC20'). */
58
58
  burnToken?: `0x${string}`;
@@ -11,6 +11,7 @@
11
11
  */
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
13
  exports.InsufficientUEABalanceError = exports.PushChainExecutionError = void 0;
14
+ const formatters_1 = require("../../formatters");
14
15
  /**
15
16
  * Typed error for Route 1 Push Chain tx failures. Thrown by
16
17
  * `extractPcTxAndTransform` when the final pcTx commits with `status === 'FAILED'`,
@@ -28,7 +29,7 @@ exports.InsufficientUEABalanceError = exports.PushChainExecutionError = void 0;
28
29
  */
29
30
  class PushChainExecutionError extends Error {
30
31
  constructor(message, opts = {}) {
31
- super(message);
32
+ super((0, formatters_1.normalizePcInsufficientFundsError)(message));
32
33
  this.code = 'PUSH_CHAIN_EXECUTION_FAILED';
33
34
  this.name = 'PushChainExecutionError';
34
35
  this.gatewayTxHash = opts.gatewayTxHash;
@@ -50,13 +51,15 @@ class InsufficientUEABalanceError extends PushChainExecutionError {
50
51
  const tokenSuffix = reason === 'PRC20' && opts.burnToken
51
52
  ? ` of token ${opts.burnToken}`
52
53
  : '';
53
- const currencyLabel = reason === 'PRC20' ? 'units' : 'wei UPC';
54
+ const availableLabel = reason === 'PRC20' ? `${opts.available} units` : (0, formatters_1.formatPc)(opts.available);
55
+ const requiredLabel = reason === 'PRC20' ? `${opts.required} units` : (0, formatters_1.formatPc)(opts.required);
56
+ const shortfallLabel = reason === 'PRC20' ? `${opts.shortfall} units` : (0, formatters_1.formatPc)(opts.shortfall);
54
57
  const remediation = reason === 'PRC20'
55
58
  ? `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.`;
59
+ : `Bridge >=${shortfallLabel} to the UEA on Push Chain before retrying.`;
57
60
  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}. ` +
61
+ `UEA ${opts.ueaAddress} has ${availableLabel}${tokenSuffix}; ` +
62
+ `outbound requires ${requiredLabel}; shortfall ${shortfallLabel}. ` +
60
63
  remediation;
61
64
  // Lift the structured info into the parent's decodedError so the
62
65
  // orchestrator's outer catch can pass it straight through to the
@@ -29,6 +29,7 @@ const push_chain_tx_1 = require("./push-chain-tx");
29
29
  const svm_helpers_1 = require("./svm-helpers");
30
30
  const helpers_2 = require("./helpers");
31
31
  const response_builder_2 = require("./response-builder");
32
+ const formatters_1 = require("../../formatters");
32
33
  function executeFundsOnly(ctx, execute, eventBuffer, getResponseCallbacks) {
33
34
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
34
35
  const transformFn = (tx, buf = []) => (0, response_builder_2.transformToUniversalTxResponse)(ctx, tx, buf, getResponseCallbacks());
@@ -139,7 +140,7 @@ function executeFundsOnlyEvm(ctx, execute, eventBuffer, transformFn, chain, rpcU
139
140
  }
140
141
  catch (err) {
141
142
  if (!(err instanceof push_chain_tx_1.PushChainExecutionError)) {
142
- const errMsg = err instanceof Error ? err.message : String(err);
143
+ const errMsg = (0, formatters_1.normalizePcInsufficientFundsError)(err instanceof Error ? err.message : String(err));
143
144
  (0, context_2.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_199_02, errMsg);
144
145
  throw new push_chain_tx_1.PushChainExecutionError(errMsg, { gatewayTxHash: txHash });
145
146
  }
@@ -230,7 +231,7 @@ function executeFundsOnlySvm(ctx, execute, eventBuffer, transformFn, chain, rpcU
230
231
  }
231
232
  catch (err) {
232
233
  if (!(err instanceof push_chain_tx_1.PushChainExecutionError)) {
233
- const errMsg = err instanceof Error ? err.message : String(err);
234
+ const errMsg = (0, formatters_1.normalizePcInsufficientFundsError)(err instanceof Error ? err.message : String(err));
234
235
  (0, context_2.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_199_02, errMsg);
235
236
  throw new push_chain_tx_1.PushChainExecutionError(errMsg, { gatewayTxHash: txSignature });
236
237
  }
@@ -32,6 +32,7 @@ const push_chain_tx_1 = require("./push-chain-tx");
32
32
  const svm_bridge_1 = require("./svm-bridge");
33
33
  const signing_1 = require("./signing");
34
34
  const quote_1 = require("./quote");
35
+ const formatters_1 = require("../../formatters");
35
36
  function executeFundsWithPayload(ctx, execute, eventBuffer, getResponseCallbacks) {
36
37
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
37
38
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
@@ -240,7 +241,7 @@ function executeFundsWithPayload(ctx, execute, eventBuffer, getResponseCallbacks
240
241
  }
241
242
  catch (err) {
242
243
  if (!(err instanceof push_chain_tx_1.PushChainExecutionError)) {
243
- const errMsg = err instanceof Error ? err.message : String(err);
244
+ const errMsg = (0, formatters_1.normalizePcInsufficientFundsError)(err instanceof Error ? err.message : String(err));
244
245
  (0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_199_02, errMsg);
245
246
  throw new push_chain_tx_1.PushChainExecutionError(errMsg, { gatewayTxHash: txHash });
246
247
  }
@@ -16,6 +16,7 @@ const chain_1 = require("../../constants/chain");
16
16
  const enums_1 = require("../../constants/enums");
17
17
  const tx_1 = require("../../generated/v1/tx");
18
18
  const progress_hook_types_1 = require("../../progress-hook/progress-hook.types");
19
+ const formatters_1 = require("../../formatters");
19
20
  const utils_1 = require("../../utils");
20
21
  const evm_client_1 = require("../../vm-client/evm-client");
21
22
  const payload_builders_1 = require("../payload-builders");
@@ -103,7 +104,7 @@ function executeStandardPayload(ctx, execute, eventBuffer, getResponseCallbacks)
103
104
  catch (err) {
104
105
  // Push Chain broadcast failed. Wallet rejection surfaces as 104-04;
105
106
  // everything else (RPC fail, on-chain revert) as 199-02.
106
- const errMsg = err instanceof Error ? err.message : String(err);
107
+ const errMsg = (0, formatters_1.normalizePcInsufficientFundsError)(err instanceof Error ? err.message : String(err));
107
108
  const { isUserDecline } = (0, progress_hook_1.classifyDeclineError)(errMsg);
108
109
  (0, context_1.fireProgressHook)(ctx, isUserDecline ? progress_hook_types_1.PROGRESS_HOOK.SEND_TX_104_04 : progress_hook_types_1.PROGRESS_HOOK.SEND_TX_199_02, errMsg);
109
110
  // Mark that a terminal-ish error hook has already fired so the
@@ -266,7 +267,7 @@ function executeStandardPayload(ctx, execute, eventBuffer, getResponseCallbacks)
266
267
  // Wallet decline, contract revert during sign, or RPC failure — the
267
268
  // 104-04 builder's classifyDeclineError heuristic picks the right
268
269
  // copy (real decline → "Verification Declined"; else "Signature Failed").
269
- const errMsg = err instanceof Error ? err.message : String(err);
270
+ const errMsg = (0, formatters_1.normalizePcInsufficientFundsError)(err instanceof Error ? err.message : String(err));
270
271
  (0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_104_04, errMsg);
271
272
  throw err;
272
273
  }
@@ -352,15 +353,15 @@ function executeStandardPayload(ctx, execute, eventBuffer, getResponseCallbacks)
352
353
  const maxTransferable = funds + maxFromDeposit;
353
354
  maxTransferableNote =
354
355
  `Max transferable right now (at $1000 cap, pool-limited): ` +
355
- `~${(Number(maxTransferable) / 1e18).toFixed(4)} $PC ` +
356
- `(UEA balance ${(Number(funds) / 1e18).toFixed(4)} + ` +
357
- `pool-safe deposit ${(Number(maxFromDeposit) / 1e18).toFixed(4)} − gas).`;
356
+ `~${(0, formatters_1.formatPc)(maxTransferable, 4)} ` +
357
+ `(UEA balance ${(0, formatters_1.formatPc)(funds, 4)} + ` +
358
+ `pool-safe deposit ${(0, formatters_1.formatPc)(maxFromDeposit, 4)} - gas).`;
358
359
  (0, context_1.printLog)(ctx, `Fee-lock capacity: ${maxTransferableNote}`);
359
360
  }
360
361
  }
361
- throw new Error(`Insufficient deposit: the fee-lock will deposit ~${(Number(safeUPC) / 1e18).toFixed(4)} $PC ` +
362
- `but the transaction requires ~${(Number(requiredFunds) / 1e18).toFixed(4)} $PC ` +
363
- `(shortfall: ~${(Number(shortfall) / 1e18).toFixed(4)} $PC). ` +
362
+ throw new Error(`Insufficient deposit: the fee-lock will deposit ~${(0, formatters_1.formatPc)(safeUPC, 4)} ` +
363
+ `but the transaction requires ~${(0, formatters_1.formatPc)(requiredFunds, 4)} ` +
364
+ `(shortfall: ~${(0, formatters_1.formatPc)(shortfall, 4)}). ` +
364
365
  `The fee-lock deposit is capped at $1000 USD. ` +
365
366
  (maxTransferableNote ? maxTransferableNote + ' ' : '') +
366
367
  `Reduce the transfer value or pre-fund the UEA (${UEA}) on Push Chain.`);
@@ -412,7 +413,7 @@ function executeStandardPayload(ctx, execute, eventBuffer, getResponseCallbacks)
412
413
  }
413
414
  catch (err) {
414
415
  if (!(err instanceof push_chain_tx_1.PushChainExecutionError)) {
415
- const errMsg = err instanceof Error ? err.message : String(err);
416
+ const errMsg = (0, formatters_1.normalizePcInsufficientFundsError)(err instanceof Error ? err.message : String(err));
416
417
  (0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_199_02, errMsg);
417
418
  throw new push_chain_tx_1.PushChainExecutionError(errMsg, { gatewayTxHash: feeLockTxHash });
418
419
  }
@@ -446,7 +447,7 @@ function executeStandardPayload(ctx, execute, eventBuffer, getResponseCallbacks)
446
447
  // exhaustion). Surface terminal 199-02 before re-throwing so the stream
447
448
  // ends with the spec'd error hook. Typed PushChainExecutionError lets
448
449
  // callers classify via instanceof without message sniffing.
449
- const errMsg = err instanceof Error ? err.message : String(err);
450
+ const errMsg = (0, formatters_1.normalizePcInsufficientFundsError)(err instanceof Error ? err.message : String(err));
450
451
  (0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_199_02, errMsg);
451
452
  if (err instanceof push_chain_tx_1.PushChainExecutionError)
452
453
  throw err;
@@ -14,6 +14,7 @@ export declare function queryOutboundGasFee(ctx: OrchestratorContext, prc20Token
14
14
  protocolFee: bigint;
15
15
  nativeValueForGas: bigint;
16
16
  gasPrice: bigint;
17
+ gasLimitUsed: bigint;
17
18
  universalCoreAddress: `0x${string}`;
18
19
  sizing?: GasSizingDecision;
19
20
  }>;
@@ -64,6 +64,31 @@ const UNISWAP_V3_FACTORY_ABI = [
64
64
  stateMutability: 'view',
65
65
  },
66
66
  ];
67
+ const UNISWAP_V3_QUOTER_EXACT_OUTPUT_ABI = [
68
+ {
69
+ type: 'function',
70
+ name: 'quoteExactOutputSingle',
71
+ inputs: [
72
+ {
73
+ type: 'tuple',
74
+ components: [
75
+ { name: 'tokenIn', type: 'address' },
76
+ { name: 'tokenOut', type: 'address' },
77
+ { name: 'amount', type: 'uint256' },
78
+ { name: 'fee', type: 'uint24' },
79
+ { name: 'sqrtPriceLimitX96', type: 'uint160' },
80
+ ],
81
+ },
82
+ ],
83
+ outputs: [
84
+ { name: 'amountIn', type: 'uint256' },
85
+ { name: 'sqrtPriceX96After', type: 'uint160' },
86
+ { name: 'initializedTicksCrossed', type: 'uint32' },
87
+ { name: 'gasEstimate', type: 'uint256' },
88
+ ],
89
+ stateMutability: 'view',
90
+ },
91
+ ];
67
92
  const UNIVERSAL_CORE_SWAP_HELPERS_ABI = [
68
93
  { type: 'function', name: 'WPC', inputs: [], outputs: [{ name: '', type: 'address', internalType: 'address' }], stateMutability: 'view' },
69
94
  { type: 'function', name: 'uniswapV3Factory', inputs: [], outputs: [{ name: '', type: 'address', internalType: 'address' }], stateMutability: 'view' },
@@ -133,7 +158,7 @@ function ensureErc20Allowance(ctx, evmClient, tokenAddress, spender, requiredAmo
133
158
  // ============================================================================
134
159
  function queryOutboundGasFee(ctx, prc20Token, gasLimit, destinationChain) {
135
160
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
136
- var _a, _b;
161
+ var _a, _b, _c;
137
162
  const gatewayPcAddress = (0, helpers_1.getUniversalGatewayPCAddress)();
138
163
  const pushChain = (0, helpers_1.getPushChainForNetwork)(ctx.pushNetwork);
139
164
  const rpcUrl = ((_b = (_a = chain_1.CHAIN_INFO[pushChain]) === null || _a === void 0 ? void 0 : _a.defaultRPC) === null || _b === void 0 ? void 0 : _b[0]) || 'unknown';
@@ -143,13 +168,13 @@ function queryOutboundGasFee(ctx, prc20Token, gasLimit, destinationChain) {
143
168
  universalCoreAddress = yield ctx.pushClient.readContract({
144
169
  address: gatewayPcAddress,
145
170
  abi: abi_1.UNIVERSAL_GATEWAY_PC,
146
- functionName: 'UNIVERSAL_CORE',
171
+ functionName: 'universalCore',
147
172
  args: [],
148
173
  });
149
- (0, context_1.printLog)(ctx, `queryOutboundGasFee — [step 2] UNIVERSAL_CORE resolved to: ${universalCoreAddress}`);
174
+ (0, context_1.printLog)(ctx, `queryOutboundGasFee — [step 2] universalCore resolved to: ${universalCoreAddress}`);
150
175
  }
151
176
  catch (err) {
152
- (0, context_1.printLog)(ctx, `queryOutboundGasFee — [step 2] FAILED to read UNIVERSAL_CORE: ${err instanceof Error ? err.message : String(err)}`);
177
+ (0, context_1.printLog)(ctx, `queryOutboundGasFee — [step 2] FAILED to read universalCore: ${err instanceof Error ? err.message : String(err)}`);
153
178
  throw err;
154
179
  }
155
180
  const callData = (0, viem_1.encodeFunctionData)({
@@ -163,6 +188,7 @@ function queryOutboundGasFee(ctx, prc20Token, gasLimit, destinationChain) {
163
188
  let gasFee;
164
189
  let protocolFee;
165
190
  let gasPrice = BigInt(0);
191
+ let gasLimitUsed = gasLimit;
166
192
  try {
167
193
  const result = yield ctx.pushClient.readContract({
168
194
  address: universalCoreAddress,
@@ -174,7 +200,8 @@ function queryOutboundGasFee(ctx, prc20Token, gasLimit, destinationChain) {
174
200
  gasFee = result[1];
175
201
  protocolFee = result[2];
176
202
  gasPrice = result[3];
177
- (0, context_1.printLog)(ctx, `queryOutboundGasFee — [step 4] success: gasToken=${gasToken}, gasFee=${gasFee}, protocolFee=${protocolFee}, gasPrice=${gasPrice}`);
203
+ gasLimitUsed = (_c = result[5]) !== null && _c !== void 0 ? _c : gasLimit;
204
+ (0, context_1.printLog)(ctx, `queryOutboundGasFee — [step 4] success: gasToken=${gasToken}, gasFee=${gasFee}, protocolFee=${protocolFee}, gasPrice=${gasPrice}, gasLimitUsed=${gasLimitUsed}`);
178
205
  }
179
206
  catch (err) {
180
207
  (0, context_1.printLog)(ctx, `queryOutboundGasFee — [step 3] FAILED: ${err instanceof Error ? err.message : String(err)}`);
@@ -205,7 +232,7 @@ function queryOutboundGasFee(ctx, prc20Token, gasLimit, destinationChain) {
205
232
  nativeValueForGas = protocolFee + gasFee * GAS_FEE_BUFFER_MULTIPLIER;
206
233
  (0, context_1.printLog)(ctx, `queryOutboundGasFee — [step 5] no destinationChain provided; legacy 1M buffer: nativeValueForGas=${nativeValueForGas}`);
207
234
  }
208
- return { gasToken, gasFee, protocolFee, nativeValueForGas, gasPrice, universalCoreAddress, sizing };
235
+ return { gasToken, gasFee, protocolFee, nativeValueForGas, gasPrice, gasLimitUsed, universalCoreAddress, sizing };
209
236
  });
210
237
  }
211
238
  // ============================================================================
@@ -219,7 +246,19 @@ function queryOutboundGasFee(ctx, prc20Token, gasLimit, destinationChain) {
219
246
  */
220
247
  function estimateNativeValueForSwap(ctx, universalCoreAddress, gasToken, gasFee, accountBalance) {
221
248
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
222
- const SWAP_BUFFER = BigInt(2); // 2x safety buffer; excess is refunded by swapAndBurnGas
249
+ var _a, _b;
250
+ // 2.2x safety buffer applied to the pool quote; excess is refunded by
251
+ // swapAndBurnGas. Composed of a 2x pool-slippage cushion plus 10%
252
+ // headroom that used to live in the R2 route-handler callers (×110/100
253
+ // after this function returned). Folded into one place here so the cap
254
+ // branch below correctly bounds the FINAL inflated value — the previous
255
+ // arrangement (outer ×1.10 on top of an inner cap that returned
256
+ // `balance - GAS_RESERVE`) caused a deterministic preflight failure
257
+ // whenever the cap fired: required = 1.10 × (balance − 3 PC) + 3 PC ≈
258
+ // balance × 1.10 − 0.3 PC > balance for any non-trivial balance. See
259
+ // preflight.ts:215.
260
+ const SWAP_BUFFER_NUM = BigInt(22);
261
+ const SWAP_BUFFER_DEN = BigInt(10);
223
262
  const BALANCE_FALLBACK_DIVISOR = BigInt(10); // 10% of balance as fallback
224
263
  const GAS_RESERVE = BigInt(3e18); // 3 UPC reserve for tx overhead
225
264
  try {
@@ -248,6 +287,34 @@ function estimateNativeValueForSwap(ctx, universalCoreAddress, gasToken, gasFee,
248
287
  (0, context_1.printLog)(ctx, `estimateNativeValueForSwap — no fee tier for gasToken, using balance fallback`);
249
288
  return balanceFallback(accountBalance, BALANCE_FALLBACK_DIVISOR, GAS_RESERVE);
250
289
  }
290
+ const pushChain = (0, helpers_1.getPushChainForNetwork)(ctx.pushNetwork);
291
+ const quoterAddress = (_b = (_a = chain_1.CHAIN_INFO[pushChain]) === null || _a === void 0 ? void 0 : _a.dex) === null || _b === void 0 ? void 0 : _b.uniV3QuoterV2;
292
+ if (quoterAddress && !quoterAddress.startsWith('0xTBD')) {
293
+ try {
294
+ const quote = yield ctx.pushClient.readContract({
295
+ address: quoterAddress,
296
+ abi: UNISWAP_V3_QUOTER_EXACT_OUTPUT_ABI,
297
+ functionName: 'quoteExactOutputSingle',
298
+ args: [
299
+ {
300
+ tokenIn: wpcAddress,
301
+ tokenOut: gasToken,
302
+ amount: gasFee,
303
+ fee: feeTier,
304
+ sqrtPriceLimitX96: BigInt(0),
305
+ },
306
+ ],
307
+ });
308
+ const exactWpcNeeded = quote[0];
309
+ const result = (exactWpcNeeded * SWAP_BUFFER_NUM) / SWAP_BUFFER_DEN;
310
+ (0, context_1.printLog)(ctx, `estimateNativeValueForSwap — exactOutput quoter=${quoterAddress}, ` +
311
+ `wpcNeeded=${exactWpcNeeded}, withBuffer(2.2x)=${result}`);
312
+ return capSwapEstimate(result, accountBalance, GAS_RESERVE);
313
+ }
314
+ catch (quoteErr) {
315
+ (0, context_1.printLog)(ctx, `estimateNativeValueForSwap — exactOutput quote failed: ${quoteErr instanceof Error ? quoteErr.message : String(quoteErr)}, falling back to slot0 price`);
316
+ }
317
+ }
251
318
  // Get pool address from factory
252
319
  const poolAddress = yield ctx.pushClient.readContract({
253
320
  address: factoryAddress,
@@ -285,17 +352,10 @@ function estimateNativeValueForSwap(ctx, universalCoreAddress, gasToken, gasFee,
285
352
  // price = gasToken_per_WPC → wpcNeeded = gasFee / price
286
353
  wpcNeeded = (gasFee * Q192) / priceNum;
287
354
  }
288
- const result = wpcNeeded * SWAP_BUFFER;
355
+ const result = (wpcNeeded * SWAP_BUFFER_NUM) / SWAP_BUFFER_DEN;
289
356
  (0, context_1.printLog)(ctx, `estimateNativeValueForSwap — pool=${poolAddress}, sqrtPriceX96=${sqrtPriceX96}, ` +
290
- `wpcNeeded=${wpcNeeded}, withBuffer(2x)=${result}`);
291
- // Cap at (balance - reserve)
292
- if (accountBalance > result + GAS_RESERVE) {
293
- return result;
294
- }
295
- else if (accountBalance > GAS_RESERVE) {
296
- return accountBalance - GAS_RESERVE;
297
- }
298
- return result; // let caller decide if balance is too low
357
+ `wpcNeeded=${wpcNeeded}, withBuffer(2.2x)=${result}`);
358
+ return capSwapEstimate(result, accountBalance, GAS_RESERVE);
299
359
  }
300
360
  catch (err) {
301
361
  (0, context_1.printLog)(ctx, `estimateNativeValueForSwap — failed: ${err instanceof Error ? err.message : String(err)}, using balance fallback`);
@@ -303,6 +363,15 @@ function estimateNativeValueForSwap(ctx, universalCoreAddress, gasToken, gasFee,
303
363
  }
304
364
  });
305
365
  }
366
+ function capSwapEstimate(result, accountBalance, reserve) {
367
+ if (accountBalance > result + reserve) {
368
+ return result;
369
+ }
370
+ if (accountBalance > reserve) {
371
+ return accountBalance - reserve;
372
+ }
373
+ return result; // let caller decide if balance is too low
374
+ }
306
375
  function balanceFallback(balance, divisor, reserve) {
307
376
  const fraction = balance / divisor;
308
377
  if (balance > fraction + reserve)
@@ -323,13 +392,13 @@ function queryRescueGasFee(ctx, prc20Token, destinationChain) {
323
392
  universalCoreAddress = yield ctx.pushClient.readContract({
324
393
  address: gatewayPcAddress,
325
394
  abi: abi_1.UNIVERSAL_GATEWAY_PC,
326
- functionName: 'UNIVERSAL_CORE',
395
+ functionName: 'universalCore',
327
396
  args: [],
328
397
  });
329
- (0, context_1.printLog)(ctx, `queryRescueGasFee — UNIVERSAL_CORE resolved to: ${universalCoreAddress}`);
398
+ (0, context_1.printLog)(ctx, `queryRescueGasFee — universalCore resolved to: ${universalCoreAddress}`);
330
399
  }
331
400
  catch (err) {
332
- (0, context_1.printLog)(ctx, `queryRescueGasFee — FAILED to read UNIVERSAL_CORE: ${err instanceof Error ? err.message : String(err)}`);
401
+ (0, context_1.printLog)(ctx, `queryRescueGasFee — FAILED to read universalCore: ${err instanceof Error ? err.message : String(err)}`);
333
402
  throw err;
334
403
  }
335
404
  let gasToken;
@@ -495,7 +564,7 @@ function estimateDepositFromLockedNative(ctx, nativeAmountForLocker, prc20Token)
495
564
  const universalCoreAddress = yield ctx.pushClient.readContract({
496
565
  address: gatewayPcAddress,
497
566
  abi: abi_1.UNIVERSAL_GATEWAY_PC,
498
- functionName: 'UNIVERSAL_CORE',
567
+ functionName: 'universalCore',
499
568
  args: [],
500
569
  });
501
570
  const [wpcAddress, feeTier] = yield Promise.all([
@@ -587,7 +656,7 @@ function estimateNativeForDesiredDeposit(ctx, desiredWpc, prc20Token) {
587
656
  const universalCoreAddress = yield ctx.pushClient.readContract({
588
657
  address: gatewayPcAddress,
589
658
  abi: abi_1.UNIVERSAL_GATEWAY_PC,
590
- functionName: 'UNIVERSAL_CORE',
659
+ functionName: 'universalCore',
591
660
  args: [],
592
661
  });
593
662
  const [wpcAddress, feeTier] = yield Promise.all([
@@ -9,9 +9,10 @@ import type { OrchestratorContext } from './context';
9
9
  /** Chains that support gateway operations (multicall, funds bridging, etc.) */
10
10
  export declare const SUPPORTED_GATEWAY_CHAINS: CHAIN[];
11
11
  export declare function isPushChain(chain: CHAIN): boolean;
12
+ export { toExternalTxHashDisplay } from '../../utils/external-tx-hash';
12
13
  export declare function getPushChainForNetwork(pushNetwork: PUSH_NETWORK): CHAIN;
13
14
  export declare function getChainNamespace(chain: CHAIN): string;
14
- export declare function chainFromNamespace(namespace: string): CHAIN | null;
15
+ export { chainFromNamespace } from '../../utils/external-tx-hash';
15
16
  export declare function getNativePRC20ForChain(targetChain: CHAIN, pushNetwork: PUSH_NETWORK): `0x${string}`;
16
17
  export declare function getUniversalGatewayPCAddress(): `0x${string}`;
17
18
  /**
@@ -5,11 +5,10 @@
5
5
  * These have minimal dependencies and are used across multiple submodules.
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.SUPPORTED_GATEWAY_CHAINS = void 0;
8
+ exports.chainFromNamespace = exports.toExternalTxHashDisplay = exports.SUPPORTED_GATEWAY_CHAINS = void 0;
9
9
  exports.isPushChain = isPushChain;
10
10
  exports.getPushChainForNetwork = getPushChainForNetwork;
11
11
  exports.getChainNamespace = getChainNamespace;
12
- exports.chainFromNamespace = chainFromNamespace;
13
12
  exports.getNativePRC20ForChain = getNativePRC20ForChain;
14
13
  exports.getUniversalGatewayPCAddress = getUniversalGatewayPCAddress;
15
14
  exports.validateFeeLockTxHash = validateFeeLockTxHash;
@@ -38,6 +37,14 @@ function isPushChain(chain) {
38
37
  chain === enums_1.CHAIN.PUSH_TESTNET_DONUT ||
39
38
  chain === enums_1.CHAIN.PUSH_LOCALNET);
40
39
  }
40
+ // `toExternalTxHashDisplay` moved to ../../utils/external-tx-hash so the
41
+ // detector module can use it without back-importing from orchestrator/internals
42
+ // (inbound-tracker.ts already imports universal-tx-detector — a back-import
43
+ // from detector would create a cross-layer tangle). Re-exported here so
44
+ // existing call sites (outbound-sync.ts / response-builder.ts / cascade.ts)
45
+ // keep their current import path unchanged.
46
+ var external_tx_hash_1 = require("../../utils/external-tx-hash");
47
+ Object.defineProperty(exports, "toExternalTxHashDisplay", { enumerable: true, get: function () { return external_tx_hash_1.toExternalTxHashDisplay; } });
41
48
  function getPushChainForNetwork(pushNetwork) {
42
49
  if (pushNetwork === enums_1.PUSH_NETWORK.MAINNET) {
43
50
  return enums_1.CHAIN.PUSH_MAINNET;
@@ -55,15 +62,10 @@ function getChainNamespace(chain) {
55
62
  const namespace = chain_1.VM_NAMESPACE[vm];
56
63
  return `${namespace}:${chainId}`;
57
64
  }
58
- function chainFromNamespace(namespace) {
59
- for (const [chainKey, info] of Object.entries(chain_1.CHAIN_INFO)) {
60
- const expected = `${chain_1.VM_NAMESPACE[info.vm]}:${info.chainId}`;
61
- if (expected === namespace) {
62
- return chainKey;
63
- }
64
- }
65
- return null;
66
- }
65
+ // Moved to ../../utils/external-tx-hash alongside `toExternalTxHashDisplay`
66
+ // so both the orchestrator and the detector module share one implementation.
67
+ var external_tx_hash_2 = require("../../utils/external-tx-hash");
68
+ Object.defineProperty(exports, "chainFromNamespace", { enumerable: true, get: function () { return external_tx_hash_2.chainFromNamespace; } });
67
69
  function getNativePRC20ForChain(targetChain, pushNetwork) {
68
70
  const synthetics = chain_1.SYNTHETIC_PUSH_ERC20[pushNetwork];
69
71
  switch (targetChain) {
@@ -140,6 +142,7 @@ function toExecuteParams(params) {
140
142
  data: params.data,
141
143
  funds: params.funds,
142
144
  gasLimit: params.gasLimit,
145
+ maxPCForGas: params.maxPCForGas,
143
146
  maxFeePerGas: params.maxFeePerGas,
144
147
  maxPriorityFeePerGas: params.maxPriorityFeePerGas,
145
148
  deadline: params.deadline,
@@ -20,6 +20,7 @@ const types_1 = require("../../generated/uexecutor/v2/types");
20
20
  const context_1 = require("./context");
21
21
  const detector_1 = require("../../universal-tx-detector/detector");
22
22
  const child_inbounds_1 = require("../../universal-tx-detector/child-inbounds");
23
+ const formatters_1 = require("../../formatters");
23
24
  const TERMINAL_SUCCESS_STATUSES = new Set([
24
25
  types_1.UniversalTxStatus.INBOUND_SUCCESS,
25
26
  types_1.UniversalTxStatus.PC_EXECUTED_SUCCESS,
@@ -205,8 +206,9 @@ function waitForInboundPushTx(ctx_1, outboundExternalTxHash_1, sourceChain_1) {
205
206
  }
206
207
  }
207
208
  else if (TERMINAL_FAILURE_STATUSES.has(status)) {
208
- const errMsg = ((_l = (_k = utx === null || utx === void 0 ? void 0 : utx.pcTx) === null || _k === void 0 ? void 0 : _k[0]) === null || _l === void 0 ? void 0 : _l.errorMsg) ||
209
- `inbound terminated with status ${status}`;
209
+ const errMsg = ((_l = (_k = utx === null || utx === void 0 ? void 0 : utx.pcTx) === null || _k === void 0 ? void 0 : _k[0]) === null || _l === void 0 ? void 0 : _l.errorMsg)
210
+ ? (0, formatters_1.normalizePcInsufficientFundsError)(utx.pcTx[0].errorMsg)
211
+ : `inbound terminated with status ${status}`;
210
212
  progressHook === null || progressHook === void 0 ? void 0 : progressHook({
211
213
  status: 'failed',
212
214
  elapsedMs: elapsed(),
@@ -67,6 +67,7 @@ export declare function waitForAllOutboundTxsV2(ctx: OrchestratorContext, pushCh
67
67
  initialWaitMs: number;
68
68
  pollingIntervalMs: number;
69
69
  timeout: number;
70
+ _resolvedSubTxId?: string;
70
71
  progressHook?: (event: {
71
72
  hopIndex: number;
72
73
  route: TransactionRouteType;