@pushchain/core 5.1.3 → 5.1.4-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 (131) hide show
  1. package/package.json +29 -6
  2. package/src/lib/constants/abi/prc20.evm.d.ts +22 -2
  3. package/src/lib/constants/abi/prc20.evm.js +11 -3
  4. package/src/lib/constants/abi/universalGatewayPC.evm.d.ts +59 -0
  5. package/src/lib/constants/abi/universalGatewayPC.evm.js +26 -0
  6. package/src/lib/constants/chain.d.ts +4 -0
  7. package/src/lib/constants/chain.js +6 -2
  8. package/src/lib/index.d.ts +2 -2
  9. package/src/lib/internal/bs58.d.ts +2 -0
  10. package/src/lib/internal/bs58.js +7 -0
  11. package/src/lib/orchestrator/cea-utils.d.ts +0 -9
  12. package/src/lib/orchestrator/cea-utils.js +15 -4
  13. package/src/lib/orchestrator/internals/account-manager.d.ts +16 -0
  14. package/src/lib/orchestrator/internals/account-manager.js +191 -0
  15. package/src/lib/orchestrator/internals/cascade.d.ts +23 -0
  16. package/src/lib/orchestrator/internals/cascade.js +945 -0
  17. package/src/lib/orchestrator/internals/confirmation.d.ts +9 -0
  18. package/src/lib/orchestrator/internals/confirmation.js +123 -0
  19. package/src/lib/orchestrator/internals/context.d.ts +38 -0
  20. package/src/lib/orchestrator/internals/context.js +49 -0
  21. package/src/lib/orchestrator/internals/execute-funds-only.d.ts +11 -0
  22. package/src/lib/orchestrator/internals/execute-funds-only.js +219 -0
  23. package/src/lib/orchestrator/internals/execute-funds-payload.d.ts +11 -0
  24. package/src/lib/orchestrator/internals/execute-funds-payload.js +222 -0
  25. package/src/lib/orchestrator/internals/execute-standard.d.ts +13 -0
  26. package/src/lib/orchestrator/internals/execute-standard.js +225 -0
  27. package/src/lib/orchestrator/internals/gas-calculator.d.ts +38 -0
  28. package/src/lib/orchestrator/internals/gas-calculator.js +424 -0
  29. package/src/lib/orchestrator/internals/gateway-client.d.ts +24 -0
  30. package/src/lib/orchestrator/internals/gateway-client.js +191 -0
  31. package/src/lib/orchestrator/internals/helpers.d.ts +26 -0
  32. package/src/lib/orchestrator/internals/helpers.js +181 -0
  33. package/src/lib/orchestrator/internals/index.d.ts +24 -0
  34. package/src/lib/orchestrator/internals/index.js +132 -0
  35. package/src/lib/orchestrator/internals/outbound-sync.d.ts +49 -0
  36. package/src/lib/orchestrator/internals/outbound-sync.js +357 -0
  37. package/src/lib/orchestrator/internals/outbound-tracker.d.ts +8 -0
  38. package/src/lib/orchestrator/internals/outbound-tracker.js +64 -0
  39. package/src/lib/orchestrator/internals/payload-builder.d.ts +24 -0
  40. package/src/lib/orchestrator/internals/payload-builder.js +178 -0
  41. package/src/lib/orchestrator/internals/push-chain-tx.d.ts +32 -0
  42. package/src/lib/orchestrator/internals/push-chain-tx.js +207 -0
  43. package/src/lib/orchestrator/internals/quote.d.ts +9 -0
  44. package/src/lib/orchestrator/internals/quote.js +114 -0
  45. package/src/lib/orchestrator/internals/rescue.d.ts +20 -0
  46. package/src/lib/orchestrator/internals/rescue.js +112 -0
  47. package/src/lib/orchestrator/internals/response-builder.d.ts +26 -0
  48. package/src/lib/orchestrator/internals/response-builder.js +504 -0
  49. package/src/lib/orchestrator/internals/route-handlers.d.ts +77 -0
  50. package/src/lib/orchestrator/internals/route-handlers.js +1192 -0
  51. package/src/lib/orchestrator/internals/signing.d.ts +27 -0
  52. package/src/lib/orchestrator/internals/signing.js +258 -0
  53. package/src/lib/orchestrator/internals/svm-bridge.d.ts +21 -0
  54. package/src/lib/orchestrator/internals/svm-bridge.js +122 -0
  55. package/src/lib/orchestrator/internals/svm-helpers.d.ts +35 -0
  56. package/src/lib/orchestrator/internals/svm-helpers.js +143 -0
  57. package/src/lib/orchestrator/internals/tx-transformer.d.ts +12 -0
  58. package/src/lib/orchestrator/internals/tx-transformer.js +120 -0
  59. package/src/lib/orchestrator/internals/uea-manager.d.ts +17 -0
  60. package/src/lib/orchestrator/internals/uea-manager.js +164 -0
  61. package/src/lib/orchestrator/orchestrator.d.ts +33 -431
  62. package/src/lib/orchestrator/orchestrator.js +124 -6125
  63. package/src/lib/orchestrator/orchestrator.types.d.ts +11 -10
  64. package/src/lib/orchestrator/payload-builders.js +34 -8
  65. package/src/lib/price-fetch/price-fetch.js +19 -7
  66. package/src/lib/push-chain/push-chain.d.ts +6 -0
  67. package/src/lib/push-chain/push-chain.js +10 -2
  68. package/src/lib/push-client/push-client.d.ts +3 -0
  69. package/src/lib/push-client/push-client.js +21 -31
  70. package/src/lib/universal/account/account.d.ts +35 -16
  71. package/src/lib/universal/account/account.js +298 -16
  72. package/src/lib/universal/signer/signer.d.ts +2 -3
  73. package/src/lib/universal/signer/signer.js +65 -115
  74. package/src/lib/universal/universal.types.d.ts +41 -0
  75. package/src/lib/utils.d.ts +7 -2
  76. package/src/lib/utils.js +65 -34
  77. package/src/lib/vm-client/evm-client.js +3 -11
  78. package/src/lib/vm-client/svm-client.d.ts +0 -4
  79. package/src/lib/vm-client/svm-client.js +2 -8
  80. package/src/index.js.map +0 -1
  81. package/src/lib/cache/cache.js.map +0 -1
  82. package/src/lib/constants/abi/cea.evm.js.map +0 -1
  83. package/src/lib/constants/abi/ceaFactory.evm.js.map +0 -1
  84. package/src/lib/constants/abi/erc20.evm.js.map +0 -1
  85. package/src/lib/constants/abi/factoryV1.js.map +0 -1
  86. package/src/lib/constants/abi/feeLocker.evm.js.map +0 -1
  87. package/src/lib/constants/abi/index.js.map +0 -1
  88. package/src/lib/constants/abi/prc20.evm.js.map +0 -1
  89. package/src/lib/constants/abi/uea-factory.js.map +0 -1
  90. package/src/lib/constants/abi/uea.evm.js.map +0 -1
  91. package/src/lib/constants/abi/uea.svm.js.map +0 -1
  92. package/src/lib/constants/abi/universalGateway.evm.js.map +0 -1
  93. package/src/lib/constants/abi/universalGatewayPC.evm.js.map +0 -1
  94. package/src/lib/constants/abi/universalGatewayV0.evm.js.map +0 -1
  95. package/src/lib/constants/chain.js.map +0 -1
  96. package/src/lib/constants/enums.js.map +0 -1
  97. package/src/lib/constants/index.js.map +0 -1
  98. package/src/lib/constants/selectors.js.map +0 -1
  99. package/src/lib/constants/tokens.js.map +0 -1
  100. package/src/lib/generated/uexecutor/v1/query.js.map +0 -1
  101. package/src/lib/generated/uexecutor/v1/types.js.map +0 -1
  102. package/src/lib/generated/uexecutor/v2/index.js.map +0 -1
  103. package/src/lib/generated/uexecutor/v2/query.js.map +0 -1
  104. package/src/lib/generated/uexecutor/v2/types.js.map +0 -1
  105. package/src/lib/generated/v1/tx.js.map +0 -1
  106. package/src/lib/index.js.map +0 -1
  107. package/src/lib/internal/buffer.js.map +0 -1
  108. package/src/lib/internal/global-buffer-polyfill.js.map +0 -1
  109. package/src/lib/orchestrator/cea-utils.js.map +0 -1
  110. package/src/lib/orchestrator/orchestrator.js.map +0 -1
  111. package/src/lib/orchestrator/orchestrator.types.js.map +0 -1
  112. package/src/lib/orchestrator/payload-builders.js.map +0 -1
  113. package/src/lib/orchestrator/route-detector.js.map +0 -1
  114. package/src/lib/price-fetch/price-fetch.js.map +0 -1
  115. package/src/lib/progress-hook/progress-hook.js.map +0 -1
  116. package/src/lib/progress-hook/progress-hook.types.js.map +0 -1
  117. package/src/lib/push-chain/helpers/abis.js.map +0 -1
  118. package/src/lib/push-chain/helpers/addresses.js.map +0 -1
  119. package/src/lib/push-chain/push-chain.js.map +0 -1
  120. package/src/lib/push-client/push-client.js.map +0 -1
  121. package/src/lib/push-client/push-client.types.js.map +0 -1
  122. package/src/lib/universal/account/account.js.map +0 -1
  123. package/src/lib/universal/account/index.js.map +0 -1
  124. package/src/lib/universal/signer/index.js.map +0 -1
  125. package/src/lib/universal/signer/signer.js.map +0 -1
  126. package/src/lib/universal/signer/signer.types.js.map +0 -1
  127. package/src/lib/universal/universal.types.js.map +0 -1
  128. package/src/lib/utils.js.map +0 -1
  129. package/src/lib/vm-client/evm-client.js.map +0 -1
  130. package/src/lib/vm-client/svm-client.js.map +0 -1
  131. package/src/lib/vm-client/vm-client.types.js.map +0 -1
@@ -0,0 +1,945 @@
1
+ "use strict";
2
+ /**
3
+ * Cascade composition functions extracted from Orchestrator.
4
+ *
5
+ * Covers: prepareTransaction, buildHopDescriptor, classifyIntoSegments,
6
+ * getSegmentType, composeCascade, createCascadedBuilder, createChainedBuilder.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.prepareTransaction = prepareTransaction;
10
+ exports.buildHopDescriptor = buildHopDescriptor;
11
+ exports.classifyIntoSegments = classifyIntoSegments;
12
+ exports.getSegmentType = getSegmentType;
13
+ exports.composeCascade = composeCascade;
14
+ exports.createCascadedBuilder = createCascadedBuilder;
15
+ const tslib_1 = require("tslib");
16
+ const web3_js_1 = require("@solana/web3.js");
17
+ const viem_1 = require("viem");
18
+ const erc20_evm_1 = require("../../constants/abi/erc20.evm");
19
+ const chain_1 = require("../../constants/chain");
20
+ const selectors_1 = require("../../constants/selectors");
21
+ const route_detector_1 = require("../route-detector");
22
+ const cea_utils_1 = require("../cea-utils");
23
+ const payload_builders_1 = require("../payload-builders");
24
+ const push_chain_1 = require("../../push-chain/push-chain");
25
+ const helpers_1 = require("./helpers");
26
+ const payload_builder_1 = require("./payload-builder");
27
+ const uea_manager_1 = require("./uea-manager");
28
+ const gas_calculator_1 = require("./gas-calculator");
29
+ const route_handlers_1 = require("./route-handlers");
30
+ // ============================================================================
31
+ // prepareTransaction
32
+ // ============================================================================
33
+ function prepareTransaction(ctx, params, callbacks) {
34
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
35
+ var _a, _b;
36
+ (0, route_detector_1.validateRouteParams)(params, {
37
+ clientChain: ctx.universalSigner.account.chain,
38
+ });
39
+ const route = (0, route_detector_1.detectRoute)(params);
40
+ // Skip getCode if accountStatusCache already confirmed deployment
41
+ let nonce;
42
+ const deployedHintCascade = (_b = (_a = ctx.accountStatusCache) === null || _a === void 0 ? void 0 : _a.uea) === null || _b === void 0 ? void 0 : _b.deployed;
43
+ const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
44
+ if (deployedHintCascade) {
45
+ nonce = yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress);
46
+ }
47
+ else {
48
+ const status = yield (0, uea_manager_1.getUeaStatusAndNonce)(ctx);
49
+ nonce = status.nonce;
50
+ }
51
+ // Build the payload based on route
52
+ const { payload, gatewayRequest } = yield (0, route_handlers_1.buildPayloadForRoute)(ctx, params, route, nonce);
53
+ const gasEstimate = params.gasLimit || selectors_1.DEFAULT_OUTBOUND_GAS_LIMIT;
54
+ const deadline = params.deadline || BigInt(Math.floor(Date.now() / 1000) + 3600);
55
+ // Build the HopDescriptor with all metadata needed for cascade nesting
56
+ const hop = yield buildHopDescriptor(ctx, params, route, ueaAddress);
57
+ const prepared = {
58
+ route,
59
+ payload,
60
+ gatewayRequest,
61
+ estimatedGas: gasEstimate,
62
+ nonce,
63
+ deadline,
64
+ _hop: hop,
65
+ thenOn: (nextTx) => createCascadedBuilder(ctx, [prepared, nextTx], callbacks),
66
+ send: () => callbacks.executeFn(params),
67
+ };
68
+ return prepared;
69
+ });
70
+ }
71
+ // ============================================================================
72
+ // buildHopDescriptor
73
+ // ============================================================================
74
+ function buildHopDescriptor(ctx, params, route, ueaAddress) {
75
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
76
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
77
+ // Pass 0 when user omits gasLimit → contract uses per-chain baseGasLimitByChainNamespace
78
+ const gasLimit = (_a = params.gasLimit) !== null && _a !== void 0 ? _a : BigInt(0);
79
+ const routeStr = route;
80
+ const baseDescriptor = {
81
+ params,
82
+ route: routeStr,
83
+ gasLimit,
84
+ ueaAddress,
85
+ revertRecipient: ueaAddress,
86
+ };
87
+ switch (route) {
88
+ case route_detector_1.TransactionRoute.UOA_TO_PUSH: {
89
+ // Route 1: Build Push Chain multicalls
90
+ const executeParams = (0, helpers_1.toExecuteParams)(params);
91
+ const pushMulticalls = (0, payload_builders_1.buildExecuteMulticall)({
92
+ execute: executeParams,
93
+ ueaAddress,
94
+ });
95
+ return Object.assign(Object.assign({}, baseDescriptor), { pushMulticalls });
96
+ }
97
+ case route_detector_1.TransactionRoute.UOA_TO_CEA: {
98
+ // Route 2: Build outbound metadata
99
+ const target = params.to;
100
+ const targetChain = target.chain;
101
+ // Branch: SVM vs EVM
102
+ if ((0, payload_builders_1.isSvmChain)(targetChain)) {
103
+ // SVM path: no CEA lookup, build SVM payload
104
+ const hasSvmExecute = !!params.svmExecute;
105
+ let svmPayload = '0x';
106
+ if (hasSvmExecute) {
107
+ const exec = params.svmExecute;
108
+ svmPayload = (0, payload_builders_1.encodeSvmExecutePayload)({
109
+ targetProgram: exec.targetProgram,
110
+ accounts: exec.accounts,
111
+ ixData: exec.ixData,
112
+ instructionId: 2,
113
+ });
114
+ }
115
+ let prc20Token = selectors_1.ZERO_ADDRESS;
116
+ let burnAmount = BigInt(0);
117
+ if ((_b = params.funds) === null || _b === void 0 ? void 0 : _b.amount) {
118
+ const token = params.funds.token;
119
+ if (token) {
120
+ prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
121
+ burnAmount = params.funds.amount;
122
+ }
123
+ }
124
+ else if (params.value && params.value > BigInt(0)) {
125
+ prc20Token = (0, helpers_1.getNativePRC20ForChain)(targetChain, ctx.pushNetwork);
126
+ burnAmount = params.value;
127
+ }
128
+ else if (hasSvmExecute) {
129
+ prc20Token = (0, helpers_1.getNativePRC20ForChain)(targetChain, ctx.pushNetwork);
130
+ burnAmount = BigInt(1);
131
+ }
132
+ let gasToken = selectors_1.ZERO_ADDRESS;
133
+ let gasFee = BigInt(0);
134
+ if (prc20Token !== selectors_1.ZERO_ADDRESS) {
135
+ const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, gasLimit);
136
+ gasToken = result.gasToken;
137
+ gasFee = result.gasFee;
138
+ }
139
+ return Object.assign(Object.assign({}, baseDescriptor), { targetChain, isSvmTarget: true, svmPayload,
140
+ prc20Token,
141
+ burnAmount,
142
+ gasToken,
143
+ gasFee });
144
+ }
145
+ // EVM path: Resolve CEA address + build CEA multicalls
146
+ const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, targetChain, (_c = ctx.rpcUrls[targetChain]) === null || _c === void 0 ? void 0 : _c[0]);
147
+ // Migration path: raw MIGRATION_SELECTOR payload, no multicall wrapping
148
+ if (params.migration) {
149
+ const prc20Token = (0, helpers_1.getNativePRC20ForChain)(targetChain, ctx.pushNetwork);
150
+ const burnAmount = BigInt(0); // Migration is logic-only — no funds. CEA rejects msg.value != 0.
151
+ let gasToken = selectors_1.ZERO_ADDRESS;
152
+ let gasFee = BigInt(0);
153
+ if (prc20Token !== selectors_1.ZERO_ADDRESS) {
154
+ const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, gasLimit);
155
+ gasToken = result.gasToken;
156
+ gasFee = result.gasFee;
157
+ }
158
+ return Object.assign(Object.assign({}, baseDescriptor), { targetChain,
159
+ ceaAddress, ceaMulticalls: [], prc20Token,
160
+ burnAmount,
161
+ gasToken,
162
+ gasFee, isMigration: true });
163
+ }
164
+ // Build CEA multicalls
165
+ const ceaMulticalls = [];
166
+ if (params.data) {
167
+ if (Array.isArray(params.data)) {
168
+ ceaMulticalls.push(...params.data);
169
+ }
170
+ else {
171
+ // When ERC-20 funds are provided with a single payload, auto-prepend a
172
+ // transfer() call so the tokens minted to the CEA are forwarded to the
173
+ // target address. This mirrors the Route 1 behavior in buildExecuteMulticall.
174
+ if ((_d = params.funds) === null || _d === void 0 ? void 0 : _d.amount) {
175
+ const token = params.funds.token;
176
+ if (token && token.mechanism !== 'native') {
177
+ const erc20Transfer = (0, viem_1.encodeFunctionData)({
178
+ abi: erc20_evm_1.ERC20_EVM,
179
+ functionName: 'transfer',
180
+ args: [target.address, params.funds.amount],
181
+ });
182
+ ceaMulticalls.push({
183
+ to: token.address,
184
+ value: BigInt(0),
185
+ data: erc20Transfer,
186
+ });
187
+ }
188
+ }
189
+ // Single call with data. Forward native value (if any) so the target
190
+ // contract receives it alongside the payload call. The vault deposits
191
+ // native value to the CEA, and the multicall forwards it to the target.
192
+ ceaMulticalls.push({
193
+ to: target.address,
194
+ value: (_e = params.value) !== null && _e !== void 0 ? _e : BigInt(0),
195
+ data: params.data,
196
+ });
197
+ }
198
+ }
199
+ else if (params.value) {
200
+ // Skip multicall when sending native value to own CEA — gateway deposits directly.
201
+ // Self-call with value would revert (CEA._handleMulticall rejects it).
202
+ if (target.address.toLowerCase() !== ceaAddress.toLowerCase()) {
203
+ ceaMulticalls.push({
204
+ to: target.address,
205
+ value: params.value,
206
+ data: '0x',
207
+ });
208
+ }
209
+ }
210
+ // Determine PRC-20 token and burn amount
211
+ let prc20Token = selectors_1.ZERO_ADDRESS;
212
+ let burnAmount = BigInt(0);
213
+ if ((_f = params.funds) === null || _f === void 0 ? void 0 : _f.amount) {
214
+ const token = params.funds.token;
215
+ if (token) {
216
+ prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
217
+ burnAmount = params.funds.amount;
218
+ }
219
+ }
220
+ else if (params.value && params.value > BigInt(0)) {
221
+ prc20Token = (0, helpers_1.getNativePRC20ForChain)(targetChain, ctx.pushNetwork);
222
+ burnAmount = params.value;
223
+ }
224
+ else if (params.data) {
225
+ prc20Token = (0, helpers_1.getNativePRC20ForChain)(targetChain, ctx.pushNetwork);
226
+ burnAmount = BigInt(1); // Minimum for precompile
227
+ }
228
+ // Query gas fee
229
+ let gasToken = selectors_1.ZERO_ADDRESS;
230
+ let gasFee = BigInt(0);
231
+ if (prc20Token !== selectors_1.ZERO_ADDRESS) {
232
+ const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, gasLimit);
233
+ gasToken = result.gasToken;
234
+ gasFee = result.gasFee;
235
+ }
236
+ return Object.assign(Object.assign({}, baseDescriptor), { targetChain,
237
+ ceaAddress,
238
+ ceaMulticalls,
239
+ prc20Token,
240
+ burnAmount,
241
+ gasToken,
242
+ gasFee });
243
+ }
244
+ case route_detector_1.TransactionRoute.CEA_TO_PUSH: {
245
+ // Route 3: Build CEA multicalls for sendUniversalTxFromCEA
246
+ const sourceChain = params.from.chain;
247
+ // SVM chains use PDA-based CEA, not factory-deployed CEA
248
+ if ((0, payload_builders_1.isSvmChain)(sourceChain)) {
249
+ const lockerContract = chain_1.CHAIN_INFO[sourceChain].lockerContract;
250
+ if (!lockerContract) {
251
+ throw new Error(`No SVM gateway program configured for chain ${sourceChain}`);
252
+ }
253
+ const programPk = new web3_js_1.PublicKey(lockerContract);
254
+ const gatewayProgramHex = ('0x' +
255
+ Buffer.from(programPk.toBytes()).toString('hex'));
256
+ // Derive CEA PDA
257
+ const ueaBytes = Buffer.from(ueaAddress.slice(2), 'hex');
258
+ const [ceaPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('push_identity'), ueaBytes], programPk);
259
+ const ceaPdaHex = ('0x' +
260
+ Buffer.from(ceaPda.toBytes()).toString('hex'));
261
+ let amount = BigInt(0);
262
+ let prc20Token;
263
+ if (((_g = params.funds) === null || _g === void 0 ? void 0 : _g.amount) && params.funds.amount > BigInt(0)) {
264
+ amount = params.funds.amount;
265
+ const token = params.funds.token;
266
+ if (token && token.address) {
267
+ prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
268
+ }
269
+ else {
270
+ prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
271
+ }
272
+ }
273
+ else if (params.value && params.value > BigInt(0)) {
274
+ amount = params.value;
275
+ prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
276
+ }
277
+ else {
278
+ // Payload-only Route 3 SVM
279
+ prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
280
+ }
281
+ let gasToken = selectors_1.ZERO_ADDRESS;
282
+ let gasFee = BigInt(0);
283
+ if (prc20Token !== selectors_1.ZERO_ADDRESS) {
284
+ const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, gasLimit);
285
+ gasToken = result.gasToken;
286
+ gasFee = result.gasFee;
287
+ }
288
+ return Object.assign(Object.assign({}, baseDescriptor), { sourceChain, ceaAddress: ceaPdaHex, isSvmTarget: true, prc20Token, burnAmount: amount > BigInt(0) ? amount : BigInt(1), gasToken,
289
+ gasFee });
290
+ }
291
+ const { cea: ceaAddress, isDeployed } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, sourceChain, (_h = ctx.rpcUrls[sourceChain]) === null || _h === void 0 ? void 0 : _h[0]);
292
+ // Determine token/amount for the inbound
293
+ let tokenAddress = selectors_1.ZERO_ADDRESS;
294
+ let amount = BigInt(0);
295
+ let nativeValue = BigInt(0);
296
+ if ((_j = params.funds) === null || _j === void 0 ? void 0 : _j.amount) {
297
+ const token = params.funds.token;
298
+ if (token) {
299
+ if (token.mechanism === 'native') {
300
+ amount = params.funds.amount;
301
+ nativeValue = params.funds.amount;
302
+ }
303
+ else {
304
+ tokenAddress = token.address;
305
+ amount = params.funds.amount;
306
+ }
307
+ }
308
+ }
309
+ else if (params.value && params.value > BigInt(0)) {
310
+ amount = params.value;
311
+ nativeValue = params.value;
312
+ }
313
+ // The PRC-20 for the outbound wrapper (Route 2 to source chain)
314
+ const prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
315
+ let gasToken = selectors_1.ZERO_ADDRESS;
316
+ let gasFee = BigInt(0);
317
+ if (prc20Token !== selectors_1.ZERO_ADDRESS) {
318
+ const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, gasLimit);
319
+ gasToken = result.gasToken;
320
+ gasFee = result.gasFee;
321
+ }
322
+ return Object.assign(Object.assign({}, baseDescriptor), { sourceChain,
323
+ ceaAddress,
324
+ prc20Token, burnAmount: amount > BigInt(0) ? amount : BigInt(1), gasToken,
325
+ gasFee });
326
+ }
327
+ default:
328
+ return baseDescriptor;
329
+ }
330
+ });
331
+ }
332
+ // ============================================================================
333
+ // classifyIntoSegments
334
+ // ============================================================================
335
+ function classifyIntoSegments(hops) {
336
+ if (hops.length === 0)
337
+ return [];
338
+ const segments = [];
339
+ let currentSegment = null;
340
+ for (const hop of hops) {
341
+ const segType = getSegmentType(hop.route);
342
+ const chain = hop.targetChain || hop.sourceChain;
343
+ const canMerge = currentSegment &&
344
+ currentSegment.type === segType &&
345
+ // Same-chain merging for OUTBOUND_TO_CEA (EVM only — SVM hops are atomic)
346
+ (segType === 'OUTBOUND_TO_CEA'
347
+ ? currentSegment.targetChain === hop.targetChain && !hop.isSvmTarget
348
+ : segType === 'PUSH_EXECUTION');
349
+ if (canMerge && currentSegment) {
350
+ // Merge into current segment
351
+ currentSegment.hops.push(hop);
352
+ if (segType === 'OUTBOUND_TO_CEA') {
353
+ currentSegment.mergedCeaMulticalls = [
354
+ ...(currentSegment.mergedCeaMulticalls || []),
355
+ ...(hop.ceaMulticalls || []),
356
+ ];
357
+ currentSegment.totalBurnAmount =
358
+ (currentSegment.totalBurnAmount || BigInt(0)) +
359
+ (hop.burnAmount || BigInt(0));
360
+ // Gas fee: take the max gasLimit across merged hops
361
+ if (hop.gasLimit > (currentSegment.gasLimit || BigInt(0))) {
362
+ currentSegment.gasLimit = hop.gasLimit;
363
+ }
364
+ // Accumulate gas fees
365
+ currentSegment.gasFee =
366
+ (currentSegment.gasFee || BigInt(0)) + (hop.gasFee || BigInt(0));
367
+ }
368
+ else if (segType === 'PUSH_EXECUTION') {
369
+ currentSegment.mergedPushMulticalls = [
370
+ ...(currentSegment.mergedPushMulticalls || []),
371
+ ...(hop.pushMulticalls || []),
372
+ ];
373
+ }
374
+ }
375
+ else {
376
+ // Start a new segment
377
+ currentSegment = {
378
+ type: segType,
379
+ hops: [hop],
380
+ targetChain: hop.targetChain,
381
+ sourceChain: hop.sourceChain,
382
+ mergedCeaMulticalls: segType === 'OUTBOUND_TO_CEA'
383
+ ? [...(hop.ceaMulticalls || [])]
384
+ : undefined,
385
+ mergedPushMulticalls: segType === 'PUSH_EXECUTION'
386
+ ? [...(hop.pushMulticalls || [])]
387
+ : undefined,
388
+ totalBurnAmount: hop.burnAmount,
389
+ prc20Token: hop.prc20Token,
390
+ gasToken: hop.gasToken,
391
+ gasFee: hop.gasFee,
392
+ gasLimit: hop.gasLimit,
393
+ };
394
+ segments.push(currentSegment);
395
+ }
396
+ }
397
+ return segments;
398
+ }
399
+ // ============================================================================
400
+ // getSegmentType
401
+ // ============================================================================
402
+ function getSegmentType(route) {
403
+ switch (route) {
404
+ case 'UOA_TO_PUSH':
405
+ return 'PUSH_EXECUTION';
406
+ case 'UOA_TO_CEA':
407
+ return 'OUTBOUND_TO_CEA';
408
+ case 'CEA_TO_PUSH':
409
+ return 'INBOUND_FROM_CEA';
410
+ default:
411
+ return 'PUSH_EXECUTION';
412
+ }
413
+ }
414
+ // ============================================================================
415
+ // composeCascade
416
+ // ============================================================================
417
+ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
418
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
419
+ let accumulatedPushMulticalls = [];
420
+ const gatewayPcAddress = (0, helpers_1.getUniversalGatewayPCAddress)();
421
+ // Compute per-outbound nativeValueForGas from UEA balance
422
+ // Each outbound segment needs native value for the gas swap on the destination chain.
423
+ // The contract refunds excess, so over-allocating is safe.
424
+ const numOutbounds = segments.filter((s) => s.type !== 'PUSH_EXECUTION').length;
425
+ const CASCADE_GAS_RESERVE = BigInt(3e18); // 3 PC reserve for gas costs
426
+ let perOutboundNativeValue;
427
+ if (ueaBalance && numOutbounds > 0 && ueaBalance > CASCADE_GAS_RESERVE) {
428
+ perOutboundNativeValue =
429
+ (ueaBalance - CASCADE_GAS_RESERVE) / BigInt(numOutbounds);
430
+ }
431
+ for (let i = segments.length - 1; i >= 0; i--) {
432
+ const segment = segments[i];
433
+ switch (segment.type) {
434
+ case 'PUSH_EXECUTION': {
435
+ // Prepend Push Chain multicalls to accumulated
436
+ accumulatedPushMulticalls = [
437
+ ...(segment.mergedPushMulticalls || []),
438
+ ...accumulatedPushMulticalls,
439
+ ];
440
+ break;
441
+ }
442
+ case 'OUTBOUND_TO_CEA': {
443
+ const firstHop = segment.hops[0];
444
+ const isSvmSegment = (firstHop === null || firstHop === void 0 ? void 0 : firstHop.isSvmTarget) === true;
445
+ let outboundPayload;
446
+ let targetForOutbound;
447
+ const isMigration = (firstHop === null || firstHop === void 0 ? void 0 : firstHop.isMigration) === true;
448
+ if (isSvmSegment) {
449
+ // SVM: use the pre-built SVM payload from the hop descriptor
450
+ outboundPayload = (_a = firstHop.svmPayload) !== null && _a !== void 0 ? _a : '0x';
451
+ // For SVM, target is the recipient/program from the hop params
452
+ const svmTarget = firstHop.params.to;
453
+ targetForOutbound =
454
+ (_c = (_b = firstHop.params.svmExecute) === null || _b === void 0 ? void 0 : _b.targetProgram) !== null && _c !== void 0 ? _c : svmTarget.address;
455
+ }
456
+ else if (isMigration) {
457
+ // Migration: use raw 4-byte MIGRATION_SELECTOR, no multicall wrapping
458
+ outboundPayload = (0, payload_builders_1.buildMigrationPayload)();
459
+ targetForOutbound = (firstHop === null || firstHop === void 0 ? void 0 : firstHop.ceaAddress) || ueaAddress;
460
+ }
461
+ else {
462
+ // EVM: build CEA payload from merged multicalls
463
+ outboundPayload = (0, payload_builders_1.buildCeaMulticallPayload)(segment.mergedCeaMulticalls || []);
464
+ // Get CEA address from the first hop
465
+ targetForOutbound = (firstHop === null || firstHop === void 0 ? void 0 : firstHop.ceaAddress) || ueaAddress;
466
+ }
467
+ // Build outbound request
468
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetForOutbound, segment.prc20Token || selectors_1.ZERO_ADDRESS, segment.totalBurnAmount || BigInt(0), (_d = segment.gasLimit) !== null && _d !== void 0 ? _d : BigInt(0), outboundPayload, ueaAddress);
469
+ // Build approval + outbound multicalls
470
+ const segGasFee = segment.gasFee || BigInt(0);
471
+ const outboundMulticalls = (0, payload_builders_1.buildOutboundApprovalAndCall)({
472
+ prc20Token: segment.prc20Token || selectors_1.ZERO_ADDRESS,
473
+ gasToken: segment.gasToken || selectors_1.ZERO_ADDRESS,
474
+ burnAmount: segment.totalBurnAmount || BigInt(0),
475
+ gasFee: segGasFee,
476
+ nativeValueForGas: perOutboundNativeValue !== null && perOutboundNativeValue !== void 0 ? perOutboundNativeValue : segGasFee * BigInt(1000),
477
+ gatewayPcAddress,
478
+ outboundRequest: outboundReq,
479
+ });
480
+ // Prepend to accumulated
481
+ accumulatedPushMulticalls = [
482
+ ...outboundMulticalls,
483
+ ...accumulatedPushMulticalls,
484
+ ];
485
+ break;
486
+ }
487
+ case 'INBOUND_FROM_CEA': {
488
+ // The accumulated multicalls = what runs on Push Chain AFTER inbound arrives.
489
+ // Wrap in UniversalPayload struct with correct UEA nonce for the relay.
490
+ // Build push multicalls from this hop's own data (e.g., counter.increment())
491
+ // This is the Route 3 hop's payload that executes on Push Chain after inbound.
492
+ const hop0 = segment.hops[0];
493
+ if ((_e = hop0 === null || hop0 === void 0 ? void 0 : hop0.params) === null || _e === void 0 ? void 0 : _e.data) {
494
+ const hopPushMulticalls = (0, payload_builders_1.buildExecuteMulticall)({
495
+ execute: {
496
+ to: hop0.params.to,
497
+ value: hop0.params.value,
498
+ data: hop0.params.data,
499
+ },
500
+ ueaAddress,
501
+ });
502
+ // Prepend the Route 3's own push calls before subsequent hops
503
+ accumulatedPushMulticalls = [
504
+ ...hopPushMulticalls,
505
+ ...accumulatedPushMulticalls,
506
+ ];
507
+ }
508
+ let intermediatePayload = '0x';
509
+ if (accumulatedPushMulticalls.length > 0) {
510
+ const multicallPayload = (0, payload_builder_1.buildMulticallPayloadData)(ctx, ueaAddress, accumulatedPushMulticalls);
511
+ // +1: the outbound tx consumes one nonce via execute()
512
+ intermediatePayload = (0, payload_builders_1.buildInboundUniversalPayload)(multicallPayload, { nonce: (ueaNonce !== null && ueaNonce !== void 0 ? ueaNonce : BigInt(0)) + BigInt(1) });
513
+ }
514
+ const hop = segment.hops[0];
515
+ const sourceChain = hop.sourceChain;
516
+ const ceaAddress = hop.ceaAddress || ueaAddress;
517
+ // SVM chains: build SVM CPI payload instead of EVM CEA multicall
518
+ if ((0, payload_builders_1.isSvmChain)(sourceChain)) {
519
+ const lockerContract = chain_1.CHAIN_INFO[sourceChain].lockerContract;
520
+ if (!lockerContract) {
521
+ throw new Error(`No SVM gateway program configured for chain ${sourceChain}`);
522
+ }
523
+ const programPk = new web3_js_1.PublicKey(lockerContract);
524
+ const gatewayProgramHex = ('0x' +
525
+ Buffer.from(programPk.toBytes()).toString('hex'));
526
+ let drainAmount = BigInt(0);
527
+ let tokenMintHex;
528
+ const params = hop.params;
529
+ if (((_f = params.funds) === null || _f === void 0 ? void 0 : _f.amount) && params.funds.amount > BigInt(0)) {
530
+ drainAmount = params.funds.amount;
531
+ const token = params.funds.token;
532
+ if (token && token.address) {
533
+ const mintPk = new web3_js_1.PublicKey(token.address);
534
+ tokenMintHex = ('0x' +
535
+ Buffer.from(mintPk.toBytes()).toString('hex'));
536
+ }
537
+ }
538
+ else if (params.value && params.value > BigInt(0)) {
539
+ drainAmount = params.value;
540
+ }
541
+ // Derive CEA PDA as revert recipient
542
+ const ueaBytes = Buffer.from(ueaAddress.slice(2), 'hex');
543
+ const [ceaPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('push_identity'), ueaBytes], programPk);
544
+ const ceaPdaHex = ('0x' +
545
+ Buffer.from(ceaPda.toBytes()).toString('hex'));
546
+ // Build SVM payload with intermediate Push Chain payload embedded
547
+ const svmPayload = (0, payload_builders_1.encodeSvmCeaToUeaPayload)({
548
+ gatewayProgramHex,
549
+ drainAmount,
550
+ tokenMintHex,
551
+ revertRecipientHex: ceaPdaHex,
552
+ extraPayload: intermediatePayload !== '0x'
553
+ ? new Uint8Array(Buffer.from(intermediatePayload.slice(2), 'hex'))
554
+ : undefined,
555
+ });
556
+ const burnAmount = BigInt(1);
557
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(gatewayProgramHex, segment.prc20Token ||
558
+ (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), burnAmount, (_g = segment.gasLimit) !== null && _g !== void 0 ? _g : BigInt(0), svmPayload, ueaAddress);
559
+ const inboundGasFee = segment.gasFee || BigInt(0);
560
+ const outboundMulticalls = (0, payload_builders_1.buildOutboundApprovalAndCall)({
561
+ prc20Token: segment.prc20Token ||
562
+ (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork),
563
+ gasToken: segment.gasToken || selectors_1.ZERO_ADDRESS,
564
+ burnAmount,
565
+ gasFee: inboundGasFee,
566
+ nativeValueForGas: perOutboundNativeValue !== null && perOutboundNativeValue !== void 0 ? perOutboundNativeValue : inboundGasFee * BigInt(1000),
567
+ gatewayPcAddress,
568
+ outboundRequest: outboundReq,
569
+ });
570
+ accumulatedPushMulticalls = [...outboundMulticalls];
571
+ break;
572
+ }
573
+ // EVM path: Build CEA multicall: [approve?, sendUniversalTxFromCEA(payload)]
574
+ const ceaMulticalls = [];
575
+ // Add hop's own CEA operations if any
576
+ // (e.g., approve + swap before bridging back)
577
+ if (hop.ceaMulticalls && hop.ceaMulticalls.length > 0) {
578
+ ceaMulticalls.push(...hop.ceaMulticalls);
579
+ }
580
+ // Determine token/amount for inbound
581
+ let tokenAddress = selectors_1.ZERO_ADDRESS;
582
+ let amount = BigInt(0);
583
+ const params = hop.params;
584
+ if ((_h = params.funds) === null || _h === void 0 ? void 0 : _h.amount) {
585
+ const token = params.funds.token;
586
+ if (token) {
587
+ if (token.mechanism === 'native') {
588
+ amount = params.funds.amount;
589
+ }
590
+ else {
591
+ tokenAddress = token.address;
592
+ amount = params.funds.amount;
593
+ // Add approve for ERC20 (CEA approves gateway)
594
+ const gatewayAddr = chain_1.UNIVERSAL_GATEWAY_ADDRESSES[sourceChain];
595
+ if (!gatewayAddr) {
596
+ throw new Error(`No UniversalGateway address configured for chain ${sourceChain}`);
597
+ }
598
+ // Reset allowance to 0 first for USDT-style tokens
599
+ const approveZeroData = (0, viem_1.encodeFunctionData)({
600
+ abi: erc20_evm_1.ERC20_EVM,
601
+ functionName: 'approve',
602
+ args: [gatewayAddr, BigInt(0)],
603
+ });
604
+ ceaMulticalls.push({
605
+ to: tokenAddress,
606
+ value: BigInt(0),
607
+ data: approveZeroData,
608
+ });
609
+ const approveData = (0, viem_1.encodeFunctionData)({
610
+ abi: erc20_evm_1.ERC20_EVM,
611
+ functionName: 'approve',
612
+ args: [gatewayAddr, amount],
613
+ });
614
+ ceaMulticalls.push({
615
+ to: tokenAddress,
616
+ value: BigInt(0),
617
+ data: approveData,
618
+ });
619
+ }
620
+ }
621
+ }
622
+ else if (params.value && params.value > BigInt(0)) {
623
+ amount = params.value;
624
+ }
625
+ // Build sendUniversalTxToUEA self-call on CEA (to=CEA, value=0)
626
+ const sendCall = (0, payload_builders_1.buildSendUniversalTxToUEA)(ceaAddress, tokenAddress, amount, intermediatePayload, ceaAddress);
627
+ ceaMulticalls.push(sendCall);
628
+ // Wrap CEA multicall in outbound from Push Chain
629
+ const ceaPayload = (0, payload_builders_1.buildCeaMulticallPayload)(ceaMulticalls);
630
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, segment.prc20Token ||
631
+ (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), segment.totalBurnAmount || BigInt(1), (_j = segment.gasLimit) !== null && _j !== void 0 ? _j : BigInt(0), ceaPayload, ueaAddress);
632
+ const inboundGasFee = segment.gasFee || BigInt(0);
633
+ const outboundMulticalls = (0, payload_builders_1.buildOutboundApprovalAndCall)({
634
+ prc20Token: segment.prc20Token ||
635
+ (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork),
636
+ gasToken: segment.gasToken || selectors_1.ZERO_ADDRESS,
637
+ burnAmount: segment.totalBurnAmount || BigInt(1),
638
+ gasFee: inboundGasFee,
639
+ nativeValueForGas: perOutboundNativeValue !== null && perOutboundNativeValue !== void 0 ? perOutboundNativeValue : inboundGasFee * BigInt(1000),
640
+ gatewayPcAddress,
641
+ outboundRequest: outboundReq,
642
+ });
643
+ // Reset accumulated -- everything is now inside this outbound
644
+ accumulatedPushMulticalls = [...outboundMulticalls];
645
+ break;
646
+ }
647
+ }
648
+ }
649
+ return accumulatedPushMulticalls;
650
+ }
651
+ // ============================================================================
652
+ // createCascadedBuilder
653
+ // ============================================================================
654
+ function createCascadedBuilder(ctx, preparedTxs, callbacks) {
655
+ return {
656
+ thenOn: (nextTx) => createCascadedBuilder(ctx, [...preparedTxs, nextTx], callbacks),
657
+ send: () => tslib_1.__awaiter(this, void 0, void 0, function* () {
658
+ const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
659
+ // Extract HopDescriptors
660
+ const hops = preparedTxs.map((tx) => tx._hop);
661
+ // Classify into segments
662
+ const segments = classifyIntoSegments(hops);
663
+ // Check if this is a single-hop Route 1 (no composition needed)
664
+ if (preparedTxs.length === 1 &&
665
+ preparedTxs[0].route === 'UOA_TO_PUSH') {
666
+ const response = yield callbacks.executeFn(hops[0].params);
667
+ const singleRoute1Result = {
668
+ initialTxHash: response.hash,
669
+ initialTxResponse: response,
670
+ hops: [
671
+ {
672
+ hopIndex: 0,
673
+ route: hops[0].route,
674
+ executionChain: (0, helpers_1.getPushChainForNetwork)(ctx.pushNetwork),
675
+ status: 'confirmed',
676
+ txHash: response.hash,
677
+ },
678
+ ],
679
+ hopCount: 1,
680
+ waitForAll: () => tslib_1.__awaiter(this, void 0, void 0, function* () {
681
+ return ({
682
+ success: true,
683
+ hops: [
684
+ {
685
+ hopIndex: 0,
686
+ route: hops[0].route,
687
+ executionChain: (0, helpers_1.getPushChainForNetwork)(ctx.pushNetwork),
688
+ status: 'confirmed',
689
+ txHash: response.hash,
690
+ },
691
+ ],
692
+ });
693
+ }),
694
+ wait: (opts) => tslib_1.__awaiter(this, void 0, void 0, function* () { return singleRoute1Result.waitForAll(opts); }),
695
+ };
696
+ return singleRoute1Result;
697
+ }
698
+ // Check if single-hop Route 2 (just execute directly)
699
+ if (preparedTxs.length === 1 &&
700
+ preparedTxs[0].route === 'UOA_TO_CEA') {
701
+ const response = yield callbacks.executeFn(hops[0].params);
702
+ const targetChain = hops[0].targetChain || (0, helpers_1.getPushChainForNetwork)(ctx.pushNetwork);
703
+ const singleRoute2Result = {
704
+ initialTxHash: response.hash,
705
+ initialTxResponse: response,
706
+ hops: [
707
+ {
708
+ hopIndex: 0,
709
+ route: hops[0].route,
710
+ executionChain: targetChain,
711
+ status: 'confirmed',
712
+ txHash: response.hash,
713
+ },
714
+ ],
715
+ hopCount: 1,
716
+ waitForAll: () => tslib_1.__awaiter(this, void 0, void 0, function* () {
717
+ return ({
718
+ success: true,
719
+ hops: [
720
+ {
721
+ hopIndex: 0,
722
+ route: hops[0].route,
723
+ executionChain: targetChain,
724
+ status: 'confirmed',
725
+ txHash: response.hash,
726
+ },
727
+ ],
728
+ });
729
+ }),
730
+ wait: (opts) => tslib_1.__awaiter(this, void 0, void 0, function* () { return singleRoute2Result.waitForAll(opts); }),
731
+ };
732
+ return singleRoute2Result;
733
+ }
734
+ // Multi-hop: compose cascade bottom-to-top
735
+ // Fetch UEA balance + nonce so composeCascade can allocate native value and build inbound payloads
736
+ const ueaBalance = yield ctx.pushClient.getBalance(ueaAddress);
737
+ const ueaCodeCascade = yield ctx.pushClient.publicClient.getCode({
738
+ address: ueaAddress,
739
+ });
740
+ const ueaNonceCascade = ueaCodeCascade !== undefined
741
+ ? yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress)
742
+ : BigInt(0);
743
+ const composedMulticalls = composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonceCascade);
744
+ // Execute the composed multicall as a single Push Chain tx
745
+ const executeParams = {
746
+ to: ueaAddress,
747
+ data: composedMulticalls,
748
+ };
749
+ const response = yield callbacks.executeFn(executeParams);
750
+ // Build hop info for tracking
751
+ const hopInfos = hops.map((hop, index) => ({
752
+ hopIndex: index,
753
+ route: hop.route,
754
+ executionChain: hop.targetChain || hop.sourceChain || (0, helpers_1.getPushChainForNetwork)(ctx.pushNetwork),
755
+ status: 'pending',
756
+ }));
757
+ // Mark first hop as submitted
758
+ if (hopInfos.length > 0) {
759
+ hopInfos[0].status = 'submitted';
760
+ hopInfos[0].txHash = response.hash;
761
+ }
762
+ const cascadeResponse = {
763
+ initialTxHash: response.hash,
764
+ initialTxResponse: response,
765
+ hops: hopInfos,
766
+ hopCount: hops.length,
767
+ waitForAll: (opts) => tslib_1.__awaiter(this, void 0, void 0, function* () {
768
+ var _a;
769
+ const { pollingIntervalMs = 10000, timeout = 300000, progressHook: cascadeProgressHook, } = opts || {};
770
+ const startTime = Date.now();
771
+ try {
772
+ // 1. Wait for initial Push Chain tx confirmation
773
+ cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
774
+ hopIndex: 0,
775
+ route: ((_a = hopInfos[0]) === null || _a === void 0 ? void 0 : _a.route) || 'UOA_TO_PUSH',
776
+ chain: (0, helpers_1.getPushChainForNetwork)(ctx.pushNetwork),
777
+ status: 'waiting',
778
+ elapsed: Date.now() - startTime,
779
+ });
780
+ yield response.wait();
781
+ // Mark all Push Chain (Route 1) hops as confirmed
782
+ for (const hop of hopInfos) {
783
+ if (hop.route === 'UOA_TO_PUSH') {
784
+ hop.status = 'confirmed';
785
+ hop.txHash = response.hash;
786
+ cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
787
+ hopIndex: hop.hopIndex,
788
+ route: hop.route,
789
+ chain: (0, helpers_1.getPushChainForNetwork)(ctx.pushNetwork),
790
+ status: 'confirmed',
791
+ txHash: response.hash,
792
+ elapsed: Date.now() - startTime,
793
+ });
794
+ }
795
+ }
796
+ // 2. Track outbound hops (Route 2: UOA_TO_CEA)
797
+ // Hops after a CEA_TO_PUSH are "child outbounds" — they execute inside
798
+ // the inbound payload on Push Chain and live under a DIFFERENT utx_id
799
+ // (the inbound UTX, not the parent). We can't track them via the parent
800
+ // utx_id polling, so we auto-confirm them.
801
+ const ceaToPushIndex = hopInfos.findIndex((h) => h.route === 'CEA_TO_PUSH');
802
+ const outboundHops = hopInfos.filter((h, i) => {
803
+ if (h.route !== 'UOA_TO_CEA')
804
+ return false;
805
+ // Child outbounds (after CEA_TO_PUSH) live under the inbound UTX,
806
+ // not the parent — auto-confirm since we can't poll them here.
807
+ if (ceaToPushIndex >= 0 && i > ceaToPushIndex) {
808
+ h.status = 'confirmed';
809
+ cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
810
+ hopIndex: h.hopIndex,
811
+ route: h.route,
812
+ chain: h.executionChain,
813
+ status: 'confirmed',
814
+ elapsed: Date.now() - startTime,
815
+ });
816
+ return false;
817
+ }
818
+ return true;
819
+ });
820
+ if (outboundHops.length > 0) {
821
+ if (outboundHops.length === 1) {
822
+ // Single direct outbound hop: use the existing V1-based tracking
823
+ const hop = outboundHops[0];
824
+ const remainingTimeout = timeout - (Date.now() - startTime);
825
+ if (remainingTimeout <= 0) {
826
+ hop.status = 'failed';
827
+ cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
828
+ hopIndex: hop.hopIndex,
829
+ route: hop.route,
830
+ chain: hop.executionChain,
831
+ status: 'timeout',
832
+ elapsed: Date.now() - startTime,
833
+ });
834
+ return {
835
+ success: false,
836
+ hops: hopInfos,
837
+ failedAt: hop.hopIndex,
838
+ };
839
+ }
840
+ cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
841
+ hopIndex: hop.hopIndex,
842
+ route: hop.route,
843
+ chain: hop.executionChain,
844
+ status: 'polling',
845
+ elapsed: Date.now() - startTime,
846
+ });
847
+ try {
848
+ const outboundDetails = yield callbacks.waitForOutboundTxFn(response.hash, {
849
+ initialWaitMs: Math.min(60000, remainingTimeout),
850
+ pollingIntervalMs,
851
+ timeout: remainingTimeout,
852
+ progressHook: (event) => {
853
+ cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
854
+ hopIndex: hop.hopIndex,
855
+ route: hop.route,
856
+ chain: hop.executionChain,
857
+ status: event.status,
858
+ elapsed: Date.now() - startTime,
859
+ });
860
+ },
861
+ });
862
+ hop.status = 'confirmed';
863
+ hop.txHash = outboundDetails.externalTxHash;
864
+ hop.outboundDetails = outboundDetails;
865
+ cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
866
+ hopIndex: hop.hopIndex,
867
+ route: hop.route,
868
+ chain: hop.executionChain,
869
+ status: 'confirmed',
870
+ txHash: outboundDetails.externalTxHash,
871
+ elapsed: Date.now() - startTime,
872
+ });
873
+ }
874
+ catch (err) {
875
+ hop.status = 'failed';
876
+ cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
877
+ hopIndex: hop.hopIndex,
878
+ route: hop.route,
879
+ chain: hop.executionChain,
880
+ status: 'failed',
881
+ elapsed: Date.now() - startTime,
882
+ });
883
+ return {
884
+ success: false,
885
+ hops: hopInfos,
886
+ failedAt: hop.hopIndex,
887
+ };
888
+ }
889
+ }
890
+ else {
891
+ // Multiple outbound hops: use V2 API which returns outboundTx[]
892
+ const allOutboundDetails = yield callbacks.waitForAllOutboundTxsFn(response.hash, outboundHops, {
893
+ initialWaitMs: Math.min(60000, timeout - (Date.now() - startTime)),
894
+ pollingIntervalMs,
895
+ timeout: timeout - (Date.now() - startTime),
896
+ progressHook: (event) => {
897
+ cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
898
+ hopIndex: event.hopIndex,
899
+ route: event.route,
900
+ chain: event.chain,
901
+ status: event.status,
902
+ txHash: event.txHash,
903
+ elapsed: Date.now() - startTime,
904
+ });
905
+ },
906
+ });
907
+ if (!allOutboundDetails.success) {
908
+ return {
909
+ success: false,
910
+ hops: hopInfos,
911
+ failedAt: allOutboundDetails.failedAt,
912
+ };
913
+ }
914
+ }
915
+ }
916
+ // 3. Route 3 (CEA_TO_PUSH) tracking - mark as submitted
917
+ const inboundHops = hopInfos.filter((h) => h.route === 'CEA_TO_PUSH');
918
+ for (const inboundHop of inboundHops) {
919
+ inboundHop.status = 'submitted';
920
+ cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
921
+ hopIndex: inboundHop.hopIndex,
922
+ route: inboundHop.route,
923
+ chain: inboundHop.executionChain,
924
+ status: 'waiting',
925
+ elapsed: Date.now() - startTime,
926
+ });
927
+ }
928
+ return { success: true, hops: hopInfos };
929
+ }
930
+ catch (err) {
931
+ const failedIdx = hopInfos.findIndex((h) => h.status !== 'confirmed');
932
+ return {
933
+ success: false,
934
+ hops: hopInfos,
935
+ failedAt: failedIdx >= 0 ? failedIdx : 0,
936
+ };
937
+ }
938
+ }),
939
+ wait: (opts) => tslib_1.__awaiter(this, void 0, void 0, function* () { return cascadeResponse.waitForAll(opts); }),
940
+ };
941
+ return cascadeResponse;
942
+ }),
943
+ };
944
+ }
945
+ //# sourceMappingURL=cascade.js.map