@pushchain/core 5.1.5-alpha.1 → 5.1.5

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 (86) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/package.json +1 -1
  3. package/src/lib/constants/abi/index.d.ts +2 -0
  4. package/src/lib/constants/abi/index.js +6 -1
  5. package/src/lib/constants/abi/uniswap-v3-router.evm.d.ts +58 -0
  6. package/src/lib/constants/abi/uniswap-v3-router.evm.js +41 -0
  7. package/src/lib/constants/abi/wpc.evm.d.ts +24 -0
  8. package/src/lib/constants/abi/wpc.evm.js +27 -0
  9. package/src/lib/constants/chain.d.ts +1 -0
  10. package/src/lib/constants/chain.js +8 -6
  11. package/src/lib/constants/tokens.js +3 -3
  12. package/src/lib/generated/uexecutor/v2/types.d.ts +14 -0
  13. package/src/lib/generated/uexecutor/v2/types.js +96 -3
  14. package/src/lib/orchestrator/internals/cascade.d.ts +1 -1
  15. package/src/lib/orchestrator/internals/cascade.js +521 -239
  16. package/src/lib/orchestrator/internals/confirmation.js +60 -15
  17. package/src/lib/orchestrator/internals/context.d.ts +23 -0
  18. package/src/lib/orchestrator/internals/context.js +54 -0
  19. package/src/lib/orchestrator/internals/execute-funds-only.js +47 -21
  20. package/src/lib/orchestrator/internals/execute-funds-payload.js +53 -31
  21. package/src/lib/orchestrator/internals/execute-standard.js +137 -26
  22. package/src/lib/orchestrator/internals/gas-calculator.d.ts +5 -2
  23. package/src/lib/orchestrator/internals/gas-calculator.js +59 -11
  24. package/src/lib/orchestrator/internals/gas-usd-sizer.d.ts +53 -0
  25. package/src/lib/orchestrator/internals/gas-usd-sizer.js +165 -0
  26. package/src/lib/orchestrator/internals/helpers.js +2 -0
  27. package/src/lib/orchestrator/internals/inbound-tracker.d.ts +83 -0
  28. package/src/lib/orchestrator/internals/inbound-tracker.js +322 -0
  29. package/src/lib/orchestrator/internals/index.d.ts +3 -1
  30. package/src/lib/orchestrator/internals/index.js +8 -2
  31. package/src/lib/orchestrator/internals/outbound-sync.d.ts +32 -1
  32. package/src/lib/orchestrator/internals/outbound-sync.js +154 -17
  33. package/src/lib/orchestrator/internals/pc-usd-oracle.d.ts +28 -0
  34. package/src/lib/orchestrator/internals/pc-usd-oracle.js +285 -0
  35. package/src/lib/orchestrator/internals/progress-route-hooks.d.ts +23 -0
  36. package/src/lib/orchestrator/internals/progress-route-hooks.js +72 -0
  37. package/src/lib/orchestrator/internals/push-chain-tx.d.ts +27 -1
  38. package/src/lib/orchestrator/internals/push-chain-tx.js +111 -15
  39. package/src/lib/orchestrator/internals/rescue.js +1 -1
  40. package/src/lib/orchestrator/internals/response-builder.d.ts +5 -0
  41. package/src/lib/orchestrator/internals/response-builder.js +242 -66
  42. package/src/lib/orchestrator/internals/route-handlers.js +480 -206
  43. package/src/lib/orchestrator/internals/tx-transformer.js +293 -35
  44. package/src/lib/orchestrator/orchestrator.d.ts +7 -1
  45. package/src/lib/orchestrator/orchestrator.js +108 -56
  46. package/src/lib/orchestrator/orchestrator.types.d.ts +111 -5
  47. package/src/lib/orchestrator/payload-builders.d.ts +16 -0
  48. package/src/lib/orchestrator/payload-builders.js +6 -5
  49. package/src/lib/orchestrator/route-detector.js +20 -8
  50. package/src/lib/orchestrator/svm-idl/normalize-address.d.ts +19 -0
  51. package/src/lib/orchestrator/svm-idl/normalize-address.js +56 -0
  52. package/src/lib/orchestrator/svm-idl/registry.d.ts +13 -4
  53. package/src/lib/orchestrator/svm-idl/registry.js +15 -14
  54. package/src/lib/orchestrator/svm-idl/resolve.js +5 -2
  55. package/src/lib/progress-hook/progress-hook.d.ts +28 -0
  56. package/src/lib/progress-hook/progress-hook.js +545 -131
  57. package/src/lib/progress-hook/progress-hook.types.d.ts +76 -29
  58. package/src/lib/progress-hook/progress-hook.types.js +74 -35
  59. package/src/lib/push-client/push-client.d.ts +7 -0
  60. package/src/lib/push-client/push-client.js +18 -1
  61. package/src/lib/universal-tx-detector/__tests__/fixtures/builders.d.ts +32 -0
  62. package/src/lib/universal-tx-detector/__tests__/fixtures/builders.js +65 -0
  63. package/src/lib/universal-tx-detector/__tests__/fixtures/scenarios.d.ts +72 -0
  64. package/src/lib/universal-tx-detector/__tests__/fixtures/scenarios.js +669 -0
  65. package/src/lib/universal-tx-detector/auto-detect.d.ts +37 -0
  66. package/src/lib/universal-tx-detector/auto-detect.js +112 -0
  67. package/src/lib/universal-tx-detector/cascade.d.ts +64 -0
  68. package/src/lib/universal-tx-detector/cascade.js +254 -0
  69. package/src/lib/universal-tx-detector/child-inbounds.d.ts +58 -0
  70. package/src/lib/universal-tx-detector/child-inbounds.js +145 -0
  71. package/src/lib/universal-tx-detector/classify.d.ts +33 -0
  72. package/src/lib/universal-tx-detector/classify.js +228 -0
  73. package/src/lib/universal-tx-detector/detector-svm.d.ts +46 -0
  74. package/src/lib/universal-tx-detector/detector-svm.js +324 -0
  75. package/src/lib/universal-tx-detector/detector.d.ts +13 -0
  76. package/src/lib/universal-tx-detector/detector.js +233 -0
  77. package/src/lib/universal-tx-detector/events.d.ts +765 -0
  78. package/src/lib/universal-tx-detector/events.js +241 -0
  79. package/src/lib/universal-tx-detector/index.d.ts +18 -0
  80. package/src/lib/universal-tx-detector/index.js +36 -0
  81. package/src/lib/universal-tx-detector/svm-events.d.ts +41 -0
  82. package/src/lib/universal-tx-detector/svm-events.js +68 -0
  83. package/src/lib/universal-tx-detector/types.d.ts +100 -0
  84. package/src/lib/universal-tx-detector/types.js +3 -0
  85. package/src/lib/utils.d.ts +30 -13
  86. package/src/lib/utils.js +47 -21
@@ -1,10 +1,16 @@
1
1
  import type { CHAIN } from '../constants/enums';
2
2
  /**
3
3
  * Chain target for cross-chain routing (Routes 2, 3, 4)
4
- * When `to` is a ChainTarget, the transaction executes on the specified external chain
4
+ * When `to` is a ChainTarget, the transaction executes on the specified external chain.
5
+ *
6
+ * `address` is typed as `string` because the accepted shape varies by VM:
7
+ * - EVM chains expect a 0x-prefixed 20-byte hex address.
8
+ * - SVM chains (Solana) accept either a base58 32-byte pubkey OR a 0x-prefixed
9
+ * 32-byte hex address; the SDK normalizes base58 to hex internally.
10
+ * Runtime validation happens in the route detector.
5
11
  */
6
12
  export type ChainTarget = {
7
- address: `0x${string}`;
13
+ address: string;
8
14
  chain: CHAIN;
9
15
  };
10
16
  /**
@@ -135,16 +141,37 @@ export interface UniversalTxResponse {
135
141
  maxFeePerGas?: bigint;
136
142
  maxPriorityFeePerGas?: bigint;
137
143
  accessList: any[];
138
- wait: () => Promise<UniversalTxReceipt>;
144
+ /**
145
+ * Wait for this transaction to reach its final state.
146
+ *
147
+ * For outbound routes (R2 / R3) the resolved receipt also includes external
148
+ * chain details. Pass `options.outboundTimeoutMs` / `options.inboundTimeoutMs`
149
+ * to override the default polling timeouts on a per-call basis — useful for
150
+ * latency-sensitive UIs that want to surface a provisional timeout and retry
151
+ * with a longer budget.
152
+ *
153
+ * When the outbound or inbound leg times out / fails, `.wait()` still
154
+ * resolves with a receipt (no throw); inspect `externalStatus` to classify
155
+ * the outcome.
156
+ */
157
+ wait: (options?: WaitOptions) => Promise<UniversalTxReceipt>;
139
158
  /**
140
159
  * Register a progress callback for events during wait().
141
160
  * Call this BEFORE calling wait() to receive tracking events, including
142
- * the SEND-TX-08-xx / SEND-TX-99-03/04/05 external chain polling events
143
- * emitted on outbound routes. Registering after wait() resolves will
161
+ * the route-specific external/inbound polling events: R2 emits the 209/299
162
+ * series, R3 emits 309/310/399. Registering after wait() resolves will
144
163
  * replay the pre-execution event buffer but miss outbound polling events.
145
164
  * @param callback - Function called with each progress event
146
165
  */
147
166
  progressHook: (callback: (event: import('../progress-hook/progress-hook.types').ProgressEvent) => void) => void;
167
+ /**
168
+ * @internal Register a wait-phase progressHook without replaying buffered
169
+ * events. Used by `trackTransaction()` to auto-attach the caller's per-call
170
+ * progressHook so `tracked.wait()` can deliver wait-phase events (209-xx /
171
+ * 299-xx / 399-xx) to the same callback — without re-emitting the
172
+ * reconstructed execute-phase stream that trackTransaction already fired.
173
+ */
174
+ _setProgressHookNoReplay?: (callback: (event: import('../progress-hook/progress-hook.types').ProgressEvent) => void) => void;
148
175
  type: string;
149
176
  typeVerbose: string;
150
177
  signature: Signature;
@@ -167,10 +194,66 @@ export interface UniversalTxResponse {
167
194
  childTxHash?: string;
168
195
  /** Transaction route (UOA_TO_PUSH, UOA_TO_CEA, CEA_TO_PUSH, CEA_TO_CEA) */
169
196
  route?: TransactionRouteType;
197
+ /**
198
+ * @internal True when this response was produced by `trackTransaction` and
199
+ * its progress events have already been replayed via the registered hook.
200
+ * `wait()` checks this to skip a redundant inner trackTransaction call that
201
+ * would otherwise emit the same reconstructed events a second time when
202
+ * the user does `tracked = trackTransaction(...); tracked.wait()`.
203
+ */
204
+ _eventsReconstructed?: boolean;
205
+ /**
206
+ * @internal True when the R3 (CEA_TO_PUSH) execution produces a child UTX
207
+ * on Push Chain — i.e., the source-chain CEA payload actually calls
208
+ * `sendUniversalTxToUEA` (funds flowing back to UEA). When false, `.wait()`
209
+ * skips `waitForInboundPushTx` to avoid a 300s timeout on payload-only R3
210
+ * flows that have no inbound leg.
211
+ */
212
+ _expectsInboundRoundTrip?: boolean;
170
213
  }
171
214
  /**
172
215
  * New Universal Transaction Receipt interface for transaction receipts
173
216
  */
217
+ /**
218
+ * Per-call options accepted by `UniversalTxResponse.wait()`.
219
+ *
220
+ * All fields are optional; omitted values fall back to the orchestrator's
221
+ * defaults (`OUTBOUND_MAX_TIMEOUT_MS`, `INBOUND_MAX_TIMEOUT_MS`).
222
+ */
223
+ export interface WaitOptions {
224
+ /**
225
+ * Override the outbound polling budget (ms) — applies to R2 / R3 while
226
+ * waiting for the external-chain tx to land via UGPC relay. When exceeded,
227
+ * `wait()` resolves with `externalStatus: 'timeout'` and the progress-hook
228
+ * stream emits `SEND-TX-299-03` (R2) or `SEND-TX-399-03` (R3 inbound).
229
+ *
230
+ * The outbound-sync initial settle-wait is automatically clamped to this
231
+ * timeout, so a 200ms budget will throw in ~200ms rather than blocking for
232
+ * the default 20s.
233
+ */
234
+ outboundTimeoutMs?: number;
235
+ /**
236
+ * Override the inbound polling budget (ms) — applies to R3 only, for the
237
+ * round-trip Push tx produced by the external CEA. When exceeded,
238
+ * `wait()` resolves with `externalStatus: 'timeout'` and emits
239
+ * `SEND-TX-399-03`.
240
+ */
241
+ inboundTimeoutMs?: number;
242
+ /**
243
+ * Override the poll interval (ms) between outbound status checks. Default
244
+ * is 5000ms. Lower values give faster feedback on fast relays; higher
245
+ * values reduce RPC pressure. Only applies to R2 / R3 outbound polling.
246
+ */
247
+ outboundPollingIntervalMs?: number;
248
+ /**
249
+ * Override the initial settle-wait (ms) before the outbound polling loop
250
+ * starts. Default is 20000ms — a buffer for UGPC relay submission latency.
251
+ * Reducing this doesn't affect the timeout budget but may cause earlier
252
+ * polls that miss the relay submission and burn RPC. Rarely needed; prefer
253
+ * `outboundTimeoutMs` which auto-clamps the initial wait.
254
+ */
255
+ outboundInitialWaitMs?: number;
256
+ }
174
257
  export interface UniversalTxReceipt {
175
258
  hash: string;
176
259
  blockNumber: bigint;
@@ -205,6 +288,20 @@ export interface UniversalTxReceipt {
205
288
  externalAmount?: string;
206
289
  /** Asset address on external chain */
207
290
  externalAssetAddr?: string;
291
+ /**
292
+ * Outcome of the external-chain leg for outbound routes (R2 / R3).
293
+ * - `success`: external tx landed and was confirmed (299-01 fired).
294
+ * - `failed`: external tx reverted or UGPC reported terminal failure (299-02 fired).
295
+ * - `timeout`: UGPC relay timed out before an external tx was observed (299-03 fired).
296
+ * Undefined on non-outbound receipts.
297
+ */
298
+ externalStatus?: 'success' | 'failed' | 'timeout';
299
+ /** Error message from the external-chain leg when `externalStatus !== 'success'`. */
300
+ externalError?: string;
301
+ /** Push Chain tx hash that closed the R3 round-trip (inbound from CEA). */
302
+ pushInboundTxHash?: string;
303
+ /** Child UTX id that owns the inbound execution. */
304
+ pushInboundUtxId?: string;
208
305
  }
209
306
  /**
210
307
  * Signature interface modeled after ethers.js v6 Signature interface
@@ -403,6 +500,8 @@ export interface HopDescriptor {
403
500
  svmPayload?: `0x${string}`;
404
501
  /** Whether this hop is a CEA migration (raw MIGRATION_SELECTOR payload) */
405
502
  isMigration?: boolean;
503
+ /** SDK 5.2 gas-abstraction sizing decision for this hop. @internal */
504
+ sizing?: import('./internals/gas-usd-sizer').GasSizingDecision;
406
505
  }
407
506
  /**
408
507
  * Segment types for cascade composition
@@ -437,6 +536,13 @@ export interface CascadeSegment {
437
536
  gasFee?: bigint;
438
537
  /** Gas limit for this segment */
439
538
  gasLimit?: bigint;
539
+ /**
540
+ * SDK 5.2 gas-abstraction sizing decision for this segment. When the
541
+ * segment merges multiple hops, the strictest (highest category) sizing
542
+ * among them is taken (C > B > A).
543
+ * @internal
544
+ */
545
+ sizing?: import('./internals/gas-usd-sizer').GasSizingDecision;
440
546
  }
441
547
  /**
442
548
  * Prepared transaction for inspection before sending
@@ -5,6 +5,22 @@ export declare function buildExecuteMulticall({ execute, ueaAddress, logger, }:
5
5
  ueaAddress: `0x${string}`;
6
6
  logger?: (msg: string) => void;
7
7
  }): MultiCall[];
8
+ /**
9
+ * Multicall tuple type definition for ABI encoding
10
+ */
11
+ export declare const MULTICALL_TUPLE_TYPE: {
12
+ readonly type: "tuple[]";
13
+ readonly components: readonly [{
14
+ readonly name: "to";
15
+ readonly type: "address";
16
+ }, {
17
+ readonly name: "value";
18
+ readonly type: "uint256";
19
+ }, {
20
+ readonly name: "data";
21
+ readonly type: "bytes";
22
+ }];
23
+ };
8
24
  /**
9
25
  * Build CEA multicall payload for outbound transactions
10
26
  * Format: UEA_MULTICALL_SELECTOR + abi.encode(Multicall[])
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MULTICALL_TUPLE_TYPE = void 0;
3
4
  exports.buildExecuteMulticall = buildExecuteMulticall;
4
5
  exports.buildCeaMulticallPayload = buildCeaMulticallPayload;
5
6
  exports.buildInboundUniversalPayload = buildInboundUniversalPayload;
@@ -124,7 +125,7 @@ function buildExecuteMulticall({ execute, ueaAddress, logger, }) {
124
125
  /**
125
126
  * Multicall tuple type definition for ABI encoding
126
127
  */
127
- const MULTICALL_TUPLE_TYPE = {
128
+ exports.MULTICALL_TUPLE_TYPE = {
128
129
  type: 'tuple[]',
129
130
  components: [
130
131
  { name: 'to', type: 'address' },
@@ -148,7 +149,7 @@ function buildCeaMulticallPayload(multicalls) {
148
149
  return '0x';
149
150
  }
150
151
  // Encode the multicall array
151
- const encoded = (0, viem_1.encodeAbiParameters)([MULTICALL_TUPLE_TYPE], [multicalls.map((m) => ({
152
+ const encoded = (0, viem_1.encodeAbiParameters)([exports.MULTICALL_TUPLE_TYPE], [multicalls.map((m) => ({
152
153
  to: m.to,
153
154
  value: m.value,
154
155
  data: m.data,
@@ -418,11 +419,11 @@ function isZeroAddress(address) {
418
419
  */
419
420
  function buildOutboundApprovalAndCall(opts) {
420
421
  var _a;
421
- const { prc20Token, burnAmount, gasFee, gatewayPcAddress, outboundRequest } = opts;
422
+ const { prc20Token, gasFee, gatewayPcAddress, outboundRequest } = opts;
422
423
  const multicalls = [];
423
424
  // ERC20 approve for burn amount (contract calls transferFrom for PRC20 burn)
424
425
  // Reset to 0 first for USDT-style tokens that revert on non-zero to non-zero approve.
425
- if (burnAmount > BigInt(0) &&
426
+ if (opts.burnAmount > BigInt(0) &&
426
427
  prc20Token.toLowerCase() !== selectors_1.ZERO_ADDRESS.toLowerCase()) {
427
428
  const approveZeroData = (0, viem_1.encodeFunctionData)({
428
429
  abi: erc20_evm_1.ERC20_EVM,
@@ -432,7 +433,7 @@ function buildOutboundApprovalAndCall(opts) {
432
433
  const approveData = (0, viem_1.encodeFunctionData)({
433
434
  abi: erc20_evm_1.ERC20_EVM,
434
435
  functionName: 'approve',
435
- args: [gatewayPcAddress, burnAmount],
436
+ args: [gatewayPcAddress, opts.burnAmount],
436
437
  });
437
438
  multicalls.push({
438
439
  to: prc20Token,
@@ -21,6 +21,7 @@ exports.getRouteInfo = getRouteInfo;
21
21
  const enums_1 = require("../constants/enums");
22
22
  const tokens_1 = require("../constants/tokens");
23
23
  const payload_builders_1 = require("./payload-builders");
24
+ const normalize_address_1 = require("./svm-idl/normalize-address");
24
25
  // ============================================================================
25
26
  // Transaction Route Enum
26
27
  // ============================================================================
@@ -253,13 +254,18 @@ function validateRouteParams(params, context) {
253
254
  throw new RouteValidationError('migration is not supported on SVM chains');
254
255
  }
255
256
  }
256
- // Validate funds token symbol is available on target chain (Route 2 outbound)
257
+ // Validate funds token symbol is available on target chain (Route 2 outbound).
258
+ // A PushChainMoveableToken (e.g. pSOL, pETH_BNB) carries a `sourceChain` pointing at
259
+ // the external chain it mirrors — bridging it to that chain is always valid since
260
+ // the target receives the underlying asset (SOL, ETH, etc.).
257
261
  if (((_b = params.funds) === null || _b === void 0 ? void 0 : _b.token) && isChainTarget(params.to)) {
258
262
  const targetChain = params.to.chain;
259
263
  if (!isPushChain(targetChain)) {
264
+ const tokenSourceChain = params.funds.token.sourceChain;
265
+ const isBridgeBack = tokenSourceChain === targetChain;
260
266
  const targetTokens = tokens_1.MOVEABLE_TOKENS[targetChain] || [];
261
267
  const hasSymbolOnTarget = targetTokens.some(t => t.symbol === params.funds.token.symbol);
262
- if (!hasSymbolOnTarget) {
268
+ if (!hasSymbolOnTarget && !isBridgeBack) {
263
269
  const tokenChain = findTokenChain(params.funds.token);
264
270
  const tokenLabel = tokenChain
265
271
  ? `${chainEnumToName(tokenChain)}.${params.funds.token.symbol}`
@@ -297,16 +303,22 @@ function validateRouteParams(params, context) {
297
303
  }
298
304
  // Validate address format
299
305
  if (isChainTarget(params.to)) {
300
- if (!params.to.address.startsWith('0x')) {
301
- throw new RouteValidationError(`Invalid address format: ${params.to.address}`);
302
- }
303
- // SVM targets require 32-byte addresses (0x + 64 hex chars)
304
306
  if ((0, payload_builders_1.isSvmChain)(params.to.chain)) {
305
- if (!(0, payload_builders_1.isValidSolanaHexAddress)(params.to.address)) {
307
+ // SVM targets accept either base58 (native Solana form) or 0x-prefixed
308
+ // 32-byte hex. Normalize to hex up front so downstream handlers only see
309
+ // the canonical form.
310
+ try {
311
+ params.to.address = (0, normalize_address_1.toSvmHexAddress)(params.to.address);
312
+ }
313
+ catch (err) {
306
314
  throw new RouteValidationError(`Invalid Solana address format: ${params.to.address}. ` +
307
- `Expected 32 bytes (0x + 64 hex chars).`);
315
+ `Expected base58 (32-byte pubkey) or 0x-prefixed 32-byte hex. ` +
316
+ `(${err.message})`);
308
317
  }
309
318
  }
319
+ else if (!params.to.address.startsWith('0x')) {
320
+ throw new RouteValidationError(`Invalid address format: ${params.to.address}`);
321
+ }
310
322
  }
311
323
  else if (typeof params.to === 'string') {
312
324
  if (!params.to.startsWith('0x')) {
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Canonical internal form of a Solana program / account address.
3
+ * All validation and payload paths key off this `0x`-prefixed 32-byte hex string.
4
+ */
5
+ export type SvmHexAddress = `0x${string}`;
6
+ /**
7
+ * Normalize an SVM address supplied by an end user to the internal `0x`-prefixed
8
+ * 32-byte hex form. Accepts either:
9
+ * - `0x`-prefixed 64-hex-char string (the internal form), or
10
+ * - base58 string that decodes to exactly 32 bytes (the native Solana form).
11
+ *
12
+ * Throws with a message naming both accepted forms if the input matches neither.
13
+ */
14
+ export declare function toSvmHexAddress(input: unknown): SvmHexAddress;
15
+ /**
16
+ * Predicate form of {@link toSvmHexAddress} — returns true if the input is a
17
+ * valid SVM address in either accepted form.
18
+ */
19
+ export declare function isValidSvmAddress(input: unknown): boolean;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toSvmHexAddress = toSvmHexAddress;
4
+ exports.isValidSvmAddress = isValidSvmAddress;
5
+ const bs58_1 = require("../../internal/bs58");
6
+ const HEX_FORM = /^0x[0-9a-fA-F]{64}$/;
7
+ /**
8
+ * Normalize an SVM address supplied by an end user to the internal `0x`-prefixed
9
+ * 32-byte hex form. Accepts either:
10
+ * - `0x`-prefixed 64-hex-char string (the internal form), or
11
+ * - base58 string that decodes to exactly 32 bytes (the native Solana form).
12
+ *
13
+ * Throws with a message naming both accepted forms if the input matches neither.
14
+ */
15
+ function toSvmHexAddress(input) {
16
+ if (typeof input !== 'string' || input.length === 0) {
17
+ throw new Error(`SVM address must be a non-empty string (base58 or 0x-prefixed 32-byte hex), got ${typeof input}`);
18
+ }
19
+ if (input.startsWith('0x')) {
20
+ if (!HEX_FORM.test(input)) {
21
+ throw new Error(`SVM address in hex form must be 0x + 64 hex chars (32 bytes), got '${input}' (length ${input.length})`);
22
+ }
23
+ return input.toLowerCase();
24
+ }
25
+ let decoded;
26
+ try {
27
+ decoded = bs58_1.bs58.decode(input);
28
+ }
29
+ catch (_a) {
30
+ throw new Error(`SVM address '${input}' is not valid base58 and does not start with 0x. ` +
31
+ `Expected base58 (e.g. 'So11111111111111111111111111111111111111112') ` +
32
+ `or 0x-prefixed 32-byte hex.`);
33
+ }
34
+ if (decoded.length !== 32) {
35
+ throw new Error(`SVM address '${input}' decodes to ${decoded.length} bytes; expected 32.`);
36
+ }
37
+ let hex = '0x';
38
+ for (let i = 0; i < decoded.length; i++) {
39
+ hex += decoded[i].toString(16).padStart(2, '0');
40
+ }
41
+ return hex;
42
+ }
43
+ /**
44
+ * Predicate form of {@link toSvmHexAddress} — returns true if the input is a
45
+ * valid SVM address in either accepted form.
46
+ */
47
+ function isValidSvmAddress(input) {
48
+ try {
49
+ toSvmHexAddress(input);
50
+ return true;
51
+ }
52
+ catch (_a) {
53
+ return false;
54
+ }
55
+ }
56
+ //# sourceMappingURL=normalize-address.js.map
@@ -1,10 +1,19 @@
1
1
  import type { Idl } from '@coral-xyz/anchor';
2
- type AddressKey = `0x${string}`;
3
- export declare function registerIdl(programAddress: string, idl: unknown): void;
2
+ import { type SvmHexAddress } from './normalize-address';
3
+ /**
4
+ * Register an Anchor IDL so the SDK can resolve accounts, PDAs, and CEA
5
+ * authorities when building SVM gateway payloads.
6
+ *
7
+ * Normal usage never needs this — `PushChain.utils.helpers.encodeTxData`
8
+ * auto-registers the IDL whenever you pass it as `abi`. Use this only when
9
+ * building `data` manually (raw discriminator + Borsh bytes).
10
+ *
11
+ * The program address is pulled from `idl.address` (Anchor ≥ 0.30 spec).
12
+ */
13
+ export declare function registerIdl(idl: unknown): void;
4
14
  export declare function getIdl(programAddress: string): Idl | undefined;
5
15
  export declare function getRegisteredIdls(): Array<{
6
- address: AddressKey;
16
+ address: SvmHexAddress;
7
17
  idl: Idl;
8
18
  }>;
9
19
  export declare function clearRegistry(): void;
10
- export {};
@@ -5,26 +5,27 @@ exports.getIdl = getIdl;
5
5
  exports.getRegisteredIdls = getRegisteredIdls;
6
6
  exports.clearRegistry = clearRegistry;
7
7
  const ix_encoder_1 = require("./ix-encoder");
8
+ const normalize_address_1 = require("./normalize-address");
8
9
  const store = new Map();
9
- function normalizeKey(address) {
10
- if (typeof address !== 'string' || !address.startsWith('0x')) {
11
- throw new Error(`registerIdl: programAddress must be 0x-prefixed 32-byte hex, got '${address}'`);
12
- }
13
- const body = address.slice(2);
14
- if (body.length !== 64 || !/^[0-9a-fA-F]+$/.test(body)) {
15
- throw new Error(`registerIdl: programAddress must be 32 bytes (66 chars incl. 0x), got length ${address.length}`);
16
- }
17
- return ('0x' + body.toLowerCase());
18
- }
19
- function registerIdl(programAddress, idl) {
20
- const key = normalizeKey(programAddress);
10
+ /**
11
+ * Register an Anchor IDL so the SDK can resolve accounts, PDAs, and CEA
12
+ * authorities when building SVM gateway payloads.
13
+ *
14
+ * Normal usage never needs this — `PushChain.utils.helpers.encodeTxData`
15
+ * auto-registers the IDL whenever you pass it as `abi`. Use this only when
16
+ * building `data` manually (raw discriminator + Borsh bytes).
17
+ *
18
+ * The program address is pulled from `idl.address` (Anchor ≥ 0.30 spec).
19
+ */
20
+ function registerIdl(idl) {
21
21
  if (!(0, ix_encoder_1.isAnchorIdl)(idl)) {
22
- throw new Error(`registerIdl: idl for ${programAddress} is not a recognized Anchor IDL (missing 'instructions' or 'address')`);
22
+ throw new Error(`registerIdl: input is not a recognized Anchor IDL (missing 'instructions' or 'address')`);
23
23
  }
24
+ const key = (0, normalize_address_1.toSvmHexAddress)(idl.address);
24
25
  store.set(key, idl);
25
26
  }
26
27
  function getIdl(programAddress) {
27
- const key = normalizeKey(programAddress);
28
+ const key = (0, normalize_address_1.toSvmHexAddress)(programAddress);
28
29
  return store.get(key);
29
30
  }
30
31
  function getRegisteredIdls() {
@@ -16,8 +16,11 @@ function resolveSvmCall(input) {
16
16
  var _a, _b;
17
17
  const idl = (0, registry_1.getIdl)(input.programAddress);
18
18
  if (!idl) {
19
- throw new Error(`resolveSvmCall: no IDL registered for ${input.programAddress}. ` +
20
- `Call PushChain.utils.svm.registerIdl(${input.programAddress}, idl) first.`);
19
+ throw new Error(`resolveSvmCall: no IDL found for ${input.programAddress}. ` +
20
+ `The SDK auto-registers Anchor IDLs when you call ` +
21
+ `PushChain.utils.helpers.encodeTxData({abi: idl, ...}). ` +
22
+ `If you built 'data' manually, call PushChain.utils.svm.registerIdl(idl) ` +
23
+ `once before prepareTransaction.`);
21
24
  }
22
25
  if (input.data.length < 8) {
23
26
  throw new Error('resolveSvmCall: data must be at least 8 bytes (Anchor discriminator)');
@@ -1,3 +1,31 @@
1
1
  import { ProgressEventFunction } from './progress-hook.types';
2
+ /**
3
+ * Shape of the `response` field emitted by `SEND_TX_104_04` (R1),
4
+ * `SEND_TX_204_04` (R2), and `SEND_TX_304_04` (R3). Exported so consumers
5
+ * can read `isUserDecline` without casting through `object`.
6
+ */
7
+ export interface DeclineHookResponse {
8
+ /** The underlying sign-time error message (decline copy when absent). */
9
+ error: string;
10
+ /** True if the error looks like a real wallet rejection. */
11
+ isUserDecline: boolean;
12
+ }
13
+ /**
14
+ * Classify a sign-time error as either a true wallet user-decline
15
+ * (viem `UserRejectedRequestError`, ethers `ACTION_REJECTED`, EIP-1193 4001,
16
+ * or textual "user rejected"/"user denied"/"declined by user") or a generic
17
+ * signature failure (insufficient funds, RPC failure, contract revert
18
+ * during sign, etc.). Shared by SEND_TX_104_04 (R1), SEND_TX_204_04 (R2),
19
+ * and SEND_TX_304_04 (R3) so the heuristic lives in one place.
20
+ *
21
+ * When `errorMessage` is omitted, treat it as a decline — the spec copy is
22
+ * already the decline copy ("Verification declined by user"), so flipping
23
+ * to "Signature Failed" would make the event self-contradict.
24
+ */
25
+ export declare function classifyDeclineError(errorMessage?: string): {
26
+ isUserDecline: boolean;
27
+ title: string;
28
+ message: string;
29
+ };
2
30
  declare const PROGRESS_HOOKS: Record<string, ProgressEventFunction>;
3
31
  export default PROGRESS_HOOKS;