@pushchain/core 6.0.8 → 6.0.10
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/orchestrator/internals/cascade.js +37 -37
- package/src/lib/orchestrator/internals/error-decoder.js +6 -0
- package/src/lib/orchestrator/internals/outbound-sync.d.ts +2 -2
- package/src/lib/orchestrator/internals/outbound-sync.js +3 -3
- package/src/lib/orchestrator/internals/route-handlers.d.ts +5 -2
- package/src/lib/orchestrator/internals/route-handlers.js +72 -102
- package/src/lib/orchestrator/internals/tx-transformer.js +3 -2
- package/src/lib/orchestrator/orchestrator.types.d.ts +5 -5
- package/src/lib/orchestrator/payload-builders.d.ts +13 -1
- package/src/lib/orchestrator/payload-builders.js +26 -1
- package/src/lib/universal-tx-detector/child-inbounds.d.ts +3 -0
- package/src/lib/universal-tx-detector/child-inbounds.js +18 -3
- package/src/lib/universal-tx-detector/detector-svm.js +10 -16
package/package.json
CHANGED
|
@@ -262,6 +262,12 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
|
|
|
262
262
|
// Build CEA multicalls
|
|
263
263
|
const ceaMulticalls = [];
|
|
264
264
|
const hasData = (0, data_utils_1.hasExecutablePayloadData)(params.data);
|
|
265
|
+
const fundsToken = ((_h = params.funds) === null || _h === void 0 ? void 0 : _h.amount)
|
|
266
|
+
? (0, route_handlers_1.resolveR2DestinationFundsToken)(params.funds.token, targetChain, ctx.pushNetwork)
|
|
267
|
+
: undefined;
|
|
268
|
+
const nativeFundsValue = ((_j = params.funds) === null || _j === void 0 ? void 0 : _j.amount) && (fundsToken === null || fundsToken === void 0 ? void 0 : fundsToken.mechanism) === 'native'
|
|
269
|
+
? params.funds.amount
|
|
270
|
+
: BigInt(0);
|
|
265
271
|
if (hasData) {
|
|
266
272
|
if (Array.isArray(params.data)) {
|
|
267
273
|
ceaMulticalls.push(...params.data);
|
|
@@ -270,16 +276,15 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
|
|
|
270
276
|
// When ERC-20 funds are provided with a single payload, auto-prepend a
|
|
271
277
|
// transfer() call so the tokens minted to the CEA are forwarded to the
|
|
272
278
|
// target address. This mirrors the Route 1 behavior in buildExecuteMulticall.
|
|
273
|
-
if ((
|
|
274
|
-
|
|
275
|
-
if (token && token.mechanism !== 'native') {
|
|
279
|
+
if ((_k = params.funds) === null || _k === void 0 ? void 0 : _k.amount) {
|
|
280
|
+
if (fundsToken && fundsToken.mechanism !== 'native') {
|
|
276
281
|
const erc20Transfer = (0, viem_1.encodeFunctionData)({
|
|
277
282
|
abi: erc20_evm_1.ERC20_EVM,
|
|
278
283
|
functionName: 'transfer',
|
|
279
284
|
args: [target.address, params.funds.amount],
|
|
280
285
|
});
|
|
281
286
|
ceaMulticalls.push({
|
|
282
|
-
to:
|
|
287
|
+
to: fundsToken.address,
|
|
283
288
|
value: BigInt(0),
|
|
284
289
|
data: erc20Transfer,
|
|
285
290
|
});
|
|
@@ -290,23 +295,20 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
|
|
|
290
295
|
// native value to the CEA, and the multicall forwards it to the target.
|
|
291
296
|
ceaMulticalls.push({
|
|
292
297
|
to: target.address,
|
|
293
|
-
value: (
|
|
298
|
+
value: ((_l = params.value) !== null && _l !== void 0 ? _l : BigInt(0)) + nativeFundsValue,
|
|
294
299
|
data: params.data,
|
|
295
300
|
});
|
|
296
301
|
}
|
|
297
302
|
}
|
|
298
303
|
else {
|
|
299
|
-
|
|
300
|
-
? params.funds.token
|
|
301
|
-
: undefined;
|
|
302
|
-
if (((_l = params.funds) === null || _l === void 0 ? void 0 : _l.amount) && token && token.mechanism !== 'native') {
|
|
304
|
+
if (((_m = params.funds) === null || _m === void 0 ? void 0 : _m.amount) && fundsToken && fundsToken.mechanism !== 'native') {
|
|
303
305
|
const erc20Transfer = (0, viem_1.encodeFunctionData)({
|
|
304
306
|
abi: erc20_evm_1.ERC20_EVM,
|
|
305
307
|
functionName: 'transfer',
|
|
306
308
|
args: [target.address, params.funds.amount],
|
|
307
309
|
});
|
|
308
310
|
ceaMulticalls.push({
|
|
309
|
-
to:
|
|
311
|
+
to: fundsToken.address,
|
|
310
312
|
value: BigInt(0),
|
|
311
313
|
data: erc20Transfer,
|
|
312
314
|
});
|
|
@@ -322,11 +324,11 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
|
|
|
322
324
|
});
|
|
323
325
|
}
|
|
324
326
|
}
|
|
325
|
-
else if (
|
|
327
|
+
else if (nativeFundsValue > BigInt(0)) {
|
|
326
328
|
if (target.address.toLowerCase() !== ceaAddress.toLowerCase()) {
|
|
327
329
|
ceaMulticalls.push({
|
|
328
330
|
to: target.address,
|
|
329
|
-
value:
|
|
331
|
+
value: nativeFundsValue,
|
|
330
332
|
data: '0x',
|
|
331
333
|
});
|
|
332
334
|
}
|
|
@@ -442,8 +444,8 @@ function buildHopDescriptor(ctx, params, route, ueaAddress) {
|
|
|
442
444
|
effectiveGasLimit = result.gasLimitUsed;
|
|
443
445
|
}
|
|
444
446
|
return Object.assign(Object.assign({}, baseDescriptor), { sourceChain, ceaAddress: ceaPdaHex, isSvmTarget: true, prc20Token,
|
|
445
|
-
// Route 3
|
|
446
|
-
//
|
|
447
|
+
// Route 3 drains pre-existing funds from the external CEA. The
|
|
448
|
+
// Push-side outbound is a payload relay, so no PRC-20 is burned.
|
|
447
449
|
burnAmount: BigInt(0), gasToken,
|
|
448
450
|
gasFee,
|
|
449
451
|
gasPrice, gasLimit: effectiveGasLimit, sizing });
|
|
@@ -781,7 +783,7 @@ function getInboundFundingForBurnToken(segments, beforeSegmentIndex, burnToken,
|
|
|
781
783
|
}
|
|
782
784
|
function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce, options) {
|
|
783
785
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
784
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r
|
|
786
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
|
|
785
787
|
let accumulatedPushMulticalls = [];
|
|
786
788
|
let requiredNativeValue = BigInt(0);
|
|
787
789
|
const gatewayPcAddress = (0, helpers_1.getUniversalGatewayPCAddress)();
|
|
@@ -964,12 +966,10 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce,
|
|
|
964
966
|
: (firstHop === null || firstHop === void 0 ? void 0 : firstHop.ceaAddress) || ueaAddress;
|
|
965
967
|
(0, payload_builders_1.assertCeaFundsParkingInvariant)(targetForOutbound, outboundPayload);
|
|
966
968
|
}
|
|
967
|
-
// Per-segment PRC-20 burn-balance pre-check
|
|
968
|
-
//
|
|
969
|
-
//
|
|
970
|
-
//
|
|
971
|
-
// gateway's transferFrom happens BEFORE the gas swap, so user must
|
|
972
|
-
// pre-hold the burn amount.
|
|
969
|
+
// Per-segment PRC-20 burn-balance pre-check. Fires whenever
|
|
970
|
+
// burnAmount > 0 even when burn token equals gas token — the gateway's
|
|
971
|
+
// transferFrom happens BEFORE the gas swap, so user must pre-hold the
|
|
972
|
+
// burn amount.
|
|
973
973
|
const segBurnAmount = segment.totalBurnAmount || BigInt(0);
|
|
974
974
|
const segBurnToken = segment.prc20Token;
|
|
975
975
|
if (segBurnAmount > BigInt(0) && segBurnToken && !isMigration) {
|
|
@@ -1026,7 +1026,7 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce,
|
|
|
1026
1026
|
}
|
|
1027
1027
|
}
|
|
1028
1028
|
// Build outbound request
|
|
1029
|
-
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, (_f = segment.maxPCForGas) !== null && _f !== void 0 ? _f : BigInt(0)
|
|
1029
|
+
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, (_f = segment.maxPCForGas) !== null && _f !== void 0 ? _f : BigInt(0));
|
|
1030
1030
|
// Build approval + outbound multicalls
|
|
1031
1031
|
const segGasFee = segment.gasFee || BigInt(0);
|
|
1032
1032
|
const svmOverride = svmNativeValueBySegment.get(segment);
|
|
@@ -1043,7 +1043,7 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce,
|
|
|
1043
1043
|
else {
|
|
1044
1044
|
evmFallbackValue = segGasFee * BigInt(1000000);
|
|
1045
1045
|
}
|
|
1046
|
-
let nativeValueForGas = (
|
|
1046
|
+
let nativeValueForGas = (_g = svmOverride !== null && svmOverride !== void 0 ? svmOverride : perOutboundNativeValue) !== null && _g !== void 0 ? _g : evmFallbackValue;
|
|
1047
1047
|
// SDK 5.2 Case C: when the segment's sizing is category C, compose
|
|
1048
1048
|
// R2/OUTBOUND_TO_CEA segments no longer apply Case A/B/C sizing
|
|
1049
1049
|
// (scoped to R1 + R3 only). segment.sizing may still be populated
|
|
@@ -1084,7 +1084,7 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce,
|
|
|
1084
1084
|
// UEA multicall so no nested outbound is needed.
|
|
1085
1085
|
const mergedHopMulticalls = [];
|
|
1086
1086
|
for (const h of segment.hops) {
|
|
1087
|
-
if ((0, data_utils_1.hasExecutablePayloadData)((
|
|
1087
|
+
if ((0, data_utils_1.hasExecutablePayloadData)((_h = h === null || h === void 0 ? void 0 : h.params) === null || _h === void 0 ? void 0 : _h.data) || ((_j = h === null || h === void 0 ? void 0 : h.params) === null || _j === void 0 ? void 0 : _j.value)) {
|
|
1088
1088
|
const hopCalls = (0, payload_builders_1.buildExecuteMulticall)({
|
|
1089
1089
|
execute: {
|
|
1090
1090
|
to: h.params.to,
|
|
@@ -1105,18 +1105,18 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce,
|
|
|
1105
1105
|
];
|
|
1106
1106
|
}
|
|
1107
1107
|
// Primary hop carries the sourceChain / CEA address (shared across merged hops).
|
|
1108
|
-
const
|
|
1108
|
+
const hop = segment.hops[0];
|
|
1109
|
+
const sourceChain = hop.sourceChain;
|
|
1110
|
+
const ceaAddress = hop.ceaAddress || ueaAddress;
|
|
1109
1111
|
let intermediatePayload = '0x';
|
|
1110
1112
|
if (accumulatedPushMulticalls.length > 0) {
|
|
1111
1113
|
const multicallPayload = (0, payload_builder_1.buildMulticallPayloadData)(ctx, ueaAddress, accumulatedPushMulticalls);
|
|
1112
1114
|
// +1: the outbound tx consumes one nonce via execute()
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1115
|
+
const nonce = (ueaNonce !== null && ueaNonce !== void 0 ? ueaNonce : BigInt(0)) + BigInt(1);
|
|
1116
|
+
intermediatePayload = (0, payload_builders_1.isSvmChain)(sourceChain)
|
|
1117
|
+
? (0, payload_builders_1.buildInboundUniversalPayloadSvm)(multicallPayload, { nonce })
|
|
1118
|
+
: (0, payload_builders_1.buildInboundUniversalPayload)(multicallPayload, { nonce });
|
|
1116
1119
|
}
|
|
1117
|
-
const hop = segment.hops[0];
|
|
1118
|
-
const sourceChain = hop.sourceChain;
|
|
1119
|
-
const ceaAddress = hop.ceaAddress || ueaAddress;
|
|
1120
1120
|
// SVM chains: build SVM CPI payload instead of EVM CEA multicall
|
|
1121
1121
|
if ((0, payload_builders_1.isSvmChain)(sourceChain)) {
|
|
1122
1122
|
const lockerContract = chain_1.CHAIN_INFO[sourceChain].lockerContract;
|
|
@@ -1129,7 +1129,7 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce,
|
|
|
1129
1129
|
let drainAmount = BigInt(0);
|
|
1130
1130
|
let tokenMintHex;
|
|
1131
1131
|
const params = hop.params;
|
|
1132
|
-
if (((
|
|
1132
|
+
if (((_k = params.funds) === null || _k === void 0 ? void 0 : _k.amount) && params.funds.amount > BigInt(0)) {
|
|
1133
1133
|
drainAmount = params.funds.amount;
|
|
1134
1134
|
const token = params.funds.token;
|
|
1135
1135
|
if (token && token.address) {
|
|
@@ -1158,7 +1158,7 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce,
|
|
|
1158
1158
|
});
|
|
1159
1159
|
const svmBurnAmount = segment.totalBurnAmount || BigInt(0);
|
|
1160
1160
|
const outboundReq = (0, payload_builders_1.buildOutboundRequest)(gatewayProgramHex, segment.prc20Token ||
|
|
1161
|
-
(0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), svmBurnAmount, (
|
|
1161
|
+
(0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), svmBurnAmount, (_l = segment.gasLimit) !== null && _l !== void 0 ? _l : BigInt(0), svmPayload, ueaAddress, (_m = segment.maxPCForGas) !== null && _m !== void 0 ? _m : BigInt(0));
|
|
1162
1162
|
const inboundGasFee = segment.gasFee || BigInt(0);
|
|
1163
1163
|
let svmInboundFallback;
|
|
1164
1164
|
if (effectiveUeaBalance > BigInt(0)) {
|
|
@@ -1172,7 +1172,7 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce,
|
|
|
1172
1172
|
// SDK 5.2 R3-style Case C bump: when the inbound segment's sizing
|
|
1173
1173
|
// is C, top up the swap budget by the overflow. No bridge-swap —
|
|
1174
1174
|
// R3 has no destination funds delivery.
|
|
1175
|
-
if (((
|
|
1175
|
+
if (((_o = segment.sizing) === null || _o === void 0 ? void 0 : _o.category) === 'C' &&
|
|
1176
1176
|
segment.sizing.overflowNativePc > BigInt(0)) {
|
|
1177
1177
|
const bumped = svmInboundNativeValue + segment.sizing.overflowNativePc;
|
|
1178
1178
|
(0, context_1.printLog)(ctx, `composeCascade — INBOUND_FROM_CEA (SVM) Case C: bumping nativeValueForGas from ${svmInboundNativeValue} to ${bumped} (overflow=${segment.sizing.overflowNativePc})`);
|
|
@@ -1209,7 +1209,7 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce,
|
|
|
1209
1209
|
let amount = BigInt(0);
|
|
1210
1210
|
for (const mergedHop of segment.hops) {
|
|
1211
1211
|
const hParams = mergedHop.params;
|
|
1212
|
-
if ((
|
|
1212
|
+
if ((_p = hParams.funds) === null || _p === void 0 ? void 0 : _p.amount) {
|
|
1213
1213
|
const token = hParams.funds.token;
|
|
1214
1214
|
if (token) {
|
|
1215
1215
|
if (token.mechanism === 'native') {
|
|
@@ -1245,7 +1245,7 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce,
|
|
|
1245
1245
|
? segment.gasLimit
|
|
1246
1246
|
: selectors_1.DEFAULT_CEA_TO_PUSH_GAS_LIMIT;
|
|
1247
1247
|
const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, segment.prc20Token ||
|
|
1248
|
-
(0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), segment.totalBurnAmount || BigInt(0), effectiveGasLimit, ceaPayload, ueaAddress, (
|
|
1248
|
+
(0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork), segment.totalBurnAmount || BigInt(0), effectiveGasLimit, ceaPayload, ueaAddress, (_q = segment.maxPCForGas) !== null && _q !== void 0 ? _q : BigInt(0));
|
|
1249
1249
|
const inboundGasFee = segment.gasFee || BigInt(0);
|
|
1250
1250
|
let evmInboundFallback;
|
|
1251
1251
|
if (effectiveUeaBalance > BigInt(0)) {
|
|
@@ -1259,7 +1259,7 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce,
|
|
|
1259
1259
|
// SDK 5.2 R3-style Case C bump: when the inbound segment's sizing
|
|
1260
1260
|
// is C, top up the swap budget by the overflow. No bridge-swap —
|
|
1261
1261
|
// R3 has no destination funds delivery.
|
|
1262
|
-
if (((
|
|
1262
|
+
if (((_r = segment.sizing) === null || _r === void 0 ? void 0 : _r.category) === 'C' &&
|
|
1263
1263
|
segment.sizing.overflowNativePc > BigInt(0)) {
|
|
1264
1264
|
const bumped = evmInboundNativeValue + segment.sizing.overflowNativePc;
|
|
1265
1265
|
(0, context_1.printLog)(ctx, `composeCascade — INBOUND_FROM_CEA (EVM) Case C: bumping nativeValueForGas from ${evmInboundNativeValue} to ${bumped} (overflow=${segment.sizing.overflowNativePc})`);
|
|
@@ -46,6 +46,12 @@ exports.KNOWN_ERROR_SELECTORS = {
|
|
|
46
46
|
hint: 'Likely cause: provided gasLimit is below the chain minimum. Omit `gasLimit` to use the per-chain default.',
|
|
47
47
|
provenance: 'Observed in dev (selector source unverified — see plan §9 #5)',
|
|
48
48
|
},
|
|
49
|
+
'0x05aab006': {
|
|
50
|
+
name: 'GasPriceBelowBase',
|
|
51
|
+
hint: 'Likely cause: an explicit outbound gasPrice override is below the current UniversalCore base price. ' +
|
|
52
|
+
'Omit gasPrice so the gateway resolves the live base price at execution time.',
|
|
53
|
+
provenance: 'TypesUGPC/UniversalGatewayPC live Route 2 docs e2e, 2026-05-19: gateway tx 0xe1b549eb47c3cbf1abc69ba6e85927832e482147b49a01a0364206098367dbf7.',
|
|
54
|
+
},
|
|
49
55
|
};
|
|
50
56
|
/** Selector for `Error(string)` (Solidity `require(false, "msg")`). */
|
|
51
57
|
exports.ERROR_STRING_SELECTOR = '0x08c379a0';
|
|
@@ -12,7 +12,7 @@ export declare const OUTBOUND_INITIAL_WAIT_MS = 15000;
|
|
|
12
12
|
/** Interval between consecutive polls (ms). */
|
|
13
13
|
export declare const OUTBOUND_POLL_INTERVAL_MS = 3000;
|
|
14
14
|
/** Maximum total timeout for the outbound polling loop (ms). */
|
|
15
|
-
export declare const OUTBOUND_MAX_TIMEOUT_MS =
|
|
15
|
+
export declare const OUTBOUND_MAX_TIMEOUT_MS = 300000;
|
|
16
16
|
/** Maximum total timeout for the R3 inbound round-trip polling loop (ms). */
|
|
17
17
|
export declare const INBOUND_MAX_TIMEOUT_MS = 300000;
|
|
18
18
|
/** Initial wait before the first inbound poll (ms). Validator observation + Push commit + indexer ingestion realistically take 15-25 s on testnet (esp. SVM source), so polling immediately just burns RPC on guaranteed-empty results. */
|
|
@@ -49,7 +49,7 @@ export declare class OutboundFailedError extends Error {
|
|
|
49
49
|
* @internal Used by .wait() for outbound routes - not part of public API.
|
|
50
50
|
* Uses polling with configurable initial wait, interval, and timeout.
|
|
51
51
|
*
|
|
52
|
-
* Default strategy: 15s initial wait, then poll every 3s,
|
|
52
|
+
* Default strategy: 15s initial wait, then poll every 3s, 300s total timeout.
|
|
53
53
|
*
|
|
54
54
|
* @param ctx - Orchestrator context
|
|
55
55
|
* @param pushChainTxHash - The Push Chain transaction hash
|
|
@@ -27,7 +27,7 @@ exports.OUTBOUND_INITIAL_WAIT_MS = 15000; // 15s
|
|
|
27
27
|
/** Interval between consecutive polls (ms). */
|
|
28
28
|
exports.OUTBOUND_POLL_INTERVAL_MS = 3000; // 3s
|
|
29
29
|
/** Maximum total timeout for the outbound polling loop (ms). */
|
|
30
|
-
exports.OUTBOUND_MAX_TIMEOUT_MS =
|
|
30
|
+
exports.OUTBOUND_MAX_TIMEOUT_MS = 300000; // 300s (5 min)
|
|
31
31
|
/** Maximum total timeout for the R3 inbound round-trip polling loop (ms). */
|
|
32
32
|
exports.INBOUND_MAX_TIMEOUT_MS = 300000; // 300s (5 min) — covers R3 inbound latency on testnet
|
|
33
33
|
/** Initial wait before the first inbound poll (ms). Validator observation + Push commit + indexer ingestion realistically take 15-25 s on testnet (esp. SVM source), so polling immediately just burns RPC on guaranteed-empty results. */
|
|
@@ -75,7 +75,7 @@ exports.OutboundFailedError = OutboundFailedError;
|
|
|
75
75
|
*
|
|
76
76
|
* Cosmos sometimes populates `observedTx.txHash` while the relayer is still
|
|
77
77
|
* voting on the outboundStatus update. The Push indexer can stay at
|
|
78
|
-
* UNSPECIFIED for the full
|
|
78
|
+
* UNSPECIFIED for the full timeout budget even though the destination tx mined
|
|
79
79
|
* minutes earlier. To avoid those false timeouts we ask the destination
|
|
80
80
|
* chain's own RPC: if it has a confirmed receipt with status=success, we
|
|
81
81
|
* trust it and return FOUND; if status=reverted, we throw OutboundFailedError
|
|
@@ -132,7 +132,7 @@ function verifyExternalEvmReceipt(ctx, destinationChain, externalTxHash) {
|
|
|
132
132
|
* @internal Used by .wait() for outbound routes - not part of public API.
|
|
133
133
|
* Uses polling with configurable initial wait, interval, and timeout.
|
|
134
134
|
*
|
|
135
|
-
* Default strategy: 15s initial wait, then poll every 3s,
|
|
135
|
+
* Default strategy: 15s initial wait, then poll every 3s, 300s total timeout.
|
|
136
136
|
*
|
|
137
137
|
* @param ctx - Orchestrator context
|
|
138
138
|
* @param pushChainTxHash - The Push Chain transaction hash
|
|
@@ -15,8 +15,11 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import type { OrchestratorContext } from './context';
|
|
17
17
|
import { CHAIN } from '../../constants/enums';
|
|
18
|
+
import { MoveableToken } from '../../constants/tokens';
|
|
18
19
|
import { TransactionRoute } from '../route-detector';
|
|
19
20
|
import type { ChainTarget, ExecuteParams, UniversalExecuteParams, UniversalOutboundTxRequest, UniversalTxRequest, UniversalTxResponse } from '../orchestrator.types';
|
|
21
|
+
import type { PUSH_NETWORK } from '../../constants/enums';
|
|
22
|
+
export declare function resolveR2DestinationFundsToken(token: MoveableToken, targetChain: CHAIN, pushNetwork: PUSH_NETWORK): MoveableToken;
|
|
20
23
|
type ExecuteFn = (params: ExecuteParams | UniversalExecuteParams) => Promise<UniversalTxResponse>;
|
|
21
24
|
/**
|
|
22
25
|
* Dispatch a universal execution to the correct route handler.
|
|
@@ -59,8 +62,8 @@ export declare function executeCeaToPush(ctx: OrchestratorContext, params: Unive
|
|
|
59
62
|
*
|
|
60
63
|
* Unlike EVM Route 3 which builds CEA multicalls, SVM Route 3 encodes a
|
|
61
64
|
* `send_universal_tx_to_uea` instruction as an execute payload targeting
|
|
62
|
-
* the SVM gateway program (self-call).
|
|
63
|
-
* the
|
|
65
|
+
* the SVM gateway program (self-call). For funds-bearing calls the outbound
|
|
66
|
+
* request amount must match the drain amount embedded in the instruction data.
|
|
64
67
|
*/
|
|
65
68
|
export declare function executeCeaToPushSvm(ctx: OrchestratorContext, params: UniversalExecuteParams, sourceChain: CHAIN, executeFn: ExecuteFn): Promise<UniversalTxResponse>;
|
|
66
69
|
/**
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* `executeFn` callback that replaces the former `this.execute()` call.
|
|
16
16
|
*/
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.resolveR2DestinationFundsToken = resolveR2DestinationFundsToken;
|
|
18
19
|
exports.executeMultiChain = executeMultiChain;
|
|
19
20
|
exports.executeUoaToCea = executeUoaToCea;
|
|
20
21
|
exports.executeUoaToCeaSvm = executeUoaToCeaSvm;
|
|
@@ -34,6 +35,7 @@ const gas_calculator_1 = require("./gas-calculator");
|
|
|
34
35
|
const preflight_1 = require("./preflight");
|
|
35
36
|
const svm_rent_1 = require("./svm-rent");
|
|
36
37
|
const chain_1 = require("../../constants/chain");
|
|
38
|
+
const tokens_1 = require("../../constants/tokens");
|
|
37
39
|
const utils_1 = require("../../utils");
|
|
38
40
|
const price_fetch_1 = require("../../price-fetch/price-fetch");
|
|
39
41
|
const route_detector_1 = require("../route-detector");
|
|
@@ -81,6 +83,24 @@ function resolveR2Prc20TokenEvm(params, targetChain, pushNetwork) {
|
|
|
81
83
|
}
|
|
82
84
|
return { prc20Token, burnAmount };
|
|
83
85
|
}
|
|
86
|
+
function resolveR2DestinationFundsToken(token, targetChain, pushNetwork) {
|
|
87
|
+
var _a, _b, _c;
|
|
88
|
+
const pushToken = token;
|
|
89
|
+
if (!pushToken.prc20Address || pushToken.sourceChain !== targetChain) {
|
|
90
|
+
return token;
|
|
91
|
+
}
|
|
92
|
+
const destinationTokens = (_a = tokens_1.MOVEABLE_TOKENS[targetChain]) !== null && _a !== void 0 ? _a : [];
|
|
93
|
+
const nativePrc20 = (0, helpers_1.getNativePRC20ForChain)(targetChain, pushNetwork);
|
|
94
|
+
if (pushToken.prc20Address.toLowerCase() === nativePrc20.toLowerCase()) {
|
|
95
|
+
return ((_b = destinationTokens.find((candidate) => candidate.mechanism === 'native')) !== null && _b !== void 0 ? _b : {
|
|
96
|
+
symbol: token.symbol,
|
|
97
|
+
decimals: token.decimals,
|
|
98
|
+
address: selectors_1.ZERO_ADDRESS,
|
|
99
|
+
mechanism: 'native',
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return ((_c = destinationTokens.find((candidate) => candidate.symbol === token.symbol)) !== null && _c !== void 0 ? _c : token);
|
|
103
|
+
}
|
|
84
104
|
/**
|
|
85
105
|
* SVM counterpart to `resolveR2Prc20TokenEvm`. SVM adds an EXECUTE-only
|
|
86
106
|
* branch (no value, no funds, but `hasSvmExecute === true`) that still needs
|
|
@@ -117,6 +137,7 @@ function resolveR3SvmDrain(params, sourceChain, pushNetwork) {
|
|
|
117
137
|
var _a;
|
|
118
138
|
let drainAmount = BigInt(0);
|
|
119
139
|
let tokenMintHex;
|
|
140
|
+
let splMintBase58;
|
|
120
141
|
let prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, pushNetwork);
|
|
121
142
|
if (((_a = params.funds) === null || _a === void 0 ? void 0 : _a.amount) && params.funds.amount > BigInt(0)) {
|
|
122
143
|
drainAmount = params.funds.amount;
|
|
@@ -124,13 +145,14 @@ function resolveR3SvmDrain(params, sourceChain, pushNetwork) {
|
|
|
124
145
|
if ((token === null || token === void 0 ? void 0 : token.mechanism) === 'approve') {
|
|
125
146
|
const mintPk = new web3_js_1.PublicKey(token.address);
|
|
126
147
|
tokenMintHex = ('0x' + Buffer.from(mintPk.toBytes()).toString('hex'));
|
|
148
|
+
splMintBase58 = token.address;
|
|
127
149
|
prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token).address;
|
|
128
150
|
}
|
|
129
151
|
}
|
|
130
152
|
else if (params.value && params.value > BigInt(0)) {
|
|
131
153
|
drainAmount = params.value;
|
|
132
154
|
}
|
|
133
|
-
return { prc20Token, drainAmount, tokenMintHex };
|
|
155
|
+
return { prc20Token, drainAmount, tokenMintHex, splMintBase58 };
|
|
134
156
|
}
|
|
135
157
|
function buildR3SvmExtraPayload(ctx, params, ueaAddress, sourceChain, inboundNonce) {
|
|
136
158
|
var _a, _b;
|
|
@@ -146,7 +168,7 @@ function buildR3SvmExtraPayload(ctx, params, ueaAddress, sourceChain, inboundNon
|
|
|
146
168
|
ueaAddress,
|
|
147
169
|
allowSelfValueCall: true,
|
|
148
170
|
}));
|
|
149
|
-
const inboundPayload = (0, payload_builders_1.
|
|
171
|
+
const inboundPayload = (0, payload_builders_1.buildInboundUniversalPayloadSvm)(multicallPayload, {
|
|
150
172
|
nonce: inboundNonce !== null && inboundNonce !== void 0 ? inboundNonce : BigInt(0),
|
|
151
173
|
});
|
|
152
174
|
return (0, viem_1.hexToBytes)(inboundPayload);
|
|
@@ -156,13 +178,19 @@ function buildR3SvmExtraPayload(ctx, params, ueaAddress, sourceChain, inboundNon
|
|
|
156
178
|
* resolved `ceaAddress` because the native-value self-call branch skips the
|
|
157
179
|
* multicall when the target IS the CEA (avoids a CEA._handleMulticall revert).
|
|
158
180
|
*/
|
|
159
|
-
function buildR2CeaPayloadEvm(params, ceaAddress, targetAddress) {
|
|
181
|
+
function buildR2CeaPayloadEvm(params, ceaAddress, targetAddress, targetChain, pushNetwork) {
|
|
160
182
|
var _a, _b, _c, _d, _e;
|
|
161
183
|
if (params.migration) {
|
|
162
184
|
return (0, payload_builders_1.buildMigrationPayload)();
|
|
163
185
|
}
|
|
164
186
|
const ceaMulticalls = [];
|
|
165
187
|
const hasData = (0, data_utils_1.hasExecutablePayloadData)(params.data);
|
|
188
|
+
const fundsToken = ((_a = params.funds) === null || _a === void 0 ? void 0 : _a.amount)
|
|
189
|
+
? resolveR2DestinationFundsToken(params.funds.token, targetChain, pushNetwork)
|
|
190
|
+
: undefined;
|
|
191
|
+
const nativeFundsValue = ((_b = params.funds) === null || _b === void 0 ? void 0 : _b.amount) && (fundsToken === null || fundsToken === void 0 ? void 0 : fundsToken.mechanism) === 'native'
|
|
192
|
+
? params.funds.amount
|
|
193
|
+
: BigInt(0);
|
|
166
194
|
if (hasData) {
|
|
167
195
|
if (Array.isArray(params.data)) {
|
|
168
196
|
ceaMulticalls.push(...params.data);
|
|
@@ -171,16 +199,15 @@ function buildR2CeaPayloadEvm(params, ceaAddress, targetAddress) {
|
|
|
171
199
|
// When ERC-20 funds are provided with a single payload, auto-prepend a
|
|
172
200
|
// transfer() call so the tokens minted to the CEA are forwarded to the
|
|
173
201
|
// target address. Mirrors the Route 1 behavior in buildExecuteMulticall.
|
|
174
|
-
if ((
|
|
175
|
-
|
|
176
|
-
if (token && token.mechanism !== 'native') {
|
|
202
|
+
if ((_c = params.funds) === null || _c === void 0 ? void 0 : _c.amount) {
|
|
203
|
+
if (fundsToken && fundsToken.mechanism !== 'native') {
|
|
177
204
|
const erc20Transfer = (0, viem_1.encodeFunctionData)({
|
|
178
205
|
abi: erc20_evm_1.ERC20_EVM,
|
|
179
206
|
functionName: 'transfer',
|
|
180
207
|
args: [targetAddress, params.funds.amount],
|
|
181
208
|
});
|
|
182
209
|
ceaMulticalls.push({
|
|
183
|
-
to:
|
|
210
|
+
to: fundsToken.address,
|
|
184
211
|
value: BigInt(0),
|
|
185
212
|
data: erc20Transfer,
|
|
186
213
|
});
|
|
@@ -190,29 +217,24 @@ function buildR2CeaPayloadEvm(params, ceaAddress, targetAddress) {
|
|
|
190
217
|
// contract receives it alongside the payload call.
|
|
191
218
|
ceaMulticalls.push({
|
|
192
219
|
to: targetAddress,
|
|
193
|
-
value: (
|
|
220
|
+
value: ((_d = params.value) !== null && _d !== void 0 ? _d : BigInt(0)) + nativeFundsValue,
|
|
194
221
|
data: params.data,
|
|
195
222
|
});
|
|
196
223
|
}
|
|
197
224
|
}
|
|
198
225
|
else {
|
|
199
|
-
|
|
200
|
-
? params.funds.token
|
|
201
|
-
: undefined;
|
|
202
|
-
if (((_d = params.funds) === null || _d === void 0 ? void 0 : _d.amount) && token && token.mechanism !== 'native') {
|
|
226
|
+
if (((_e = params.funds) === null || _e === void 0 ? void 0 : _e.amount) && fundsToken && fundsToken.mechanism !== 'native') {
|
|
203
227
|
// ERC-20 funds: forward via ERC20.transfer from the CEA.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
});
|
|
215
|
-
}
|
|
228
|
+
const erc20Transfer = (0, viem_1.encodeFunctionData)({
|
|
229
|
+
abi: erc20_evm_1.ERC20_EVM,
|
|
230
|
+
functionName: 'transfer',
|
|
231
|
+
args: [targetAddress, params.funds.amount],
|
|
232
|
+
});
|
|
233
|
+
ceaMulticalls.push({
|
|
234
|
+
to: fundsToken.address,
|
|
235
|
+
value: BigInt(0),
|
|
236
|
+
data: erc20Transfer,
|
|
237
|
+
});
|
|
216
238
|
}
|
|
217
239
|
if (params.value) {
|
|
218
240
|
// Native value transfer only. Skip the multicall when sending to the CEA
|
|
@@ -225,13 +247,13 @@ function buildR2CeaPayloadEvm(params, ceaAddress, targetAddress) {
|
|
|
225
247
|
});
|
|
226
248
|
}
|
|
227
249
|
}
|
|
228
|
-
else if (
|
|
250
|
+
else if (nativeFundsValue > BigInt(0)) {
|
|
229
251
|
// Native funds (e.g. pETH → ETH): forward value to the recipient.
|
|
230
252
|
// Self-CEA is skipped — the gateway already deposits into the CEA.
|
|
231
253
|
if (targetAddress.toLowerCase() !== ceaAddress.toLowerCase()) {
|
|
232
254
|
ceaMulticalls.push({
|
|
233
255
|
to: targetAddress,
|
|
234
|
-
value:
|
|
256
|
+
value: nativeFundsValue,
|
|
235
257
|
data: '0x',
|
|
236
258
|
});
|
|
237
259
|
}
|
|
@@ -378,7 +400,6 @@ function executeUoaToCea(ctx, params, executeFn) {
|
|
|
378
400
|
// --- 202: Gas estimation (spec-ordered before 203) ---
|
|
379
401
|
let gasFee = BigInt(0);
|
|
380
402
|
let protocolFee = BigInt(0);
|
|
381
|
-
let outboundGasPrice = BigInt(0);
|
|
382
403
|
let gasToken = selectors_1.ZERO_ADDRESS;
|
|
383
404
|
let universalCoreAddress;
|
|
384
405
|
if (prc20Token !== selectors_1.ZERO_ADDRESS) {
|
|
@@ -387,7 +408,6 @@ function executeUoaToCea(ctx, params, executeFn) {
|
|
|
387
408
|
const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, gasLimitForQuery, targetChain);
|
|
388
409
|
gasFee = result.gasFee;
|
|
389
410
|
protocolFee = result.protocolFee;
|
|
390
|
-
outboundGasPrice = result.gasPrice;
|
|
391
411
|
gasToken = result.gasToken;
|
|
392
412
|
universalCoreAddress = result.universalCoreAddress;
|
|
393
413
|
(0, context_1.printLog)(ctx, `executeUoaToCea — queried gas fee: ${gasFee.toString()}, gasToken: ${gasToken}`);
|
|
@@ -414,7 +434,7 @@ function executeUoaToCea(ctx, params, executeFn) {
|
|
|
414
434
|
const ueaNonce = (!isUEADeployed || isNativePushEOA) ? BigInt(0) : yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress);
|
|
415
435
|
// --- Build CEA payload (requires ceaAddress for self-call check) ---
|
|
416
436
|
// Delegates to `buildR2CeaPayloadEvm` — see helper definition above.
|
|
417
|
-
const ceaPayload = buildR2CeaPayloadEvm(params, ceaAddress, targetAddress);
|
|
437
|
+
const ceaPayload = buildR2CeaPayloadEvm(params, ceaAddress, targetAddress, targetChain, ctx.pushNetwork);
|
|
418
438
|
if (params.migration) {
|
|
419
439
|
(0, context_1.printLog)(ctx, `executeUoaToCea — MIGRATION: using raw MIGRATION_SELECTOR payload (${ceaPayload}), native PRC-20 ${prc20Token}`);
|
|
420
440
|
}
|
|
@@ -426,7 +446,7 @@ function executeUoaToCea(ctx, params, executeFn) {
|
|
|
426
446
|
: ceaAddress;
|
|
427
447
|
(0, payload_builders_1.assertCeaFundsParkingInvariant)(targetBytes, ceaPayload);
|
|
428
448
|
const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, gasLimitForQuery, ceaPayload, ueaAddress, // revert recipient is the UEA
|
|
429
|
-
(_d = params.maxPCForGas) !== null && _d !== void 0 ? _d : BigInt(0)
|
|
449
|
+
(_d = params.maxPCForGas) !== null && _d !== void 0 ? _d : BigInt(0));
|
|
430
450
|
(0, context_1.printLog)(ctx, `executeUoaToCea — outbound request: ${JSON.stringify({
|
|
431
451
|
recipient: outboundReq.recipient,
|
|
432
452
|
token: outboundReq.token,
|
|
@@ -605,7 +625,6 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
|
|
|
605
625
|
// --- 202: Gas estimation (spec-ordered before 203) ---
|
|
606
626
|
let gasFee = BigInt(0);
|
|
607
627
|
let protocolFeeSvm = BigInt(0);
|
|
608
|
-
let outboundGasPriceSvm = BigInt(0);
|
|
609
628
|
let gasToken = selectors_1.ZERO_ADDRESS;
|
|
610
629
|
let universalCoreAddress;
|
|
611
630
|
// Effective gas limit seeded from the user param. When the user omits
|
|
@@ -637,7 +656,6 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
|
|
|
637
656
|
});
|
|
638
657
|
gasFee = result.gasFee;
|
|
639
658
|
protocolFeeSvm = result.protocolFee;
|
|
640
|
-
outboundGasPriceSvm = result.gasPrice;
|
|
641
659
|
gasToken = result.gasToken;
|
|
642
660
|
universalCoreAddress = result.universalCoreAddress;
|
|
643
661
|
// When user omits gasLimit (sent as 0), the contract computes fees using
|
|
@@ -664,7 +682,7 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
|
|
|
664
682
|
// --- Build outbound request ---
|
|
665
683
|
// targetBytes: for execute = program id (resolver output); for withdraw = to.address
|
|
666
684
|
const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, effectiveGasLimit, svmPayload, ueaAddress, // revert recipient is the UEA
|
|
667
|
-
(_f = params.maxPCForGas) !== null && _f !== void 0 ? _f : BigInt(0)
|
|
685
|
+
(_f = params.maxPCForGas) !== null && _f !== void 0 ? _f : BigInt(0));
|
|
668
686
|
(0, context_1.printLog)(ctx, `executeUoaToCeaSvm — outbound request: ${JSON.stringify({
|
|
669
687
|
recipient: outboundReq.recipient,
|
|
670
688
|
token: outboundReq.token,
|
|
@@ -971,7 +989,6 @@ function executeCeaToPush(ctx, params, executeFn) {
|
|
|
971
989
|
// 11. Query gas fees from UniversalCore
|
|
972
990
|
let gasFee = BigInt(0);
|
|
973
991
|
let protocolFeeR3 = BigInt(0);
|
|
974
|
-
let outboundGasPriceR3 = BigInt(0);
|
|
975
992
|
let nativeValueForGas = BigInt(0);
|
|
976
993
|
let gasToken = selectors_1.ZERO_ADDRESS;
|
|
977
994
|
let sizingDecisionR3;
|
|
@@ -981,11 +998,10 @@ function executeCeaToPush(ctx, params, executeFn) {
|
|
|
981
998
|
const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, outboundGasLimit, sourceChain);
|
|
982
999
|
gasToken = result.gasToken;
|
|
983
1000
|
gasFee = result.gasFee;
|
|
984
|
-
outboundGasPriceR3 = result.gasPrice;
|
|
985
1001
|
protocolFeeR3 = result.protocolFee;
|
|
986
1002
|
nativeValueForGas = result.nativeValueForGas;
|
|
987
1003
|
sizingDecisionR3 = result.sizing;
|
|
988
|
-
outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, prc20Token, burnAmount, outboundGasLimit, ceaPayload, ueaAddress, (_k = params.maxPCForGas) !== null && _k !== void 0 ? _k : BigInt(0)
|
|
1004
|
+
outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, prc20Token, burnAmount, outboundGasLimit, ceaPayload, ueaAddress, (_k = params.maxPCForGas) !== null && _k !== void 0 ? _k : BigInt(0));
|
|
989
1005
|
(0, context_1.printLog)(ctx, `executeCeaToPush — queried gas fee: ${gasFee.toString()}, gasToken: ${gasToken}, nativeValueForGas: ${nativeValueForGas.toString()}, sizing=${(_m = (_l = result.sizing) === null || _l === void 0 ? void 0 : _l.category) !== null && _m !== void 0 ? _m : 'legacy'}`);
|
|
990
1006
|
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_302_02, sourceChain, protocolFeeR3, gasFee);
|
|
991
1007
|
}
|
|
@@ -1129,8 +1145,8 @@ function executeCeaToPush(ctx, params, executeFn) {
|
|
|
1129
1145
|
*
|
|
1130
1146
|
* Unlike EVM Route 3 which builds CEA multicalls, SVM Route 3 encodes a
|
|
1131
1147
|
* `send_universal_tx_to_uea` instruction as an execute payload targeting
|
|
1132
|
-
* the SVM gateway program (self-call).
|
|
1133
|
-
* the
|
|
1148
|
+
* the SVM gateway program (self-call). For funds-bearing calls the outbound
|
|
1149
|
+
* request amount must match the drain amount embedded in the instruction data.
|
|
1134
1150
|
*/
|
|
1135
1151
|
function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
|
|
1136
1152
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
@@ -1148,10 +1164,9 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
|
|
|
1148
1164
|
const gatewayProgramHex = ('0x' + Buffer.from(programPk.toBytes()).toString('hex'));
|
|
1149
1165
|
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_301, sourceChain, lockerContract);
|
|
1150
1166
|
(0, context_1.printLog)(ctx, `executeCeaToPushSvm — sourceChain: ${sourceChain}, gateway: ${lockerContract}`);
|
|
1151
|
-
// Route 3 SVM
|
|
1152
|
-
//
|
|
1153
|
-
//
|
|
1154
|
-
// encodes; otherwise the gateway rejects with InvalidMint.
|
|
1167
|
+
// Route 3 SVM drains pre-existing funds from the Solana CEA. The Push-side
|
|
1168
|
+
// outbound is a payload relay; do not burn pSOL/pSPL from the UEA for the
|
|
1169
|
+
// drain amount.
|
|
1155
1170
|
const { prc20Token, drainAmount, tokenMintHex } = resolveR3SvmDrain(params, sourceChain, ctx.pushNetwork);
|
|
1156
1171
|
// Derive CEA PDA as revert recipient: ["push_identity", ueaAddress_20_bytes]
|
|
1157
1172
|
const ueaBytes = Buffer.from(ueaAddress.slice(2), 'hex'); // 20 bytes
|
|
@@ -1176,12 +1191,6 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
|
|
|
1176
1191
|
revertRecipientHex: ceaPdaHex,
|
|
1177
1192
|
});
|
|
1178
1193
|
(0, context_1.printLog)(ctx, `executeCeaToPushSvm — drainAmount: ${drainAmount.toString()}, payload length: ${(svmPayload.length - 2) / 2} bytes`);
|
|
1179
|
-
// burnAmount = 0 (payload-only outbound — drain amount lives inside ixData).
|
|
1180
|
-
// Mirrors the 2026-03-19 cascade fix that removed the 1-wei workaround for
|
|
1181
|
-
// SVM payload-only hops because it caused `InsufficientBalance (0xf4d678b8)`
|
|
1182
|
-
// reverts on fresh UEAs that don't hold the source-chain native PRC-20
|
|
1183
|
-
// (e.g. pSOL). The UGPC upgrade post-2026-03-19 already accepts amount = 0
|
|
1184
|
-
// on the cascade path, and the same precompile is used here.
|
|
1185
1194
|
const burnAmount = BigInt(0);
|
|
1186
1195
|
let effectiveGasLimit = (_a = params.gasLimit) !== null && _a !== void 0 ? _a : BigInt(0);
|
|
1187
1196
|
// Build outbound request: recipient = gateway program (self-call)
|
|
@@ -1190,7 +1199,6 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
|
|
|
1190
1199
|
// Query gas fees
|
|
1191
1200
|
let gasFee = BigInt(0);
|
|
1192
1201
|
let protocolFeeR3Svm = BigInt(0);
|
|
1193
|
-
let outboundGasPriceR3Svm = BigInt(0);
|
|
1194
1202
|
let nativeValueForGas = BigInt(0);
|
|
1195
1203
|
let gasToken = selectors_1.ZERO_ADDRESS;
|
|
1196
1204
|
let sizingDecisionR3Svm;
|
|
@@ -1205,17 +1213,16 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
|
|
|
1205
1213
|
prc20Token,
|
|
1206
1214
|
quote: result,
|
|
1207
1215
|
splMintBase58: undefined,
|
|
1208
|
-
burnAmount
|
|
1216
|
+
burnAmount,
|
|
1209
1217
|
pathTag: 'executeCeaToPushSvm',
|
|
1210
1218
|
});
|
|
1211
1219
|
gasToken = result.gasToken;
|
|
1212
1220
|
gasFee = result.gasFee;
|
|
1213
|
-
outboundGasPriceR3Svm = result.gasPrice;
|
|
1214
1221
|
protocolFeeR3Svm = result.protocolFee;
|
|
1215
1222
|
nativeValueForGas = result.nativeValueForGas;
|
|
1216
1223
|
sizingDecisionR3Svm = result.sizing;
|
|
1217
1224
|
effectiveGasLimit = result.gasLimitUsed;
|
|
1218
|
-
outboundReq = (0, payload_builders_1.buildOutboundRequest)(gatewayProgramHex, prc20Token, burnAmount, effectiveGasLimit, svmPayload, ueaAddress, (_c = params.maxPCForGas) !== null && _c !== void 0 ? _c : BigInt(0)
|
|
1225
|
+
outboundReq = (0, payload_builders_1.buildOutboundRequest)(gatewayProgramHex, prc20Token, burnAmount, effectiveGasLimit, svmPayload, ueaAddress, (_c = params.maxPCForGas) !== null && _c !== void 0 ? _c : BigInt(0));
|
|
1219
1226
|
(0, context_1.printLog)(ctx, `executeCeaToPushSvm — resolved effectiveGasLimit: ${effectiveGasLimit} ` +
|
|
1220
1227
|
`(gasFee=${result.gasFee}, gasPrice=${result.gasPrice})`);
|
|
1221
1228
|
(0, context_1.printLog)(ctx, `executeCeaToPushSvm — gasFee: ${gasFee.toString()}, gasToken: ${gasToken}, nativeValueForGas: ${nativeValueForGas.toString()}, sizing=${(_e = (_d = result.sizing) === null || _d === void 0 ? void 0 : _d.category) !== null && _e !== void 0 ? _e : 'legacy'}`);
|
|
@@ -1278,8 +1285,8 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
|
|
|
1278
1285
|
}
|
|
1279
1286
|
// SVM warn-threshold telemetry (no truncation — pre-flight handles drain protection).
|
|
1280
1287
|
(0, preflight_1.maybeFireSvmWarnThreshold)(ctx, nativeValueForGas, gasToken, 'R3_SVM');
|
|
1281
|
-
// Pre-flight (R3 SVM): no PRC-20 burn check.
|
|
1282
|
-
//
|
|
1288
|
+
// Pre-flight (R3 SVM): no PRC-20 burn check. The drain amount lives in the
|
|
1289
|
+
// embedded Solana instruction and is withdrawn from the external CEA.
|
|
1283
1290
|
(0, preflight_1.runPreflight)({
|
|
1284
1291
|
ctx,
|
|
1285
1292
|
ueaAddress,
|
|
@@ -1355,7 +1362,7 @@ function executeCeaToCea(ctx, params, executeFn) {
|
|
|
1355
1362
|
*/
|
|
1356
1363
|
function buildPayloadForRoute(ctx, params, route, nonce) {
|
|
1357
1364
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1358
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q
|
|
1365
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
1359
1366
|
const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
|
|
1360
1367
|
switch (route) {
|
|
1361
1368
|
case route_detector_1.TransactionRoute.UOA_TO_PUSH: {
|
|
@@ -1406,56 +1413,19 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
|
|
|
1406
1413
|
}
|
|
1407
1414
|
// EVM path: Resolve CEA address first (needed for self-transfer check)
|
|
1408
1415
|
const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, target.chain, (_d = ctx.rpcUrls[target.chain]) === null || _d === void 0 ? void 0 : _d[0]);
|
|
1409
|
-
|
|
1410
|
-
const
|
|
1411
|
-
if ((0, data_utils_1.hasExecutablePayloadData)(params.data)) {
|
|
1412
|
-
if (Array.isArray(params.data)) {
|
|
1413
|
-
multicalls.push(...params.data);
|
|
1414
|
-
}
|
|
1415
|
-
else {
|
|
1416
|
-
// Single call with data. Native value (if any) is already delivered to
|
|
1417
|
-
// CEA by the Vault via executeUniversalTx{value: amount}(). Attaching
|
|
1418
|
-
// value to the call would revert if the target function is not payable.
|
|
1419
|
-
// To call a payable function with value, use explicit multicalls.
|
|
1420
|
-
multicalls.push({
|
|
1421
|
-
to: target.address,
|
|
1422
|
-
value: BigInt(0),
|
|
1423
|
-
data: params.data,
|
|
1424
|
-
});
|
|
1425
|
-
}
|
|
1426
|
-
}
|
|
1427
|
-
else if (params.value) {
|
|
1428
|
-
// Skip multicall when sending native value to own CEA — gateway deposits directly.
|
|
1429
|
-
// Self-call with value would revert (CEA._handleMulticall rejects it).
|
|
1430
|
-
if (target.address.toLowerCase() !== ceaAddress.toLowerCase()) {
|
|
1431
|
-
multicalls.push({
|
|
1432
|
-
to: target.address,
|
|
1433
|
-
value: params.value,
|
|
1434
|
-
data: '0x',
|
|
1435
|
-
});
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1438
|
-
const payload = (0, payload_builders_1.buildCeaMulticallPayload)(multicalls);
|
|
1439
|
-
let prc20Token = selectors_1.ZERO_ADDRESS;
|
|
1440
|
-
let burnAmount = BigInt(0);
|
|
1441
|
-
if ((_e = params.funds) === null || _e === void 0 ? void 0 : _e.amount) {
|
|
1442
|
-
const token = params.funds.token;
|
|
1443
|
-
if (token) {
|
|
1444
|
-
prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token).address;
|
|
1445
|
-
burnAmount = params.funds.amount;
|
|
1446
|
-
}
|
|
1447
|
-
}
|
|
1416
|
+
const payload = buildR2CeaPayloadEvm(params, ceaAddress, target.address, target.chain, ctx.pushNetwork);
|
|
1417
|
+
const { prc20Token, burnAmount } = resolveR2Prc20TokenEvm(params, target.chain, ctx.pushNetwork);
|
|
1448
1418
|
const targetBytes = payload === '0x'
|
|
1449
1419
|
? selectors_1.ZERO_ADDRESS
|
|
1450
1420
|
: ceaAddress;
|
|
1451
1421
|
(0, payload_builders_1.assertCeaFundsParkingInvariant)(targetBytes, payload);
|
|
1452
|
-
const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, (
|
|
1422
|
+
const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, (_e = params.gasLimit) !== null && _e !== void 0 ? _e : BigInt(0), payload, ueaAddress, (_f = params.maxPCForGas) !== null && _f !== void 0 ? _f : BigInt(0));
|
|
1453
1423
|
return { payload, gatewayRequest: outboundReq };
|
|
1454
1424
|
}
|
|
1455
1425
|
case route_detector_1.TransactionRoute.CEA_TO_PUSH: {
|
|
1456
1426
|
// Route 3: CEA -> Push Chain
|
|
1457
1427
|
// Build CEA multicall (sendUniversalTxToUEA self-call) and wrap in outbound
|
|
1458
|
-
if (!((
|
|
1428
|
+
if (!((_g = params.from) === null || _g === void 0 ? void 0 : _g.chain)) {
|
|
1459
1429
|
throw new Error('Route 3 (CEA -> Push) requires from.chain');
|
|
1460
1430
|
}
|
|
1461
1431
|
const sourceChain = params.from.chain;
|
|
@@ -1482,17 +1452,17 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
|
|
|
1482
1452
|
});
|
|
1483
1453
|
// Payload-only outbound: drain amount lives inside ixData.
|
|
1484
1454
|
const burnAmount = BigInt(0);
|
|
1485
|
-
const outboundReq = (0, payload_builders_1.buildOutboundRequest)(gatewayProgramHex, prc20Token, burnAmount, (
|
|
1455
|
+
const outboundReq = (0, payload_builders_1.buildOutboundRequest)(gatewayProgramHex, prc20Token, burnAmount, (_h = params.gasLimit) !== null && _h !== void 0 ? _h : BigInt(0), svmPayload, ueaAddress, (_j = params.maxPCForGas) !== null && _j !== void 0 ? _j : BigInt(0));
|
|
1486
1456
|
return { payload: svmPayload, gatewayRequest: outboundReq };
|
|
1487
1457
|
}
|
|
1488
|
-
const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, sourceChain, (
|
|
1458
|
+
const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, sourceChain, (_k = ctx.rpcUrls[sourceChain]) === null || _k === void 0 ? void 0 : _k[0]);
|
|
1489
1459
|
// CEA auto-deploys on-chain: Vault.finalizeUniversalTx calls CEAFactory.deployCEA()
|
|
1490
1460
|
// if CEA doesn't exist yet. No SDK-side blocking needed.
|
|
1491
1461
|
// Build CEA multicalls (self-calls via sendUniversalTxToUEA)
|
|
1492
1462
|
const ceaMulticalls = [];
|
|
1493
1463
|
let tokenAddress = selectors_1.ZERO_ADDRESS;
|
|
1494
1464
|
let amount = BigInt(0);
|
|
1495
|
-
if ((
|
|
1465
|
+
if ((_l = params.funds) === null || _l === void 0 ? void 0 : _l.amount) {
|
|
1496
1466
|
const token = params.funds.token;
|
|
1497
1467
|
if (token) {
|
|
1498
1468
|
if (token.mechanism === 'native') {
|
|
@@ -1516,7 +1486,7 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
|
|
|
1516
1486
|
// Wrap in UniversalPayload struct for the relay.
|
|
1517
1487
|
let pushPayload = '0x';
|
|
1518
1488
|
if ((0, data_utils_1.hasExecutablePayloadData)(params.data) ||
|
|
1519
|
-
(((
|
|
1489
|
+
(((_m = params.funds) === null || _m === void 0 ? void 0 : _m.amount) && ((_o = params.value) !== null && _o !== void 0 ? _o : BigInt(0)) > BigInt(0))) {
|
|
1520
1490
|
const multicallData = (0, payload_builders_1.buildExecuteMulticall)({
|
|
1521
1491
|
execute: {
|
|
1522
1492
|
to: pushDestination,
|
|
@@ -1538,8 +1508,8 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
|
|
|
1538
1508
|
// Route 3: CEA uses its own pre-existing balance — no PRC-20 burn needed.
|
|
1539
1509
|
// burnAmount = 0 makes this a payload-only outbound relay.
|
|
1540
1510
|
const burnAmount = BigInt(0);
|
|
1541
|
-
const outboundGasLimit = (
|
|
1542
|
-
const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, prc20Token, burnAmount, outboundGasLimit, ceaPayload, ueaAddress, (
|
|
1511
|
+
const outboundGasLimit = (_p = params.gasLimit) !== null && _p !== void 0 ? _p : selectors_1.DEFAULT_CEA_TO_PUSH_GAS_LIMIT;
|
|
1512
|
+
const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, prc20Token, burnAmount, outboundGasLimit, ceaPayload, ueaAddress, (_q = params.maxPCForGas) !== null && _q !== void 0 ? _q : BigInt(0));
|
|
1543
1513
|
return { payload: ceaPayload, gatewayRequest: outboundReq };
|
|
1544
1514
|
}
|
|
1545
1515
|
default:
|
|
@@ -172,8 +172,9 @@ function reconstructR3(universalTxResponse, universalTxData, isFailed, errorMsg)
|
|
|
172
172
|
events.push(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_302_02](sourceChain, BigInt(0), BigInt(0)));
|
|
173
173
|
events.push(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_303_01](sourceChain));
|
|
174
174
|
events.push(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_303_02](universalTxResponse.from, ceaAddress, sourceChain));
|
|
175
|
-
// R3 pre-flight reconstruction
|
|
176
|
-
//
|
|
175
|
+
// R3 pre-flight reconstruction is intentionally approximate: historical
|
|
176
|
+
// response data does not include the SDK-side PRC-20 balance snapshot, so
|
|
177
|
+
// replay only the native check.
|
|
177
178
|
// R3 single-route uses 303-04 (R3 bucket); see runPreflight in preflight.ts.
|
|
178
179
|
events.push(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_303_04](BigInt(0), BigInt(0), true, universalTxResponse.from, 'R3_EVM', { kind: 'NATIVE' }));
|
|
179
180
|
events.push(progress_hook_1.default[progress_hook_types_1.PROGRESS_HOOK.SEND_TX_304_01]());
|
|
@@ -275,7 +275,7 @@ export interface WaitOptions {
|
|
|
275
275
|
inboundTimeoutMs?: number;
|
|
276
276
|
/**
|
|
277
277
|
* Override the poll interval (ms) between outbound status checks. Default
|
|
278
|
-
* is
|
|
278
|
+
* is 3000ms. Lower values give faster feedback on fast relays; higher
|
|
279
279
|
* values reduce RPC pressure. Only applies to R2 / R3 outbound polling.
|
|
280
280
|
*/
|
|
281
281
|
outboundPollingIntervalMs?: number;
|
|
@@ -671,7 +671,7 @@ export interface CascadeHopInfo {
|
|
|
671
671
|
export interface CascadeTrackOptions {
|
|
672
672
|
/** Polling interval (default: 5000ms) */
|
|
673
673
|
pollingIntervalMs?: number;
|
|
674
|
-
/** Total timeout (default:
|
|
674
|
+
/** Total timeout (default: 300000ms = 5 min) */
|
|
675
675
|
timeout?: number;
|
|
676
676
|
/** Per-hop progress callback */
|
|
677
677
|
progressHook?: (event: CascadeProgressEvent) => void;
|
|
@@ -777,16 +777,16 @@ export interface OutboundTxDetails {
|
|
|
777
777
|
*/
|
|
778
778
|
export interface WaitForOutboundOptions {
|
|
779
779
|
/**
|
|
780
|
-
* Initial wait before first poll (default:
|
|
780
|
+
* Initial wait before first poll (default: 15000ms)
|
|
781
781
|
* Gives relay time to process before we start polling
|
|
782
782
|
*/
|
|
783
783
|
initialWaitMs?: number;
|
|
784
784
|
/**
|
|
785
|
-
* Interval between polls (default:
|
|
785
|
+
* Interval between polls (default: 3000ms)
|
|
786
786
|
*/
|
|
787
787
|
pollingIntervalMs?: number;
|
|
788
788
|
/**
|
|
789
|
-
* Total timeout in milliseconds (default:
|
|
789
|
+
* Total timeout in milliseconds (default: 300000ms)
|
|
790
790
|
* Measured from start, includes initial wait
|
|
791
791
|
*/
|
|
792
792
|
timeout?: number;
|
|
@@ -35,7 +35,8 @@ export declare const MULTICALL_TUPLE_TYPE: {
|
|
|
35
35
|
*/
|
|
36
36
|
export declare function buildCeaMulticallPayload(multicalls: MultiCall[]): `0x${string}`;
|
|
37
37
|
/**
|
|
38
|
-
* Build an ABI-encoded UniversalPayload struct for inbound relay
|
|
38
|
+
* Build an ABI-encoded UniversalPayload struct for EVM-origin inbound relay
|
|
39
|
+
* (Route 3 CEA→Push).
|
|
39
40
|
*
|
|
40
41
|
* The relay and Push Chain gateway expect the payload parameter of sendUniversalTxToUEA
|
|
41
42
|
* to be a full UniversalPayload struct: (address to, uint256 value, bytes data, uint256 gasLimit,
|
|
@@ -53,6 +54,17 @@ export declare function buildInboundUniversalPayload(multicallData: `0x${string}
|
|
|
53
54
|
nonce?: bigint;
|
|
54
55
|
deadline?: bigint;
|
|
55
56
|
}): `0x${string}`;
|
|
57
|
+
/**
|
|
58
|
+
* Build a Borsh-encoded UniversalPayload for SVM-origin inbound relay.
|
|
59
|
+
*
|
|
60
|
+
* Push Chain decodes SVM-origin UniversalTx payloads with
|
|
61
|
+
* DecodeUniversalPayloadSolana, not the EVM ABI decoder used for EVM origins.
|
|
62
|
+
*/
|
|
63
|
+
export declare function buildInboundUniversalPayloadSvm(multicallData: `0x${string}`, opts?: {
|
|
64
|
+
gasLimit?: bigint;
|
|
65
|
+
nonce?: bigint;
|
|
66
|
+
deadline?: bigint;
|
|
67
|
+
}): `0x${string}`;
|
|
56
68
|
/**
|
|
57
69
|
* Build sendUniversalTxToUEA call for CEA self-call (Route 3)
|
|
58
70
|
*
|
|
@@ -4,6 +4,7 @@ exports.MULTICALL_TUPLE_TYPE = void 0;
|
|
|
4
4
|
exports.buildExecuteMulticall = buildExecuteMulticall;
|
|
5
5
|
exports.buildCeaMulticallPayload = buildCeaMulticallPayload;
|
|
6
6
|
exports.buildInboundUniversalPayload = buildInboundUniversalPayload;
|
|
7
|
+
exports.buildInboundUniversalPayloadSvm = buildInboundUniversalPayloadSvm;
|
|
7
8
|
exports.buildSendUniversalTxToUEA = buildSendUniversalTxToUEA;
|
|
8
9
|
exports.buildOutboundRequest = buildOutboundRequest;
|
|
9
10
|
exports.assertCeaFundsParkingInvariant = assertCeaFundsParkingInvariant;
|
|
@@ -24,6 +25,8 @@ const selectors_1 = require("../constants/selectors");
|
|
|
24
25
|
const chain_1 = require("../constants/chain");
|
|
25
26
|
const enums_1 = require("../constants/enums");
|
|
26
27
|
const data_utils_1 = require("./data-utils");
|
|
28
|
+
const tx_1 = require("../generated/v1/tx");
|
|
29
|
+
const signing_1 = require("./internals/signing");
|
|
27
30
|
function buildExecuteMulticall({ execute, ueaAddress, logger, allowSelfValueCall = false, }) {
|
|
28
31
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
29
32
|
const log = (msg) => logger === null || logger === void 0 ? void 0 : logger(msg);
|
|
@@ -161,7 +164,8 @@ function buildCeaMulticallPayload(multicalls) {
|
|
|
161
164
|
return `${selectors_1.UEA_MULTICALL_SELECTOR}${encoded.slice(2)}`;
|
|
162
165
|
}
|
|
163
166
|
/**
|
|
164
|
-
* Build an ABI-encoded UniversalPayload struct for inbound relay
|
|
167
|
+
* Build an ABI-encoded UniversalPayload struct for EVM-origin inbound relay
|
|
168
|
+
* (Route 3 CEA→Push).
|
|
165
169
|
*
|
|
166
170
|
* The relay and Push Chain gateway expect the payload parameter of sendUniversalTxToUEA
|
|
167
171
|
* to be a full UniversalPayload struct: (address to, uint256 value, bytes data, uint256 gasLimit,
|
|
@@ -205,6 +209,27 @@ function buildInboundUniversalPayload(multicallData, opts) {
|
|
|
205
209
|
},
|
|
206
210
|
]);
|
|
207
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* Build a Borsh-encoded UniversalPayload for SVM-origin inbound relay.
|
|
214
|
+
*
|
|
215
|
+
* Push Chain decodes SVM-origin UniversalTx payloads with
|
|
216
|
+
* DecodeUniversalPayloadSolana, not the EVM ABI decoder used for EVM origins.
|
|
217
|
+
*/
|
|
218
|
+
function buildInboundUniversalPayloadSvm(multicallData, opts) {
|
|
219
|
+
var _a, _b, _c;
|
|
220
|
+
const payload = {
|
|
221
|
+
to: selectors_1.ZERO_ADDRESS,
|
|
222
|
+
value: BigInt(0).toString(),
|
|
223
|
+
data: multicallData,
|
|
224
|
+
gasLimit: ((_a = opts === null || opts === void 0 ? void 0 : opts.gasLimit) !== null && _a !== void 0 ? _a : BigInt(100000000)).toString(),
|
|
225
|
+
maxFeePerGas: BigInt(10000000000).toString(),
|
|
226
|
+
maxPriorityFeePerGas: BigInt(0).toString(),
|
|
227
|
+
nonce: ((_b = opts === null || opts === void 0 ? void 0 : opts.nonce) !== null && _b !== void 0 ? _b : BigInt(0)).toString(),
|
|
228
|
+
deadline: ((_c = opts === null || opts === void 0 ? void 0 : opts.deadline) !== null && _c !== void 0 ? _c : BigInt(9999999999)).toString(),
|
|
229
|
+
vType: tx_1.VerificationType.universalTxVerification,
|
|
230
|
+
};
|
|
231
|
+
return `0x${(0, signing_1.encodeUniversalPayloadSvm)(payload).toString('hex')}`;
|
|
232
|
+
}
|
|
208
233
|
/**
|
|
209
234
|
* Build sendUniversalTxToUEA call for CEA self-call (Route 3)
|
|
210
235
|
*
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
* utxId = sha256(`<sourceChainCaip>:<txHash>:<logIndex>`)
|
|
7
7
|
* See push-chain/x/uexecutor/types/keys.go:49-53 (GetInboundUniversalTxKey).
|
|
8
8
|
*
|
|
9
|
+
* For SVM sources, `<txHash>` is the 0x-prefixed hex encoding of the 64-byte
|
|
10
|
+
* Solana signature and `<logIndex>` is the event's index in `meta.logMessages`.
|
|
11
|
+
*
|
|
9
12
|
* For Push-initiated outbounds (pcTx entries) the formula drops the log index:
|
|
10
13
|
* utxId = sha256(`<pcChainCaip>:<pcTxHash>`)
|
|
11
14
|
* See push-chain/x/uexecutor/types/keys.go:63-67 (GetPcUniversalTxKey).
|
|
@@ -14,6 +14,9 @@ const tslib_1 = require("tslib");
|
|
|
14
14
|
* utxId = sha256(`<sourceChainCaip>:<txHash>:<logIndex>`)
|
|
15
15
|
* See push-chain/x/uexecutor/types/keys.go:49-53 (GetInboundUniversalTxKey).
|
|
16
16
|
*
|
|
17
|
+
* For SVM sources, `<txHash>` is the 0x-prefixed hex encoding of the 64-byte
|
|
18
|
+
* Solana signature and `<logIndex>` is the event's index in `meta.logMessages`.
|
|
19
|
+
*
|
|
17
20
|
* For Push-initiated outbounds (pcTx entries) the formula drops the log index:
|
|
18
21
|
* utxId = sha256(`<pcChainCaip>:<pcTxHash>`)
|
|
19
22
|
* See push-chain/x/uexecutor/types/keys.go:63-67 (GetPcUniversalTxKey).
|
|
@@ -22,6 +25,7 @@ const tslib_1 = require("tslib");
|
|
|
22
25
|
* `pushClient.getUniversalTxByIdV2`, no event-attribute predicate needed.
|
|
23
26
|
*/
|
|
24
27
|
const viem_1 = require("viem");
|
|
28
|
+
const bs58_1 = require("../internal/bs58");
|
|
25
29
|
const normalize_1 = require("./normalize");
|
|
26
30
|
const STATUS_NAMES = {
|
|
27
31
|
0: 'UNIVERSAL_TX_STATUS_UNSPECIFIED',
|
|
@@ -45,14 +49,25 @@ const INBOUND_EVENT_NAMES = new Set([
|
|
|
45
49
|
'UniversalTx',
|
|
46
50
|
'RevertUniversalTx',
|
|
47
51
|
]);
|
|
52
|
+
const HEX_64_BYTE_RE = /^(?:0x)?[0-9a-fA-F]{128}$/;
|
|
53
|
+
function normalizeInboundTxHashForKey(sourceChainCaip, externalTxHash) {
|
|
54
|
+
if (!sourceChainCaip.startsWith('solana:')) {
|
|
55
|
+
return externalTxHash.startsWith('0x')
|
|
56
|
+
? externalTxHash
|
|
57
|
+
: `0x${externalTxHash}`;
|
|
58
|
+
}
|
|
59
|
+
if (externalTxHash.startsWith('0x'))
|
|
60
|
+
return externalTxHash;
|
|
61
|
+
if (HEX_64_BYTE_RE.test(externalTxHash))
|
|
62
|
+
return `0x${externalTxHash}`;
|
|
63
|
+
return (0, viem_1.toHex)(bs58_1.bs58.decode(externalTxHash));
|
|
64
|
+
}
|
|
48
65
|
/**
|
|
49
66
|
* Deterministic child universalTxId for an inbound event on an external chain.
|
|
50
67
|
* Mirrors `GetInboundUniversalTxKey` from the chain module.
|
|
51
68
|
*/
|
|
52
69
|
function deriveChildUniversalTxId(sourceChainCaip, externalTxHash, logIndex) {
|
|
53
|
-
const normHash = externalTxHash
|
|
54
|
-
? externalTxHash
|
|
55
|
-
: `0x${externalTxHash}`;
|
|
70
|
+
const normHash = normalizeInboundTxHashForKey(sourceChainCaip, externalTxHash);
|
|
56
71
|
const input = `${sourceChainCaip}:${normHash}:${logIndex}`;
|
|
57
72
|
return (0, viem_1.sha256)((0, viem_1.toBytes)(input));
|
|
58
73
|
}
|
|
@@ -15,12 +15,9 @@ const tslib_1 = require("tslib");
|
|
|
15
15
|
* return, same universalTxId derivation strategy (primary-log fallback
|
|
16
16
|
* via deriveChildUniversalTxId on source-chain inbounds).
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* If the keeper uses a different representation (hex bytes, sub_tx_id
|
|
22
|
-
* field, etc.) the `rawSource` argument to deriveChildUniversalTxId below
|
|
23
|
-
* needs to swap — single line.
|
|
18
|
+
* The Push keeper stores SVM inbound ids using the 0x-prefixed hex encoding
|
|
19
|
+
* of the 64-byte Solana signature and the index of the Anchor event inside
|
|
20
|
+
* `meta.logMessages`: `${caip}:${hexSig}:${logMessageIndex}`.
|
|
24
21
|
*/
|
|
25
22
|
const anchor_1 = require("@coral-xyz/anchor");
|
|
26
23
|
const web3_js_1 = require("@solana/web3.js");
|
|
@@ -91,12 +88,11 @@ function detectUniversalTxSvm(signature_1, chain_2) {
|
|
|
91
88
|
}
|
|
92
89
|
const eventCoder = new anchor_1.BorshEventCoder(universalGatewayV0_json_1.default);
|
|
93
90
|
const matchingLogs = [];
|
|
94
|
-
// Walk logMessages for `Program data:` entries. Each matched event
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
for (const rawLog of logMessages) {
|
|
91
|
+
// Walk logMessages for `Program data:` entries. Each matched event keeps
|
|
92
|
+
// the actual Solana log-message index because that is the value validators
|
|
93
|
+
// submit as the inbound log_index and the keeper uses for the UTX key.
|
|
94
|
+
for (let logIndex = 0; logIndex < logMessages.length; logIndex++) {
|
|
95
|
+
const rawLog = logMessages[logIndex];
|
|
100
96
|
if (typeof rawLog !== 'string')
|
|
101
97
|
continue;
|
|
102
98
|
const prefix = 'Program data: ';
|
|
@@ -135,10 +131,9 @@ function detectUniversalTxSvm(signature_1, chain_2) {
|
|
|
135
131
|
matchingLogs.push({
|
|
136
132
|
eventName,
|
|
137
133
|
address: emitter,
|
|
138
|
-
logIndex
|
|
134
|
+
logIndex,
|
|
139
135
|
args,
|
|
140
136
|
});
|
|
141
|
-
ordinal += 1;
|
|
142
137
|
}
|
|
143
138
|
if (matchingLogs.length === 0) {
|
|
144
139
|
notes.push('svm tx has no decoded universal-tx events');
|
|
@@ -148,8 +143,7 @@ function detectUniversalTxSvm(signature_1, chain_2) {
|
|
|
148
143
|
notes.push(...classified.notes);
|
|
149
144
|
const decoded = Object.assign({}, classified.decoded);
|
|
150
145
|
// Inbound fallback for universalTxId: same formula the EVM branch uses,
|
|
151
|
-
//
|
|
152
|
-
// see deriveSvmChildUniversalTxId in child-inbounds.ts).
|
|
146
|
+
// with the canonical 0x-hex Solana signature and log-message index.
|
|
153
147
|
if (!decoded.universalTxId) {
|
|
154
148
|
const caip = chain;
|
|
155
149
|
if (INBOUND_KINDS.has(classified.kind) &&
|