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