@pushchain/core 5.1.16 → 5.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/lib/constants/index.d.ts +1 -1
- package/src/lib/constants/index.js +2 -1
- package/src/lib/constants/selectors.d.ts +8 -0
- package/src/lib/constants/selectors.js +9 -1
- package/src/lib/formatters.d.ts +1 -0
- package/src/lib/formatters.js +32 -0
- package/src/lib/orchestrator/internals/cascade.d.ts +8 -1
- package/src/lib/orchestrator/internals/cascade.js +486 -143
- package/src/lib/orchestrator/internals/error-decoder.d.ts +2 -6
- package/src/lib/orchestrator/internals/error-decoder.js +10 -10
- package/src/lib/orchestrator/internals/errors.d.ts +1 -1
- package/src/lib/orchestrator/internals/errors.js +7 -4
- package/src/lib/orchestrator/internals/execute-standard.js +7 -6
- package/src/lib/orchestrator/internals/gas-calculator.js +78 -11
- package/src/lib/orchestrator/internals/helpers.d.ts +2 -1
- package/src/lib/orchestrator/internals/helpers.js +13 -11
- package/src/lib/orchestrator/internals/outbound-sync.d.ts +1 -0
- package/src/lib/orchestrator/internals/outbound-sync.js +93 -37
- package/src/lib/orchestrator/internals/preflight.d.ts +9 -23
- package/src/lib/orchestrator/internals/preflight.js +22 -48
- package/src/lib/orchestrator/internals/response-builder.js +23 -7
- package/src/lib/orchestrator/internals/route-handlers.js +34 -49
- package/src/lib/orchestrator/internals/tx-transformer.js +2 -1
- package/src/lib/orchestrator/orchestrator.d.ts +7 -1
- package/src/lib/orchestrator/orchestrator.js +40 -2
- package/src/lib/orchestrator/orchestrator.types.d.ts +4 -14
- package/src/lib/progress-hook/progress-hook.js +66 -16
- package/src/lib/progress-hook/progress-hook.types.d.ts +4 -1
- package/src/lib/progress-hook/progress-hook.types.js +8 -0
- package/src/lib/push-chain/push-chain.d.ts +11 -1
- package/src/lib/push-chain/push-chain.js +2 -2
- package/src/lib/universal-tx-detector/cascade.js +14 -4
- package/src/lib/universal-tx-detector/child-inbounds.js +9 -1
- package/src/lib/universal-tx-detector/detector.js +9 -3
- package/src/lib/universal-tx-detector/normalize.d.ts +42 -0
- package/src/lib/universal-tx-detector/normalize.js +127 -0
- package/src/lib/utils/external-tx-hash.d.ts +37 -0
- package/src/lib/utils/external-tx-hash.js +62 -0
|
@@ -11,6 +11,7 @@ exports.prepareTransaction = prepareTransaction;
|
|
|
11
11
|
exports.buildHopDescriptor = buildHopDescriptor;
|
|
12
12
|
exports.classifyIntoSegments = classifyIntoSegments;
|
|
13
13
|
exports.getSegmentType = getSegmentType;
|
|
14
|
+
exports.composeCascadeDetailed = composeCascadeDetailed;
|
|
14
15
|
exports.composeCascade = composeCascade;
|
|
15
16
|
exports.createCascadedBuilder = createCascadedBuilder;
|
|
16
17
|
const tslib_1 = require("tslib");
|
|
@@ -38,6 +39,7 @@ const preflight_1 = require("./preflight");
|
|
|
38
39
|
const svm_rent_1 = require("./svm-rent");
|
|
39
40
|
const errors_1 = require("./errors");
|
|
40
41
|
const context_2 = require("./context");
|
|
42
|
+
const inbound_tracker_1 = require("./inbound-tracker");
|
|
41
43
|
// ============================================================================
|
|
42
44
|
// dispatchCascadeProgressEvent
|
|
43
45
|
// ============================================================================
|
|
@@ -120,9 +122,16 @@ function prepareTransaction(ctx, params, callbacks) {
|
|
|
120
122
|
// ============================================================================
|
|
121
123
|
function buildHopDescriptor(ctx, params, route, ueaAddress) {
|
|
122
124
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
123
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
124
|
-
// Pass 0 when user omits gasLimit
|
|
125
|
-
|
|
125
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
126
|
+
// Pass 0 when user omits gasLimit so the contract uses the per-chain base,
|
|
127
|
+
// except EVM Route 3 where a CEA deploy + sendUniversalTxToUEA needs more
|
|
128
|
+
// than the generic 500k source-chain base.
|
|
129
|
+
const defaultGasLimit = route === route_detector_1.TransactionRoute.CEA_TO_PUSH &&
|
|
130
|
+
((_a = params.from) === null || _a === void 0 ? void 0 : _a.chain) &&
|
|
131
|
+
!(0, payload_builders_1.isSvmChain)(params.from.chain)
|
|
132
|
+
? selectors_1.DEFAULT_CEA_TO_PUSH_GAS_LIMIT
|
|
133
|
+
: BigInt(0);
|
|
134
|
+
const gasLimit = (_b = params.gasLimit) !== null && _b !== void 0 ? _b : defaultGasLimit;
|
|
126
135
|
const routeStr = route;
|
|
127
136
|
const baseDescriptor = {
|
|
128
137
|
params,
|
|
@@ -134,6 +143,10 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
|
|
|
134
143
|
switch (route) {
|
|
135
144
|
case route_detector_1.TransactionRoute.UOA_TO_PUSH: {
|
|
136
145
|
// Route 1: Build Push Chain multicalls
|
|
146
|
+
const seedAmount = (_c = params.value) !== null && _c !== void 0 ? _c : BigInt(0);
|
|
147
|
+
if (isValueOnlyNativeSeedToOwnUea(params, ueaAddress)) {
|
|
148
|
+
return Object.assign(Object.assign({}, baseDescriptor), { pushMulticalls: [], nativeSeedOnly: true, nativeSeedAmount: seedAmount });
|
|
149
|
+
}
|
|
137
150
|
const executeParams = (0, helpers_1.toExecuteParams)(params);
|
|
138
151
|
const pushMulticalls = (0, payload_builders_1.buildExecuteMulticall)({
|
|
139
152
|
execute: executeParams,
|
|
@@ -155,7 +168,7 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
|
|
|
155
168
|
});
|
|
156
169
|
let prc20Token = selectors_1.ZERO_ADDRESS;
|
|
157
170
|
let burnAmount = BigInt(0);
|
|
158
|
-
if ((
|
|
171
|
+
if ((_d = params.funds) === null || _d === void 0 ? void 0 : _d.amount) {
|
|
159
172
|
const token = params.funds.token;
|
|
160
173
|
if (token) {
|
|
161
174
|
prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token).address;
|
|
@@ -181,9 +194,9 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
|
|
|
181
194
|
// Conditional CEA-ATA rent bump (SPL-only). Mirrors single-route
|
|
182
195
|
// executeUoaToCeaSvm path. See svm-rent.ts for rationale.
|
|
183
196
|
const splMintBase58 = burnAmount > BigInt(0)
|
|
184
|
-
? (
|
|
197
|
+
? (_f = (_e = params.funds) === null || _e === void 0 ? void 0 : _e.token) === null || _f === void 0 ? void 0 : _f.address
|
|
185
198
|
: undefined;
|
|
186
|
-
|
|
199
|
+
let effectiveGasLimit = yield (0, svm_rent_1.maybeBumpForCeaAtaRent)({
|
|
187
200
|
ctx,
|
|
188
201
|
ueaAddress,
|
|
189
202
|
targetChain,
|
|
@@ -196,16 +209,19 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
|
|
|
196
209
|
gasToken = result.gasToken;
|
|
197
210
|
gasFee = result.gasFee;
|
|
198
211
|
sizing = result.sizing;
|
|
212
|
+
if (!params.gasLimit && result.gasPrice > BigInt(0)) {
|
|
213
|
+
effectiveGasLimit = result.gasFee / result.gasPrice;
|
|
214
|
+
(0, context_1.printLog)(ctx, `buildHopDescriptor — SVM derived effectiveGasLimit: ${effectiveGasLimit} (gasFee=${result.gasFee} / gasPrice=${result.gasPrice})`);
|
|
215
|
+
}
|
|
199
216
|
}
|
|
200
217
|
return Object.assign(Object.assign({}, baseDescriptor), { targetChain, isSvmTarget: true, svmPayload,
|
|
201
218
|
prc20Token,
|
|
202
219
|
burnAmount,
|
|
203
220
|
gasToken,
|
|
204
|
-
gasFee,
|
|
205
|
-
sizing });
|
|
221
|
+
gasFee, gasLimit: effectiveGasLimit, sizing });
|
|
206
222
|
}
|
|
207
223
|
// EVM path: Resolve CEA address + build CEA multicalls
|
|
208
|
-
const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, targetChain, (
|
|
224
|
+
const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, targetChain, (_g = ctx.rpcUrls[targetChain]) === null || _g === void 0 ? void 0 : _g[0]);
|
|
209
225
|
// Migration path: raw MIGRATION_SELECTOR payload, no multicall wrapping
|
|
210
226
|
if (params.migration) {
|
|
211
227
|
const prc20Token = (0, helpers_1.getNativePRC20ForChain)(targetChain, ctx.pushNetwork);
|
|
@@ -235,7 +251,7 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
|
|
|
235
251
|
// When ERC-20 funds are provided with a single payload, auto-prepend a
|
|
236
252
|
// transfer() call so the tokens minted to the CEA are forwarded to the
|
|
237
253
|
// target address. This mirrors the Route 1 behavior in buildExecuteMulticall.
|
|
238
|
-
if ((
|
|
254
|
+
if ((_h = params.funds) === null || _h === void 0 ? void 0 : _h.amount) {
|
|
239
255
|
const token = params.funds.token;
|
|
240
256
|
if (token && token.mechanism !== 'native') {
|
|
241
257
|
const erc20Transfer = (0, viem_1.encodeFunctionData)({
|
|
@@ -255,7 +271,7 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
|
|
|
255
271
|
// native value to the CEA, and the multicall forwards it to the target.
|
|
256
272
|
ceaMulticalls.push({
|
|
257
273
|
to: target.address,
|
|
258
|
-
value: (
|
|
274
|
+
value: (_j = params.value) !== null && _j !== void 0 ? _j : BigInt(0),
|
|
259
275
|
data: params.data,
|
|
260
276
|
});
|
|
261
277
|
}
|
|
@@ -271,7 +287,7 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
|
|
|
271
287
|
});
|
|
272
288
|
}
|
|
273
289
|
}
|
|
274
|
-
else if ((
|
|
290
|
+
else if ((_k = params.funds) === null || _k === void 0 ? void 0 : _k.amount) {
|
|
275
291
|
// Funds-only transfer (no data, no value). Without this branch the
|
|
276
292
|
// CEA multicall is empty and relayed funds stay in the CEA instead
|
|
277
293
|
// of reaching the caller's recipient.
|
|
@@ -303,7 +319,7 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
|
|
|
303
319
|
// Determine PRC-20 token and burn amount
|
|
304
320
|
let prc20Token = selectors_1.ZERO_ADDRESS;
|
|
305
321
|
let burnAmount = BigInt(0);
|
|
306
|
-
if ((
|
|
322
|
+
if ((_l = params.funds) === null || _l === void 0 ? void 0 : _l.amount) {
|
|
307
323
|
const token = params.funds.token;
|
|
308
324
|
if (token) {
|
|
309
325
|
prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token).address;
|
|
@@ -345,6 +361,10 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
|
|
|
345
361
|
case route_detector_1.TransactionRoute.CEA_TO_PUSH: {
|
|
346
362
|
// Route 3: Build CEA multicalls for sendUniversalTxFromCEA
|
|
347
363
|
const sourceChain = params.from.chain;
|
|
364
|
+
const seedAmount = (_m = params.value) !== null && _m !== void 0 ? _m : BigInt(0);
|
|
365
|
+
if (isValueOnlyNativeSeedToOwnUea(params, ueaAddress)) {
|
|
366
|
+
return Object.assign(Object.assign({}, baseDescriptor), { sourceChain, burnAmount: BigInt(0), gasToken: selectors_1.ZERO_ADDRESS, gasFee: BigInt(0), nativeSeedOnly: true, nativeSeedAmount: seedAmount });
|
|
367
|
+
}
|
|
348
368
|
// SVM chains use PDA-based CEA, not factory-deployed CEA
|
|
349
369
|
if ((0, payload_builders_1.isSvmChain)(sourceChain)) {
|
|
350
370
|
const lockerContract = chain_1.CHAIN_INFO[sourceChain].lockerContract;
|
|
@@ -361,7 +381,7 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
|
|
|
361
381
|
Buffer.from(ceaPda.toBytes()).toString('hex'));
|
|
362
382
|
let amount = BigInt(0);
|
|
363
383
|
let prc20Token;
|
|
364
|
-
if (((
|
|
384
|
+
if (((_o = params.funds) === null || _o === void 0 ? void 0 : _o.amount) && params.funds.amount > BigInt(0)) {
|
|
365
385
|
amount = params.funds.amount;
|
|
366
386
|
const token = params.funds.token;
|
|
367
387
|
if (token && token.address) {
|
|
@@ -388,18 +408,21 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
|
|
|
388
408
|
gasFee = result.gasFee;
|
|
389
409
|
sizing = result.sizing;
|
|
390
410
|
}
|
|
391
|
-
return Object.assign(Object.assign({}, baseDescriptor), { sourceChain, ceaAddress: ceaPdaHex, isSvmTarget: true, prc20Token,
|
|
411
|
+
return Object.assign(Object.assign({}, baseDescriptor), { sourceChain, ceaAddress: ceaPdaHex, isSvmTarget: true, prc20Token,
|
|
412
|
+
// Route 3 is payload-only on Push: the drain amount lives inside the
|
|
413
|
+
// SVM payload. Burning here would require the UEA to pre-hold pSOL.
|
|
414
|
+
burnAmount: BigInt(0), gasToken,
|
|
392
415
|
gasFee,
|
|
393
416
|
sizing });
|
|
394
417
|
}
|
|
395
|
-
const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, sourceChain, (
|
|
418
|
+
const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, sourceChain, (_p = ctx.rpcUrls[sourceChain]) === null || _p === void 0 ? void 0 : _p[0]);
|
|
396
419
|
// CEA auto-deploys on-chain: Vault.finalizeUniversalTx calls CEAFactory.deployCEA()
|
|
397
420
|
// if CEA doesn't exist yet. No SDK-side blocking needed.
|
|
398
421
|
// Determine token/amount for the inbound
|
|
399
422
|
let tokenAddress = selectors_1.ZERO_ADDRESS;
|
|
400
423
|
let amount = BigInt(0);
|
|
401
424
|
let nativeValue = BigInt(0);
|
|
402
|
-
if ((
|
|
425
|
+
if ((_q = params.funds) === null || _q === void 0 ? void 0 : _q.amount) {
|
|
403
426
|
const token = params.funds.token;
|
|
404
427
|
if (token) {
|
|
405
428
|
if (token.mechanism === 'native') {
|
|
@@ -429,7 +452,11 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
|
|
|
429
452
|
}
|
|
430
453
|
return Object.assign(Object.assign({}, baseDescriptor), { sourceChain,
|
|
431
454
|
ceaAddress,
|
|
432
|
-
prc20Token,
|
|
455
|
+
prc20Token,
|
|
456
|
+
// Route 3 is payload-only on Push: the CEA-side sendUniversalTxToUEA
|
|
457
|
+
// carries `amount`. Burning here would require the UEA to pre-hold the
|
|
458
|
+
// token it is trying to receive from the external CEA.
|
|
459
|
+
burnAmount: BigInt(0), gasToken,
|
|
433
460
|
gasFee,
|
|
434
461
|
sizing });
|
|
435
462
|
}
|
|
@@ -463,7 +490,9 @@ function classifyIntoSegments(hops) {
|
|
|
463
490
|
? currentSegment.targetChain === hop.targetChain && !hop.isSvmTarget
|
|
464
491
|
: segType === 'INBOUND_FROM_CEA'
|
|
465
492
|
? currentSegment.sourceChain === hop.sourceChain &&
|
|
466
|
-
!(0, payload_builders_1.isSvmChain)(hop.sourceChain)
|
|
493
|
+
!(0, payload_builders_1.isSvmChain)(hop.sourceChain) &&
|
|
494
|
+
isNativeSeedOnlyHop(currentSegment.hops[0]) ===
|
|
495
|
+
isNativeSeedOnlyHop(hop)
|
|
467
496
|
: segType === 'PUSH_EXECUTION');
|
|
468
497
|
if (canMerge && currentSegment) {
|
|
469
498
|
// Merge into current segment
|
|
@@ -559,46 +588,128 @@ function getSegmentType(route) {
|
|
|
559
588
|
return 'PUSH_EXECUTION';
|
|
560
589
|
}
|
|
561
590
|
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
function
|
|
591
|
+
function sumMulticallNativeValue(multicalls) {
|
|
592
|
+
return (multicalls !== null && multicalls !== void 0 ? multicalls : []).reduce((sum, mc) => { var _a; return sum + ((_a = mc.value) !== null && _a !== void 0 ? _a : BigInt(0)); }, BigInt(0));
|
|
593
|
+
}
|
|
594
|
+
function isEmptyPayloadData(data) {
|
|
595
|
+
return (data === undefined ||
|
|
596
|
+
data === '0x' ||
|
|
597
|
+
(Array.isArray(data) && data.length === 0));
|
|
598
|
+
}
|
|
599
|
+
function isValueOnlyNativeSeedToOwnUea(params, ueaAddress) {
|
|
600
|
+
var _a, _b;
|
|
601
|
+
return (((_a = params.value) !== null && _a !== void 0 ? _a : BigInt(0)) > BigInt(0) &&
|
|
602
|
+
!((_b = params.funds) === null || _b === void 0 ? void 0 : _b.amount) &&
|
|
603
|
+
isEmptyPayloadData(params.data) &&
|
|
604
|
+
typeof params.to === 'string' &&
|
|
605
|
+
params.to.toLowerCase() === ueaAddress.toLowerCase());
|
|
606
|
+
}
|
|
607
|
+
function isNativeSeedOnlyHop(hop) {
|
|
608
|
+
return (hop === null || hop === void 0 ? void 0 : hop.nativeSeedOnly) === true;
|
|
609
|
+
}
|
|
610
|
+
function isNativeSeedOnlySegment(segment) {
|
|
611
|
+
return (segment.type === 'INBOUND_FROM_CEA' &&
|
|
612
|
+
segment.hops.length > 0 &&
|
|
613
|
+
segment.hops.every((hop) => isNativeSeedOnlyHop(hop)));
|
|
614
|
+
}
|
|
615
|
+
function segmentNeedsOutboundGas(segment) {
|
|
616
|
+
return segment.type !== 'PUSH_EXECUTION' && !isNativeSeedOnlySegment(segment);
|
|
617
|
+
}
|
|
618
|
+
function sumNativeSeedAmount(hops) {
|
|
619
|
+
return hops.reduce((sum, hop) => { var _a; return sum + ((_a = hop.nativeSeedAmount) !== null && _a !== void 0 ? _a : BigInt(0)); }, BigInt(0));
|
|
620
|
+
}
|
|
621
|
+
function getCascadeNativeSeed(segments) {
|
|
622
|
+
var _a, _b, _c;
|
|
623
|
+
let total = BigInt(0);
|
|
624
|
+
for (const segment of segments) {
|
|
625
|
+
for (const hop of segment.hops) {
|
|
626
|
+
const value = (_a = hop.params.value) !== null && _a !== void 0 ? _a : BigInt(0);
|
|
627
|
+
if (isNativeSeedOnlyHop(hop)) {
|
|
628
|
+
total += (_b = hop.nativeSeedAmount) !== null && _b !== void 0 ? _b : value;
|
|
629
|
+
continue;
|
|
630
|
+
}
|
|
631
|
+
// In a CEA->Push hop that also supplies `funds`, `value` is native PC
|
|
632
|
+
// forwarded inside the inbound UEA payload. That value is available to
|
|
633
|
+
// later Push-side cascade calls and must be counted for sizing fresh UEAs.
|
|
634
|
+
if (segment.type === 'INBOUND_FROM_CEA' &&
|
|
635
|
+
value > BigInt(0) &&
|
|
636
|
+
((_c = hop.params.funds) === null || _c === void 0 ? void 0 : _c.amount)) {
|
|
637
|
+
total += value;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
return total;
|
|
642
|
+
}
|
|
643
|
+
function getInboundFundingForBurnToken(segments, beforeSegmentIndex, burnToken, pushNetwork) {
|
|
644
|
+
var _a;
|
|
645
|
+
let total = BigInt(0);
|
|
646
|
+
const normalizedBurnToken = burnToken.toLowerCase();
|
|
647
|
+
for (const segment of segments.slice(0, beforeSegmentIndex)) {
|
|
648
|
+
if (segment.type !== 'INBOUND_FROM_CEA')
|
|
649
|
+
continue;
|
|
650
|
+
for (const hop of segment.hops) {
|
|
651
|
+
const params = hop.params;
|
|
652
|
+
let fundedToken;
|
|
653
|
+
let fundedAmount = BigInt(0);
|
|
654
|
+
if ((_a = params.funds) === null || _a === void 0 ? void 0 : _a.amount) {
|
|
655
|
+
const token = params.funds.token;
|
|
656
|
+
if (token) {
|
|
657
|
+
fundedToken = push_chain_1.PushChain.utils.tokens.getPRC20Address(token).address;
|
|
658
|
+
fundedAmount = params.funds.amount;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
else if (params.value &&
|
|
662
|
+
params.value > BigInt(0) &&
|
|
663
|
+
hop.sourceChain &&
|
|
664
|
+
!isNativeSeedOnlyHop(hop)) {
|
|
665
|
+
fundedToken = (0, helpers_1.getNativePRC20ForChain)(hop.sourceChain, pushNetwork);
|
|
666
|
+
fundedAmount = params.value;
|
|
667
|
+
}
|
|
668
|
+
if ((fundedToken === null || fundedToken === void 0 ? void 0 : fundedToken.toLowerCase()) === normalizedBurnToken) {
|
|
669
|
+
total += fundedAmount;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
return total;
|
|
674
|
+
}
|
|
675
|
+
function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
566
676
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
567
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o
|
|
677
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
568
678
|
let accumulatedPushMulticalls = [];
|
|
679
|
+
let requiredNativeValue = BigInt(0);
|
|
569
680
|
const gatewayPcAddress = (0, helpers_1.getUniversalGatewayPCAddress)();
|
|
681
|
+
const currentUeaBalance = ueaBalance !== null && ueaBalance !== void 0 ? ueaBalance : BigInt(0);
|
|
682
|
+
const cascadeNativeSeed = getCascadeNativeSeed(segments);
|
|
683
|
+
const effectiveUeaBalance = currentUeaBalance + cascadeNativeSeed;
|
|
684
|
+
if (cascadeNativeSeed > BigInt(0)) {
|
|
685
|
+
(0, context_1.printLog)(ctx, `composeCascade — counting cascade native seed ${cascadeNativeSeed.toString()} for cascade sizing (current UEA balance ${currentUeaBalance.toString()})`);
|
|
686
|
+
}
|
|
570
687
|
// Compute per-outbound nativeValueForGas from UEA balance
|
|
571
688
|
// Each outbound segment needs native value for the gas swap on the destination chain.
|
|
572
689
|
// The contract refunds excess, so over-allocating is safe.
|
|
573
|
-
const numOutbounds = segments.filter(
|
|
690
|
+
const numOutbounds = segments.filter(segmentNeedsOutboundGas).length;
|
|
574
691
|
const CASCADE_GAS_RESERVE = BigInt(3e18); // 3 PC reserve for gas costs
|
|
575
692
|
// Cascade pre-flight: catches the zero-balance silent-segment-zero failure
|
|
576
693
|
// mode. When `ueaBalance <= CASCADE_GAS_RESERVE`, `perOutboundNativeValue`
|
|
577
694
|
// is left undefined and downstream segments silently allocate value=0,
|
|
578
695
|
// producing under-funded swaps that revert inside Uniswap. Throw early
|
|
579
696
|
// with a structured error so the caller knows which path / hop failed.
|
|
580
|
-
// Honor the per-call opt-out from the first non-PUSH segment (segments share
|
|
581
|
-
// the same UniversalExecuteParams.options.allowUnderfundedSwap shape).
|
|
582
|
-
const firstOutboundSegment = segments.find((s) => s.type !== 'PUSH_EXECUTION');
|
|
583
|
-
const cascadeAllowUnderfunded = ((_d = (_c = (_b = (_a = firstOutboundSegment === null || firstOutboundSegment === void 0 ? void 0 : firstOutboundSegment.hops) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.params) === null || _c === void 0 ? void 0 : _c.options) === null || _d === void 0 ? void 0 : _d.allowUnderfundedSwap) ===
|
|
584
|
-
true;
|
|
585
697
|
if (numOutbounds > 0) {
|
|
586
698
|
(0, preflight_1.runPreflight)({
|
|
587
699
|
ctx,
|
|
588
700
|
ueaAddress,
|
|
589
|
-
ueaBalance:
|
|
701
|
+
ueaBalance: effectiveUeaBalance,
|
|
590
702
|
// Required = at least one wei of allocatable PC after reserve. The
|
|
591
703
|
// per-segment min-swap check below catches finer-grained shortfalls.
|
|
592
704
|
requiredValue: BigInt(1),
|
|
593
705
|
gasReserve: CASCADE_GAS_RESERVE,
|
|
594
706
|
pathTag: 'CASCADE',
|
|
595
|
-
allowUnderfundedSwap: cascadeAllowUnderfunded,
|
|
596
707
|
});
|
|
597
708
|
}
|
|
598
709
|
let perOutboundNativeValue;
|
|
599
|
-
if (
|
|
710
|
+
if (numOutbounds > 0 && effectiveUeaBalance > CASCADE_GAS_RESERVE) {
|
|
600
711
|
perOutboundNativeValue =
|
|
601
|
-
(
|
|
712
|
+
(effectiveUeaBalance - CASCADE_GAS_RESERVE) / BigInt(numOutbounds);
|
|
602
713
|
}
|
|
603
714
|
// Pool-price-based native-value override for SVM outbounds.
|
|
604
715
|
// The flat split above works for EVM gas tokens (pBNB/pETH — cheap per unit),
|
|
@@ -614,7 +725,7 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
|
614
725
|
s.gasFee > BigInt(0);
|
|
615
726
|
});
|
|
616
727
|
const svmNativeValueBySegment = new Map();
|
|
617
|
-
if (svmSegments.length > 0 &&
|
|
728
|
+
if (svmSegments.length > 0 && effectiveUeaBalance > CASCADE_GAS_RESERVE) {
|
|
618
729
|
const universalCoreAddress = yield ctx.pushClient.readContract({
|
|
619
730
|
address: gatewayPcAddress,
|
|
620
731
|
abi: abi_1.UNIVERSAL_GATEWAY_PC,
|
|
@@ -629,11 +740,11 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
|
629
740
|
? perOutboundNativeValue * BigInt(numEvmOutbounds)
|
|
630
741
|
: BigInt(0);
|
|
631
742
|
const outerSafety = BigInt(1e18); // 1 PC for outer-tx gas
|
|
632
|
-
const svmTotal =
|
|
633
|
-
?
|
|
743
|
+
const svmTotal = effectiveUeaBalance > evmReservation + outerSafety
|
|
744
|
+
? effectiveUeaBalance - evmReservation - outerSafety
|
|
634
745
|
: BigInt(0);
|
|
635
746
|
const perSvmCap = svmTotal / BigInt(svmSegments.length);
|
|
636
|
-
const UNCAPPED_BALANCE = BigInt(
|
|
747
|
+
const UNCAPPED_BALANCE = BigInt('1000000000000000000000000000000'); // effectively disable estimator's internal cap
|
|
637
748
|
for (const seg of svmSegments) {
|
|
638
749
|
// Cascade-level segment index (position in `segments`, not in `svmSegments`)
|
|
639
750
|
// so the error's `segmentIndex` lines up with what the caller sees.
|
|
@@ -643,8 +754,13 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
|
643
754
|
(0, preflight_1.maybeFireSvmWarnThreshold)(ctx, estimated, seg.gasToken, 'CASCADE');
|
|
644
755
|
const flat = perOutboundNativeValue !== null && perOutboundNativeValue !== void 0 ? perOutboundNativeValue : BigInt(0);
|
|
645
756
|
let value = estimated > flat ? estimated : flat;
|
|
646
|
-
if (perSvmCap > BigInt(0) && value > perSvmCap)
|
|
647
|
-
|
|
757
|
+
if (perSvmCap > BigInt(0) && value > perSvmCap) {
|
|
758
|
+
const cappedValue = perSvmCap >= estimated ? perSvmCap : estimated;
|
|
759
|
+
if (cappedValue !== perSvmCap) {
|
|
760
|
+
(0, context_1.printLog)(ctx, `composeCascade — SVM outbound to ${(_a = seg.hops[0]) === null || _a === void 0 ? void 0 : _a.targetChain}: perSvmCap=${perSvmCap} is below live pool quote=${estimated}; using quote to avoid underfunded gas swap`);
|
|
761
|
+
}
|
|
762
|
+
value = cappedValue;
|
|
763
|
+
}
|
|
648
764
|
// Upward-allocation ceiling (mirrors R3 SVM Fix #4 in route-handlers.ts:1690).
|
|
649
765
|
// When UEA balance is huge, the flat split or perSvmCap can produce a value
|
|
650
766
|
// far above what the swap actually needs. Submitting that into a Uniswap
|
|
@@ -659,16 +775,16 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
|
659
775
|
? estimated * BigInt(5)
|
|
660
776
|
: UPWARD_BASE_CEILING_CASCADE_SVM;
|
|
661
777
|
if (value > upwardCeilingCascadeSvm) {
|
|
662
|
-
(0, context_1.printLog)(ctx, `composeCascade — SVM outbound to ${(
|
|
778
|
+
(0, context_1.printLog)(ctx, `composeCascade — SVM outbound to ${(_b = seg.hops[0]) === null || _b === void 0 ? void 0 : _b.targetChain}: capping value from ${value} to upwardCeiling=${upwardCeilingCascadeSvm} (pool quote=${estimated})`);
|
|
663
779
|
value = upwardCeilingCascadeSvm;
|
|
664
780
|
}
|
|
665
|
-
(0, context_1.printLog)(ctx, `composeCascade — SVM outbound to ${(
|
|
781
|
+
(0, context_1.printLog)(ctx, `composeCascade — SVM outbound to ${(_c = seg.hops[0]) === null || _c === void 0 ? void 0 : _c.targetChain}: gasFee=${seg.gasFee}, estimatedWpc=${estimated}, flat=${flat}, perSvmCap=${perSvmCap}, upwardCeiling=${upwardCeilingCascadeSvm}, chosen=${value}`);
|
|
666
782
|
// Per-segment minimum-viable-swap check: if the chosen `value` is below
|
|
667
783
|
// the live pool quote (`estimated`), the cap squeezed the budget below
|
|
668
784
|
// what the swap actually needs and the segment will revert inside
|
|
669
785
|
// Uniswap with STF. Throw cleanly with `segmentIndex` so the caller
|
|
670
786
|
// knows which hop's pool is misbehaving.
|
|
671
|
-
if (estimated > BigInt(0) && value < estimated
|
|
787
|
+
if (estimated > BigInt(0) && value < estimated) {
|
|
672
788
|
const shortfall = estimated - value;
|
|
673
789
|
(0, context_2.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_003_04, estimated, value, shortfall, ueaAddress, 'CASCADE', { kind: 'NATIVE', segmentIndex: cascadeIdx });
|
|
674
790
|
throw new errors_1.InsufficientUEABalanceError({
|
|
@@ -689,6 +805,11 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
|
689
805
|
switch (segment.type) {
|
|
690
806
|
case 'PUSH_EXECUTION': {
|
|
691
807
|
// Prepend Push Chain multicalls to accumulated
|
|
808
|
+
const seedAmount = sumNativeSeedAmount(segment.hops);
|
|
809
|
+
if (seedAmount > requiredNativeValue) {
|
|
810
|
+
requiredNativeValue = seedAmount;
|
|
811
|
+
}
|
|
812
|
+
requiredNativeValue += sumMulticallNativeValue(segment.mergedPushMulticalls);
|
|
692
813
|
accumulatedPushMulticalls = [
|
|
693
814
|
...(segment.mergedPushMulticalls || []),
|
|
694
815
|
...accumulatedPushMulticalls,
|
|
@@ -703,7 +824,7 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
|
703
824
|
const isMigration = (firstHop === null || firstHop === void 0 ? void 0 : firstHop.isMigration) === true;
|
|
704
825
|
if (isSvmSegment) {
|
|
705
826
|
// SVM: use the pre-built SVM payload from the hop descriptor
|
|
706
|
-
outboundPayload = (
|
|
827
|
+
outboundPayload = (_d = firstHop.svmPayload) !== null && _d !== void 0 ? _d : '0x';
|
|
707
828
|
// For SVM, to.address IS the target (program for execute, recipient for withdraw)
|
|
708
829
|
const svmTarget = firstHop.params.to;
|
|
709
830
|
targetForOutbound = svmTarget.address;
|
|
@@ -736,12 +857,24 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
|
736
857
|
});
|
|
737
858
|
const sufficient = onHand >= segBurnAmount;
|
|
738
859
|
(0, context_2.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_003_03, segBurnAmount, onHand, sufficient, ueaAddress, 'CASCADE', { kind: 'PRC20', burnToken: segBurnToken, segmentIndex: segIdxLoop });
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
(
|
|
860
|
+
const priorPushExecutionMayFundBurn = segments
|
|
861
|
+
.slice(0, segIdxLoop)
|
|
862
|
+
.some((s) => s.type === 'PUSH_EXECUTION');
|
|
863
|
+
const priorInboundFundingForBurn = getInboundFundingForBurnToken(segments, segIdxLoop, segBurnToken, ctx.pushNetwork);
|
|
864
|
+
const priorInboundMayFundBurn = onHand + priorInboundFundingForBurn >= segBurnAmount;
|
|
865
|
+
if (!sufficient &&
|
|
866
|
+
!priorPushExecutionMayFundBurn &&
|
|
867
|
+
!priorInboundMayFundBurn) {
|
|
868
|
+
const projectedOnHand = onHand + priorInboundFundingForBurn;
|
|
869
|
+
const shortfall = segBurnAmount - projectedOnHand;
|
|
870
|
+
(0, context_2.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_003_04, segBurnAmount, projectedOnHand, shortfall, ueaAddress, 'CASCADE', {
|
|
871
|
+
kind: 'PRC20',
|
|
872
|
+
burnToken: segBurnToken,
|
|
873
|
+
segmentIndex: segIdxLoop,
|
|
874
|
+
});
|
|
742
875
|
throw new errors_1.InsufficientUEABalanceError({
|
|
743
876
|
required: segBurnAmount,
|
|
744
|
-
available:
|
|
877
|
+
available: projectedOnHand,
|
|
745
878
|
shortfall,
|
|
746
879
|
ueaAddress,
|
|
747
880
|
pathTag: 'CASCADE',
|
|
@@ -750,9 +883,15 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
|
750
883
|
segmentIndex: segIdxLoop,
|
|
751
884
|
});
|
|
752
885
|
}
|
|
886
|
+
else if (!sufficient && priorPushExecutionMayFundBurn) {
|
|
887
|
+
(0, context_1.printLog)(ctx, `composeCascade — skipping PRC-20 preflight failure for segment ${segIdxLoop}; earlier Push execution may fund ${segBurnToken} before this outbound runs`);
|
|
888
|
+
}
|
|
889
|
+
else if (!sufficient && priorInboundMayFundBurn) {
|
|
890
|
+
(0, context_1.printLog)(ctx, `composeCascade — skipping PRC-20 preflight failure for segment ${segIdxLoop}; earlier inbound funds ${priorInboundFundingForBurn.toString()} units of ${segBurnToken} before this outbound runs`);
|
|
891
|
+
}
|
|
753
892
|
}
|
|
754
893
|
// Build outbound request
|
|
755
|
-
const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetForOutbound, segment.prc20Token || selectors_1.ZERO_ADDRESS, segment.totalBurnAmount || BigInt(0), (
|
|
894
|
+
const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetForOutbound, segment.prc20Token || selectors_1.ZERO_ADDRESS, segment.totalBurnAmount || BigInt(0), (_e = segment.gasLimit) !== null && _e !== void 0 ? _e : BigInt(0), outboundPayload, ueaAddress);
|
|
756
895
|
// Build approval + outbound multicalls
|
|
757
896
|
const segGasFee = segment.gasFee || BigInt(0);
|
|
758
897
|
const svmOverride = svmNativeValueBySegment.get(segment);
|
|
@@ -762,13 +901,14 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
|
762
901
|
// across outbound segments. The contract refunds excess via
|
|
763
902
|
// swapAndBurnGas, so overshooting is safe; undershooting causes STF.
|
|
764
903
|
let evmFallbackValue;
|
|
765
|
-
if (
|
|
766
|
-
evmFallbackValue =
|
|
904
|
+
if (effectiveUeaBalance > BigInt(0)) {
|
|
905
|
+
evmFallbackValue =
|
|
906
|
+
effectiveUeaBalance / BigInt(Math.max(numOutbounds, 1));
|
|
767
907
|
}
|
|
768
908
|
else {
|
|
769
909
|
evmFallbackValue = segGasFee * BigInt(1000000);
|
|
770
910
|
}
|
|
771
|
-
let nativeValueForGas = (
|
|
911
|
+
let nativeValueForGas = (_f = svmOverride !== null && svmOverride !== void 0 ? svmOverride : perOutboundNativeValue) !== null && _f !== void 0 ? _f : evmFallbackValue;
|
|
772
912
|
// SDK 5.2 Case C: when the segment's sizing is category C, compose
|
|
773
913
|
// R2/OUTBOUND_TO_CEA segments no longer apply Case A/B/C sizing
|
|
774
914
|
// (scoped to R1 + R3 only). segment.sizing may still be populated
|
|
@@ -784,6 +924,7 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
|
784
924
|
gatewayPcAddress,
|
|
785
925
|
outboundRequest: outboundReq,
|
|
786
926
|
});
|
|
927
|
+
requiredNativeValue += sumMulticallNativeValue(outboundMulticalls);
|
|
787
928
|
// Prepend to accumulated
|
|
788
929
|
accumulatedPushMulticalls = [
|
|
789
930
|
...outboundMulticalls,
|
|
@@ -794,13 +935,21 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
|
794
935
|
case 'INBOUND_FROM_CEA': {
|
|
795
936
|
// The accumulated multicalls = what runs on Push Chain AFTER inbound arrives.
|
|
796
937
|
// Wrap in UniversalPayload struct with correct UEA nonce for the relay.
|
|
938
|
+
if (isNativeSeedOnlySegment(segment)) {
|
|
939
|
+
const seedAmount = sumNativeSeedAmount(segment.hops);
|
|
940
|
+
if (seedAmount > requiredNativeValue) {
|
|
941
|
+
requiredNativeValue = seedAmount;
|
|
942
|
+
}
|
|
943
|
+
(0, context_1.printLog)(ctx, `composeCascade — value-only CEA_TO_PUSH seed ${seedAmount.toString()} folded into root Push execution; no source-chain outbound needed`);
|
|
944
|
+
break;
|
|
945
|
+
}
|
|
797
946
|
// Build push multicalls from EVERY merged hop's own data in original
|
|
798
947
|
// order (e.g. counter.increment() + value transfer). Multi-hop R3
|
|
799
948
|
// segments (same sourceChain) bundle all target ops into the single
|
|
800
949
|
// UEA multicall so no nested outbound is needed.
|
|
801
950
|
const mergedHopMulticalls = [];
|
|
802
951
|
for (const h of segment.hops) {
|
|
803
|
-
if (((
|
|
952
|
+
if (((_g = h === null || h === void 0 ? void 0 : h.params) === null || _g === void 0 ? void 0 : _g.data) || ((_h = h === null || h === void 0 ? void 0 : h.params) === null || _h === void 0 ? void 0 : _h.value)) {
|
|
804
953
|
const hopCalls = (0, payload_builders_1.buildExecuteMulticall)({
|
|
805
954
|
execute: {
|
|
806
955
|
to: h.params.to,
|
|
@@ -813,6 +962,7 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
|
813
962
|
}
|
|
814
963
|
}
|
|
815
964
|
if (mergedHopMulticalls.length > 0) {
|
|
965
|
+
requiredNativeValue += sumMulticallNativeValue(mergedHopMulticalls);
|
|
816
966
|
accumulatedPushMulticalls = [
|
|
817
967
|
...mergedHopMulticalls,
|
|
818
968
|
...accumulatedPushMulticalls,
|
|
@@ -824,7 +974,9 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
|
824
974
|
if (accumulatedPushMulticalls.length > 0) {
|
|
825
975
|
const multicallPayload = (0, payload_builder_1.buildMulticallPayloadData)(ctx, ueaAddress, accumulatedPushMulticalls);
|
|
826
976
|
// +1: the outbound tx consumes one nonce via execute()
|
|
827
|
-
intermediatePayload = (0, payload_builders_1.buildInboundUniversalPayload)(multicallPayload, {
|
|
977
|
+
intermediatePayload = (0, payload_builders_1.buildInboundUniversalPayload)(multicallPayload, {
|
|
978
|
+
nonce: (ueaNonce !== null && ueaNonce !== void 0 ? ueaNonce : BigInt(0)) + BigInt(1),
|
|
979
|
+
});
|
|
828
980
|
}
|
|
829
981
|
const hop = segment.hops[0];
|
|
830
982
|
const sourceChain = hop.sourceChain;
|
|
@@ -841,7 +993,7 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
|
841
993
|
let drainAmount = BigInt(0);
|
|
842
994
|
let tokenMintHex;
|
|
843
995
|
const params = hop.params;
|
|
844
|
-
if (((
|
|
996
|
+
if (((_j = params.funds) === null || _j === void 0 ? void 0 : _j.amount) && params.funds.amount > BigInt(0)) {
|
|
845
997
|
drainAmount = params.funds.amount;
|
|
846
998
|
const token = params.funds.token;
|
|
847
999
|
if (token && token.address) {
|
|
@@ -870,11 +1022,12 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
|
870
1022
|
});
|
|
871
1023
|
const svmBurnAmount = segment.totalBurnAmount || BigInt(0);
|
|
872
1024
|
const outboundReq = (0, payload_builders_1.buildOutboundRequest)(gatewayProgramHex, segment.prc20Token ||
|
|
873
|
-
(0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), svmBurnAmount, (
|
|
1025
|
+
(0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), svmBurnAmount, (_k = segment.gasLimit) !== null && _k !== void 0 ? _k : BigInt(0), svmPayload, ueaAddress);
|
|
874
1026
|
const inboundGasFee = segment.gasFee || BigInt(0);
|
|
875
1027
|
let svmInboundFallback;
|
|
876
|
-
if (
|
|
877
|
-
svmInboundFallback =
|
|
1028
|
+
if (effectiveUeaBalance > BigInt(0)) {
|
|
1029
|
+
svmInboundFallback =
|
|
1030
|
+
effectiveUeaBalance / BigInt(Math.max(numOutbounds, 1));
|
|
878
1031
|
}
|
|
879
1032
|
else {
|
|
880
1033
|
svmInboundFallback = inboundGasFee * BigInt(1000000);
|
|
@@ -883,7 +1036,7 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
|
883
1036
|
// SDK 5.2 R3-style Case C bump: when the inbound segment's sizing
|
|
884
1037
|
// is C, top up the swap budget by the overflow. No bridge-swap —
|
|
885
1038
|
// R3 has no destination funds delivery.
|
|
886
|
-
if (((
|
|
1039
|
+
if (((_l = segment.sizing) === null || _l === void 0 ? void 0 : _l.category) === 'C' &&
|
|
887
1040
|
segment.sizing.overflowNativePc > BigInt(0)) {
|
|
888
1041
|
const bumped = svmInboundNativeValue + segment.sizing.overflowNativePc;
|
|
889
1042
|
(0, context_1.printLog)(ctx, `composeCascade — INBOUND_FROM_CEA (SVM) Case C: bumping nativeValueForGas from ${svmInboundNativeValue} to ${bumped} (overflow=${segment.sizing.overflowNativePc})`);
|
|
@@ -899,6 +1052,7 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
|
899
1052
|
gatewayPcAddress,
|
|
900
1053
|
outboundRequest: outboundReq,
|
|
901
1054
|
});
|
|
1055
|
+
requiredNativeValue += sumMulticallNativeValue(outboundMulticalls);
|
|
902
1056
|
accumulatedPushMulticalls = [...outboundMulticalls];
|
|
903
1057
|
break;
|
|
904
1058
|
}
|
|
@@ -918,7 +1072,7 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
|
918
1072
|
let amount = BigInt(0);
|
|
919
1073
|
for (const mergedHop of segment.hops) {
|
|
920
1074
|
const hParams = mergedHop.params;
|
|
921
|
-
if ((
|
|
1075
|
+
if ((_m = hParams.funds) === null || _m === void 0 ? void 0 : _m.amount) {
|
|
922
1076
|
const token = hParams.funds.token;
|
|
923
1077
|
if (token) {
|
|
924
1078
|
if (token.mechanism === 'native') {
|
|
@@ -965,7 +1119,8 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
|
965
1119
|
amount += hParams.value;
|
|
966
1120
|
}
|
|
967
1121
|
}
|
|
968
|
-
// Build sendUniversalTxToUEA self-call on CEA
|
|
1122
|
+
// Build sendUniversalTxToUEA self-call on CEA. CEA self-calls must
|
|
1123
|
+
// always use value=0; native funds are spent from the CEA balance.
|
|
969
1124
|
const sendCall = (0, payload_builders_1.buildSendUniversalTxToUEA)(ceaAddress, tokenAddress, amount, intermediatePayload, ceaAddress);
|
|
970
1125
|
ceaMulticalls.push(sendCall);
|
|
971
1126
|
// Wrap CEA multicall in outbound from Push Chain
|
|
@@ -974,17 +1129,17 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
|
974
1129
|
// gasLimit — UGPC.sendUniversalTxOutbound reverts with
|
|
975
1130
|
// "Missing or invalid parameters" on 0. When the hop didn't supply
|
|
976
1131
|
// one (typical for a pure-R3 leg composed inside a cascade), fall
|
|
977
|
-
// back to
|
|
978
|
-
// path does.
|
|
1132
|
+
// back to the EVM Route 3 default.
|
|
979
1133
|
const effectiveGasLimit = segment.gasLimit && segment.gasLimit > BigInt(0)
|
|
980
1134
|
? segment.gasLimit
|
|
981
|
-
: selectors_1.
|
|
1135
|
+
: selectors_1.DEFAULT_CEA_TO_PUSH_GAS_LIMIT;
|
|
982
1136
|
const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, segment.prc20Token ||
|
|
983
1137
|
(0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), segment.totalBurnAmount || BigInt(0), effectiveGasLimit, ceaPayload, ueaAddress);
|
|
984
1138
|
const inboundGasFee = segment.gasFee || BigInt(0);
|
|
985
1139
|
let evmInboundFallback;
|
|
986
|
-
if (
|
|
987
|
-
evmInboundFallback =
|
|
1140
|
+
if (effectiveUeaBalance > BigInt(0)) {
|
|
1141
|
+
evmInboundFallback =
|
|
1142
|
+
effectiveUeaBalance / BigInt(Math.max(numOutbounds, 1));
|
|
988
1143
|
}
|
|
989
1144
|
else {
|
|
990
1145
|
evmInboundFallback = inboundGasFee * BigInt(1000000);
|
|
@@ -993,7 +1148,7 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
|
993
1148
|
// SDK 5.2 R3-style Case C bump: when the inbound segment's sizing
|
|
994
1149
|
// is C, top up the swap budget by the overflow. No bridge-swap —
|
|
995
1150
|
// R3 has no destination funds delivery.
|
|
996
|
-
if (((
|
|
1151
|
+
if (((_o = segment.sizing) === null || _o === void 0 ? void 0 : _o.category) === 'C' &&
|
|
997
1152
|
segment.sizing.overflowNativePc > BigInt(0)) {
|
|
998
1153
|
const bumped = evmInboundNativeValue + segment.sizing.overflowNativePc;
|
|
999
1154
|
(0, context_1.printLog)(ctx, `composeCascade — INBOUND_FROM_CEA (EVM) Case C: bumping nativeValueForGas from ${evmInboundNativeValue} to ${bumped} (overflow=${segment.sizing.overflowNativePc})`);
|
|
@@ -1009,19 +1164,29 @@ function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
|
1009
1164
|
gatewayPcAddress,
|
|
1010
1165
|
outboundRequest: outboundReq,
|
|
1011
1166
|
});
|
|
1167
|
+
requiredNativeValue += sumMulticallNativeValue(outboundMulticalls);
|
|
1012
1168
|
// Reset accumulated -- everything is now inside this outbound
|
|
1013
1169
|
accumulatedPushMulticalls = [...outboundMulticalls];
|
|
1014
1170
|
break;
|
|
1015
1171
|
}
|
|
1016
1172
|
}
|
|
1017
1173
|
}
|
|
1018
|
-
return
|
|
1174
|
+
return {
|
|
1175
|
+
multicalls: accumulatedPushMulticalls,
|
|
1176
|
+
requiredNativeValue,
|
|
1177
|
+
};
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
function composeCascade(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
|
|
1181
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1182
|
+
const result = yield composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce);
|
|
1183
|
+
return result.multicalls;
|
|
1019
1184
|
});
|
|
1020
1185
|
}
|
|
1021
1186
|
// ============================================================================
|
|
1022
1187
|
// createCascadedBuilder
|
|
1023
1188
|
// ============================================================================
|
|
1024
|
-
function createCascadedBuilder(ctx, preparedTxs, callbacks) {
|
|
1189
|
+
function createCascadedBuilder(ctx, preparedTxs, callbacks, defaultEventHook) {
|
|
1025
1190
|
return {
|
|
1026
1191
|
send: () => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1027
1192
|
var _a;
|
|
@@ -1031,8 +1196,7 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks) {
|
|
|
1031
1196
|
// Classify into segments
|
|
1032
1197
|
const segments = classifyIntoSegments(hops);
|
|
1033
1198
|
// Check if this is a single-hop Route 1 (no composition needed)
|
|
1034
|
-
if (preparedTxs.length === 1 &&
|
|
1035
|
-
preparedTxs[0].route === 'UOA_TO_PUSH') {
|
|
1199
|
+
if (preparedTxs.length === 1 && preparedTxs[0].route === 'UOA_TO_PUSH') {
|
|
1036
1200
|
const response = yield callbacks.executeFn(hops[0].params);
|
|
1037
1201
|
const singleRoute1Result = {
|
|
1038
1202
|
initialTxHash: response.hash,
|
|
@@ -1066,8 +1230,7 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks) {
|
|
|
1066
1230
|
return singleRoute1Result;
|
|
1067
1231
|
}
|
|
1068
1232
|
// Check if single-hop Route 2 (just execute directly)
|
|
1069
|
-
if (preparedTxs.length === 1 &&
|
|
1070
|
-
preparedTxs[0].route === 'UOA_TO_CEA') {
|
|
1233
|
+
if (preparedTxs.length === 1 && preparedTxs[0].route === 'UOA_TO_CEA') {
|
|
1071
1234
|
const response = yield callbacks.executeFn(hops[0].params);
|
|
1072
1235
|
const targetChain = hops[0].targetChain || (0, helpers_1.getPushChainForNetwork)(ctx.pushNetwork);
|
|
1073
1236
|
const singleRoute2Result = {
|
|
@@ -1111,8 +1274,11 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks) {
|
|
|
1111
1274
|
? yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress)
|
|
1112
1275
|
: BigInt(0);
|
|
1113
1276
|
let composedMulticalls;
|
|
1277
|
+
let cascadeRequiredNativeValue = BigInt(0);
|
|
1114
1278
|
try {
|
|
1115
|
-
|
|
1279
|
+
const composed = yield composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonceCascade);
|
|
1280
|
+
composedMulticalls = composed.multicalls;
|
|
1281
|
+
cascadeRequiredNativeValue = composed.requiredNativeValue;
|
|
1116
1282
|
}
|
|
1117
1283
|
catch (err) {
|
|
1118
1284
|
// Pre-flight or per-segment min-swap failure inside composeCascade
|
|
@@ -1129,6 +1295,7 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks) {
|
|
|
1129
1295
|
// Execute the composed multicall as a single Push Chain tx
|
|
1130
1296
|
const executeParams = {
|
|
1131
1297
|
to: ueaAddress,
|
|
1298
|
+
value: cascadeRequiredNativeValue,
|
|
1132
1299
|
data: composedMulticalls,
|
|
1133
1300
|
};
|
|
1134
1301
|
const response = yield callbacks.executeFn(executeParams);
|
|
@@ -1136,7 +1303,9 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks) {
|
|
|
1136
1303
|
const hopInfos = hops.map((hop, index) => ({
|
|
1137
1304
|
hopIndex: index,
|
|
1138
1305
|
route: hop.route,
|
|
1139
|
-
executionChain: hop.targetChain ||
|
|
1306
|
+
executionChain: hop.targetChain ||
|
|
1307
|
+
hop.sourceChain ||
|
|
1308
|
+
(0, helpers_1.getPushChainForNetwork)(ctx.pushNetwork),
|
|
1140
1309
|
status: 'pending',
|
|
1141
1310
|
}));
|
|
1142
1311
|
// Mark first hop as submitted
|
|
@@ -1150,8 +1319,8 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks) {
|
|
|
1150
1319
|
hops: hopInfos,
|
|
1151
1320
|
hopCount: hops.length,
|
|
1152
1321
|
waitForAll: (opts) => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1153
|
-
var _a, _b, _c, _d, _e;
|
|
1154
|
-
const { pollingIntervalMs = 10000, timeout = 300000, progressHook: cascadeProgressHook, eventHook, } = opts || {};
|
|
1322
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
1323
|
+
const { pollingIntervalMs = 10000, timeout = 300000, progressHook: cascadeProgressHook, eventHook = defaultEventHook, } = opts || {};
|
|
1155
1324
|
const startTime = Date.now();
|
|
1156
1325
|
// Per-hop progress emission. Fans out cascade markers to BOTH the
|
|
1157
1326
|
// explicit `eventHook` (passed into waitForAll) AND the init-time
|
|
@@ -1180,9 +1349,7 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks) {
|
|
|
1180
1349
|
if (!isMulti)
|
|
1181
1350
|
return;
|
|
1182
1351
|
const n = hopIndex0 + 1;
|
|
1183
|
-
const fromChain = hopIndex0 === 0
|
|
1184
|
-
? signerOriginChain
|
|
1185
|
-
: chainLabels[hopIndex0 - 1];
|
|
1352
|
+
const fromChain = hopIndex0 === 0 ? signerOriginChain : chainLabels[hopIndex0 - 1];
|
|
1186
1353
|
const toChain = chainLabels[hopIndex0];
|
|
1187
1354
|
emitHopEvent(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_002_01](n, totalHops, fromChain, toChain));
|
|
1188
1355
|
};
|
|
@@ -1229,49 +1396,13 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks) {
|
|
|
1229
1396
|
elapsed: Date.now() - startTime,
|
|
1230
1397
|
});
|
|
1231
1398
|
yield response.wait();
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
hopIndex: hop.hopIndex,
|
|
1239
|
-
route: hop.route,
|
|
1240
|
-
chain: (0, helpers_1.getPushChainForNetwork)(ctx.pushNetwork),
|
|
1241
|
-
status: 'confirmed',
|
|
1242
|
-
txHash: response.hash,
|
|
1243
|
-
elapsed: Date.now() - startTime,
|
|
1244
|
-
});
|
|
1245
|
-
emitHopComplete(hop.hopIndex);
|
|
1246
|
-
}
|
|
1247
|
-
}
|
|
1248
|
-
// 2. Track outbound hops (Route 2: UOA_TO_CEA)
|
|
1249
|
-
// Hops after a CEA_TO_PUSH are "child outbounds" — they execute inside
|
|
1250
|
-
// the inbound payload on Push Chain and live under a DIFFERENT utx_id
|
|
1251
|
-
// (the inbound UTX, not the parent). We can't track them via the parent
|
|
1252
|
-
// utx_id polling, so we auto-confirm them.
|
|
1253
|
-
const ceaToPushIndex = hopInfos.findIndex((h) => h.route === 'CEA_TO_PUSH');
|
|
1254
|
-
const outboundHops = hopInfos.filter((h, i) => {
|
|
1255
|
-
if (h.route !== 'UOA_TO_CEA')
|
|
1256
|
-
return false;
|
|
1257
|
-
// Child outbounds (after CEA_TO_PUSH) live under the inbound UTX,
|
|
1258
|
-
// not the parent — auto-confirm since we can't poll them here.
|
|
1259
|
-
if (ceaToPushIndex >= 0 && i > ceaToPushIndex) {
|
|
1260
|
-
h.status = 'confirmed';
|
|
1261
|
-
cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
|
|
1262
|
-
hopIndex: h.hopIndex,
|
|
1263
|
-
route: h.route,
|
|
1264
|
-
chain: h.executionChain,
|
|
1265
|
-
status: 'confirmed',
|
|
1266
|
-
elapsed: Date.now() - startTime,
|
|
1267
|
-
});
|
|
1268
|
-
return false;
|
|
1269
|
-
}
|
|
1270
|
-
return true;
|
|
1271
|
-
});
|
|
1272
|
-
if (outboundHops.length > 0) {
|
|
1399
|
+
const ceaToPushIndex = hopInfos.findIndex((h, index) => h.route === 'CEA_TO_PUSH' && !isNativeSeedOnlyHop(hops[index]));
|
|
1400
|
+
const isAfterCeaToPush = (index) => ceaToPushIndex >= 0 && index > ceaToPushIndex;
|
|
1401
|
+
const trackOutboundHops = (outboundHops, pushTxHash, resolvedSubTxId) => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1402
|
+
var _a, _b, _c;
|
|
1403
|
+
if (outboundHops.length === 0)
|
|
1404
|
+
return null;
|
|
1273
1405
|
if (outboundHops.length === 1) {
|
|
1274
|
-
// Single direct outbound hop: use the existing V1-based tracking
|
|
1275
1406
|
const hop = outboundHops[0];
|
|
1276
1407
|
const hopHooks = (0, progress_route_hooks_1.pickWaitHooks)(hop.route);
|
|
1277
1408
|
const remainingTimeout = timeout - (Date.now() - startTime);
|
|
@@ -1303,10 +1434,12 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks) {
|
|
|
1303
1434
|
});
|
|
1304
1435
|
let lastEmittedStatus;
|
|
1305
1436
|
try {
|
|
1306
|
-
const outboundDetails = yield callbacks.waitForOutboundTxFn(
|
|
1437
|
+
const outboundDetails = yield callbacks.waitForOutboundTxFn(pushTxHash, {
|
|
1307
1438
|
initialWaitMs: Math.min(60000, remainingTimeout),
|
|
1308
1439
|
pollingIntervalMs,
|
|
1309
1440
|
timeout: remainingTimeout,
|
|
1441
|
+
_resolvedSubTxId: resolvedSubTxId,
|
|
1442
|
+
_expectedDestinationChain: hop.executionChain,
|
|
1310
1443
|
progressHook: (event) => {
|
|
1311
1444
|
cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
|
|
1312
1445
|
hopIndex: hop.hopIndex,
|
|
@@ -1322,17 +1455,24 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks) {
|
|
|
1322
1455
|
}
|
|
1323
1456
|
},
|
|
1324
1457
|
});
|
|
1458
|
+
// `outboundDetails.externalTxHash` from waitForOutboundTx
|
|
1459
|
+
// is the raw `0x`-hex form (internal canonical). For the
|
|
1460
|
+
// user-facing `hop.txHash`, `hop.outboundDetails`, and
|
|
1461
|
+
// cascade progress event, normalize to base58 for SVM so
|
|
1462
|
+
// consumers can paste it straight into a Solana explorer.
|
|
1463
|
+
const displayTxHash = (_a = (0, helpers_1.toExternalTxHashDisplay)(outboundDetails.destinationChain, outboundDetails.externalTxHash)) !== null && _a !== void 0 ? _a : outboundDetails.externalTxHash;
|
|
1464
|
+
const userFacingOutboundDetails = Object.assign(Object.assign({}, outboundDetails), { externalTxHash: displayTxHash });
|
|
1325
1465
|
hop.status = 'confirmed';
|
|
1326
|
-
hop.txHash =
|
|
1327
|
-
hop.outboundDetails =
|
|
1328
|
-
emitHopEvent(hopHooks.success(
|
|
1466
|
+
hop.txHash = displayTxHash;
|
|
1467
|
+
hop.outboundDetails = userFacingOutboundDetails;
|
|
1468
|
+
emitHopEvent(hopHooks.success(userFacingOutboundDetails));
|
|
1329
1469
|
emitHopComplete(hop.hopIndex);
|
|
1330
1470
|
cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
|
|
1331
1471
|
hopIndex: hop.hopIndex,
|
|
1332
1472
|
route: hop.route,
|
|
1333
1473
|
chain: hop.executionChain,
|
|
1334
1474
|
status: 'confirmed',
|
|
1335
|
-
txHash:
|
|
1475
|
+
txHash: displayTxHash,
|
|
1336
1476
|
elapsed: Date.now() - startTime,
|
|
1337
1477
|
});
|
|
1338
1478
|
}
|
|
@@ -1364,19 +1504,17 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks) {
|
|
|
1364
1504
|
}
|
|
1365
1505
|
}
|
|
1366
1506
|
else {
|
|
1367
|
-
// Multiple outbound hops: use V2 API which returns outboundTx[]
|
|
1368
|
-
// Emit "Awaiting External Chain" once per outbound hop before
|
|
1369
|
-
// polling begins, using the route-specific awaiting hook.
|
|
1370
1507
|
for (const outHop of outboundHops) {
|
|
1371
1508
|
emitHopStart(outHop.hopIndex);
|
|
1372
1509
|
emitHopEvent((0, progress_route_hooks_1.pickWaitHooks)(outHop.route).awaiting(outHop.executionChain));
|
|
1373
1510
|
}
|
|
1374
1511
|
const perHopLastStatus = new Map();
|
|
1375
1512
|
const confirmedHops = new Set();
|
|
1376
|
-
const allOutboundDetails = yield callbacks.waitForAllOutboundTxsFn(
|
|
1513
|
+
const allOutboundDetails = yield callbacks.waitForAllOutboundTxsFn(pushTxHash, outboundHops, {
|
|
1377
1514
|
initialWaitMs: Math.min(60000, timeout - (Date.now() - startTime)),
|
|
1378
1515
|
pollingIntervalMs,
|
|
1379
1516
|
timeout: timeout - (Date.now() - startTime),
|
|
1517
|
+
_resolvedSubTxId: resolvedSubTxId,
|
|
1380
1518
|
progressHook: (event) => {
|
|
1381
1519
|
var _a, _b;
|
|
1382
1520
|
cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
|
|
@@ -1390,8 +1528,7 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks) {
|
|
|
1390
1528
|
const matchedHop = outboundHops.find((h) => h.hopIndex === event.hopIndex);
|
|
1391
1529
|
const eventHooks = (0, progress_route_hooks_1.pickWaitHooks)(matchedHop === null || matchedHop === void 0 ? void 0 : matchedHop.route);
|
|
1392
1530
|
const prev = perHopLastStatus.get(event.hopIndex);
|
|
1393
|
-
if (event.status === 'polling' &&
|
|
1394
|
-
prev !== 'polling') {
|
|
1531
|
+
if (event.status === 'polling' && prev !== 'polling') {
|
|
1395
1532
|
perHopLastStatus.set(event.hopIndex, 'polling');
|
|
1396
1533
|
emitHopEvent(eventHooks.polling((_b = (_a = matchedHop === null || matchedHop === void 0 ? void 0 : matchedHop.executionChain) !== null && _a !== void 0 ? _a : event.chain) !== null && _b !== void 0 ? _b : '', Date.now() - startTime));
|
|
1397
1534
|
}
|
|
@@ -1412,12 +1549,12 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks) {
|
|
|
1412
1549
|
},
|
|
1413
1550
|
});
|
|
1414
1551
|
if (!allOutboundDetails.success) {
|
|
1415
|
-
const failedIdx = (
|
|
1552
|
+
const failedIdx = (_b = allOutboundDetails.failedAt) !== null && _b !== void 0 ? _b : 0;
|
|
1416
1553
|
const failedHop = outboundHops.find((h) => h.hopIndex === failedIdx);
|
|
1417
1554
|
const failMsg = failedHop
|
|
1418
1555
|
? `Outbound failed for hop ${failedIdx} on ${failedHop.executionChain}`
|
|
1419
1556
|
: `Outbound cascade failed at hop ${failedIdx}`;
|
|
1420
|
-
emitHopEvent((0, progress_route_hooks_1.pickWaitHooks)(failedHop === null || failedHop === void 0 ? void 0 : failedHop.route).failed((
|
|
1557
|
+
emitHopEvent((0, progress_route_hooks_1.pickWaitHooks)(failedHop === null || failedHop === void 0 ? void 0 : failedHop.route).failed((_c = failedHop === null || failedHop === void 0 ? void 0 : failedHop.executionChain) !== null && _c !== void 0 ? _c : '', failMsg));
|
|
1421
1558
|
emitCascadeFailed(failedIdx, failMsg);
|
|
1422
1559
|
return {
|
|
1423
1560
|
success: false,
|
|
@@ -1426,22 +1563,228 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks) {
|
|
|
1426
1563
|
};
|
|
1427
1564
|
}
|
|
1428
1565
|
}
|
|
1566
|
+
return null;
|
|
1567
|
+
});
|
|
1568
|
+
// Mark only root Push Chain (Route 1) hops as confirmed. Route 1
|
|
1569
|
+
// hops after a Route 3 leg execute in the child inbound Push tx and
|
|
1570
|
+
// must wait for that child UTX to reach terminal success.
|
|
1571
|
+
for (const hop of hopInfos) {
|
|
1572
|
+
if (hop.route === 'CEA_TO_PUSH' &&
|
|
1573
|
+
isNativeSeedOnlyHop(hops[hop.hopIndex])) {
|
|
1574
|
+
emitHopStart(hop.hopIndex);
|
|
1575
|
+
hop.status = 'confirmed';
|
|
1576
|
+
hop.txHash = response.hash;
|
|
1577
|
+
cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
|
|
1578
|
+
hopIndex: hop.hopIndex,
|
|
1579
|
+
route: hop.route,
|
|
1580
|
+
chain: (0, helpers_1.getPushChainForNetwork)(ctx.pushNetwork),
|
|
1581
|
+
status: 'confirmed',
|
|
1582
|
+
txHash: response.hash,
|
|
1583
|
+
elapsed: Date.now() - startTime,
|
|
1584
|
+
});
|
|
1585
|
+
emitHopComplete(hop.hopIndex);
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
for (const hop of hopInfos) {
|
|
1589
|
+
if (hop.route === 'UOA_TO_PUSH' && !isAfterCeaToPush(hop.hopIndex)) {
|
|
1590
|
+
hop.status = 'confirmed';
|
|
1591
|
+
hop.txHash = response.hash;
|
|
1592
|
+
cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
|
|
1593
|
+
hopIndex: hop.hopIndex,
|
|
1594
|
+
route: hop.route,
|
|
1595
|
+
chain: (0, helpers_1.getPushChainForNetwork)(ctx.pushNetwork),
|
|
1596
|
+
status: 'confirmed',
|
|
1597
|
+
txHash: response.hash,
|
|
1598
|
+
elapsed: Date.now() - startTime,
|
|
1599
|
+
});
|
|
1600
|
+
emitHopComplete(hop.hopIndex);
|
|
1601
|
+
}
|
|
1429
1602
|
}
|
|
1430
|
-
//
|
|
1431
|
-
//
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1603
|
+
// 2. Track direct outbound hops (Route 2: UOA_TO_CEA) that belong
|
|
1604
|
+
// to the root Push UTX.
|
|
1605
|
+
const rootOutboundHops = hopInfos.filter((h) => h.route === 'UOA_TO_CEA' && !isAfterCeaToPush(h.hopIndex));
|
|
1606
|
+
const rootOutboundFailure = yield trackOutboundHops(rootOutboundHops, response.hash);
|
|
1607
|
+
if (rootOutboundFailure)
|
|
1608
|
+
return rootOutboundFailure;
|
|
1609
|
+
let childInbound;
|
|
1610
|
+
if (ceaToPushIndex >= 0) {
|
|
1611
|
+
const inboundHop = hopInfos[ceaToPushIndex];
|
|
1612
|
+
const hopHooks = (0, progress_route_hooks_1.pickWaitHooks)(inboundHop.route);
|
|
1613
|
+
const remainingTimeout = timeout - (Date.now() - startTime);
|
|
1614
|
+
if (remainingTimeout <= 0) {
|
|
1615
|
+
inboundHop.status = 'failed';
|
|
1616
|
+
emitHopEvent(hopHooks.timeout(inboundHop.executionChain, Date.now() - startTime));
|
|
1617
|
+
emitCascadeTimeout(inboundHop.hopIndex);
|
|
1618
|
+
return {
|
|
1619
|
+
success: false,
|
|
1620
|
+
hops: hopInfos,
|
|
1621
|
+
failedAt: inboundHop.hopIndex,
|
|
1622
|
+
};
|
|
1623
|
+
}
|
|
1437
1624
|
emitHopStart(inboundHop.hopIndex);
|
|
1625
|
+
emitHopEvent(hopHooks.awaiting(inboundHop.executionChain));
|
|
1438
1626
|
cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
|
|
1439
1627
|
hopIndex: inboundHop.hopIndex,
|
|
1440
1628
|
route: inboundHop.route,
|
|
1441
1629
|
chain: inboundHop.executionChain,
|
|
1442
|
-
status: '
|
|
1630
|
+
status: 'polling',
|
|
1443
1631
|
elapsed: Date.now() - startTime,
|
|
1444
1632
|
});
|
|
1633
|
+
let outboundLastStatus;
|
|
1634
|
+
let sourceOutboundDetails;
|
|
1635
|
+
try {
|
|
1636
|
+
sourceOutboundDetails = yield callbacks.waitForOutboundTxFn(response.hash, {
|
|
1637
|
+
initialWaitMs: Math.min(60000, remainingTimeout),
|
|
1638
|
+
pollingIntervalMs,
|
|
1639
|
+
timeout: remainingTimeout,
|
|
1640
|
+
_expectedDestinationChain: inboundHop.executionChain,
|
|
1641
|
+
progressHook: (event) => {
|
|
1642
|
+
cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
|
|
1643
|
+
hopIndex: inboundHop.hopIndex,
|
|
1644
|
+
route: inboundHop.route,
|
|
1645
|
+
chain: inboundHop.executionChain,
|
|
1646
|
+
status: event.status,
|
|
1647
|
+
elapsed: Date.now() - startTime,
|
|
1648
|
+
});
|
|
1649
|
+
if (event.status === 'polling' &&
|
|
1650
|
+
outboundLastStatus !== 'polling') {
|
|
1651
|
+
outboundLastStatus = 'polling';
|
|
1652
|
+
emitHopEvent(hopHooks.polling(inboundHop.executionChain, event.elapsed));
|
|
1653
|
+
}
|
|
1654
|
+
},
|
|
1655
|
+
});
|
|
1656
|
+
}
|
|
1657
|
+
catch (err) {
|
|
1658
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1659
|
+
const isTimeout = errMsg.startsWith('Timeout waiting for outbound transaction');
|
|
1660
|
+
emitHopEvent(isTimeout
|
|
1661
|
+
? hopHooks.timeout(inboundHop.executionChain, remainingTimeout)
|
|
1662
|
+
: hopHooks.failed(inboundHop.executionChain, errMsg));
|
|
1663
|
+
if (isTimeout)
|
|
1664
|
+
emitCascadeTimeout(inboundHop.hopIndex);
|
|
1665
|
+
else
|
|
1666
|
+
emitCascadeFailed(inboundHop.hopIndex, errMsg);
|
|
1667
|
+
inboundHop.status = 'failed';
|
|
1668
|
+
const failedExternalTxHash = err === null || err === void 0 ? void 0 : err.externalTxHash;
|
|
1669
|
+
const failedDisplayTxHash = (_d = (0, helpers_1.toExternalTxHashDisplay)(inboundHop.executionChain, failedExternalTxHash)) !== null && _d !== void 0 ? _d : failedExternalTxHash;
|
|
1670
|
+
if (failedDisplayTxHash)
|
|
1671
|
+
inboundHop.txHash = failedDisplayTxHash;
|
|
1672
|
+
cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
|
|
1673
|
+
hopIndex: inboundHop.hopIndex,
|
|
1674
|
+
route: inboundHop.route,
|
|
1675
|
+
chain: inboundHop.executionChain,
|
|
1676
|
+
status: 'failed',
|
|
1677
|
+
txHash: failedDisplayTxHash,
|
|
1678
|
+
elapsed: Date.now() - startTime,
|
|
1679
|
+
});
|
|
1680
|
+
return {
|
|
1681
|
+
success: false,
|
|
1682
|
+
hops: hopInfos,
|
|
1683
|
+
failedAt: inboundHop.hopIndex,
|
|
1684
|
+
};
|
|
1685
|
+
}
|
|
1686
|
+
const displayExternalTxHash = (_e = (0, helpers_1.toExternalTxHashDisplay)(sourceOutboundDetails.destinationChain, sourceOutboundDetails.externalTxHash)) !== null && _e !== void 0 ? _e : sourceOutboundDetails.externalTxHash;
|
|
1687
|
+
inboundHop.outboundDetails = Object.assign(Object.assign({}, sourceOutboundDetails), { externalTxHash: displayExternalTxHash });
|
|
1688
|
+
inboundHop.txHash = displayExternalTxHash;
|
|
1689
|
+
emitHopEvent(hopHooks.success(inboundHop.outboundDetails));
|
|
1690
|
+
emitHopEvent(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_310_01](inboundHop.executionChain));
|
|
1691
|
+
let inboundLastStatus;
|
|
1692
|
+
const inboundRemainingTimeout = timeout - (Date.now() - startTime);
|
|
1693
|
+
try {
|
|
1694
|
+
childInbound = yield ((_f = callbacks.waitForInboundPushTxFn) !== null && _f !== void 0 ? _f : ((externalTxHash, sourceChain, opts) => (0, inbound_tracker_1.waitForInboundPushTx)(ctx, externalTxHash, sourceChain, opts)))(sourceOutboundDetails.externalTxHash, inboundHop.executionChain, {
|
|
1695
|
+
initialWaitMs: Math.min(60000, inboundRemainingTimeout),
|
|
1696
|
+
pollingIntervalMs,
|
|
1697
|
+
timeout: inboundRemainingTimeout,
|
|
1698
|
+
progressHook: (event) => {
|
|
1699
|
+
if (event.status === 'polling' &&
|
|
1700
|
+
inboundLastStatus !== 'polling') {
|
|
1701
|
+
inboundLastStatus = 'polling';
|
|
1702
|
+
emitHopEvent(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_310_02](inboundHop.executionChain, event.elapsedMs));
|
|
1703
|
+
}
|
|
1704
|
+
},
|
|
1705
|
+
});
|
|
1706
|
+
}
|
|
1707
|
+
catch (err) {
|
|
1708
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1709
|
+
const isTimeout = err instanceof inbound_tracker_1.InboundTimeoutError;
|
|
1710
|
+
emitHopEvent(isTimeout
|
|
1711
|
+
? progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_399_03](inboundHop.executionChain, inboundRemainingTimeout, 'inbound')
|
|
1712
|
+
: progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_399_02](errMsg, 'inbound', inboundHop.executionChain));
|
|
1713
|
+
if (isTimeout)
|
|
1714
|
+
emitCascadeTimeout(inboundHop.hopIndex);
|
|
1715
|
+
else
|
|
1716
|
+
emitCascadeFailed(inboundHop.hopIndex, errMsg);
|
|
1717
|
+
inboundHop.status = 'failed';
|
|
1718
|
+
cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
|
|
1719
|
+
hopIndex: inboundHop.hopIndex,
|
|
1720
|
+
route: inboundHop.route,
|
|
1721
|
+
chain: inboundHop.executionChain,
|
|
1722
|
+
status: 'failed',
|
|
1723
|
+
elapsed: Date.now() - startTime,
|
|
1724
|
+
});
|
|
1725
|
+
return {
|
|
1726
|
+
success: false,
|
|
1727
|
+
hops: hopInfos,
|
|
1728
|
+
failedAt: inboundHop.hopIndex,
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
if (childInbound.status !== 'confirmed') {
|
|
1732
|
+
const failMsg = (_g = childInbound.errorMessage) !== null && _g !== void 0 ? _g : 'inbound execution failed on Push Chain';
|
|
1733
|
+
inboundHop.status = 'failed';
|
|
1734
|
+
inboundHop.txHash =
|
|
1735
|
+
childInbound.pushTxHash || displayExternalTxHash;
|
|
1736
|
+
emitHopEvent(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_399_02](failMsg, 'inbound', inboundHop.executionChain));
|
|
1737
|
+
emitCascadeFailed(inboundHop.hopIndex, failMsg);
|
|
1738
|
+
cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
|
|
1739
|
+
hopIndex: inboundHop.hopIndex,
|
|
1740
|
+
route: inboundHop.route,
|
|
1741
|
+
chain: inboundHop.executionChain,
|
|
1742
|
+
status: 'failed',
|
|
1743
|
+
txHash: inboundHop.txHash,
|
|
1744
|
+
elapsed: Date.now() - startTime,
|
|
1745
|
+
});
|
|
1746
|
+
return {
|
|
1747
|
+
success: false,
|
|
1748
|
+
hops: hopInfos,
|
|
1749
|
+
failedAt: inboundHop.hopIndex,
|
|
1750
|
+
};
|
|
1751
|
+
}
|
|
1752
|
+
inboundHop.status = 'confirmed';
|
|
1753
|
+
inboundHop.txHash =
|
|
1754
|
+
childInbound.pushTxHash || displayExternalTxHash;
|
|
1755
|
+
emitHopEvent(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_399_01](inboundHop.executionChain, childInbound.pushTxHash));
|
|
1756
|
+
emitHopComplete(inboundHop.hopIndex);
|
|
1757
|
+
cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
|
|
1758
|
+
hopIndex: inboundHop.hopIndex,
|
|
1759
|
+
route: inboundHop.route,
|
|
1760
|
+
chain: inboundHop.executionChain,
|
|
1761
|
+
status: 'confirmed',
|
|
1762
|
+
txHash: inboundHop.txHash,
|
|
1763
|
+
elapsed: Date.now() - startTime,
|
|
1764
|
+
});
|
|
1765
|
+
}
|
|
1766
|
+
if (childInbound) {
|
|
1767
|
+
for (const hop of hopInfos) {
|
|
1768
|
+
if (hop.route === 'UOA_TO_PUSH' &&
|
|
1769
|
+
isAfterCeaToPush(hop.hopIndex)) {
|
|
1770
|
+
emitHopStart(hop.hopIndex);
|
|
1771
|
+
hop.status = 'confirmed';
|
|
1772
|
+
hop.txHash = childInbound.pushTxHash;
|
|
1773
|
+
cascadeProgressHook === null || cascadeProgressHook === void 0 ? void 0 : cascadeProgressHook({
|
|
1774
|
+
hopIndex: hop.hopIndex,
|
|
1775
|
+
route: hop.route,
|
|
1776
|
+
chain: (0, helpers_1.getPushChainForNetwork)(ctx.pushNetwork),
|
|
1777
|
+
status: 'confirmed',
|
|
1778
|
+
txHash: childInbound.pushTxHash,
|
|
1779
|
+
elapsed: Date.now() - startTime,
|
|
1780
|
+
});
|
|
1781
|
+
emitHopComplete(hop.hopIndex);
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
const childOutboundHops = hopInfos.filter((h) => h.route === 'UOA_TO_CEA' && isAfterCeaToPush(h.hopIndex));
|
|
1785
|
+
const childOutboundFailure = yield trackOutboundHops(childOutboundHops, childInbound.pushTxHash || response.hash, childInbound.childUtxId);
|
|
1786
|
+
if (childOutboundFailure)
|
|
1787
|
+
return childOutboundFailure;
|
|
1445
1788
|
}
|
|
1446
1789
|
emitCascadeSuccess();
|
|
1447
1790
|
return { success: true, hops: hopInfos };
|