@pushchain/core 6.0.16 → 6.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,4 +1,140 @@
1
- @pushchain/core@6.0.2 (unreleased)
1
+ @pushchain/core@6.0.17 (2026-06-09)
2
+
3
+ - fix: make SVM payload encoding browser-safe
4
+ - fix: prevent underfunded outbound gas swaps
5
+
6
+ ---
7
+
8
+ @pushchain/core@6.0.17 (unreleased)
9
+
10
+ - Fixed outbound (UOA_TO_CEA) transactions reverting on-chain with Uniswap `STF`
11
+ when the UEA held a modest amount of native PC - e.g. pETH -> Ethereum ETH.
12
+ Destination gas is funded by swapping the UEA's PC into the gas token, and
13
+ `capSwapEstimate` was sizing `nativeValueForGas` down to `balance - reserve`
14
+ even when that fell below the swap's actual requirement, so the swap reverted
15
+ with `STF` while the native pre-flight stayed silent (its check was
16
+ self-satisfying). The estimator now keeps the full requirement when the
17
+ affordable amount can't cover the swap floor, so the pre-flight reports the
18
+ real PC shortfall instead of emitting a cryptic on-chain `STF`.
19
+
20
+ - Fixed SVM payload encoding in browser environments whose `Buffer` polyfill
21
+ does not implement Node's `writeBigUInt64LE` / `writeBigInt64LE` methods.
22
+ The encoder now writes u64/i64 fields with `DataView`, preserving the same
23
+ little-endian wire format without crashing during Solana transaction builds.
24
+
25
+ ---
26
+
27
+ @pushchain/core@6.0.16 (2026-06-05)
28
+
29
+ - Fixed SVM domain-separator chainId encoding to match the pre-EIP-712 UEA_SVM
30
+ behavior on Donut (raw string encoding), restoring correct SVM signature
31
+ validation.
32
+
33
+ - Fixed Route 1 deposit-sizing progress-hook ordering: the `103-03-04` event is
34
+ now emitted after UEA resolution so it matches the funds-payload path sequence.
35
+
36
+ - Additional cascade execution test coverage and validation fixes.
37
+
38
+ ---
39
+
40
+ @pushchain/core@6.0.15 (2026-06-02)
41
+
42
+ - Internal fixes to response-building logic.
43
+
44
+ ---
45
+
46
+ @pushchain/core@6.0.14 (2026-06-02)
47
+
48
+ - `trackTransaction` now resolves the originating source chain correctly even when
49
+ the tracked hash points to Push Chain, with explicit chain-support checks.
50
+
51
+ - Added `finalTxHash` to `UniversalTxResponse` — a single hash that resolves across
52
+ all routes (R1 → Push tx, R2 → external tx, R3 → Push inbound tx).
53
+
54
+ ---
55
+
56
+ @pushchain/core@6.0.13 (2026-05-26)
57
+
58
+ - Maintenance release (version bump only; no code changes).
59
+
60
+ ---
61
+
62
+ @pushchain/core@6.0.12 (2026-05-26)
63
+
64
+ - Maintenance release (version bump only; no code changes).
65
+
66
+ ---
67
+
68
+ @pushchain/core@6.0.11 (2026-05-25)
69
+
70
+ - Added transaction-indexing polling for SVM multihop: the SDK now retries with
71
+ backoff (1s polling, 30s timeout) before fetching transaction details, fixing
72
+ failures when querying immediately after confirmation.
73
+
74
+ ---
75
+
76
+ @pushchain/core@6.0.10 (2026-05-23)
77
+
78
+ - Added Borsh-encoded SVM-origin inbound relay payload building for Route 3
79
+ (CEA → Push).
80
+
81
+ - Improved SVM child-inbound detection using log index for more reliable
82
+ transaction identification.
83
+
84
+ ---
85
+
86
+ @pushchain/core@6.0.9 (2026-05-19)
87
+
88
+ - Added a decoder for the `GasPriceBelowBase` (0x05aab006) error with an
89
+ actionable hint about gasPrice overrides.
90
+
91
+ ---
92
+
93
+ @pushchain/core@6.0.8 (2026-05-19)
94
+
95
+ - Maintenance release (version bump only; no code changes).
96
+
97
+ ---
98
+
99
+ @pushchain/core@6.0.7 (2026-05-19)
100
+
101
+ - Fixed corner-case bugs in cascade execution and funds-payload handling for
102
+ cross-chain transactions.
103
+
104
+ ---
105
+
106
+ @pushchain/core@6.0.6 (2026-05-18)
107
+
108
+ - Clarified progress-event message text (no functional changes).
109
+
110
+ ---
111
+
112
+ @pushchain/core@6.0.5 (2026-05-18)
113
+
114
+ - Added multihop progress markers: `SEND_TX_199_03` / `SEND_TX_199_99` (Route 1
115
+ intermediate success) and `SEND_TX_399_99` (Route 3 round-trip completion), with
116
+ improved cascade event sequencing.
117
+
118
+ ---
119
+
120
+ @pushchain/core@6.0.4 (2026-05-18)
121
+
122
+ - Fixed payload-builder detection of executable data vs. value-only transfers,
123
+ including multicall empty-array handling.
124
+
125
+ - Fixed progress-hook wiring for funds-only execution paths.
126
+
127
+ ---
128
+
129
+ @pushchain/core@6.0.3 (2026-05-16)
130
+
131
+ - Added per-call `progressHook` support: `sendTransaction(params, { progressHook })`
132
+ is additive with the init-time hook, auto-registers on the returned response for
133
+ wait-phase progress, and dedupes by reference to avoid double-firing.
134
+
135
+ ---
136
+
137
+ @pushchain/core@6.0.2 (2026-05-16)
2
138
 
3
139
  - Token constants now expose the missing `USDT.bsc` chain-suffix accessor,
4
140
  while keeping `USDT.bnb` as a deprecated alias for backward compatibility.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushchain/core",
3
- "version": "6.0.16",
3
+ "version": "6.0.18",
4
4
  "homepage": "https://push.org",
5
5
  "keywords": [
6
6
  "pushchain",
@@ -1107,7 +1107,7 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce,
1107
1107
  else {
1108
1108
  evmFallbackValue = segGasFee * BigInt(1000000);
1109
1109
  }
1110
- let nativeValueForGas = (_h = (_g = svmOverride !== null && svmOverride !== void 0 ? svmOverride : evmNativeValueBySegment.get(segment)) !== null && _g !== void 0 ? _g : perOutboundNativeValue) !== null && _h !== void 0 ? _h : evmFallbackValue;
1110
+ const nativeValueForGas = (_h = (_g = svmOverride !== null && svmOverride !== void 0 ? svmOverride : evmNativeValueBySegment.get(segment)) !== null && _g !== void 0 ? _g : perOutboundNativeValue) !== null && _h !== void 0 ? _h : evmFallbackValue;
1111
1111
  // SDK 5.2 Case C: when the segment's sizing is category C, compose
1112
1112
  // R2/OUTBOUND_TO_CEA segments no longer apply Case A/B/C sizing
1113
1113
  // (scoped to R1 + R3 only). segment.sizing may still be populated
@@ -158,7 +158,7 @@ function executeFundsWithPayload(ctx, execute, eventBuffer, getResponseCallbacks
158
158
  }
159
159
  }
160
160
  // Final USD(8) -> native conversion reusing the cached price fetched above.
161
- let nativeAmount = usdToNative(depositUsd);
161
+ const nativeAmount = usdToNative(depositUsd);
162
162
  (0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_103_03_04, nativeAmount, depositUsd, chain);
163
163
  const bridgeAmount = execute.funds.amount;
164
164
  const symbol = execute.funds.token.symbol;
@@ -25,6 +25,23 @@ export declare function queryOutboundGasFee(ctx: OrchestratorContext, prc20Token
25
25
  * Falls back to a percentage of accountBalance if any on-chain query fails.
26
26
  */
27
27
  export declare function estimateNativeValueForSwap(ctx: OrchestratorContext, universalCoreAddress: `0x${string}`, gasToken: `0x${string}`, gasFee: bigint, accountBalance: bigint): Promise<bigint>;
28
+ /**
29
+ * Bound the buffered swap estimate by the UEA's affordable PC.
30
+ *
31
+ * `result` is the 2.2x-buffered WPC needed for the gas swap; `minNeeded` is the
32
+ * un-buffered minimum the WPC->gasToken `exactOutput` swap actually consumes.
33
+ *
34
+ * Reducing the buffer to fit a tight balance is fine - but ONLY while the
35
+ * affordable amount (`accountBalance - reserve`) still covers `minNeeded`.
36
+ * Below that floor the swap cannot complete and reverts on-chain with Uniswap
37
+ * `STF`. The previous logic returned `accountBalance - reserve` unconditionally,
38
+ * which (a) under-funded `msg.value` into a doomed swap and (b) made the native
39
+ * preflight self-satisfying (`balance >= (balance - reserve) + reserve`), so the
40
+ * shortfall went undetected. When the balance can't cover the floor we now
41
+ * return the full `result` so the caller's preflight reports the real shortfall
42
+ * instead of emitting a cryptic STF.
43
+ */
44
+ export declare function capSwapEstimate(result: bigint, accountBalance: bigint, reserve: bigint, minNeeded?: bigint): bigint;
28
45
  export declare function queryRescueGasFee(ctx: OrchestratorContext, prc20Token: `0x${string}`, destinationChain?: CHAIN): Promise<{
29
46
  gasToken: `0x${string}`;
30
47
  gasFee: bigint;
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.ensureErc20Allowance = ensureErc20Allowance;
8
8
  exports.queryOutboundGasFee = queryOutboundGasFee;
9
9
  exports.estimateNativeValueForSwap = estimateNativeValueForSwap;
10
+ exports.capSwapEstimate = capSwapEstimate;
10
11
  exports.queryRescueGasFee = queryRescueGasFee;
11
12
  exports.calculateNativeAmountForDeposit = calculateNativeAmountForDeposit;
12
13
  exports.calculateGasAmountFromAmountOutMinETH = calculateGasAmountFromAmountOutMinETH;
@@ -309,7 +310,7 @@ function estimateNativeValueForSwap(ctx, universalCoreAddress, gasToken, gasFee,
309
310
  const result = (exactWpcNeeded * SWAP_BUFFER_NUM) / SWAP_BUFFER_DEN;
310
311
  (0, context_1.printLog)(ctx, `estimateNativeValueForSwap — exactOutput quoter=${quoterAddress}, ` +
311
312
  `wpcNeeded=${exactWpcNeeded}, withBuffer(2.2x)=${result}`);
312
- return capSwapEstimate(result, accountBalance, GAS_RESERVE);
313
+ return capSwapEstimate(result, accountBalance, GAS_RESERVE, exactWpcNeeded);
313
314
  }
314
315
  catch (quoteErr) {
315
316
  (0, context_1.printLog)(ctx, `estimateNativeValueForSwap — exactOutput quote failed: ${quoteErr instanceof Error ? quoteErr.message : String(quoteErr)}, falling back to slot0 price`);
@@ -355,7 +356,7 @@ function estimateNativeValueForSwap(ctx, universalCoreAddress, gasToken, gasFee,
355
356
  const result = (wpcNeeded * SWAP_BUFFER_NUM) / SWAP_BUFFER_DEN;
356
357
  (0, context_1.printLog)(ctx, `estimateNativeValueForSwap — pool=${poolAddress}, sqrtPriceX96=${sqrtPriceX96}, ` +
357
358
  `wpcNeeded=${wpcNeeded}, withBuffer(2.2x)=${result}`);
358
- return capSwapEstimate(result, accountBalance, GAS_RESERVE);
359
+ return capSwapEstimate(result, accountBalance, GAS_RESERVE, wpcNeeded);
359
360
  }
360
361
  catch (err) {
361
362
  (0, context_1.printLog)(ctx, `estimateNativeValueForSwap — failed: ${err instanceof Error ? err.message : String(err)}, using balance fallback`);
@@ -363,14 +364,30 @@ function estimateNativeValueForSwap(ctx, universalCoreAddress, gasToken, gasFee,
363
364
  }
364
365
  });
365
366
  }
366
- function capSwapEstimate(result, accountBalance, reserve) {
367
+ /**
368
+ * Bound the buffered swap estimate by the UEA's affordable PC.
369
+ *
370
+ * `result` is the 2.2x-buffered WPC needed for the gas swap; `minNeeded` is the
371
+ * un-buffered minimum the WPC->gasToken `exactOutput` swap actually consumes.
372
+ *
373
+ * Reducing the buffer to fit a tight balance is fine - but ONLY while the
374
+ * affordable amount (`accountBalance - reserve`) still covers `minNeeded`.
375
+ * Below that floor the swap cannot complete and reverts on-chain with Uniswap
376
+ * `STF`. The previous logic returned `accountBalance - reserve` unconditionally,
377
+ * which (a) under-funded `msg.value` into a doomed swap and (b) made the native
378
+ * preflight self-satisfying (`balance >= (balance - reserve) + reserve`), so the
379
+ * shortfall went undetected. When the balance can't cover the floor we now
380
+ * return the full `result` so the caller's preflight reports the real shortfall
381
+ * instead of emitting a cryptic STF.
382
+ */
383
+ function capSwapEstimate(result, accountBalance, reserve, minNeeded = BigInt(0)) {
367
384
  if (accountBalance > result + reserve) {
368
385
  return result;
369
386
  }
370
- if (accountBalance > reserve) {
387
+ if (accountBalance > reserve && accountBalance - reserve >= minNeeded) {
371
388
  return accountBalance - reserve;
372
389
  }
373
- return result; // let caller decide if balance is too low
390
+ return result; // balance can't cover the swap floor - surface true need to preflight
374
391
  }
375
392
  function balanceFallback(balance, divisor, reserve) {
376
393
  const fraction = balance / divisor;
@@ -213,14 +213,28 @@ function encodeUniversalPayload(payload) {
213
213
  ]);
214
214
  }
215
215
  function encodeUniversalPayloadSvm(payload) {
216
- const writeU64 = (val) => {
216
+ const U64_MAX = (BigInt(1) << BigInt(64)) - BigInt(1);
217
+ const I64_MIN = -(BigInt(1) << BigInt(63));
218
+ const I64_MAX = (BigInt(1) << BigInt(63)) - BigInt(1);
219
+ const viewFor = (buf) => new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
220
+ const assertRange = (field, val, min, max) => {
221
+ if (val < min || val > max) {
222
+ throw new RangeError(`SVM payload field ${field} out of range: ${val.toString()} ` +
223
+ `(expected ${min.toString()}..${max.toString()})`);
224
+ }
225
+ };
226
+ const writeU64 = (field, val) => {
227
+ const n = BigInt(val);
228
+ assertRange(field, n, BigInt(0), U64_MAX);
217
229
  const b = Buffer.alloc(8);
218
- b.writeBigUInt64LE(BigInt(val), 0);
230
+ viewFor(b).setBigUint64(0, n, true);
219
231
  return b;
220
232
  };
221
- const writeI64 = (val) => {
233
+ const writeI64 = (field, val) => {
234
+ const n = BigInt(val);
235
+ assertRange(field, n, I64_MIN, I64_MAX);
222
236
  const b = Buffer.alloc(8);
223
- b.writeBigInt64LE(BigInt(val), 0);
237
+ viewFor(b).setBigInt64(0, n, true);
224
238
  return b;
225
239
  };
226
240
  const writeVecU8 = (val) => {
@@ -228,7 +242,7 @@ function encodeUniversalPayloadSvm(payload) {
228
242
  ? val
229
243
  : Buffer.from(val);
230
244
  const len = Buffer.alloc(4);
231
- len.writeUInt32LE(bytes.length, 0);
245
+ viewFor(len).setUint32(0, bytes.length, true);
232
246
  return Buffer.concat([len, bytes]);
233
247
  };
234
248
  const writeU8 = (val) => Buffer.from([val]);
@@ -237,18 +251,18 @@ function encodeUniversalPayloadSvm(payload) {
237
251
  const hex = to.slice(2).padStart(40, '0');
238
252
  return Buffer.from(hex, 'hex');
239
253
  })();
240
- const valueBytes = writeU64(payload.value);
254
+ const valueBytes = writeU64('value', payload.value);
241
255
  const dataBytes = (() => {
242
256
  const data = payload.data;
243
257
  const hex = data.slice(2);
244
258
  const buf = hex.length ? Buffer.from(hex, 'hex') : Buffer.alloc(0);
245
259
  return writeVecU8(buf);
246
260
  })();
247
- const gasLimitBytes = writeU64(payload.gasLimit);
248
- const maxFeePerGasBytes = writeU64(payload.maxFeePerGas);
249
- const maxPriorityFeePerGasBytes = writeU64(payload.maxPriorityFeePerGas);
250
- const nonceBytes = writeU64(payload.nonce);
251
- const deadlineBytes = writeI64(payload.deadline);
261
+ const gasLimitBytes = writeU64('gasLimit', payload.gasLimit);
262
+ const maxFeePerGasBytes = writeU64('maxFeePerGas', payload.maxFeePerGas);
263
+ const maxPriorityFeePerGasBytes = writeU64('maxPriorityFeePerGas', payload.maxPriorityFeePerGas);
264
+ const nonceBytes = writeU64('nonce', payload.nonce);
265
+ const deadlineBytes = writeI64('deadline', payload.deadline);
252
266
  const vTypeVal = (() => {
253
267
  const v = payload.vType;
254
268
  if (typeof v === 'number')
@@ -127,14 +127,14 @@ export declare function buildMigrationPayload(): `0x${string}`;
127
127
  * Build approval multicalls + sendUniversalTxOutbound call for Push Chain.
128
128
  *
129
129
  * Extracted from executeUoaToCea to be reusable in cascade composition.
130
- * Handles two cases:
131
- * - gasToken === prc20Token: single approval for burnAmount + gasFee
132
- * - gasToken !== prc20Token: two separate approvals
130
+ * The PRC-20 approval covers only the burn amount. Destination gas is funded
131
+ * through native PC msg.value on sendUniversalTxOutbound; UniversalCore swaps
132
+ * that native value into the route gas token inside swapAndBurnGas.
133
133
  *
134
134
  * @param opts.prc20Token - PRC-20 token to burn
135
- * @param opts.gasToken - Gas token for fee payment
135
+ * @param opts.gasToken - Gas token for fee payment, used by the outbound request
136
136
  * @param opts.burnAmount - Amount to burn
137
- * @param opts.gasFee - Gas fee amount
137
+ * @param opts.gasFee - Gas fee amount, used only for fallback native value sizing
138
138
  * @param opts.gatewayPcAddress - UniversalGatewayPC precompile address
139
139
  * @param opts.outboundRequest - The outbound request struct
140
140
  * @returns Array of MultiCall operations (approvals + outbound call)
@@ -342,14 +342,14 @@ function buildMigrationPayload() {
342
342
  * Build approval multicalls + sendUniversalTxOutbound call for Push Chain.
343
343
  *
344
344
  * Extracted from executeUoaToCea to be reusable in cascade composition.
345
- * Handles two cases:
346
- * - gasToken === prc20Token: single approval for burnAmount + gasFee
347
- * - gasToken !== prc20Token: two separate approvals
345
+ * The PRC-20 approval covers only the burn amount. Destination gas is funded
346
+ * through native PC msg.value on sendUniversalTxOutbound; UniversalCore swaps
347
+ * that native value into the route gas token inside swapAndBurnGas.
348
348
  *
349
349
  * @param opts.prc20Token - PRC-20 token to burn
350
- * @param opts.gasToken - Gas token for fee payment
350
+ * @param opts.gasToken - Gas token for fee payment, used by the outbound request
351
351
  * @param opts.burnAmount - Amount to burn
352
- * @param opts.gasFee - Gas fee amount
352
+ * @param opts.gasFee - Gas fee amount, used only for fallback native value sizing
353
353
  * @param opts.gatewayPcAddress - UniversalGatewayPC precompile address
354
354
  * @param opts.outboundRequest - The outbound request struct
355
355
  * @returns Array of MultiCall operations (approvals + outbound call)