@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
@@ -16,6 +16,11 @@ const enums_1 = require("../../constants/enums");
16
16
  // ============================================================================
17
17
  // EIP-712 Domain Separator (shared by execution & migration hashes)
18
18
  // ============================================================================
19
+ // NOTE: Push Chain testnet UEA implementation (UEA_VM[EVM] =
20
+ // 0x4C8401F885c79b0e6e0241812f089e8529045d11) still uses the pre-audit
21
+ // 4-field domain (typehash 0x2aef22f9…) — the salted post-audit typehash
22
+ // 0xb90aaffa… in the audit-fixes contracts hasn't been deployed yet. Drop
23
+ // `salt` until the contracts redeploy. See docs/audit-2026-05/sdk-changes-required.md#11.
19
24
  function buildDomainSeparator(vm, chainId, version, verifyingContract) {
20
25
  const domainTypeHash = (0, viem_1.keccak256)((0, viem_1.toBytes)(vm === enums_1.VM.EVM
21
26
  ? 'EIP712Domain(string version,uint256 chainId,address verifyingContract)'
@@ -23,12 +28,12 @@ function buildDomainSeparator(vm, chainId, version, verifyingContract) {
23
28
  return (0, viem_1.keccak256)((0, viem_1.encodeAbiParameters)([
24
29
  { name: 'typeHash', type: 'bytes32' },
25
30
  { name: 'version', type: 'bytes32' },
26
- { name: 'chainId', type: vm === enums_1.VM.EVM ? 'uint256' : 'string' },
31
+ { name: 'chainId', type: vm === enums_1.VM.EVM ? 'uint256' : 'bytes32' },
27
32
  { name: 'verifyingContract', type: 'address' },
28
33
  ], [
29
34
  domainTypeHash,
30
35
  (0, viem_1.keccak256)((0, viem_1.toBytes)(version)),
31
- vm === enums_1.VM.EVM ? BigInt(chainId) : chainId,
36
+ vm === enums_1.VM.EVM ? BigInt(chainId) : (0, viem_1.keccak256)((0, viem_1.toBytes)(chainId)),
32
37
  verifyingContract,
33
38
  ]));
34
39
  }
@@ -82,7 +82,7 @@ function buildSvmUniversalTxRequestFromReq(req, revertRecipient, signatureDataOv
82
82
  // ============================================================================
83
83
  function getSvmProtocolFee(svmClient, programId) {
84
84
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
85
- var _a, _b, _c;
85
+ var _a, _b, _c, _d, _e;
86
86
  const [feeVaultPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('fee_vault')], programId);
87
87
  try {
88
88
  const feeVault = yield svmClient.readContract({
@@ -91,10 +91,10 @@ function getSvmProtocolFee(svmClient, programId) {
91
91
  functionName: 'feeVault',
92
92
  args: [feeVaultPda.toBase58()],
93
93
  });
94
- const protocolFeeLamports = BigInt((_c = (_b = ((_a = feeVault.protocolFeeLamports) !== null && _a !== void 0 ? _a : feeVault.protocol_fee_lamports)) === null || _b === void 0 ? void 0 : _b.toString()) !== null && _c !== void 0 ? _c : '0');
94
+ const protocolFeeLamports = BigInt((_e = (_d = ((_c = (_b = (_a = feeVault.inboundFeeLamports) !== null && _a !== void 0 ? _a : feeVault.inbound_fee_lamports) !== null && _b !== void 0 ? _b : feeVault.protocolFeeLamports) !== null && _c !== void 0 ? _c : feeVault.protocol_fee_lamports)) === null || _d === void 0 ? void 0 : _d.toString()) !== null && _e !== void 0 ? _e : '0');
95
95
  return { feeVaultPda, protocolFeeLamports };
96
96
  }
97
- catch (_d) {
97
+ catch (_f) {
98
98
  return { feeVaultPda, protocolFeeLamports: BigInt(0) };
99
99
  }
100
100
  });
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Conditional CEA-ATA rent bump for SVM SPL outbounds.
2
+ * SVM finalize gas-budget helpers.
3
3
  *
4
4
  * The gateway program (svm-gateway) computes per-tx fees with a conditional
5
5
  * rent for first-time SPL transfers — see `calculateSplExecuteFees` in
@@ -7,21 +7,26 @@
7
7
  *
8
8
  * const ataExists = await ceaAtaExists(connection, ceaAta);
9
9
  * const ceaAtaRent = ataExists ? 0n : BigInt(getMinimumBalanceForRentExemption(165));
10
- * gasFee = executedTxRent + ceaAtaRent + COMPUTE_BUFFER;
10
+ * gasFee = signatureFee + executedTxRent + ceaAtaRent + COMPUTE_BUFFER;
11
11
  *
12
12
  * The gateway's on-chain `getOutboundTxGasAndFees` quote does NOT factor in
13
13
  * the ATA rent today, so the SDK has to bump `effectiveGasLimit` itself
14
- * before calling that quote. Native-SOL outbounds use `calculateSolExecuteFees`
15
- * which has no conditional rent, so no SDK bump is needed there.
14
+ * before building the outbound request. Native-SOL outbounds still need the
15
+ * same base finalize budget plus compute buffer.
16
16
  */
17
17
  import { PublicKey } from '@solana/web3.js';
18
18
  import type { OrchestratorContext } from './context';
19
19
  import type { CHAIN as ChainType } from '../../constants/enums';
20
- /** ATA rent (165-byte SPL token account) + executed-tx rent (8-byte) + compute buffer.
21
- * Mirrors getMinimumBalanceForRentExemption(165) ≈ 2_039_280
22
- * + getMinimumBalanceForRentExemption(8) ≈ 9_200
23
- * + COMPUTE_BUFFER 100_000.
24
- * Signature fee (5_000) is already included in the gateway's base quote. */
20
+ import { queryOutboundGasFee } from './gas-calculator';
21
+ /** Base Solana fee per relayer signature, mirrored from SVM gateway execute.rs. */
22
+ export declare const SVM_SIGNATURE_FEE_LAMPORTS: bigint;
23
+ /** Operational buffer used by SVM gateway tests for tx fees / compute. */
24
+ export declare const SVM_FINALIZE_COMPUTE_BUFFER_LAMPORTS: bigint;
25
+ /** Devnet/mainnet rent-exempt minimum for ExecutedSubTx::LEN = 8 bytes. */
26
+ export declare const SVM_EXECUTED_SUB_TX_RENT_FALLBACK: bigint;
27
+ /** Devnet/mainnet rent-exempt minimum for a 165-byte SPL token account. */
28
+ export declare const SVM_TOKEN_ACCOUNT_RENT_FALLBACK: bigint;
29
+ /** Back-compat export for older tests/callers: ATA rent + base finalization overhead. */
25
30
  export declare const CEA_ATA_RENT_LAMPORTS_BUMP: bigint;
26
31
  /** Derive CEA PDA from a 20-byte EVM address. Seeds: ["push_identity", evmBytes]. */
27
32
  export declare function deriveSvmCeaPda(evmAddress: `0x${string}`): PublicKey;
@@ -37,6 +42,33 @@ export interface MaybeBumpInput {
37
42
  burnAmount: bigint;
38
43
  effectiveGasLimit: bigint;
39
44
  }
45
+ export interface SvmFinalizeGasBudgetInput {
46
+ ctx: OrchestratorContext;
47
+ ueaAddress: `0x${string}`;
48
+ targetChain: ChainType;
49
+ /** SPL mint as base58. undefined means native SOL / no SPL staging. */
50
+ splMintBase58: string | undefined;
51
+ /** Burn amount in token units. 0 means no SPL staging. */
52
+ burnAmount: bigint;
53
+ }
54
+ export declare function gasLimitForSvmGasFeeBudget(gasFeeBudget: bigint, gasPrice: bigint): bigint;
55
+ type OutboundGasQuote = Awaited<ReturnType<typeof queryOutboundGasFee>>;
56
+ export declare function ensureSvmFinalizeGasBudgetQuote({ ctx, ueaAddress, targetChain, prc20Token, quote, splMintBase58, burnAmount, pathTag, }: {
57
+ ctx: OrchestratorContext;
58
+ ueaAddress: `0x${string}`;
59
+ targetChain: ChainType;
60
+ prc20Token: `0x${string}`;
61
+ quote: OutboundGasQuote;
62
+ splMintBase58: string | undefined;
63
+ burnAmount: bigint;
64
+ pathTag: string;
65
+ }): Promise<OutboundGasQuote>;
66
+ /**
67
+ * Returns the minimum SVM `gas_fee` budget in lamports for the gateway
68
+ * finalize instruction, including a small compute buffer. If RPC is not
69
+ * available, falls back to Solana protocol rent constants.
70
+ */
71
+ export declare function getSvmFinalizeGasBudget(input: SvmFinalizeGasBudgetInput): Promise<bigint>;
40
72
  /**
41
73
  * Returns the (possibly bumped) effectiveGasLimit. Logs the decision via
42
74
  * printLog. On any RPC failure or invalid input, logs a warning and returns
@@ -44,3 +76,4 @@ export interface MaybeBumpInput {
44
76
  * outbounds on RPC flake; pre-flight will surface insufficient gas anyway.
45
77
  */
46
78
  export declare function maybeBumpForCeaAtaRent(input: MaybeBumpInput): Promise<bigint>;
79
+ export {};
@@ -1,12 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CEA_ATA_RENT_LAMPORTS_BUMP = void 0;
3
+ exports.CEA_ATA_RENT_LAMPORTS_BUMP = exports.SVM_TOKEN_ACCOUNT_RENT_FALLBACK = exports.SVM_EXECUTED_SUB_TX_RENT_FALLBACK = exports.SVM_FINALIZE_COMPUTE_BUFFER_LAMPORTS = exports.SVM_SIGNATURE_FEE_LAMPORTS = void 0;
4
4
  exports.deriveSvmCeaPda = deriveSvmCeaPda;
5
5
  exports.deriveAtaPubkey = deriveAtaPubkey;
6
+ exports.gasLimitForSvmGasFeeBudget = gasLimitForSvmGasFeeBudget;
7
+ exports.ensureSvmFinalizeGasBudgetQuote = ensureSvmFinalizeGasBudgetQuote;
8
+ exports.getSvmFinalizeGasBudget = getSvmFinalizeGasBudget;
6
9
  exports.maybeBumpForCeaAtaRent = maybeBumpForCeaAtaRent;
7
10
  const tslib_1 = require("tslib");
8
11
  /**
9
- * Conditional CEA-ATA rent bump for SVM SPL outbounds.
12
+ * SVM finalize gas-budget helpers.
10
13
  *
11
14
  * The gateway program (svm-gateway) computes per-tx fees with a conditional
12
15
  * rent for first-time SPL transfers — see `calculateSplExecuteFees` in
@@ -14,31 +17,40 @@ const tslib_1 = require("tslib");
14
17
  *
15
18
  * const ataExists = await ceaAtaExists(connection, ceaAta);
16
19
  * const ceaAtaRent = ataExists ? 0n : BigInt(getMinimumBalanceForRentExemption(165));
17
- * gasFee = executedTxRent + ceaAtaRent + COMPUTE_BUFFER;
20
+ * gasFee = signatureFee + executedTxRent + ceaAtaRent + COMPUTE_BUFFER;
18
21
  *
19
22
  * The gateway's on-chain `getOutboundTxGasAndFees` quote does NOT factor in
20
23
  * the ATA rent today, so the SDK has to bump `effectiveGasLimit` itself
21
- * before calling that quote. Native-SOL outbounds use `calculateSolExecuteFees`
22
- * which has no conditional rent, so no SDK bump is needed there.
24
+ * before building the outbound request. Native-SOL outbounds still need the
25
+ * same base finalize budget plus compute buffer.
23
26
  */
24
27
  const web3_js_1 = require("@solana/web3.js");
25
28
  const buffer_1 = require("buffer");
26
29
  const context_1 = require("./context");
27
30
  const chain_1 = require("../../constants/chain");
31
+ const gas_calculator_1 = require("./gas-calculator");
28
32
  // ---------------------------------------------------------------------------
29
33
  // Constants — mirror gateway test-utils.ts. These are SOL protocol values;
30
34
  // they only change if Solana itself adjusts rent rates (effectively never).
31
35
  // ---------------------------------------------------------------------------
32
- /** ATA rent (165-byte SPL token account) + executed-tx rent (8-byte) + compute buffer.
33
- * Mirrors getMinimumBalanceForRentExemption(165) ≈ 2_039_280
34
- * + getMinimumBalanceForRentExemption(8) ≈ 9_200
35
- * + COMPUTE_BUFFER 100_000.
36
- * Signature fee (5_000) is already included in the gateway's base quote. */
37
- exports.CEA_ATA_RENT_LAMPORTS_BUMP = BigInt(2148480);
36
+ /** Base Solana fee per relayer signature, mirrored from SVM gateway execute.rs. */
37
+ exports.SVM_SIGNATURE_FEE_LAMPORTS = BigInt(5000);
38
+ /** Operational buffer used by SVM gateway tests for tx fees / compute. */
39
+ exports.SVM_FINALIZE_COMPUTE_BUFFER_LAMPORTS = BigInt(100000);
40
+ /** Devnet/mainnet rent-exempt minimum for ExecutedSubTx::LEN = 8 bytes. */
41
+ exports.SVM_EXECUTED_SUB_TX_RENT_FALLBACK = BigInt(946560);
42
+ /** Devnet/mainnet rent-exempt minimum for a 165-byte SPL token account. */
43
+ exports.SVM_TOKEN_ACCOUNT_RENT_FALLBACK = BigInt(2039280);
44
+ /** Back-compat export for older tests/callers: ATA rent + base finalization overhead. */
45
+ exports.CEA_ATA_RENT_LAMPORTS_BUMP = exports.SVM_SIGNATURE_FEE_LAMPORTS +
46
+ exports.SVM_EXECUTED_SUB_TX_RENT_FALLBACK +
47
+ exports.SVM_TOKEN_ACCOUNT_RENT_FALLBACK +
48
+ exports.SVM_FINALIZE_COMPUTE_BUFFER_LAMPORTS;
38
49
  /** SVM gateway program ID (matches CHAIN_INFO[SOLANA_DEVNET].lockerContract). */
39
50
  const SVM_GATEWAY_PROGRAM = new web3_js_1.PublicKey('CFVSincHYbETh2k7w6u1ENEkjbSLtveRCEBupKidw2VS');
40
51
  const TOKEN_PROGRAM_ID = new web3_js_1.PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');
41
52
  const ASSOCIATED_TOKEN_PROGRAM_ID = new web3_js_1.PublicKey('ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL');
53
+ const SVM_RENT_RPC_TIMEOUT_MS = 3000;
42
54
  // ---------------------------------------------------------------------------
43
55
  // PDA derivations
44
56
  // ---------------------------------------------------------------------------
@@ -53,6 +65,129 @@ function deriveAtaPubkey(owner, mint) {
53
65
  const [ata] = web3_js_1.PublicKey.findProgramAddressSync([owner.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()], ASSOCIATED_TOKEN_PROGRAM_ID);
54
66
  return ata;
55
67
  }
68
+ function getRpcUrl(ctx, targetChain) {
69
+ var _a, _b, _c, _d, _e;
70
+ return ((_c = (_b = (_a = ctx.rpcUrls) === null || _a === void 0 ? void 0 : _a[targetChain]) === null || _b === void 0 ? void 0 : _b[0]) !== null && _c !== void 0 ? _c : (_e = (_d = chain_1.CHAIN_INFO[targetChain]) === null || _d === void 0 ? void 0 : _d.defaultRPC) === null || _e === void 0 ? void 0 : _e[0]);
71
+ }
72
+ function isAccountInitialized(info) {
73
+ return info !== null && info.data.length > 0;
74
+ }
75
+ function withSvmRentRpcTimeout(promise, label) {
76
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
77
+ let timer;
78
+ try {
79
+ return yield Promise.race([
80
+ promise,
81
+ new Promise((_, reject) => {
82
+ var _a, _b;
83
+ timer = setTimeout(() => reject(new Error(`${label} timed out after ${SVM_RENT_RPC_TIMEOUT_MS}ms`)), SVM_RENT_RPC_TIMEOUT_MS);
84
+ (_b = (_a = timer).unref) === null || _b === void 0 ? void 0 : _b.call(_a);
85
+ }),
86
+ ]);
87
+ }
88
+ finally {
89
+ if (timer)
90
+ clearTimeout(timer);
91
+ }
92
+ });
93
+ }
94
+ function gasLimitForSvmGasFeeBudget(gasFeeBudget, gasPrice) {
95
+ if (gasPrice <= BigInt(0)) {
96
+ throw new Error(`Cannot derive SVM gasLimit from gasPrice=${gasPrice.toString()}`);
97
+ }
98
+ return (gasFeeBudget + gasPrice - BigInt(1)) / gasPrice;
99
+ }
100
+ function ensureSvmFinalizeGasBudgetQuote(_a) {
101
+ return tslib_1.__awaiter(this, arguments, void 0, function* ({ ctx, ueaAddress, targetChain, prc20Token, quote, splMintBase58, burnAmount, pathTag, }) {
102
+ const minGasFee = yield getSvmFinalizeGasBudget({
103
+ ctx,
104
+ ueaAddress,
105
+ targetChain,
106
+ splMintBase58,
107
+ burnAmount,
108
+ });
109
+ if (quote.gasFee >= minGasFee) {
110
+ (0, context_1.printLog)(ctx, `${pathTag} — SVM finalize gas budget ok: gasFee=${quote.gasFee.toString()}, ` +
111
+ `minimum=${minGasFee.toString()}, gasLimit=${quote.gasLimitUsed.toString()}`);
112
+ return quote;
113
+ }
114
+ const bumpedGasLimit = gasLimitForSvmGasFeeBudget(minGasFee, quote.gasPrice);
115
+ (0, context_1.printLog)(ctx, `${pathTag} — bumping SVM gasLimit ${quote.gasLimitUsed.toString()} → ${bumpedGasLimit.toString()}; ` +
116
+ `gasFee=${quote.gasFee.toString()} below finalize minimum=${minGasFee.toString()}`);
117
+ const bumpedQuote = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, bumpedGasLimit, targetChain);
118
+ if (bumpedQuote.gasFee < minGasFee) {
119
+ (0, context_1.printLog)(ctx, `${pathTag} — WARNING: bumped SVM gasFee=${bumpedQuote.gasFee.toString()} ` +
120
+ `still below finalize minimum=${minGasFee.toString()}`);
121
+ }
122
+ return bumpedQuote;
123
+ });
124
+ }
125
+ /**
126
+ * Returns the minimum SVM `gas_fee` budget in lamports for the gateway
127
+ * finalize instruction, including a small compute buffer. If RPC is not
128
+ * available, falls back to Solana protocol rent constants.
129
+ */
130
+ function getSvmFinalizeGasBudget(input) {
131
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
132
+ const { ctx, ueaAddress, targetChain, splMintBase58, burnAmount } = input;
133
+ const needsSplAta = Boolean(splMintBase58) && burnAmount > BigInt(0);
134
+ const rpcUrl = getRpcUrl(ctx, targetChain);
135
+ let executedTxRent = exports.SVM_EXECUTED_SUB_TX_RENT_FALLBACK;
136
+ let ataRent = BigInt(0);
137
+ if (!needsSplAta) {
138
+ const budget = exports.SVM_SIGNATURE_FEE_LAMPORTS +
139
+ executedTxRent +
140
+ ataRent +
141
+ exports.SVM_FINALIZE_COMPUTE_BUFFER_LAMPORTS;
142
+ (0, context_1.printLog)(ctx, `getSvmFinalizeGasBudget — no SPL ATA needed for ${targetChain}; ` +
143
+ `using fallback base budget ${budget.toString()}`);
144
+ return budget;
145
+ }
146
+ if (!rpcUrl) {
147
+ ataRent = exports.SVM_TOKEN_ACCOUNT_RENT_FALLBACK;
148
+ (0, context_1.printLog)(ctx, `getSvmFinalizeGasBudget — no Solana RPC URL for ${targetChain}; ` +
149
+ `using fallback budget ${(exports.SVM_SIGNATURE_FEE_LAMPORTS +
150
+ executedTxRent +
151
+ ataRent +
152
+ exports.SVM_FINALIZE_COMPUTE_BUFFER_LAMPORTS).toString()}`);
153
+ return (exports.SVM_SIGNATURE_FEE_LAMPORTS +
154
+ executedTxRent +
155
+ ataRent +
156
+ exports.SVM_FINALIZE_COMPUTE_BUFFER_LAMPORTS);
157
+ }
158
+ try {
159
+ const conn = new web3_js_1.Connection(rpcUrl, 'confirmed');
160
+ executedTxRent = BigInt(yield withSvmRentRpcTimeout(conn.getMinimumBalanceForRentExemption(8), 'getMinimumBalanceForRentExemption(8)'));
161
+ if (needsSplAta && splMintBase58) {
162
+ const ceaPda = deriveSvmCeaPda(ueaAddress);
163
+ const splMint = new web3_js_1.PublicKey(splMintBase58);
164
+ const ceaAta = deriveAtaPubkey(ceaPda, splMint);
165
+ const info = yield withSvmRentRpcTimeout(conn.getAccountInfo(ceaAta), `getAccountInfo(${ceaAta.toBase58()})`);
166
+ if (!isAccountInitialized(info)) {
167
+ ataRent = BigInt(yield withSvmRentRpcTimeout(conn.getMinimumBalanceForRentExemption(165), 'getMinimumBalanceForRentExemption(165)'));
168
+ (0, context_1.printLog)(ctx, `getSvmFinalizeGasBudget — CEA ATA ${ceaAta.toBase58()} not deployed on ${targetChain}; ` +
169
+ `including ${ataRent.toString()} lamports ATA rent`);
170
+ }
171
+ else {
172
+ (0, context_1.printLog)(ctx, `getSvmFinalizeGasBudget — CEA ATA ${ceaAta.toBase58()} already deployed on ${targetChain}; no ATA rent`);
173
+ }
174
+ }
175
+ }
176
+ catch (err) {
177
+ ataRent = exports.SVM_TOKEN_ACCOUNT_RENT_FALLBACK;
178
+ (0, context_1.printLog)(ctx, `getSvmFinalizeGasBudget — RPC error on ${targetChain}: ` +
179
+ `${err instanceof Error ? err.message : String(err)}; using fallback rents`);
180
+ }
181
+ const budget = exports.SVM_SIGNATURE_FEE_LAMPORTS +
182
+ executedTxRent +
183
+ ataRent +
184
+ exports.SVM_FINALIZE_COMPUTE_BUFFER_LAMPORTS;
185
+ (0, context_1.printLog)(ctx, `getSvmFinalizeGasBudget — budget=${budget.toString()} ` +
186
+ `(signature=${exports.SVM_SIGNATURE_FEE_LAMPORTS}, executedTxRent=${executedTxRent}, ataRent=${ataRent}, ` +
187
+ `computeBuffer=${exports.SVM_FINALIZE_COMPUTE_BUFFER_LAMPORTS})`);
188
+ return budget;
189
+ });
190
+ }
56
191
  /**
57
192
  * Returns the (possibly bumped) effectiveGasLimit. Logs the decision via
58
193
  * printLog. On any RPC failure or invalid input, logs a warning and returns
@@ -61,14 +196,13 @@ function deriveAtaPubkey(owner, mint) {
61
196
  */
62
197
  function maybeBumpForCeaAtaRent(input) {
63
198
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
64
- var _a, _b, _c, _d, _e;
65
199
  const { ctx, ueaAddress, targetChain, splMintBase58, burnAmount, effectiveGasLimit, } = input;
66
200
  // Only applies to SPL outbounds. Native SOL outbounds use the gateway's
67
201
  // calculateSolExecuteFees which has no ATA conditional.
68
202
  if (!splMintBase58 || burnAmount === BigInt(0)) {
69
203
  return effectiveGasLimit;
70
204
  }
71
- const rpcUrl = (_c = (_b = (_a = ctx.rpcUrls) === null || _a === void 0 ? void 0 : _a[targetChain]) === null || _b === void 0 ? void 0 : _b[0]) !== null && _c !== void 0 ? _c : (_e = (_d = chain_1.CHAIN_INFO[targetChain]) === null || _d === void 0 ? void 0 : _d.defaultRPC) === null || _e === void 0 ? void 0 : _e[0];
205
+ const rpcUrl = getRpcUrl(ctx, targetChain);
72
206
  if (!rpcUrl) {
73
207
  (0, context_1.printLog)(ctx, `maybeBumpForCeaAtaRent — no Solana RPC URL for ${targetChain}; skipping rent bump`);
74
208
  return effectiveGasLimit;
@@ -78,7 +212,7 @@ function maybeBumpForCeaAtaRent(input) {
78
212
  const splMint = new web3_js_1.PublicKey(splMintBase58);
79
213
  const ceaAta = deriveAtaPubkey(ceaPda, splMint);
80
214
  const conn = new web3_js_1.Connection(rpcUrl, 'confirmed');
81
- const info = yield conn.getAccountInfo(ceaAta);
215
+ const info = yield withSvmRentRpcTimeout(conn.getAccountInfo(ceaAta), `getAccountInfo(${ceaAta.toBase58()})`);
82
216
  const ataExists = info !== null && info.data.length > 0;
83
217
  if (!ataExists) {
84
218
  const bumped = effectiveGasLimit + exports.CEA_ATA_RENT_LAMPORTS_BUMP;
@@ -16,6 +16,7 @@ const types_1 = require("../../generated/uexecutor/v1/types");
16
16
  const types_2 = require("../../generated/uexecutor/v2/types");
17
17
  const progress_hook_1 = tslib_1.__importDefault(require("../../progress-hook/progress-hook"));
18
18
  const progress_hook_types_1 = require("../../progress-hook/progress-hook.types");
19
+ const formatters_1 = require("../../formatters");
19
20
  const payload_builders_1 = require("../payload-builders");
20
21
  const route_detector_1 = require("../route-detector");
21
22
  // ============================================================================
@@ -58,9 +59,12 @@ function reconstructProgressEvents(universalTxResponse, universalTxData) {
58
59
  // scoped phase ('outbound' + source-chain). Emitting in both places
59
60
  // would double-fire the terminal with a misleading 'push' title on top.
60
61
  const isFailed = isPcFailed || isOutboundFailed;
62
+ const pcErrorMsg = (pcTx === null || pcTx === void 0 ? void 0 : pcTx.errorMsg)
63
+ ? (0, formatters_1.normalizePcInsufficientFundsError)(pcTx.errorMsg)
64
+ : 'Unknown error';
61
65
  const errorMsg = isOutboundFailed
62
66
  ? 'Outbound transaction failed (status: OUTBOUND_FAILED)'
63
- : ((pcTx === null || pcTx === void 0 ? void 0 : pcTx.errorMsg) || 'Unknown error');
67
+ : pcErrorMsg;
64
68
  // Route 2 / 3 reconstruction — keep parity with live emission from
65
69
  // route-handlers.ts (which fires 201/203/207 or 301/303/307 then suppresses
66
70
  // R1 IDs via fireProgressHook). Skip the R1 101–107 sequence here; it would
@@ -70,7 +74,6 @@ function reconstructProgressEvents(universalTxResponse, universalTxData) {
70
74
  // leg itself failed — so prefer pcTx.errorMsg over the outbound-failed
71
75
  // sentinel. The outer `errorMsg` above carries the outbound text when
72
76
  // `isOutboundFailed` is set; that path is handled by wait() instead.
73
- const pcErrorMsg = (pcTx === null || pcTx === void 0 ? void 0 : pcTx.errorMsg) || 'Unknown error';
74
77
  // Multichain cascade: a single Push-chain tx that fanned out into >1
75
78
  // outbound legs (executeTransactions([tx1, tx2, …])). The cosmos record
76
79
  // carries one UniversalTx with multiple outboundTx entries; wrap the
@@ -0,0 +1,60 @@
1
+ export declare const DEFAULT_MAX_PC_FOR_GAS_BUFFER_BPS = 1000;
2
+ export declare const MAX_PC_FOR_GAS_BUFFER_BPS_LIMIT = 10000;
3
+ export interface MaxPCForGasCapInput {
4
+ /**
5
+ * Native PC estimate intended for the gas-token swap, excluding protocolFee.
6
+ */
7
+ gasLegNativePc: bigint;
8
+ /**
9
+ * Protocol fee returned by UniversalCore. Included in nativeValueForGas but
10
+ * excluded from maxPCForGas because UGPC subtracts it before applying the cap.
11
+ */
12
+ protocolFee?: bigint;
13
+ /**
14
+ * Slippage buffer in basis points. Defaults to 1000 bps (10%).
15
+ */
16
+ bufferBps?: number;
17
+ }
18
+ export interface MaxPCForGasCapFromNativeValueInput {
19
+ /**
20
+ * Total native PC value planned for sendUniversalTxOutbound msg.value.
21
+ */
22
+ nativeValueForGas: bigint;
23
+ /**
24
+ * Protocol fee returned by UniversalCore.
25
+ */
26
+ protocolFee: bigint;
27
+ /**
28
+ * Slippage buffer in basis points. Defaults to 1000 bps (10%).
29
+ */
30
+ bufferBps?: number;
31
+ }
32
+ export interface MaxPCForGasCapQuote {
33
+ /**
34
+ * Value to pass as UniversalExecuteParams.maxPCForGas.
35
+ */
36
+ maxPCForGas: bigint;
37
+ /**
38
+ * Minimum native PC msg.value that satisfies the cap:
39
+ * protocolFee + maxPCForGas.
40
+ */
41
+ nativeValueForGas: bigint;
42
+ /**
43
+ * Gas-swap leg before the buffer, excluding protocolFee.
44
+ */
45
+ gasLegNativePc: bigint;
46
+ protocolFee: bigint;
47
+ bufferBps: number;
48
+ }
49
+ /**
50
+ * Builds a contract-safe maxPCForGas cap from a native PC gas-leg estimate.
51
+ *
52
+ * UGPC validates maxPCForGas against `msg.value - protocolFee`, so the helper
53
+ * returns the minimum matching nativeValueForGas as well as the cap.
54
+ */
55
+ export declare function quoteMaxPCForGasCap(input: MaxPCForGasCapInput): MaxPCForGasCapQuote;
56
+ /**
57
+ * Builds a maxPCForGas cap from queryOutboundGasFee's nativeValueForGas and
58
+ * protocolFee fields.
59
+ */
60
+ export declare function quoteMaxPCForGasCapFromNativeValue(input: MaxPCForGasCapFromNativeValueInput): MaxPCForGasCapQuote;
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MAX_PC_FOR_GAS_BUFFER_BPS_LIMIT = exports.DEFAULT_MAX_PC_FOR_GAS_BUFFER_BPS = void 0;
4
+ exports.quoteMaxPCForGasCap = quoteMaxPCForGasCap;
5
+ exports.quoteMaxPCForGasCapFromNativeValue = quoteMaxPCForGasCapFromNativeValue;
6
+ const BPS_DENOMINATOR = BigInt(10000);
7
+ exports.DEFAULT_MAX_PC_FOR_GAS_BUFFER_BPS = 1000;
8
+ exports.MAX_PC_FOR_GAS_BUFFER_BPS_LIMIT = 10000;
9
+ /**
10
+ * Builds a contract-safe maxPCForGas cap from a native PC gas-leg estimate.
11
+ *
12
+ * UGPC validates maxPCForGas against `msg.value - protocolFee`, so the helper
13
+ * returns the minimum matching nativeValueForGas as well as the cap.
14
+ */
15
+ function quoteMaxPCForGasCap(input) {
16
+ var _a;
17
+ assertNonNegativeBigInt('gasLegNativePc', input.gasLegNativePc);
18
+ const protocolFee = (_a = input.protocolFee) !== null && _a !== void 0 ? _a : BigInt(0);
19
+ assertNonNegativeBigInt('protocolFee', protocolFee);
20
+ const bufferBps = normalizeBufferBps(input.bufferBps);
21
+ const maxPCForGas = applyBufferCeil(input.gasLegNativePc, bufferBps);
22
+ return {
23
+ maxPCForGas,
24
+ nativeValueForGas: protocolFee + maxPCForGas,
25
+ gasLegNativePc: input.gasLegNativePc,
26
+ protocolFee,
27
+ bufferBps,
28
+ };
29
+ }
30
+ /**
31
+ * Builds a maxPCForGas cap from queryOutboundGasFee's nativeValueForGas and
32
+ * protocolFee fields.
33
+ */
34
+ function quoteMaxPCForGasCapFromNativeValue(input) {
35
+ assertNonNegativeBigInt('nativeValueForGas', input.nativeValueForGas);
36
+ assertNonNegativeBigInt('protocolFee', input.protocolFee);
37
+ if (input.nativeValueForGas < input.protocolFee) {
38
+ throw new RangeError('nativeValueForGas must be greater than or equal to protocolFee');
39
+ }
40
+ return quoteMaxPCForGasCap({
41
+ gasLegNativePc: input.nativeValueForGas - input.protocolFee,
42
+ protocolFee: input.protocolFee,
43
+ bufferBps: input.bufferBps,
44
+ });
45
+ }
46
+ function normalizeBufferBps(bufferBps) {
47
+ const normalized = bufferBps !== null && bufferBps !== void 0 ? bufferBps : exports.DEFAULT_MAX_PC_FOR_GAS_BUFFER_BPS;
48
+ if (!Number.isInteger(normalized) ||
49
+ normalized < 0 ||
50
+ normalized > exports.MAX_PC_FOR_GAS_BUFFER_BPS_LIMIT) {
51
+ throw new RangeError(`bufferBps must be an integer between 0 and ${exports.MAX_PC_FOR_GAS_BUFFER_BPS_LIMIT}`);
52
+ }
53
+ return normalized;
54
+ }
55
+ function applyBufferCeil(amount, bufferBps) {
56
+ if (amount === BigInt(0))
57
+ return BigInt(0);
58
+ const multiplier = BPS_DENOMINATOR + BigInt(bufferBps);
59
+ return (amount * multiplier + BPS_DENOMINATOR - BigInt(1)) / BPS_DENOMINATOR;
60
+ }
61
+ function assertNonNegativeBigInt(name, value) {
62
+ if (value < BigInt(0)) {
63
+ throw new RangeError(`${name} must be non-negative`);
64
+ }
65
+ }
66
+ //# sourceMappingURL=max-pc-for-gas.js.map
@@ -85,9 +85,15 @@ export declare class Orchestrator {
85
85
  * The cascade composes all hops bottom-to-top into a single Push Chain tx.
86
86
  *
87
87
  * @param preparedTxs - Array of prepared transactions
88
+ * @param options - Optional per-call options. `progressHook` is additive with
89
+ * the init-time hook from `PushChain.initialize` and receives every
90
+ * `ProgressEvent` emitted during this cascade (pre-flight, broadcast, and
91
+ * tracking). Dedups by reference if it matches the init-time hook.
88
92
  * @returns Object with send() method
89
93
  */
90
- createCascadedBuilder(preparedTxs: PreparedUniversalTx[]): {
94
+ createCascadedBuilder(preparedTxs: PreparedUniversalTx[], options?: {
95
+ progressHook?: (event: ProgressEvent) => void;
96
+ }): {
91
97
  send: () => Promise<CascadedTxResponse>;
92
98
  };
93
99
  /**
@@ -99,6 +105,7 @@ export declare class Orchestrator {
99
105
  protocolFee: bigint;
100
106
  nativeValueForGas: bigint;
101
107
  gasPrice: bigint;
108
+ gasLimitUsed: bigint;
102
109
  }>;
103
110
  /**
104
111
  * Extracts the first universal sub-tx ID from a Push Chain transaction's Cosmos events.
@@ -30,6 +30,7 @@ class Orchestrator {
30
30
  return {
31
31
  executeFn: this.execute.bind(this),
32
32
  waitForOutboundTxFn: (hash, opts) => (0, internals_1.waitForOutboundTx)(this.ctx, hash, opts),
33
+ waitForInboundPushTxFn: (hash, sourceChain, opts) => (0, internals_1.waitForInboundPushTx)(this.ctx, hash, sourceChain, opts),
33
34
  waitForAllOutboundTxsFn: (hash, hops, opts) => (0, internals_1.waitForAllOutboundTxsV2)(this.ctx, hash, hops, opts),
34
35
  };
35
36
  }
@@ -296,10 +297,47 @@ class Orchestrator {
296
297
  * The cascade composes all hops bottom-to-top into a single Push Chain tx.
297
298
  *
298
299
  * @param preparedTxs - Array of prepared transactions
300
+ * @param options - Optional per-call options. `progressHook` is additive with
301
+ * the init-time hook from `PushChain.initialize` and receives every
302
+ * `ProgressEvent` emitted during this cascade (pre-flight, broadcast, and
303
+ * tracking). Dedups by reference if it matches the init-time hook.
299
304
  * @returns Object with send() method
300
305
  */
301
- createCascadedBuilder(preparedTxs) {
302
- return (0, internals_1.createCascadedBuilder)(this.ctx, preparedTxs, this._getCascadeCallbacks());
306
+ createCascadedBuilder(preparedTxs, options) {
307
+ const perCallHook = options === null || options === void 0 ? void 0 : options.progressHook;
308
+ if (!perCallHook) {
309
+ return (0, internals_1.createCascadedBuilder)(this.ctx, preparedTxs, this._getCascadeCallbacks());
310
+ }
311
+ // Wrap ctx.progressHook for the synchronous send() phase — captures every
312
+ // fireProgressHook(ctx, ...) call during pre-flight, composeCascade, and
313
+ // the Push Chain broadcast. Restored when send() resolves/rejects so the
314
+ // wrapper doesn't leak across calls.
315
+ //
316
+ // For the asynchronous tracking phase (waitForAll), we pass perCallHook as
317
+ // the cascade builder's `defaultEventHook` — waitForAll falls back to it
318
+ // when callers don't pass their own `eventHook`, and the existing
319
+ // `dispatchCascadeProgressEvent` path delivers events to BOTH that hook
320
+ // and ctx.progressHook (the init-time hook, restored by then) with
321
+ // reference-dedup.
322
+ const innerBuilder = (0, internals_1.createCascadedBuilder)(this.ctx, preparedTxs, this._getCascadeCallbacks(), perCallHook);
323
+ return {
324
+ send: () => tslib_1.__awaiter(this, void 0, void 0, function* () {
325
+ const originalHook = this.progressHook;
326
+ const wrapped = (event) => {
327
+ if (originalHook && originalHook !== perCallHook) {
328
+ originalHook(event);
329
+ }
330
+ perCallHook(event);
331
+ };
332
+ this.progressHook = wrapped;
333
+ try {
334
+ return yield innerBuilder.send();
335
+ }
336
+ finally {
337
+ this.progressHook = originalHook;
338
+ }
339
+ }),
340
+ };
303
341
  }
304
342
  /**
305
343
  * Queries gas fee for an outbound transaction from the UniversalCore contract.