@pushchain/core 5.1.4 → 5.1.5-alpha.1

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 (28) hide show
  1. package/package.json +1 -1
  2. package/src/lib/index.d.ts +1 -1
  3. package/src/lib/orchestrator/internals/cascade.js +68 -25
  4. package/src/lib/orchestrator/internals/execute-funds-payload.js +4 -4
  5. package/src/lib/orchestrator/internals/execute-standard.js +55 -7
  6. package/src/lib/orchestrator/internals/gas-calculator.d.ts +10 -0
  7. package/src/lib/orchestrator/internals/gas-calculator.js +95 -3
  8. package/src/lib/orchestrator/internals/gateway-client.js +6 -6
  9. package/src/lib/orchestrator/internals/response-builder.js +24 -1
  10. package/src/lib/orchestrator/internals/route-handlers.js +27 -43
  11. package/src/lib/orchestrator/orchestrator.types.d.ts +13 -23
  12. package/src/lib/orchestrator/route-detector.js +0 -3
  13. package/src/lib/orchestrator/svm-idl/build-payload.d.ts +11 -0
  14. package/src/lib/orchestrator/svm-idl/build-payload.js +36 -0
  15. package/src/lib/orchestrator/svm-idl/ix-encoder.d.ts +3 -0
  16. package/src/lib/orchestrator/svm-idl/ix-encoder.js +47 -0
  17. package/src/lib/orchestrator/svm-idl/registry.d.ts +10 -0
  18. package/src/lib/orchestrator/svm-idl/registry.js +36 -0
  19. package/src/lib/orchestrator/svm-idl/resolve.d.ts +14 -0
  20. package/src/lib/orchestrator/svm-idl/resolve.js +85 -0
  21. package/src/lib/progress-hook/progress-hook.js +40 -0
  22. package/src/lib/progress-hook/progress-hook.types.d.ts +5 -0
  23. package/src/lib/progress-hook/progress-hook.types.js +6 -0
  24. package/src/lib/push-chain/push-chain.js +14 -4
  25. package/src/lib/universal/account/account.d.ts +13 -0
  26. package/src/lib/universal/account/account.js +1 -0
  27. package/src/lib/utils.d.ts +20 -1
  28. package/src/lib/utils.js +28 -0
@@ -35,6 +35,7 @@ const utils_1 = require("../../utils");
35
35
  const price_fetch_1 = require("../../price-fetch/price-fetch");
36
36
  const route_detector_1 = require("../route-detector");
37
37
  const cea_utils_1 = require("../cea-utils");
38
+ const build_payload_1 = require("../svm-idl/build-payload");
38
39
  const payload_builders_1 = require("../payload-builders");
39
40
  const selectors_1 = require("../../constants/selectors");
40
41
  const erc20_evm_1 = require("../../constants/abi/erc20.evm");
@@ -393,22 +394,15 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
393
394
  const targetChain = target.chain;
394
395
  const targetAddress = target.address; // 0x-prefixed, 32 bytes
395
396
  const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
396
- const hasSvmExecute = !!params.svmExecute;
397
+ const { svmPayload, targetBytes, hasExecute: hasSvmExecute, } = (0, build_payload_1.buildSvmPayloadFromParams)({
398
+ data: params.data,
399
+ to: target,
400
+ senderUea: ueaAddress,
401
+ });
397
402
  (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — target: ${targetAddress}, chain: ${targetChain}, ` +
398
403
  `hasSvmExecute: ${hasSvmExecute}, value: ${(_b = (_a = params.value) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : '0'}`);
399
- // --- Build SVM payload ---
400
- let svmPayload = '0x'; // empty for withdraw (SOL/SPL)
401
404
  if (hasSvmExecute) {
402
- // Execute case: encode the CPI payload
403
- const exec = params.svmExecute;
404
- svmPayload = (0, payload_builders_1.encodeSvmExecutePayload)({
405
- targetProgram: exec.targetProgram,
406
- accounts: exec.accounts,
407
- ixData: exec.ixData,
408
- instructionId: 2,
409
- });
410
- (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — encoded execute payload: ${(svmPayload.length - 2) / 2} bytes, ` +
411
- `${exec.accounts.length} accounts`);
405
+ (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — encoded execute payload: ${(svmPayload.length - 2) / 2} bytes`);
412
406
  }
413
407
  // --- Determine PRC-20 token and burn amount ---
414
408
  let prc20Token = selectors_1.ZERO_ADDRESS;
@@ -439,13 +433,8 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
439
433
  burnAmount = BigInt(0);
440
434
  (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — EXECUTE-only: using PRC-20 ${prc20Token} with zero burn amount`);
441
435
  }
442
- // --- Determine target bytes ---
443
- // For withdraw: target is the Solana recipient pubkey
444
- // For execute: target is the target Solana program
445
- const targetBytes = hasSvmExecute
446
- ? params.svmExecute.targetProgram
447
- : targetAddress;
448
436
  // --- Build outbound request ---
437
+ // targetBytes: for execute = program id (resolver output); for withdraw = to.address
449
438
  const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, (_d = params.gasLimit) !== null && _d !== void 0 ? _d : BigInt(0), svmPayload, ueaAddress // revert recipient is the UEA
450
439
  );
451
440
  (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — outbound request: ${JSON.stringify({
@@ -972,7 +961,7 @@ function executeCeaToCea(ctx, params, executeFn) {
972
961
  */
973
962
  function buildPayloadForRoute(ctx, params, route, nonce) {
974
963
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
975
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
964
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
976
965
  const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
977
966
  switch (route) {
978
967
  case route_detector_1.TransactionRoute.UOA_TO_PUSH: {
@@ -995,20 +984,15 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
995
984
  const target = params.to;
996
985
  // Branch: SVM vs EVM
997
986
  if ((0, payload_builders_1.isSvmChain)(target.chain)) {
998
- // SVM: build SVM payload (binary or empty for withdraw)
999
- let payload = '0x';
1000
- if (params.svmExecute) {
1001
- payload = (0, payload_builders_1.encodeSvmExecutePayload)({
1002
- targetProgram: params.svmExecute.targetProgram,
1003
- accounts: params.svmExecute.accounts,
1004
- ixData: params.svmExecute.ixData,
1005
- instructionId: 2,
1006
- });
1007
- }
1008
- const targetBytes = (_b = (_a = params.svmExecute) === null || _a === void 0 ? void 0 : _a.targetProgram) !== null && _b !== void 0 ? _b : target.address;
987
+ // SVM: build SVM payload (binary or empty for withdraw) via IDL resolver
988
+ const { svmPayload: payload, targetBytes, hasExecute, } = (0, build_payload_1.buildSvmPayloadFromParams)({
989
+ data: params.data,
990
+ to: target,
991
+ senderUea: ueaAddress,
992
+ });
1009
993
  let prc20Token = selectors_1.ZERO_ADDRESS;
1010
994
  let burnAmount = BigInt(0);
1011
- if ((_c = params.funds) === null || _c === void 0 ? void 0 : _c.amount) {
995
+ if ((_a = params.funds) === null || _a === void 0 ? void 0 : _a.amount) {
1012
996
  const token = params.funds.token;
1013
997
  if (token) {
1014
998
  prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
@@ -1019,15 +1003,15 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1019
1003
  prc20Token = (0, helpers_1.getNativePRC20ForChain)(target.chain, ctx.pushNetwork);
1020
1004
  burnAmount = params.value;
1021
1005
  }
1022
- else if (params.svmExecute) {
1006
+ else if (hasExecute) {
1023
1007
  prc20Token = (0, helpers_1.getNativePRC20ForChain)(target.chain, ctx.pushNetwork);
1024
1008
  burnAmount = BigInt(1);
1025
1009
  }
1026
- const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, (_d = params.gasLimit) !== null && _d !== void 0 ? _d : BigInt(0), payload, ueaAddress);
1010
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, (_b = params.gasLimit) !== null && _b !== void 0 ? _b : BigInt(0), payload, ueaAddress);
1027
1011
  return { payload, gatewayRequest: outboundReq };
1028
1012
  }
1029
1013
  // EVM path: Resolve CEA address first (needed for self-transfer check)
1030
- const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, target.chain, (_e = ctx.rpcUrls[target.chain]) === null || _e === void 0 ? void 0 : _e[0]);
1014
+ const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, target.chain, (_c = ctx.rpcUrls[target.chain]) === null || _c === void 0 ? void 0 : _c[0]);
1031
1015
  // Build CEA outbound payload
1032
1016
  const multicalls = [];
1033
1017
  if (params.data) {
@@ -1060,7 +1044,7 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1060
1044
  const payload = (0, payload_builders_1.buildCeaMulticallPayload)(multicalls);
1061
1045
  let prc20Token = selectors_1.ZERO_ADDRESS;
1062
1046
  let burnAmount = BigInt(0);
1063
- if ((_f = params.funds) === null || _f === void 0 ? void 0 : _f.amount) {
1047
+ if ((_d = params.funds) === null || _d === void 0 ? void 0 : _d.amount) {
1064
1048
  const token = params.funds.token;
1065
1049
  if (token) {
1066
1050
  prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
@@ -1068,13 +1052,13 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1068
1052
  }
1069
1053
  }
1070
1054
  const targetBytes = ceaAddress;
1071
- const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, (_g = params.gasLimit) !== null && _g !== void 0 ? _g : BigInt(0), payload, ueaAddress);
1055
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, (_e = params.gasLimit) !== null && _e !== void 0 ? _e : BigInt(0), payload, ueaAddress);
1072
1056
  return { payload, gatewayRequest: outboundReq };
1073
1057
  }
1074
1058
  case route_detector_1.TransactionRoute.CEA_TO_PUSH: {
1075
1059
  // Route 3: CEA -> Push Chain
1076
1060
  // Build CEA multicall (approve + sendUniversalTxFromCEA) and wrap in outbound
1077
- if (!((_h = params.from) === null || _h === void 0 ? void 0 : _h.chain)) {
1061
+ if (!((_f = params.from) === null || _f === void 0 ? void 0 : _f.chain)) {
1078
1062
  throw new Error('Route 3 (CEA -> Push) requires from.chain');
1079
1063
  }
1080
1064
  const sourceChain = params.from.chain;
@@ -1089,7 +1073,7 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1089
1073
  const gatewayProgramHex = ('0x' + Buffer.from(programPk.toBytes()).toString('hex'));
1090
1074
  let drainAmount = BigInt(0);
1091
1075
  let tokenMintHex;
1092
- if (((_j = params.funds) === null || _j === void 0 ? void 0 : _j.amount) && params.funds.amount > BigInt(0)) {
1076
+ if (((_g = params.funds) === null || _g === void 0 ? void 0 : _g.amount) && params.funds.amount > BigInt(0)) {
1093
1077
  drainAmount = params.funds.amount;
1094
1078
  const token = params.funds.token;
1095
1079
  if (token && token.address) {
@@ -1114,10 +1098,10 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1114
1098
  });
1115
1099
  // burnAmount = 1 (minimum for precompile; drain amount lives inside the ixData)
1116
1100
  const burnAmount = BigInt(1);
1117
- const outboundReq = (0, payload_builders_1.buildOutboundRequest)(gatewayProgramHex, prc20Token, burnAmount, (_k = params.gasLimit) !== null && _k !== void 0 ? _k : BigInt(0), svmPayload, ueaAddress);
1101
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(gatewayProgramHex, prc20Token, burnAmount, (_h = params.gasLimit) !== null && _h !== void 0 ? _h : BigInt(0), svmPayload, ueaAddress);
1118
1102
  return { payload: svmPayload, gatewayRequest: outboundReq };
1119
1103
  }
1120
- const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, sourceChain, (_l = ctx.rpcUrls[sourceChain]) === null || _l === void 0 ? void 0 : _l[0]);
1104
+ const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, sourceChain, (_j = ctx.rpcUrls[sourceChain]) === null || _j === void 0 ? void 0 : _j[0]);
1121
1105
  // CEA auto-deploys on-chain: Vault.finalizeUniversalTx calls CEAFactory.deployCEA()
1122
1106
  // if CEA doesn't exist yet. No SDK-side blocking needed.
1123
1107
  const gatewayAddr = chain_1.UNIVERSAL_GATEWAY_ADDRESSES[sourceChain];
@@ -1128,7 +1112,7 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1128
1112
  const ceaMulticalls = [];
1129
1113
  let tokenAddress = selectors_1.ZERO_ADDRESS;
1130
1114
  let amount = BigInt(0);
1131
- if ((_m = params.funds) === null || _m === void 0 ? void 0 : _m.amount) {
1115
+ if ((_k = params.funds) === null || _k === void 0 ? void 0 : _k.amount) {
1132
1116
  const token = params.funds.token;
1133
1117
  if (token) {
1134
1118
  if (token.mechanism === 'native') {
@@ -1192,7 +1176,7 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1192
1176
  // Route 3: CEA uses its own pre-existing balance — no PRC-20 burn needed.
1193
1177
  // burnAmount = 0 makes this a payload-only outbound relay.
1194
1178
  const burnAmount = BigInt(0);
1195
- const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, prc20Token, burnAmount, (_o = params.gasLimit) !== null && _o !== void 0 ? _o : BigInt(0), ceaPayload, ueaAddress);
1179
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, prc20Token, burnAmount, (_l = params.gasLimit) !== null && _l !== void 0 ? _l : BigInt(0), ceaPayload, ueaAddress);
1196
1180
  return { payload: ceaPayload, gatewayRequest: outboundReq };
1197
1181
  }
1198
1182
  default:
@@ -110,7 +110,7 @@ export type ExecuteParams = {
110
110
  * Internal: Minimum fee-locking deposit in USD (8 decimals).
111
111
  * Overrides the default $1 floor when the caller needs a larger deposit
112
112
  * (e.g., Route 2 needs enough UPC for the outbound swap).
113
- * Still capped at $10 by lockFee.
113
+ * Still capped at $1000 by lockFee.
114
114
  * @internal
115
115
  */
116
116
  _minimumDepositUsd?: bigint;
@@ -138,7 +138,10 @@ export interface UniversalTxResponse {
138
138
  wait: () => Promise<UniversalTxReceipt>;
139
139
  /**
140
140
  * Register a progress callback for events during wait().
141
- * Call this BEFORE calling wait() to receive tracking events.
141
+ * 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
144
+ * replay the pre-execution event buffer but miss outbound polling events.
142
145
  * @param callback - Function called with each progress event
143
146
  */
144
147
  progressHook: (callback: (event: import('../progress-hook/progress-hook.types').ProgressEvent) => void) => void;
@@ -291,16 +294,9 @@ export type UniversalExecuteParams = Omit<ExecuteParams, 'to'> & {
291
294
  * - ChainTarget: External chain target (Routes 2, 3, 4)
292
295
  */
293
296
  to: UniversalTo;
294
- /**
295
- * SVM-specific: CPI execution parameters for Solana targets (Route 2).
296
- * When present, the outbound transaction will execute a Cross-Program Invocation
297
- * on the specified Solana program with the given accounts and instruction data.
298
- * Only applicable when `to.chain` is a Solana chain.
299
- */
300
- svmExecute?: SvmExecuteParams;
301
297
  /**
302
298
  * When true, sends MIGRATION_SELECTOR as raw CEA payload (no multicall wrapping).
303
- * Used for CEA contract upgrades. Incompatible with value/funds/data/svmExecute.
299
+ * Used for CEA contract upgrades. Incompatible with value/funds/data.
304
300
  * Only applicable for Route 2 (UOA_TO_CEA) on EVM chains.
305
301
  */
306
302
  migration?: boolean;
@@ -329,18 +325,6 @@ export interface SvmExecutePayloadFields {
329
325
  /** Instruction ID: 2 = execute (default) */
330
326
  instructionId?: number;
331
327
  }
332
- /**
333
- * User-facing parameters for CPI execution on a Solana program via Route 2.
334
- * Passed as `svmExecute` in UniversalExecuteParams when targeting SVM chains.
335
- */
336
- export interface SvmExecuteParams {
337
- /** Target Solana program to CPI into (32 bytes, 0x-prefixed hex) */
338
- targetProgram: `0x${string}`;
339
- /** Accounts required for the CPI call */
340
- accounts: SvmGatewayAccountMeta[];
341
- /** Raw instruction data for the target program */
342
- ixData: Uint8Array;
343
- }
344
328
  /**
345
329
  * Request structure for sendUniversalTxOutbound on Push Chain
346
330
  * Used for Routes 2, 3, 4 (outbound from Push)
@@ -534,8 +518,14 @@ export interface CascadeTrackOptions {
534
518
  pollingIntervalMs?: number;
535
519
  /** Total timeout (default: 600000ms = 10 min) */
536
520
  timeout?: number;
537
- /** Progress callback */
521
+ /** Per-hop progress callback */
538
522
  progressHook?: (event: CascadeProgressEvent) => void;
523
+ /**
524
+ * Unified ProgressEvent stream for SEND-TX-08/99_03-05 telemetry.
525
+ * Fires per outbound hop during external chain polling. Use alongside
526
+ * `progressHook` — they emit complementary views of the same events.
527
+ */
528
+ eventHook?: (event: import('../progress-hook/progress-hook.types').ProgressEvent) => void;
539
529
  }
540
530
  /**
541
531
  * Progress event emitted during cascade tracking
@@ -249,9 +249,6 @@ function validateRouteParams(params, context) {
249
249
  if (params.data) {
250
250
  throw new RouteValidationError('migration is incompatible with data');
251
251
  }
252
- if (params.svmExecute) {
253
- throw new RouteValidationError('migration is incompatible with svmExecute');
254
- }
255
252
  if (isChainTarget(params.to) && (0, payload_builders_1.isSvmChain)(params.to.chain)) {
256
253
  throw new RouteValidationError('migration is not supported on SVM chains');
257
254
  }
@@ -0,0 +1,11 @@
1
+ import type { ChainTarget } from '../orchestrator.types';
2
+ export interface SvmPayloadBundle {
3
+ svmPayload: `0x${string}`;
4
+ targetBytes: `0x${string}`;
5
+ hasExecute: boolean;
6
+ }
7
+ export declare function buildSvmPayloadFromParams(params: {
8
+ data?: `0x${string}` | unknown;
9
+ to: ChainTarget;
10
+ senderUea: `0x${string}`;
11
+ }): SvmPayloadBundle;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildSvmPayloadFromParams = buildSvmPayloadFromParams;
4
+ const viem_1 = require("viem");
5
+ const payload_builders_1 = require("../payload-builders");
6
+ const resolve_1 = require("./resolve");
7
+ function buildSvmPayloadFromParams(params) {
8
+ const hasExecute = typeof params.data === 'string' &&
9
+ params.data.startsWith('0x') &&
10
+ params.data.length > 2;
11
+ if (!hasExecute) {
12
+ return {
13
+ svmPayload: '0x',
14
+ targetBytes: params.to.address,
15
+ hasExecute: false,
16
+ };
17
+ }
18
+ const resolved = (0, resolve_1.resolveSvmCall)({
19
+ programAddress: params.to.address,
20
+ data: (0, viem_1.hexToBytes)(params.data),
21
+ senderUea: params.senderUea,
22
+ targetChain: params.to.chain,
23
+ });
24
+ const svmPayload = (0, payload_builders_1.encodeSvmExecutePayload)({
25
+ targetProgram: resolved.targetProgram,
26
+ accounts: resolved.accounts,
27
+ ixData: resolved.ixData,
28
+ instructionId: 2,
29
+ });
30
+ return {
31
+ svmPayload,
32
+ targetBytes: resolved.targetProgram,
33
+ hasExecute: true,
34
+ };
35
+ }
36
+ //# sourceMappingURL=build-payload.js.map
@@ -0,0 +1,3 @@
1
+ import { type Idl } from '@coral-xyz/anchor';
2
+ export declare function encodeAnchorIxData(idl: Idl, functionName: string, positionalArgs?: unknown[]): Uint8Array;
3
+ export declare function isAnchorIdl(value: unknown): value is Idl;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.encodeAnchorIxData = encodeAnchorIxData;
4
+ exports.isAnchorIdl = isAnchorIdl;
5
+ const anchor_1 = require("@coral-xyz/anchor");
6
+ const web3_js_1 = require("@solana/web3.js");
7
+ const buffer_1 = require("buffer");
8
+ function encodeAnchorIxData(idl, functionName, positionalArgs = []) {
9
+ var _a, _b;
10
+ const ix = findInstruction(idl, functionName);
11
+ if (!ix) {
12
+ throw new Error(`Instruction '${functionName}' not found in IDL '${(_b = (_a = idl.metadata) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : 'unknown'}'`);
13
+ }
14
+ if (positionalArgs.length !== ix.args.length) {
15
+ throw new Error(`Arg count mismatch for '${ix.name}': IDL expects ${ix.args.length}, got ${positionalArgs.length}`);
16
+ }
17
+ const named = {};
18
+ for (let i = 0; i < ix.args.length; i++) {
19
+ named[ix.args[i].name] = normalizeArg(positionalArgs[i], ix.args[i]);
20
+ }
21
+ const coder = new anchor_1.BorshInstructionCoder(idl);
22
+ return new Uint8Array(coder.encode(ix.name, named));
23
+ }
24
+ function findInstruction(idl, name) {
25
+ const snake = name.replace(/([A-Z])/g, (_, c) => '_' + c.toLowerCase());
26
+ const camel = name.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
27
+ return idl.instructions.find((i) => i.name === name || i.name === snake || i.name === camel);
28
+ }
29
+ function normalizeArg(value, field) {
30
+ if (typeof value === 'bigint') {
31
+ return new anchor_1.BN(value.toString());
32
+ }
33
+ if (field.type === 'pubkey' &&
34
+ typeof value === 'string' &&
35
+ value.startsWith('0x') &&
36
+ value.length === 66) {
37
+ return new web3_js_1.PublicKey(buffer_1.Buffer.from(value.slice(2), 'hex'));
38
+ }
39
+ return value;
40
+ }
41
+ function isAnchorIdl(value) {
42
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
43
+ return false;
44
+ const maybe = value;
45
+ return Array.isArray(maybe['instructions']) && typeof maybe['address'] === 'string';
46
+ }
47
+ //# sourceMappingURL=ix-encoder.js.map
@@ -0,0 +1,10 @@
1
+ import type { Idl } from '@coral-xyz/anchor';
2
+ type AddressKey = `0x${string}`;
3
+ export declare function registerIdl(programAddress: string, idl: unknown): void;
4
+ export declare function getIdl(programAddress: string): Idl | undefined;
5
+ export declare function getRegisteredIdls(): Array<{
6
+ address: AddressKey;
7
+ idl: Idl;
8
+ }>;
9
+ export declare function clearRegistry(): void;
10
+ export {};
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerIdl = registerIdl;
4
+ exports.getIdl = getIdl;
5
+ exports.getRegisteredIdls = getRegisteredIdls;
6
+ exports.clearRegistry = clearRegistry;
7
+ const ix_encoder_1 = require("./ix-encoder");
8
+ 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);
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')`);
23
+ }
24
+ store.set(key, idl);
25
+ }
26
+ function getIdl(programAddress) {
27
+ const key = normalizeKey(programAddress);
28
+ return store.get(key);
29
+ }
30
+ function getRegisteredIdls() {
31
+ return Array.from(store.entries()).map(([address, idl]) => ({ address, idl }));
32
+ }
33
+ function clearRegistry() {
34
+ store.clear();
35
+ }
36
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1,14 @@
1
+ import { CHAIN } from '../../constants/enums';
2
+ import type { SvmGatewayAccountMeta } from '../orchestrator.types';
3
+ export interface ResolveInput {
4
+ programAddress: `0x${string}`;
5
+ data: Uint8Array;
6
+ senderUea: `0x${string}`;
7
+ targetChain: CHAIN;
8
+ }
9
+ export interface ResolvedSvmCall {
10
+ targetProgram: `0x${string}`;
11
+ accounts: SvmGatewayAccountMeta[];
12
+ ixData: Uint8Array;
13
+ }
14
+ export declare function resolveSvmCall(input: ResolveInput): ResolvedSvmCall;
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveSvmCall = resolveSvmCall;
4
+ const web3_js_1 = require("@solana/web3.js");
5
+ const buffer_1 = require("buffer");
6
+ const account_1 = require("../../universal/account/account");
7
+ const registry_1 = require("./registry");
8
+ const CEA_AUTHORITY_NAMES = new Set([
9
+ 'cea_authority',
10
+ 'ceaAuthority',
11
+ 'cea',
12
+ 'cea_signer',
13
+ 'ceaSigner',
14
+ ]);
15
+ function resolveSvmCall(input) {
16
+ var _a, _b;
17
+ const idl = (0, registry_1.getIdl)(input.programAddress);
18
+ if (!idl) {
19
+ throw new Error(`resolveSvmCall: no IDL registered for ${input.programAddress}. ` +
20
+ `Call PushChain.utils.svm.registerIdl(${input.programAddress}, idl) first.`);
21
+ }
22
+ if (input.data.length < 8) {
23
+ throw new Error('resolveSvmCall: data must be at least 8 bytes (Anchor discriminator)');
24
+ }
25
+ const disc = input.data.slice(0, 8);
26
+ const ix = idl.instructions.find((i) => buffer_1.Buffer.from(i.discriminator).equals(buffer_1.Buffer.from(disc)));
27
+ if (!ix) {
28
+ throw new Error(`resolveSvmCall: no instruction in IDL '${(_b = (_a = idl.metadata) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : idl.address}' matches discriminator [${Array.from(disc).join(',')}]`);
29
+ }
30
+ const programPk = new web3_js_1.PublicKey(buffer_1.Buffer.from(input.programAddress.slice(2), 'hex'));
31
+ const { address: ceaPdaHex } = (0, account_1.deriveSvmCeaPda)(input.senderUea, input.targetChain, true);
32
+ const accounts = [];
33
+ for (const item of ix.accounts) {
34
+ if ('accounts' in item) {
35
+ throw new Error(`resolveSvmCall: nested account group '${item.name}' in '${ix.name}' not supported`);
36
+ }
37
+ const flat = item;
38
+ const pubkey = resolveAccountPubkey(flat, programPk, ceaPdaHex, ix.name);
39
+ accounts.push({ pubkey, isWritable: Boolean(flat.writable) });
40
+ }
41
+ return {
42
+ targetProgram: input.programAddress,
43
+ accounts,
44
+ ixData: input.data,
45
+ };
46
+ }
47
+ function resolveAccountPubkey(spec, programPk, ceaPdaHex, ixName) {
48
+ if (spec.address) {
49
+ return pkToHex(new web3_js_1.PublicKey(spec.address));
50
+ }
51
+ if (spec.pda) {
52
+ const seeds = resolveConstSeeds(spec.pda.seeds, spec.name, ixName);
53
+ const programForPda = spec.pda.program
54
+ ? new web3_js_1.PublicKey(resolveConstSeedValue(spec.pda.program, spec.name, ixName))
55
+ : programPk;
56
+ const [pda] = web3_js_1.PublicKey.findProgramAddressSync(seeds, programForPda);
57
+ return pkToHex(pda);
58
+ }
59
+ if (CEA_AUTHORITY_NAMES.has(spec.name)) {
60
+ return ceaPdaHex;
61
+ }
62
+ throw new Error(`resolveSvmCall: cannot resolve account '${spec.name}' in '${ixName}'. ` +
63
+ `Add 'address' or 'pda.seeds' to the IDL, or rename to a CEA convention (cea_authority).`);
64
+ }
65
+ function resolveConstSeeds(seeds, accountName, ixName) {
66
+ return seeds.map((seed, idx) => {
67
+ if (seed.kind !== 'const') {
68
+ throw new Error(`resolveSvmCall: non-const PDA seed (kind='${seed.kind}') on account '${accountName}' in '${ixName}' not supported yet`);
69
+ }
70
+ if (!Array.isArray(seed.value)) {
71
+ throw new Error(`resolveSvmCall: const seed at index ${idx} on '${accountName}' is malformed`);
72
+ }
73
+ return buffer_1.Buffer.from(seed.value);
74
+ });
75
+ }
76
+ function resolveConstSeedValue(seed, accountName, ixName) {
77
+ if (seed.kind !== 'const' || !Array.isArray(seed.value)) {
78
+ throw new Error(`resolveSvmCall: only const program-seed supported on '${accountName}' in '${ixName}'`);
79
+ }
80
+ return buffer_1.Buffer.from(seed.value);
81
+ }
82
+ function pkToHex(pk) {
83
+ return ('0x' + buffer_1.Buffer.from(pk.toBytes()).toString('hex'));
84
+ }
85
+ //# sourceMappingURL=resolve.js.map
@@ -158,6 +158,25 @@ const RAW_HOOKS = {
158
158
  response: null,
159
159
  level: 'INFO',
160
160
  }),
161
+ // External chain polling (08-x) — emitted from .wait() / cascade.waitForAll()
162
+ // after Push Chain tx success, while the SDK polls the relay for the
163
+ // external chain tx hash on outbound routes.
164
+ [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_08_01]: (destinationChain) => ({
165
+ id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_08_01,
166
+ title: 'Awaiting External Chain',
167
+ message: destinationChain
168
+ ? `Waiting for relay to dispatch to ${destinationChain}...`
169
+ : 'Waiting for relay to dispatch to external chain...',
170
+ response: null,
171
+ level: 'INFO',
172
+ }),
173
+ [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_08_02]: (elapsedMs) => ({
174
+ id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_08_02,
175
+ title: 'Polling External Chain',
176
+ message: `Polling relay for external transaction hash (elapsed: ${Math.round(elapsedMs / 1000)}s)`,
177
+ response: null,
178
+ level: 'INFO',
179
+ }),
161
180
  [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_01]: (txResponse) => ({
162
181
  id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_01,
163
182
  title: 'Push Chain Tx Success',
@@ -172,6 +191,27 @@ const RAW_HOOKS = {
172
191
  response: null,
173
192
  level: 'ERROR',
174
193
  }),
194
+ [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_03]: (details) => ({
195
+ id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_03,
196
+ title: 'External Chain Tx Confirmed',
197
+ message: `External tx confirmed on ${details.destinationChain}: ${details.externalTxHash}`,
198
+ response: details,
199
+ level: 'SUCCESS',
200
+ }),
201
+ [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_04]: (elapsedMs) => ({
202
+ id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_04,
203
+ title: 'External Chain Tx Timeout',
204
+ message: `Timed out waiting for external chain tx after ${Math.round(elapsedMs / 1000)}s. Push Chain tx succeeded; the relay may still be processing.`,
205
+ response: null,
206
+ level: 'WARNING',
207
+ }),
208
+ [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_05]: (errorMessage) => ({
209
+ id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_05,
210
+ title: 'External Chain Tx Failed',
211
+ message: errorMessage,
212
+ response: null,
213
+ level: 'ERROR',
214
+ }),
175
215
  // UEA Migration hooks
176
216
  [progress_hook_types_1.PROGRESS_HOOK.UEA_MIG_01]: () => ({
177
217
  id: progress_hook_types_1.PROGRESS_HOOK.UEA_MIG_01,
@@ -34,8 +34,13 @@ export declare enum PROGRESS_HOOK {
34
34
  SEND_TX_06_05 = "SEND-TX-06-05",// Syncing with Push Chain
35
35
  SEND_TX_06_06 = "SEND-TX-06-06",// Funds Credited on Push Chain (was 06_05)
36
36
  SEND_TX_07 = "SEND-TX-07",// Broadcasting to Push Chain
37
+ SEND_TX_08_01 = "SEND-TX-08-01",// Awaiting External Chain
38
+ SEND_TX_08_02 = "SEND-TX-08-02",// Polling External Chain
37
39
  SEND_TX_99_01 = "SEND-TX-99-01",
38
40
  SEND_TX_99_02 = "SEND-TX-99-02",
41
+ SEND_TX_99_03 = "SEND-TX-99-03",// External Chain Tx Confirmed (SUCCESS)
42
+ SEND_TX_99_04 = "SEND-TX-99-04",// External Chain Tx Timeout (WARNING)
43
+ SEND_TX_99_05 = "SEND-TX-99-05",// External Chain Tx Failed (ERROR)
39
44
  UEA_MIG_01 = "UEA-MIG-01",
40
45
  UEA_MIG_02 = "UEA-MIG-02",
41
46
  UEA_MIG_03 = "UEA-MIG-03",
@@ -28,9 +28,15 @@ var PROGRESS_HOOK;
28
28
  PROGRESS_HOOK["SEND_TX_06_06"] = "SEND-TX-06-06";
29
29
  // 07-x: Execution / broadcasting flow
30
30
  PROGRESS_HOOK["SEND_TX_07"] = "SEND-TX-07";
31
+ // 08-x: External chain polling (after Push Chain tx success, outbound routes)
32
+ PROGRESS_HOOK["SEND_TX_08_01"] = "SEND-TX-08-01";
33
+ PROGRESS_HOOK["SEND_TX_08_02"] = "SEND-TX-08-02";
31
34
  // 99-x: Final outcome
32
35
  PROGRESS_HOOK["SEND_TX_99_01"] = "SEND-TX-99-01";
33
36
  PROGRESS_HOOK["SEND_TX_99_02"] = "SEND-TX-99-02";
37
+ PROGRESS_HOOK["SEND_TX_99_03"] = "SEND-TX-99-03";
38
+ PROGRESS_HOOK["SEND_TX_99_04"] = "SEND-TX-99-04";
39
+ PROGRESS_HOOK["SEND_TX_99_05"] = "SEND-TX-99-05";
34
40
  // UEA Migration hooks
35
41
  PROGRESS_HOOK["UEA_MIG_01"] = "UEA-MIG-01";
36
42
  PROGRESS_HOOK["UEA_MIG_02"] = "UEA-MIG-02";
@@ -396,15 +396,25 @@ class PushChain {
396
396
  */
397
397
  validatedUniversalSigner, (_c = options === null || options === void 0 ? void 0 : options.network) !== null && _c !== void 0 ? _c : enums_1.PUSH_NETWORK.TESTNET_DONUT, (_d = options === null || options === void 0 ? void 0 : options.rpcUrls) !== null && _d !== void 0 ? _d : {}, (_e = options === null || options === void 0 ? void 0 : options.printTraces) !== null && _e !== void 0 ? _e : false, options === null || options === void 0 ? void 0 : options.progressHook);
398
398
  const instance = new _a(orchestrator, validatedUniversalSigner, blockExplorers, isReadOnly);
399
- // Background fetch account status (non-blocking, 30s timeout)
400
- // Stored on instance so consumers can await it if needed: await client.accountStatusReady
399
+ // Background fetch account status (non-blocking, 30s timeout).
400
+ // The timer must be cleared once the race settles — otherwise the
401
+ // unref'd setTimeout keeps the Node event loop alive for the full
402
+ // 30s after the caller's work is done, making scripts appear "stuck".
401
403
  const ACCOUNT_STATUS_TIMEOUT = 30000;
404
+ let accountStatusTimer;
402
405
  instance.accountStatusReady = Promise.race([
403
406
  // eslint-disable-next-line @typescript-eslint/no-empty-function
404
407
  instance.getAccountStatus().then(() => { }),
405
- new Promise((_, reject) => setTimeout(() => reject(new Error('Account status fetch timed out')), ACCOUNT_STATUS_TIMEOUT)),
406
- ]).catch(() => {
408
+ new Promise((_, reject) => {
409
+ accountStatusTimer = setTimeout(() => reject(new Error('Account status fetch timed out')), ACCOUNT_STATUS_TIMEOUT);
410
+ }),
411
+ ])
412
+ .catch(() => {
407
413
  // Silently ignore — lazy check in execute() will retry if needed
414
+ })
415
+ .finally(() => {
416
+ if (accountStatusTimer)
417
+ clearTimeout(accountStatusTimer);
408
418
  });
409
419
  // Let execute() await the background fetch instead of re-fetching
410
420
  orchestrator.accountStatusReadyPromise = instance.accountStatusReady;
@@ -68,6 +68,19 @@ export declare function convertExecutorToOrigin(executorAddress: string, options
68
68
  /** @internal Skip deprecation warning for SDK-internal calls */
69
69
  _internal?: boolean;
70
70
  }): Promise<OriginAccountInfo>;
71
+ /**
72
+ * Derive the SVM CEA (PDA) for a UEA address on a Solana-based chain.
73
+ *
74
+ * Solana doesn't use a CEAFactory contract — instead, the CEA is a
75
+ * Program Derived Address (PDA) with seeds: ["push_identity", ueaBytes]
76
+ * and the SVM gateway program as the program ID.
77
+ *
78
+ * @param ueaAddress - The UEA (or Push EOA) hex address (20 bytes).
79
+ * @param targetChain - The SVM target chain.
80
+ * @param skipNetworkCheck - Whether to skip deployment status check.
81
+ * @returns The derived 32-byte Solana PDA address (as 0x-prefixed hex) and deployment status.
82
+ */
83
+ export declare function deriveSvmCeaPda(ueaAddress: `0x${string}`, targetChain: CHAIN, skipNetworkCheck: boolean): DerivedExecutorAccount;
71
84
  /**
72
85
  * Derive the executor account (UEA or CEA) for a given account.
73
86
  *