@pushchain/core 5.1.3 → 5.1.4-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/package.json +29 -6
  2. package/src/lib/constants/abi/prc20.evm.d.ts +22 -2
  3. package/src/lib/constants/abi/prc20.evm.js +11 -3
  4. package/src/lib/constants/abi/universalGatewayPC.evm.d.ts +59 -0
  5. package/src/lib/constants/abi/universalGatewayPC.evm.js +26 -0
  6. package/src/lib/constants/chain.d.ts +4 -0
  7. package/src/lib/constants/chain.js +6 -2
  8. package/src/lib/index.d.ts +2 -2
  9. package/src/lib/internal/bs58.d.ts +2 -0
  10. package/src/lib/internal/bs58.js +7 -0
  11. package/src/lib/orchestrator/cea-utils.d.ts +0 -9
  12. package/src/lib/orchestrator/cea-utils.js +15 -4
  13. package/src/lib/orchestrator/internals/account-manager.d.ts +16 -0
  14. package/src/lib/orchestrator/internals/account-manager.js +191 -0
  15. package/src/lib/orchestrator/internals/cascade.d.ts +23 -0
  16. package/src/lib/orchestrator/internals/cascade.js +945 -0
  17. package/src/lib/orchestrator/internals/confirmation.d.ts +9 -0
  18. package/src/lib/orchestrator/internals/confirmation.js +123 -0
  19. package/src/lib/orchestrator/internals/context.d.ts +38 -0
  20. package/src/lib/orchestrator/internals/context.js +49 -0
  21. package/src/lib/orchestrator/internals/execute-funds-only.d.ts +11 -0
  22. package/src/lib/orchestrator/internals/execute-funds-only.js +219 -0
  23. package/src/lib/orchestrator/internals/execute-funds-payload.d.ts +11 -0
  24. package/src/lib/orchestrator/internals/execute-funds-payload.js +222 -0
  25. package/src/lib/orchestrator/internals/execute-standard.d.ts +13 -0
  26. package/src/lib/orchestrator/internals/execute-standard.js +225 -0
  27. package/src/lib/orchestrator/internals/gas-calculator.d.ts +38 -0
  28. package/src/lib/orchestrator/internals/gas-calculator.js +424 -0
  29. package/src/lib/orchestrator/internals/gateway-client.d.ts +24 -0
  30. package/src/lib/orchestrator/internals/gateway-client.js +191 -0
  31. package/src/lib/orchestrator/internals/helpers.d.ts +26 -0
  32. package/src/lib/orchestrator/internals/helpers.js +181 -0
  33. package/src/lib/orchestrator/internals/index.d.ts +24 -0
  34. package/src/lib/orchestrator/internals/index.js +132 -0
  35. package/src/lib/orchestrator/internals/outbound-sync.d.ts +49 -0
  36. package/src/lib/orchestrator/internals/outbound-sync.js +357 -0
  37. package/src/lib/orchestrator/internals/outbound-tracker.d.ts +8 -0
  38. package/src/lib/orchestrator/internals/outbound-tracker.js +64 -0
  39. package/src/lib/orchestrator/internals/payload-builder.d.ts +24 -0
  40. package/src/lib/orchestrator/internals/payload-builder.js +178 -0
  41. package/src/lib/orchestrator/internals/push-chain-tx.d.ts +32 -0
  42. package/src/lib/orchestrator/internals/push-chain-tx.js +207 -0
  43. package/src/lib/orchestrator/internals/quote.d.ts +9 -0
  44. package/src/lib/orchestrator/internals/quote.js +114 -0
  45. package/src/lib/orchestrator/internals/rescue.d.ts +20 -0
  46. package/src/lib/orchestrator/internals/rescue.js +112 -0
  47. package/src/lib/orchestrator/internals/response-builder.d.ts +26 -0
  48. package/src/lib/orchestrator/internals/response-builder.js +504 -0
  49. package/src/lib/orchestrator/internals/route-handlers.d.ts +77 -0
  50. package/src/lib/orchestrator/internals/route-handlers.js +1192 -0
  51. package/src/lib/orchestrator/internals/signing.d.ts +27 -0
  52. package/src/lib/orchestrator/internals/signing.js +258 -0
  53. package/src/lib/orchestrator/internals/svm-bridge.d.ts +21 -0
  54. package/src/lib/orchestrator/internals/svm-bridge.js +122 -0
  55. package/src/lib/orchestrator/internals/svm-helpers.d.ts +35 -0
  56. package/src/lib/orchestrator/internals/svm-helpers.js +143 -0
  57. package/src/lib/orchestrator/internals/tx-transformer.d.ts +12 -0
  58. package/src/lib/orchestrator/internals/tx-transformer.js +120 -0
  59. package/src/lib/orchestrator/internals/uea-manager.d.ts +17 -0
  60. package/src/lib/orchestrator/internals/uea-manager.js +164 -0
  61. package/src/lib/orchestrator/orchestrator.d.ts +33 -431
  62. package/src/lib/orchestrator/orchestrator.js +124 -6125
  63. package/src/lib/orchestrator/orchestrator.types.d.ts +11 -10
  64. package/src/lib/orchestrator/payload-builders.js +34 -8
  65. package/src/lib/price-fetch/price-fetch.js +19 -7
  66. package/src/lib/push-chain/push-chain.d.ts +6 -0
  67. package/src/lib/push-chain/push-chain.js +10 -2
  68. package/src/lib/push-client/push-client.d.ts +3 -0
  69. package/src/lib/push-client/push-client.js +21 -31
  70. package/src/lib/universal/account/account.d.ts +35 -16
  71. package/src/lib/universal/account/account.js +298 -16
  72. package/src/lib/universal/signer/signer.d.ts +2 -3
  73. package/src/lib/universal/signer/signer.js +65 -115
  74. package/src/lib/universal/universal.types.d.ts +41 -0
  75. package/src/lib/utils.d.ts +7 -2
  76. package/src/lib/utils.js +65 -34
  77. package/src/lib/vm-client/evm-client.js +3 -11
  78. package/src/lib/vm-client/svm-client.d.ts +0 -4
  79. package/src/lib/vm-client/svm-client.js +2 -8
  80. package/src/index.js.map +0 -1
  81. package/src/lib/cache/cache.js.map +0 -1
  82. package/src/lib/constants/abi/cea.evm.js.map +0 -1
  83. package/src/lib/constants/abi/ceaFactory.evm.js.map +0 -1
  84. package/src/lib/constants/abi/erc20.evm.js.map +0 -1
  85. package/src/lib/constants/abi/factoryV1.js.map +0 -1
  86. package/src/lib/constants/abi/feeLocker.evm.js.map +0 -1
  87. package/src/lib/constants/abi/index.js.map +0 -1
  88. package/src/lib/constants/abi/prc20.evm.js.map +0 -1
  89. package/src/lib/constants/abi/uea-factory.js.map +0 -1
  90. package/src/lib/constants/abi/uea.evm.js.map +0 -1
  91. package/src/lib/constants/abi/uea.svm.js.map +0 -1
  92. package/src/lib/constants/abi/universalGateway.evm.js.map +0 -1
  93. package/src/lib/constants/abi/universalGatewayPC.evm.js.map +0 -1
  94. package/src/lib/constants/abi/universalGatewayV0.evm.js.map +0 -1
  95. package/src/lib/constants/chain.js.map +0 -1
  96. package/src/lib/constants/enums.js.map +0 -1
  97. package/src/lib/constants/index.js.map +0 -1
  98. package/src/lib/constants/selectors.js.map +0 -1
  99. package/src/lib/constants/tokens.js.map +0 -1
  100. package/src/lib/generated/uexecutor/v1/query.js.map +0 -1
  101. package/src/lib/generated/uexecutor/v1/types.js.map +0 -1
  102. package/src/lib/generated/uexecutor/v2/index.js.map +0 -1
  103. package/src/lib/generated/uexecutor/v2/query.js.map +0 -1
  104. package/src/lib/generated/uexecutor/v2/types.js.map +0 -1
  105. package/src/lib/generated/v1/tx.js.map +0 -1
  106. package/src/lib/index.js.map +0 -1
  107. package/src/lib/internal/buffer.js.map +0 -1
  108. package/src/lib/internal/global-buffer-polyfill.js.map +0 -1
  109. package/src/lib/orchestrator/cea-utils.js.map +0 -1
  110. package/src/lib/orchestrator/orchestrator.js.map +0 -1
  111. package/src/lib/orchestrator/orchestrator.types.js.map +0 -1
  112. package/src/lib/orchestrator/payload-builders.js.map +0 -1
  113. package/src/lib/orchestrator/route-detector.js.map +0 -1
  114. package/src/lib/price-fetch/price-fetch.js.map +0 -1
  115. package/src/lib/progress-hook/progress-hook.js.map +0 -1
  116. package/src/lib/progress-hook/progress-hook.types.js.map +0 -1
  117. package/src/lib/push-chain/helpers/abis.js.map +0 -1
  118. package/src/lib/push-chain/helpers/addresses.js.map +0 -1
  119. package/src/lib/push-chain/push-chain.js.map +0 -1
  120. package/src/lib/push-client/push-client.js.map +0 -1
  121. package/src/lib/push-client/push-client.types.js.map +0 -1
  122. package/src/lib/universal/account/account.js.map +0 -1
  123. package/src/lib/universal/account/index.js.map +0 -1
  124. package/src/lib/universal/signer/index.js.map +0 -1
  125. package/src/lib/universal/signer/signer.js.map +0 -1
  126. package/src/lib/universal/signer/signer.types.js.map +0 -1
  127. package/src/lib/universal/universal.types.js.map +0 -1
  128. package/src/lib/utils.js.map +0 -1
  129. package/src/lib/vm-client/evm-client.js.map +0 -1
  130. package/src/lib/vm-client/svm-client.js.map +0 -1
  131. package/src/lib/vm-client/vm-client.types.js.map +0 -1
@@ -0,0 +1,1192 @@
1
+ "use strict";
2
+ /**
3
+ * Route handler functions extracted from Orchestrator.
4
+ *
5
+ * Each function corresponds to one of the multichain execution routes:
6
+ * - executeMultiChain (dispatch)
7
+ * - executeUoaToCea (Route 2 EVM)
8
+ * - executeUoaToCeaSvm (Route 2 SVM)
9
+ * - executeCeaToPush (Route 3 EVM)
10
+ * - executeCeaToPushSvm(Route 3 SVM)
11
+ * - executeCeaToCea (Route 4 stub)
12
+ * - buildPayloadForRoute
13
+ *
14
+ * Every method receives an `OrchestratorContext` as first parameter and an
15
+ * `executeFn` callback that replaces the former `this.execute()` call.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.executeMultiChain = executeMultiChain;
19
+ exports.executeUoaToCea = executeUoaToCea;
20
+ exports.executeUoaToCeaSvm = executeUoaToCeaSvm;
21
+ exports.executeCeaToPush = executeCeaToPush;
22
+ exports.executeCeaToPushSvm = executeCeaToPushSvm;
23
+ exports.executeCeaToCea = executeCeaToCea;
24
+ exports.buildPayloadForRoute = buildPayloadForRoute;
25
+ const tslib_1 = require("tslib");
26
+ const web3_js_1 = require("@solana/web3.js");
27
+ const viem_1 = require("viem");
28
+ const context_1 = require("./context");
29
+ const helpers_1 = require("./helpers");
30
+ const payload_builder_1 = require("./payload-builder");
31
+ const uea_manager_1 = require("./uea-manager");
32
+ const gas_calculator_1 = require("./gas-calculator");
33
+ const chain_1 = require("../../constants/chain");
34
+ const route_detector_1 = require("../route-detector");
35
+ const cea_utils_1 = require("../cea-utils");
36
+ const payload_builders_1 = require("../payload-builders");
37
+ const selectors_1 = require("../../constants/selectors");
38
+ const erc20_evm_1 = require("../../constants/abi/erc20.evm");
39
+ const push_chain_1 = require("../../push-chain/push-chain");
40
+ // ---------------------------------------------------------------------------
41
+ // executeMultiChain
42
+ // ---------------------------------------------------------------------------
43
+ /**
44
+ * Dispatch a universal execution to the correct route handler.
45
+ */
46
+ function executeMultiChain(ctx, params, executeFn) {
47
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
48
+ // Validate route parameters
49
+ (0, route_detector_1.validateRouteParams)(params, {
50
+ clientChain: ctx.universalSigner.account.chain,
51
+ });
52
+ // Detect the transaction route
53
+ const route = (0, route_detector_1.detectRoute)(params);
54
+ (0, context_1.printLog)(ctx, `executeMultiChain — detected route: ${route}, params: ${JSON.stringify({
55
+ to: typeof params.to === 'string'
56
+ ? params.to
57
+ : { address: params.to.address, chain: params.to.chain },
58
+ from: params.from,
59
+ hasValue: params.value !== undefined,
60
+ hasData: params.data !== undefined,
61
+ hasFunds: params.funds !== undefined,
62
+ }, null, 2)}`);
63
+ let response;
64
+ switch (route) {
65
+ case route_detector_1.TransactionRoute.UOA_TO_PUSH:
66
+ response = yield executeFn((0, helpers_1.toExecuteParams)(params));
67
+ break;
68
+ case route_detector_1.TransactionRoute.UOA_TO_CEA:
69
+ response = yield executeUoaToCea(ctx, params, executeFn);
70
+ break;
71
+ case route_detector_1.TransactionRoute.CEA_TO_PUSH:
72
+ response = yield executeCeaToPush(ctx, params, executeFn);
73
+ break;
74
+ case route_detector_1.TransactionRoute.CEA_TO_CEA:
75
+ response = yield executeCeaToCea(ctx, params, executeFn);
76
+ break;
77
+ default:
78
+ throw new Error(`Unknown transaction route: ${route}`);
79
+ }
80
+ // Set the route on the response for .wait() to use
81
+ response.route = route;
82
+ return response;
83
+ });
84
+ }
85
+ // ---------------------------------------------------------------------------
86
+ // executeUoaToCea (Route 2 — EVM)
87
+ // ---------------------------------------------------------------------------
88
+ /**
89
+ * Route 2: Execute outbound transaction from Push Chain to external CEA.
90
+ *
91
+ * Builds a multicall that executes on Push Chain (from UEA context):
92
+ * 1. Approves the gateway to spend PRC-20 tokens (if needed)
93
+ * 2. Calls sendUniversalTxOutbound on UniversalGatewayPC precompile
94
+ *
95
+ * The multicall is executed through the normal execute() flow which handles
96
+ * fee-locking on the origin chain and signature verification.
97
+ */
98
+ function executeUoaToCea(ctx, params, executeFn) {
99
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
100
+ var _a, _b, _c, _d, _e;
101
+ const target = params.to;
102
+ const targetChain = target.chain;
103
+ const targetAddress = target.address;
104
+ const isSvm = (0, payload_builders_1.isSvmChain)(targetChain);
105
+ // Validate target address based on VM type
106
+ if (isSvm) {
107
+ // SVM: 32-byte hex address
108
+ if (!(0, payload_builders_1.isValidSolanaHexAddress)(targetAddress)) {
109
+ throw new Error(`Invalid Solana address: ${targetAddress}. ` +
110
+ `Expected 0x + 64 hex chars (32 bytes).`);
111
+ }
112
+ const ZERO_32 = ('0x' + '0'.repeat(64));
113
+ if (targetAddress.toLowerCase() === ZERO_32.toLowerCase()) {
114
+ throw new Error(`Cannot send to zero address on Solana. ` +
115
+ `This would result in permanent loss of funds.`);
116
+ }
117
+ }
118
+ else {
119
+ // EVM: 20-byte hex address
120
+ // Zero address is allowed for multicall (data is array) — the actual targets are in the data entries.
121
+ const isMulticall = Array.isArray(params.data);
122
+ if (targetAddress.toLowerCase() === selectors_1.ZERO_ADDRESS.toLowerCase() && !isMulticall) {
123
+ throw new Error(`Cannot send to zero address (0x0000...0000). ` +
124
+ `This would result in permanent loss of funds.`);
125
+ }
126
+ }
127
+ // Validate chain supports outbound operations
128
+ if (!(0, cea_utils_1.chainSupportsOutbound)(targetChain)) {
129
+ throw new Error(`Chain ${targetChain} does not support outbound operations. ` +
130
+ `Supported chains: BNB_TESTNET, ETHEREUM_SEPOLIA, SOLANA_DEVNET, etc.`);
131
+ }
132
+ // Branch based on VM type
133
+ if (isSvm) {
134
+ return executeUoaToCeaSvm(ctx, params, target, executeFn);
135
+ }
136
+ // ===== EVM path (existing logic) =====
137
+ // Get UEA address
138
+ const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
139
+ (0, context_1.printLog)(ctx, `executeUoaToCea — target chain: ${targetChain}, target address: ${targetAddress}, UEA: ${ueaAddress}`);
140
+ // Get CEA address for this UEA on target chain
141
+ const { cea: ceaAddress, isDeployed: ceaDeployed } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, targetChain, (_a = ctx.rpcUrls[targetChain]) === null || _a === void 0 ? void 0 : _a[0]);
142
+ (0, context_1.printLog)(ctx, `executeUoaToCea — CEA address: ${ceaAddress}, deployed: ${ceaDeployed}`);
143
+ // Migration path: raw MIGRATION_SELECTOR payload, no multicall wrapping
144
+ let ceaPayload;
145
+ let prc20Token = selectors_1.ZERO_ADDRESS;
146
+ let burnAmount = BigInt(0);
147
+ if (params.migration) {
148
+ ceaPayload = (0, payload_builders_1.buildMigrationPayload)();
149
+ prc20Token = (0, helpers_1.getNativePRC20ForChain)(targetChain, ctx.pushNetwork);
150
+ burnAmount = BigInt(0); // Migration is logic-only — no funds. CEA rejects msg.value != 0.
151
+ (0, context_1.printLog)(ctx, `executeUoaToCea — MIGRATION: using raw MIGRATION_SELECTOR payload (${ceaPayload}), native PRC-20 ${prc20Token}`);
152
+ }
153
+ else {
154
+ // Build multicall for CEA execution on target chain
155
+ const ceaMulticalls = [];
156
+ // If there's data to execute on target
157
+ if (params.data) {
158
+ if (Array.isArray(params.data)) {
159
+ // User provided explicit multicall array
160
+ ceaMulticalls.push(...params.data);
161
+ }
162
+ else {
163
+ // When ERC-20 funds are provided with a single payload, auto-prepend a
164
+ // transfer() call so the tokens minted to the CEA are forwarded to the
165
+ // target address. This mirrors the Route 1 behavior in buildExecuteMulticall.
166
+ if ((_b = params.funds) === null || _b === void 0 ? void 0 : _b.amount) {
167
+ const token = params.funds.token;
168
+ if (token && token.mechanism !== 'native') {
169
+ const erc20Transfer = (0, viem_1.encodeFunctionData)({
170
+ abi: erc20_evm_1.ERC20_EVM,
171
+ functionName: 'transfer',
172
+ args: [targetAddress, params.funds.amount],
173
+ });
174
+ ceaMulticalls.push({
175
+ to: token.address,
176
+ value: BigInt(0),
177
+ data: erc20Transfer,
178
+ });
179
+ }
180
+ }
181
+ // Single call with data. Forward native value (if any) so the target
182
+ // contract receives it alongside the payload call. The vault deposits
183
+ // native value to the CEA, and the multicall forwards it to the target.
184
+ ceaMulticalls.push({
185
+ to: targetAddress,
186
+ value: (_c = params.value) !== null && _c !== void 0 ? _c : BigInt(0),
187
+ data: params.data,
188
+ });
189
+ }
190
+ }
191
+ else if (params.value) {
192
+ // Native value transfer only.
193
+ // If sending to the CEA itself, skip the multicall — the gateway deposits native
194
+ // value directly to CEA. A self-call with value would revert (CEA._handleMulticall
195
+ // rejects value-bearing self-calls).
196
+ if (targetAddress.toLowerCase() !== ceaAddress.toLowerCase()) {
197
+ ceaMulticalls.push({
198
+ to: targetAddress,
199
+ value: params.value,
200
+ data: '0x',
201
+ });
202
+ }
203
+ }
204
+ // Build CEA multicall payload (this is what gets executed on the external chain)
205
+ ceaPayload = (0, payload_builders_1.buildCeaMulticallPayload)(ceaMulticalls);
206
+ // Determine token to burn on Push Chain
207
+ // NOTE: Even for PAYLOAD-only (no value), we need a valid PRC-20 token to:
208
+ // 1. Look up the target chain namespace in the gateway
209
+ // 2. Query and pay gas fees for the relay
210
+ if ((_d = params.funds) === null || _d === void 0 ? void 0 : _d.amount) {
211
+ // User explicitly specified funds with token
212
+ const token = params.funds.token;
213
+ if (token) {
214
+ prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
215
+ burnAmount = params.funds.amount;
216
+ }
217
+ }
218
+ else if (params.value && params.value > BigInt(0)) {
219
+ // Native value transfer: auto-select the PRC-20 token for target chain
220
+ prc20Token = (0, helpers_1.getNativePRC20ForChain)(targetChain, ctx.pushNetwork);
221
+ burnAmount = params.value;
222
+ (0, context_1.printLog)(ctx, `executeUoaToCea — auto-selected native PRC-20 ${prc20Token} for chain ${targetChain}, amount: ${burnAmount.toString()}`);
223
+ }
224
+ else if (params.data) {
225
+ // PAYLOAD-only (no value transfer): still need native token for chain namespace + gas fees
226
+ prc20Token = (0, helpers_1.getNativePRC20ForChain)(targetChain, ctx.pushNetwork);
227
+ burnAmount = BigInt(0);
228
+ (0, context_1.printLog)(ctx, `executeUoaToCea — PAYLOAD-only: using native PRC-20 ${prc20Token} for chain ${targetChain} with zero burn amount`);
229
+ }
230
+ }
231
+ // Build outbound request struct for the gateway
232
+ // NOTE: `target` is a LEGACY/DUMMY parameter for contract compatibility.
233
+ // The deployed UniversalGatewayPC still expects this field, but the relay does NOT use it
234
+ // to determine the actual destination. The relay determines destination from the PRC-20 token's
235
+ // SOURCE_CHAIN_NAMESPACE. We pass the CEA address as a non-zero placeholder.
236
+ // This field will be removed in future contract upgrades.
237
+ const targetBytes = ceaAddress; // Dummy value - any non-zero address works
238
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, (_e = params.gasLimit) !== null && _e !== void 0 ? _e : BigInt(0), ceaPayload, ueaAddress // revert recipient is the UEA
239
+ );
240
+ (0, context_1.printLog)(ctx, `executeUoaToCea — outbound request: ${JSON.stringify({
241
+ target: outboundReq.target,
242
+ token: outboundReq.token,
243
+ amount: outboundReq.amount.toString(),
244
+ gasLimit: outboundReq.gasLimit.toString(),
245
+ payloadLength: outboundReq.payload.length,
246
+ revertRecipient: outboundReq.revertRecipient,
247
+ }, null, 2)}`);
248
+ // Get UniversalGatewayPC address
249
+ const gatewayPcAddress = (0, helpers_1.getUniversalGatewayPCAddress)();
250
+ // Pre-fetch UEA status early — balance is needed for gas value calculation
251
+ const signerChain = ctx.universalSigner.account.chain;
252
+ const isNativePushEOA = (0, helpers_1.isPushChain)(signerChain);
253
+ const [ueaCode, ueaBalance] = yield Promise.all([
254
+ ctx.pushClient.publicClient.getCode({ address: ueaAddress }),
255
+ ctx.pushClient.getBalance(ueaAddress),
256
+ ]);
257
+ const isUEADeployed = isNativePushEOA || ueaCode !== undefined;
258
+ if (!isUEADeployed) {
259
+ throw new Error('UEA is not deployed. Please send an inbound transaction to Push Chain first ' +
260
+ '(e.g. sendTransaction with value) to deploy your Universal Execution Account before using outbound transfers.');
261
+ }
262
+ const ueaNonce = isNativePushEOA ? BigInt(0) : yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress);
263
+ // Build the multicall that will execute ON Push Chain from UEA context
264
+ // This includes: 1) approve PRC-20 (if needed), 2) call sendUniversalTxOutbound
265
+ const pushChainMulticalls = [];
266
+ // Query gas fee from UniversalCore contract (needed for approval amount)
267
+ let gasFee = BigInt(0);
268
+ let nativeValueForGas = BigInt(0);
269
+ let gasToken = selectors_1.ZERO_ADDRESS;
270
+ if (prc20Token !== selectors_1.ZERO_ADDRESS) {
271
+ try {
272
+ const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, outboundReq.gasLimit);
273
+ gasFee = result.gasFee;
274
+ gasToken = result.gasToken;
275
+ nativeValueForGas = result.nativeValueForGas;
276
+ (0, context_1.printLog)(ctx, `executeUoaToCea — queried gas fee: ${gasFee.toString()}, gasToken: ${gasToken}, nativeValueForGas: ${nativeValueForGas.toString()}`);
277
+ }
278
+ catch (err) {
279
+ throw new Error(`Failed to query outbound gas fee: ${err}`);
280
+ }
281
+ }
282
+ // Adjust nativeValueForGas: the 1Mx multiplier from queryOutboundGasFee produces
283
+ // a value far too low for the actual WPC/gasToken swap price. Set it to 200 UPC
284
+ // (capped by balance) — enough for the swap, but avoids draining thin pools.
285
+ // The contract's swapAndBurnGas does exactOutputSingle and refunds excess PC.
286
+ const EVM_NATIVE_VALUE_TARGET = BigInt(200e18); // 200 UPC
287
+ const EVM_GAS_RESERVE = BigInt(3e18); // 3 UPC for tx overhead
288
+ // Reuse ueaBalance fetched earlier (line 383) — no tx sent between, balance is stable
289
+ const currentBalance = ueaBalance;
290
+ let adjustedValue;
291
+ if (currentBalance > EVM_NATIVE_VALUE_TARGET + EVM_GAS_RESERVE) {
292
+ // Enough balance: use 200 UPC target
293
+ adjustedValue = EVM_NATIVE_VALUE_TARGET;
294
+ }
295
+ else if (currentBalance > EVM_GAS_RESERVE) {
296
+ // Low balance: use what's available minus reserve
297
+ adjustedValue = currentBalance - EVM_GAS_RESERVE;
298
+ }
299
+ else {
300
+ // Very low balance: use original query value as-is
301
+ adjustedValue = nativeValueForGas;
302
+ }
303
+ if (adjustedValue !== nativeValueForGas) {
304
+ (0, context_1.printLog)(ctx, `executeUoaToCea — adjusting nativeValueForGas from ${nativeValueForGas.toString()} to ${adjustedValue.toString()} (UEA balance: ${currentBalance.toString()})`);
305
+ nativeValueForGas = adjustedValue;
306
+ }
307
+ // Build outbound multicalls (approve burn + sendUniversalTxOutbound with native value)
308
+ const outboundMulticalls = (0, payload_builders_1.buildOutboundApprovalAndCall)({
309
+ prc20Token,
310
+ gasToken,
311
+ burnAmount,
312
+ gasFee,
313
+ nativeValueForGas,
314
+ gatewayPcAddress,
315
+ outboundRequest: outboundReq,
316
+ });
317
+ pushChainMulticalls.push(...outboundMulticalls);
318
+ (0, context_1.printLog)(ctx, `executeUoaToCea — Push Chain multicall has ${pushChainMulticalls.length} operations`);
319
+ // TODO: Enable pre-flight balance checks once outbound flow is stable
320
+ // if (burnAmount > BigInt(0)) {
321
+ // const prc20Balance = await ctx.pushClient.publicClient.readContract({
322
+ // address: prc20Token,
323
+ // abi: [{ name: 'balanceOf', type: 'function', stateMutability: 'view', inputs: [{ name: 'account', type: 'address' }], outputs: [{ name: '', type: 'uint256' }] }],
324
+ // functionName: 'balanceOf',
325
+ // args: [ueaAddress],
326
+ // }) as bigint;
327
+ // if (prc20Balance < burnAmount) {
328
+ // throw new Error(
329
+ // `Insufficient PRC-20 token balance on UEA. ` +
330
+ // `Required: ${burnAmount.toString()}, Available: ${prc20Balance.toString()}, ` +
331
+ // `Token: ${prc20Token}, UEA: ${ueaAddress}. ` +
332
+ // `Please bridge tokens to Push Chain first.`
333
+ // );
334
+ // }
335
+ // }
336
+ // const currentUeaBalance = await ctx.pushClient.getBalance(ueaAddress);
337
+ // if (currentUeaBalance < nativeValueForGas) {
338
+ // throw new Error(
339
+ // `Insufficient native balance on UEA for outbound gas. ` +
340
+ // `Required: ${nativeValueForGas.toString()} wei, Available: ${currentUeaBalance.toString()} wei, ` +
341
+ // `UEA: ${ueaAddress}. Please send UPC to your UEA first.`
342
+ // );
343
+ // }
344
+ // Execute through the normal execute() flow
345
+ // This handles fee-locking on origin chain and executes the multicall from UEA context
346
+ // Sum native values from multicall entries for proper fee calculation
347
+ const multicallNativeValue = pushChainMulticalls.reduce((sum, mc) => { var _a; return sum + ((_a = mc.value) !== null && _a !== void 0 ? _a : BigInt(0)); }, BigInt(0));
348
+ const executeParams = {
349
+ to: ueaAddress, // multicall executes from UEA
350
+ value: multicallNativeValue, // ensures correct requiredFunds calculation
351
+ data: pushChainMulticalls, // array triggers multicall mode
352
+ _ueaStatus: {
353
+ isDeployed: isUEADeployed,
354
+ nonce: ueaNonce,
355
+ balance: ueaBalance,
356
+ },
357
+ _skipFeeLocking: true, // outbound executes on Push Chain, no external fee locking
358
+ };
359
+ const response = yield executeFn(executeParams);
360
+ // Add chain info to response
361
+ response.chain = targetChain;
362
+ response.chainNamespace = (0, helpers_1.getChainNamespace)(targetChain);
363
+ return response;
364
+ });
365
+ }
366
+ // ---------------------------------------------------------------------------
367
+ // executeUoaToCeaSvm (Route 2 — SVM)
368
+ // ---------------------------------------------------------------------------
369
+ /**
370
+ * Route 2 for SVM targets: Outbound from Push Chain to Solana.
371
+ *
372
+ * Three cases:
373
+ * 1. Withdraw SOL: Burn pSOL on Push Chain, recipient gets native SOL
374
+ * 2. Withdraw SPL: Burn PRC-20 on Push Chain, recipient gets SPL token
375
+ * 3. Execute (CPI): Burn pSOL + execute CPI on target Solana program
376
+ */
377
+ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
378
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
379
+ var _a, _b, _c, _d, _e, _f;
380
+ const targetChain = target.chain;
381
+ const targetAddress = target.address; // 0x-prefixed, 32 bytes
382
+ const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
383
+ const hasSvmExecute = !!params.svmExecute;
384
+ (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — target: ${targetAddress}, chain: ${targetChain}, ` +
385
+ `hasSvmExecute: ${hasSvmExecute}, value: ${(_b = (_a = params.value) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : '0'}`);
386
+ // --- Build SVM payload ---
387
+ let svmPayload = '0x'; // empty for withdraw (SOL/SPL)
388
+ if (hasSvmExecute) {
389
+ // Execute case: encode the CPI payload
390
+ const exec = params.svmExecute;
391
+ svmPayload = (0, payload_builders_1.encodeSvmExecutePayload)({
392
+ targetProgram: exec.targetProgram,
393
+ accounts: exec.accounts,
394
+ ixData: exec.ixData,
395
+ instructionId: 2,
396
+ });
397
+ (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — encoded execute payload: ${(svmPayload.length - 2) / 2} bytes, ` +
398
+ `${exec.accounts.length} accounts`);
399
+ }
400
+ // --- Determine PRC-20 token and burn amount ---
401
+ let prc20Token = selectors_1.ZERO_ADDRESS;
402
+ let burnAmount = BigInt(0);
403
+ if ((_c = params.funds) === null || _c === void 0 ? void 0 : _c.amount) {
404
+ // User explicitly specified funds with token
405
+ const token = params.funds.token;
406
+ if (token) {
407
+ prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
408
+ burnAmount = params.funds.amount;
409
+ }
410
+ }
411
+ else if (params.value && params.value > BigInt(0)) {
412
+ // Native value transfer: auto-select pSOL for Solana chains
413
+ prc20Token = (0, helpers_1.getNativePRC20ForChain)(targetChain, ctx.pushNetwork);
414
+ burnAmount = params.value;
415
+ (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — auto-selected native PRC-20 ${prc20Token} for chain ${targetChain}, amount: ${burnAmount.toString()}`);
416
+ }
417
+ else if (hasSvmExecute) {
418
+ // Execute-only (no value): check if user specified an SPL token context
419
+ const token = params.funds && params.funds.token;
420
+ if (token) {
421
+ prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
422
+ }
423
+ else {
424
+ prc20Token = (0, helpers_1.getNativePRC20ForChain)(targetChain, ctx.pushNetwork);
425
+ }
426
+ burnAmount = BigInt(0);
427
+ (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — EXECUTE-only: using PRC-20 ${prc20Token} with zero burn amount`);
428
+ }
429
+ // --- Determine target bytes ---
430
+ // For withdraw: target is the Solana recipient pubkey
431
+ // For execute: target is the target Solana program
432
+ const targetBytes = hasSvmExecute
433
+ ? params.svmExecute.targetProgram
434
+ : targetAddress;
435
+ // --- Build outbound request ---
436
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, (_d = params.gasLimit) !== null && _d !== void 0 ? _d : BigInt(0), svmPayload, ueaAddress // revert recipient is the UEA
437
+ );
438
+ (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — outbound request: ${JSON.stringify({
439
+ target: outboundReq.target,
440
+ token: outboundReq.token,
441
+ amount: outboundReq.amount.toString(),
442
+ gasLimit: outboundReq.gasLimit.toString(),
443
+ payloadLength: (outboundReq.payload.length - 2) / 2,
444
+ revertRecipient: outboundReq.revertRecipient,
445
+ }, null, 2)}`);
446
+ // --- Pre-fetch UEA status early — balance is needed for gas value calculation ---
447
+ const gatewayPcAddress = (0, helpers_1.getUniversalGatewayPCAddress)();
448
+ const signerChainSvm = ctx.universalSigner.account.chain;
449
+ const isNativePushEOASvm = (0, helpers_1.isPushChain)(signerChainSvm);
450
+ // Skip getCode if accountStatusCache already confirmed deployment
451
+ const deployedHintSvm = (_f = (_e = ctx.accountStatusCache) === null || _e === void 0 ? void 0 : _e.uea) === null || _f === void 0 ? void 0 : _f.deployed;
452
+ let ueaBalance;
453
+ let isUEADeployed;
454
+ if (deployedHintSvm || isNativePushEOASvm) {
455
+ isUEADeployed = true;
456
+ ueaBalance = yield ctx.pushClient.getBalance(ueaAddress);
457
+ }
458
+ else {
459
+ const [ueaCode, balance] = yield Promise.all([
460
+ ctx.pushClient.publicClient.getCode({ address: ueaAddress }),
461
+ ctx.pushClient.getBalance(ueaAddress),
462
+ ]);
463
+ isUEADeployed = ueaCode !== undefined;
464
+ ueaBalance = balance;
465
+ }
466
+ if (!isUEADeployed) {
467
+ throw new Error('UEA is not deployed. Please send an inbound transaction to Push Chain first ' +
468
+ '(e.g. sendTransaction with value) to deploy your Universal Execution Account before using outbound transfers.');
469
+ }
470
+ const ueaNonce = isNativePushEOASvm ? BigInt(0) : yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress);
471
+ // --- Query gas fee (identical to EVM path) ---
472
+ let gasFee = BigInt(0);
473
+ let gasToken = selectors_1.ZERO_ADDRESS;
474
+ let universalCoreAddress;
475
+ let nativeValueForGas = BigInt(0);
476
+ if (prc20Token !== selectors_1.ZERO_ADDRESS) {
477
+ const gasLimit = outboundReq.gasLimit;
478
+ try {
479
+ const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, gasLimit);
480
+ gasFee = result.gasFee;
481
+ gasToken = result.gasToken;
482
+ nativeValueForGas = result.nativeValueForGas;
483
+ universalCoreAddress = result.universalCoreAddress;
484
+ // When user omits gasLimit (sent as 0), the contract computes fees using its internal
485
+ // baseGasLimitByChainNamespace. But the relay reads the stored gasLimit=0 from the
486
+ // on-chain outbound record and uses it as the Solana compute budget — 0 CU means the
487
+ // relay cannot execute the tx. Derive the effective limit from gasFee/gasPrice so
488
+ // the stored record has a non-zero compute budget the relay can use.
489
+ if (!params.gasLimit && result.gasPrice > BigInt(0)) {
490
+ outboundReq.gasLimit = result.gasFee / result.gasPrice;
491
+ (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — derived effectiveGasLimit: ${outboundReq.gasLimit} (gasFee=${result.gasFee} / gasPrice=${result.gasPrice})`);
492
+ }
493
+ (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — queried gas fee: ${gasFee.toString()}, gasToken: ${gasToken}, nativeValueForGas: ${nativeValueForGas.toString()}`);
494
+ }
495
+ catch (err) {
496
+ throw new Error(`Failed to query outbound gas fee: ${err}`);
497
+ }
498
+ }
499
+ // Estimate the actual WPC needed for the swap using on-chain pool price.
500
+ // The static 1Mx multiplier from queryOutboundGasFee doesn't reflect the real
501
+ // WPC/gasToken exchange rate. estimateNativeValueForSwap reads the Uniswap V3
502
+ // pool's slot0, computes the true cost, and adds a 2x buffer.
503
+ // Excess msg.value is refunded by the contract's swapAndBurnGas.
504
+ if (universalCoreAddress && gasFee > BigInt(0)) {
505
+ const estimated = yield (0, gas_calculator_1.estimateNativeValueForSwap)(ctx, universalCoreAddress, gasToken, gasFee, ueaBalance);
506
+ if (estimated > nativeValueForGas) {
507
+ (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — adjusting nativeValueForGas from ${nativeValueForGas.toString()} to ${estimated.toString()} (pool-price estimate, UEA balance: ${ueaBalance.toString()})`);
508
+ nativeValueForGas = estimated;
509
+ }
510
+ }
511
+ // --- Build Push Chain multicalls (approve + sendUniversalTxOutbound) ---
512
+ // Reuse the same builder as EVM — this part is identical
513
+ const pushChainMulticalls = (0, payload_builders_1.buildOutboundApprovalAndCall)({
514
+ prc20Token,
515
+ gasToken,
516
+ burnAmount,
517
+ gasFee,
518
+ nativeValueForGas,
519
+ gatewayPcAddress,
520
+ outboundRequest: outboundReq,
521
+ });
522
+ (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — Push Chain multicall has ${pushChainMulticalls.length} operations`);
523
+ // Sum native values from multicall entries for proper fee calculation
524
+ const multicallNativeValue = pushChainMulticalls.reduce((sum, mc) => { var _a; return sum + ((_a = mc.value) !== null && _a !== void 0 ? _a : BigInt(0)); }, BigInt(0));
525
+ const executeParams = {
526
+ to: ueaAddress,
527
+ value: multicallNativeValue, // ensures correct requiredFunds calculation
528
+ data: pushChainMulticalls,
529
+ _ueaStatus: {
530
+ isDeployed: isUEADeployed,
531
+ nonce: ueaNonce,
532
+ balance: ueaBalance,
533
+ },
534
+ _skipFeeLocking: true, // outbound executes on Push Chain, no external fee locking
535
+ };
536
+ const response = yield executeFn(executeParams);
537
+ // Add chain info to response
538
+ response.chain = targetChain;
539
+ response.chainNamespace = (0, helpers_1.getChainNamespace)(targetChain);
540
+ return response;
541
+ });
542
+ }
543
+ // ---------------------------------------------------------------------------
544
+ // executeCeaToPush (Route 3 — EVM)
545
+ // ---------------------------------------------------------------------------
546
+ /**
547
+ * Route 3: Execute inbound transaction from CEA to Push Chain.
548
+ *
549
+ * This route instructs CEA on an external chain to call sendUniversalTxFromCEA,
550
+ * bridging funds/payloads back to Push Chain.
551
+ *
552
+ * Flow:
553
+ * 1. Build multicall for CEA: [approve Gateway (if ERC20), sendUniversalTxFromCEA]
554
+ * 2. Execute via Route 2 (UOA -> CEA) with PAYLOAD-only (CEA uses its own funds)
555
+ * 3. CEA executes multicall, Gateway locks funds, relayer mints PRC-20 on Push Chain
556
+ */
557
+ function executeCeaToPush(ctx, params, executeFn) {
558
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
559
+ var _a, _b, _c, _d, _e, _f, _g;
560
+ // 1. Validate and extract source chain
561
+ if (!((_a = params.from) === null || _a === void 0 ? void 0 : _a.chain)) {
562
+ throw new Error('Route 3 (CEA -> Push) requires from.chain to specify the source CEA chain');
563
+ }
564
+ const sourceChain = params.from.chain;
565
+ // SVM chains use a fundamentally different flow (gateway self-call, not CEA multicall)
566
+ if ((0, payload_builders_1.isSvmChain)(sourceChain)) {
567
+ return executeCeaToPushSvm(ctx, params, sourceChain, executeFn);
568
+ }
569
+ // 2. Extract destination on Push Chain
570
+ // For Route 3, 'to' is a Push Chain address (string), not a ChainTarget
571
+ const pushDestination = params.to;
572
+ if (typeof params.to !== 'string') {
573
+ throw new Error('Route 3 (CEA -> Push): to must be a Push Chain address (string), not a ChainTarget');
574
+ }
575
+ // 3. Get UEA address (will be recipient on Push Chain from CEA's perspective)
576
+ const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
577
+ // 4. Get CEA address on source chain
578
+ const { cea: ceaAddress, isDeployed: ceaDeployed } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, sourceChain, (_b = ctx.rpcUrls[sourceChain]) === null || _b === void 0 ? void 0 : _b[0]);
579
+ (0, context_1.printLog)(ctx, `executeCeaToPush — sourceChain: ${sourceChain}, CEA: ${ceaAddress}, deployed: ${ceaDeployed}`);
580
+ if (!ceaDeployed) {
581
+ throw new Error(`CEA not deployed on ${sourceChain}. ` +
582
+ `Deploy CEA first using Route 2 (UOA -> CEA) before using Route 3.`);
583
+ }
584
+ // 5. Get UniversalGateway address on source chain
585
+ const gatewayAddress = chain_1.UNIVERSAL_GATEWAY_ADDRESSES[sourceChain];
586
+ if (!gatewayAddress) {
587
+ throw new Error(`No UniversalGateway address configured for chain ${sourceChain}`);
588
+ }
589
+ // 6. Build multicall for CEA to execute on source chain (self-calls via sendUniversalTxToUEA)
590
+ const ceaMulticalls = [];
591
+ // Determine token and amount for sendUniversalTxToUEA
592
+ let tokenAddress = selectors_1.ZERO_ADDRESS;
593
+ let amount = BigInt(0);
594
+ if ((_c = params.funds) === null || _c === void 0 ? void 0 : _c.amount) {
595
+ // ERC20 token transfer from CEA
596
+ const token = params.funds.token;
597
+ if (token) {
598
+ if (token.mechanism === 'native') {
599
+ // Native token (e.g., BNB on BSC)
600
+ tokenAddress = selectors_1.ZERO_ADDRESS;
601
+ amount = params.funds.amount;
602
+ }
603
+ else {
604
+ // ERC20 token - need approval for gateway before sendUniversalTxToUEA
605
+ tokenAddress = token.address;
606
+ amount = params.funds.amount;
607
+ }
608
+ }
609
+ }
610
+ else if (params.value && params.value > BigInt(0)) {
611
+ // Native value transfer (e.g., BNB, ETH)
612
+ tokenAddress = selectors_1.ZERO_ADDRESS;
613
+ amount = params.value;
614
+ }
615
+ // bridgeAmount = only the burn amount (what the Vault will actually deposit to CEA).
616
+ // Previously this included ceaExistingBalance (CEA's pre-existing balance on the
617
+ // external chain), but that approach is racy: the balance can change between the SDK
618
+ // query and relay execution, causing sendUniversalTxToUEA to revert with
619
+ // InsufficientBalance. Pre-existing CEA funds remain parked and can be swept separately.
620
+ let bridgeAmount = amount;
621
+ // Note: CEA contract may reject amount=0 in sendUniversalTxToUEA.
622
+ // Keeping bridgeAmount as-is (0) for payload-only to test precompile behavior.
623
+ // For ERC20 tokens, add approve call for the bridge amount
624
+ // (CEA approves gateway to spend the Vault-deposited amount)
625
+ // Reset to 0 first for USDT-style tokens that revert on non-zero to non-zero approve.
626
+ if (tokenAddress !== selectors_1.ZERO_ADDRESS && bridgeAmount > BigInt(0)) {
627
+ const approveZeroData = (0, viem_1.encodeFunctionData)({
628
+ abi: erc20_evm_1.ERC20_EVM,
629
+ functionName: 'approve',
630
+ args: [gatewayAddress, BigInt(0)],
631
+ });
632
+ ceaMulticalls.push({
633
+ to: tokenAddress,
634
+ value: BigInt(0),
635
+ data: approveZeroData,
636
+ });
637
+ const approveData = (0, viem_1.encodeFunctionData)({
638
+ abi: erc20_evm_1.ERC20_EVM,
639
+ functionName: 'approve',
640
+ args: [gatewayAddress, bridgeAmount],
641
+ });
642
+ ceaMulticalls.push({
643
+ to: tokenAddress,
644
+ value: BigInt(0),
645
+ data: approveData,
646
+ });
647
+ }
648
+ // Pre-fetch UEA nonce — needed for the inbound UniversalPayload struct
649
+ // Skip getCode if accountStatusCache already confirmed deployment
650
+ let isUEADeployed;
651
+ let ueaNonce;
652
+ const deployedHintR3 = (_e = (_d = ctx.accountStatusCache) === null || _d === void 0 ? void 0 : _d.uea) === null || _e === void 0 ? void 0 : _e.deployed;
653
+ if (deployedHintR3) {
654
+ isUEADeployed = true;
655
+ ueaNonce = yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress);
656
+ }
657
+ else {
658
+ const ueaCode = yield ctx.pushClient.publicClient.getCode({ address: ueaAddress });
659
+ isUEADeployed = ueaCode !== undefined;
660
+ ueaNonce = isUEADeployed ? yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress) : BigInt(0);
661
+ }
662
+ // Build payload for Push Chain execution (if any)
663
+ // This is what happens AFTER funds arrive on Push Chain.
664
+ // The relay expects a full UniversalPayload struct (to, value, data, gasLimit, ...),
665
+ // where `data` contains the multicall payload (with UEA_MULTICALL_SELECTOR prefix).
666
+ let pushPayload = '0x';
667
+ if (params.data) {
668
+ const multicallData = (0, payload_builders_1.buildExecuteMulticall)({
669
+ execute: {
670
+ to: pushDestination,
671
+ value: params.value,
672
+ data: params.data,
673
+ },
674
+ ueaAddress,
675
+ });
676
+ const multicallPayload = (0, payload_builder_1.buildMulticallPayloadData)(ctx, pushDestination, multicallData);
677
+ // Use ueaNonce + 1: the outbound tx itself consumes one nonce via execute(),
678
+ // so the inbound will arrive when the UEA nonce is already incremented.
679
+ pushPayload = (0, payload_builders_1.buildInboundUniversalPayload)(multicallPayload, { nonce: ueaNonce + BigInt(1) });
680
+ }
681
+ // Build sendUniversalTxToUEA self-call on CEA
682
+ // CEA multicall: to=CEA (self-call), value=0
683
+ // CEA internally calls gateway.sendUniversalTxFromCEA(...)
684
+ // Uses bridgeAmount (= burn amount deposited by Vault)
685
+ const sendUniversalTxCall = (0, payload_builders_1.buildSendUniversalTxToUEA)(ceaAddress, // to: CEA address (self-call)
686
+ tokenAddress, // token: address(0) for native, ERC20 address otherwise
687
+ bridgeAmount, // amount: burn amount only (Vault-deposited)
688
+ pushPayload, // payload: Push Chain execution payload
689
+ ueaAddress // revertRecipient: UEA on Push Chain (receives refund if inbound fails)
690
+ );
691
+ ceaMulticalls.push(sendUniversalTxCall);
692
+ // 7. Encode CEA multicalls into outbound payload
693
+ // CEA will self-execute this multicall (to=CEA, value=0)
694
+ const ceaPayload = (0, payload_builders_1.buildCeaMulticallPayload)(ceaMulticalls);
695
+ (0, context_1.printLog)(ctx, `executeCeaToPush — CEA payload (first 100 chars): ${ceaPayload.slice(0, 100)}...`);
696
+ // 8. Determine PRC-20 token for the outbound burn on Push Chain.
697
+ // For ERC20 flows (params.funds with token), use the token's PRC-20 (e.g. pUSDT)
698
+ // so the Vault deposits ERC20 to CEA. For native flows, use the chain's native PRC-20 (e.g. pBNB).
699
+ let prc20Token;
700
+ if (((_f = params.funds) === null || _f === void 0 ? void 0 : _f.amount) && params.funds.token) {
701
+ const token = params.funds.token;
702
+ prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
703
+ }
704
+ else {
705
+ prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
706
+ }
707
+ // burnAmount = PRC20 to burn on Push Chain (NOT the bridge amount).
708
+ // Vault deposits burnAmount to CEA. CEA uses burnAmount + pre-existing balance for the bridge.
709
+ const burnAmount = amount;
710
+ (0, context_1.printLog)(ctx, `executeCeaToPush — prc20Token: ${prc20Token}, burnAmount: ${burnAmount.toString()}`);
711
+ // 9. Build outbound request (same structure as Route 2)
712
+ // target = CEA address (for self-execution), value = 0 in payload
713
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, // target: CEA address (to=CEA for self-execution)
714
+ prc20Token, // token: native PRC-20 for source chain (for namespace lookup)
715
+ burnAmount, // amount: 1 wei (precompile workaround)
716
+ (_g = params.gasLimit) !== null && _g !== void 0 ? _g : BigInt(0), ceaPayload, // payload: ABI-encoded CEA multicall
717
+ ueaAddress // revertRecipient: UEA
718
+ );
719
+ (0, context_1.printLog)(ctx, `executeCeaToPush — outbound request: ${JSON.stringify({
720
+ target: outboundReq.target,
721
+ token: outboundReq.token,
722
+ amount: outboundReq.amount.toString(),
723
+ gasLimit: outboundReq.gasLimit.toString(),
724
+ payloadLength: outboundReq.payload.length,
725
+ revertRecipient: outboundReq.revertRecipient,
726
+ }, null, 2)}`);
727
+ // 10. Fetch UEA balance — needed for gas value calculation
728
+ // (UEA code + nonce already fetched above for the inbound UniversalPayload)
729
+ const gatewayPcAddress = (0, helpers_1.getUniversalGatewayPCAddress)();
730
+ const ueaBalance = yield ctx.pushClient.getBalance(ueaAddress);
731
+ // 11. Query gas fees from UniversalCore
732
+ let gasFee = BigInt(0);
733
+ let nativeValueForGas = BigInt(0);
734
+ let gasToken = selectors_1.ZERO_ADDRESS;
735
+ if (prc20Token !== selectors_1.ZERO_ADDRESS) {
736
+ const gasLimit = outboundReq.gasLimit;
737
+ try {
738
+ const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, gasLimit);
739
+ gasToken = result.gasToken;
740
+ gasFee = result.gasFee;
741
+ nativeValueForGas = result.nativeValueForGas;
742
+ (0, context_1.printLog)(ctx, `executeCeaToPush — queried gas fee: ${gasFee.toString()}, gasToken: ${gasToken}, nativeValueForGas: ${nativeValueForGas.toString()}`);
743
+ }
744
+ catch (err) {
745
+ throw new Error(`Failed to query outbound gas fee for Route 3: ${err}`);
746
+ }
747
+ }
748
+ // Adjust nativeValueForGas using UEA balance (contract refunds excess)
749
+ // Reuse ueaBalance from line 978 — no tx sent between, balance is stable
750
+ const currentBalance = ueaBalance;
751
+ // Cosmos-EVM tx overhead costs ~1 PC per operation; 3 PC covers approve(s) + buffer.
752
+ const OUTBOUND_GAS_RESERVE_R3 = BigInt(3e18);
753
+ if (currentBalance > OUTBOUND_GAS_RESERVE_R3 && currentBalance - OUTBOUND_GAS_RESERVE_R3 > nativeValueForGas) {
754
+ const adjustedValue = currentBalance - OUTBOUND_GAS_RESERVE_R3;
755
+ (0, context_1.printLog)(ctx, `executeCeaToPush — adjusting nativeValueForGas from ${nativeValueForGas.toString()} to ${adjustedValue.toString()} (UEA balance: ${currentBalance.toString()})`);
756
+ nativeValueForGas = adjustedValue;
757
+ }
758
+ // 12. Build Push Chain multicalls (approvals + sendUniversalTxOutbound)
759
+ const pushChainMulticalls = (0, payload_builders_1.buildOutboundApprovalAndCall)({
760
+ prc20Token,
761
+ gasToken,
762
+ burnAmount,
763
+ gasFee,
764
+ nativeValueForGas,
765
+ gatewayPcAddress,
766
+ outboundRequest: outboundReq,
767
+ });
768
+ (0, context_1.printLog)(ctx, `executeCeaToPush — Push Chain multicall has ${pushChainMulticalls.length} operations`);
769
+ // Sum native values from multicall entries for proper fee calculation
770
+ const multicallNativeValue = pushChainMulticalls.reduce((sum, mc) => { var _a; return sum + ((_a = mc.value) !== null && _a !== void 0 ? _a : BigInt(0)); }, BigInt(0));
771
+ // 13. Execute through the normal execute() flow
772
+ const executeParams = {
773
+ to: ueaAddress,
774
+ value: multicallNativeValue, // ensures correct requiredFunds calculation
775
+ data: pushChainMulticalls,
776
+ _ueaStatus: {
777
+ isDeployed: isUEADeployed,
778
+ nonce: ueaNonce,
779
+ balance: ueaBalance,
780
+ },
781
+ _skipFeeLocking: true, // outbound executes on Push Chain, no external fee locking
782
+ };
783
+ const response = yield executeFn(executeParams);
784
+ // Add Route 3 context to response
785
+ response.chain = sourceChain;
786
+ const chainInfo = chain_1.CHAIN_INFO[sourceChain];
787
+ response.chainNamespace = `${chain_1.VM_NAMESPACE[chainInfo.vm]}:${chainInfo.chainId}`;
788
+ return response;
789
+ });
790
+ }
791
+ // ---------------------------------------------------------------------------
792
+ // executeCeaToPushSvm (Route 3 — SVM)
793
+ // ---------------------------------------------------------------------------
794
+ /**
795
+ * Route 3 SVM: Execute CEA-to-Push for Solana chains.
796
+ *
797
+ * Unlike EVM Route 3 which builds CEA multicalls, SVM Route 3 encodes a
798
+ * `send_universal_tx_to_uea` instruction as an execute payload targeting
799
+ * the SVM gateway program (self-call). The drain amount is embedded in
800
+ * the instruction data, not in the outbound request amount.
801
+ */
802
+ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
803
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
804
+ var _a, _b;
805
+ if (typeof params.to !== 'string') {
806
+ throw new Error('Route 3 SVM (CEA -> Push): to must be a Push Chain address (string), not a ChainTarget');
807
+ }
808
+ const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
809
+ // Get gateway program ID from chain config and convert to 0x-hex 32 bytes
810
+ const lockerContract = chain_1.CHAIN_INFO[sourceChain].lockerContract;
811
+ if (!lockerContract) {
812
+ throw new Error(`No SVM gateway program configured for chain ${sourceChain}`);
813
+ }
814
+ const programPk = new web3_js_1.PublicKey(lockerContract);
815
+ const gatewayProgramHex = ('0x' + Buffer.from(programPk.toBytes()).toString('hex'));
816
+ (0, context_1.printLog)(ctx, `executeCeaToPushSvm — sourceChain: ${sourceChain}, gateway: ${lockerContract}`);
817
+ // Determine drain amount and token
818
+ let drainAmount = BigInt(0);
819
+ let tokenMintHex;
820
+ let prc20Token;
821
+ if (((_a = params.funds) === null || _a === void 0 ? void 0 : _a.amount) && params.funds.amount > BigInt(0)) {
822
+ // SPL token drain
823
+ drainAmount = params.funds.amount;
824
+ const token = params.funds.token;
825
+ if (token && token.address) {
826
+ // Convert SPL mint address to 32-byte hex
827
+ const mintPk = new web3_js_1.PublicKey(token.address);
828
+ tokenMintHex = ('0x' + Buffer.from(mintPk.toBytes()).toString('hex'));
829
+ prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
830
+ }
831
+ else {
832
+ prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
833
+ }
834
+ }
835
+ else if (params.value && params.value > BigInt(0)) {
836
+ // Native SOL drain
837
+ drainAmount = params.value;
838
+ prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
839
+ }
840
+ else {
841
+ // Payload-only Route 3 SVM: no funds to drain, just execute data on Push Chain
842
+ drainAmount = BigInt(0);
843
+ prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
844
+ }
845
+ // Build the SVM CPI payload (send_universal_tx_to_uea wrapped in execute)
846
+ // If params.data is provided, pass it as extraPayload for Push Chain execution
847
+ let extraPayload;
848
+ if (params.data && typeof params.data === 'string') {
849
+ extraPayload = (0, viem_1.hexToBytes)(params.data);
850
+ }
851
+ // Derive CEA PDA as revert recipient: ["push_identity", ueaAddress_20_bytes]
852
+ const ueaBytes = Buffer.from(ueaAddress.slice(2), 'hex'); // 20 bytes
853
+ const [ceaPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('push_identity'), ueaBytes], programPk);
854
+ const ceaPdaHex = ('0x' + Buffer.from(ceaPda.toBytes()).toString('hex'));
855
+ const svmPayload = (0, payload_builders_1.encodeSvmCeaToUeaPayload)({
856
+ gatewayProgramHex,
857
+ drainAmount,
858
+ tokenMintHex,
859
+ extraPayload,
860
+ revertRecipientHex: ceaPdaHex,
861
+ });
862
+ (0, context_1.printLog)(ctx, `executeCeaToPushSvm — drainAmount: ${drainAmount.toString()}, payload length: ${(svmPayload.length - 2) / 2} bytes`);
863
+ // burnAmount = 1 (minimum for precompile; drain amount lives inside the ixData)
864
+ // The precompile rejects amount=0, so we use BigInt(1) as a workaround.
865
+ const burnAmount = BigInt(1);
866
+ // Build outbound request: target = gateway program (self-call)
867
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(gatewayProgramHex, prc20Token, burnAmount, (_b = params.gasLimit) !== null && _b !== void 0 ? _b : BigInt(0), svmPayload, ueaAddress);
868
+ (0, context_1.printLog)(ctx, `executeCeaToPushSvm — outbound request: target=${outboundReq.target.slice(0, 20)}..., token=${outboundReq.token}`);
869
+ // Pre-fetch UEA status early — balance is needed for gas value calculation
870
+ const gatewayPcAddress = (0, helpers_1.getUniversalGatewayPCAddress)();
871
+ const [ueaCode, ueaBalance] = yield Promise.all([
872
+ ctx.pushClient.publicClient.getCode({ address: ueaAddress }),
873
+ ctx.pushClient.getBalance(ueaAddress),
874
+ ]);
875
+ const isUEADeployed = ueaCode !== undefined;
876
+ const ueaNonce = isUEADeployed ? yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress) : BigInt(0);
877
+ // Query gas fees
878
+ let gasFee = BigInt(0);
879
+ let nativeValueForGas = BigInt(0);
880
+ let gasToken = selectors_1.ZERO_ADDRESS;
881
+ if (prc20Token !== selectors_1.ZERO_ADDRESS) {
882
+ try {
883
+ const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, outboundReq.gasLimit);
884
+ gasToken = result.gasToken;
885
+ gasFee = result.gasFee;
886
+ nativeValueForGas = result.nativeValueForGas;
887
+ (0, context_1.printLog)(ctx, `executeCeaToPushSvm — gasFee: ${gasFee.toString()}, gasToken: ${gasToken}, nativeValueForGas: ${nativeValueForGas.toString()}`);
888
+ }
889
+ catch (err) {
890
+ throw new Error(`Failed to query outbound gas fee for SVM Route 3: ${err}`);
891
+ }
892
+ }
893
+ // Adjust nativeValueForGas using UEA balance (contract refunds excess)
894
+ // Re-fetch balance to minimize staleness from gas fee query RPC roundtrips
895
+ const currentBalance = yield ctx.pushClient.getBalance(ueaAddress);
896
+ // Cosmos-EVM tx overhead costs ~1 PC per operation; 3 PC covers approve(s) + buffer.
897
+ const OUTBOUND_GAS_RESERVE_R3_SVM = BigInt(3e18);
898
+ if (currentBalance > OUTBOUND_GAS_RESERVE_R3_SVM && currentBalance - OUTBOUND_GAS_RESERVE_R3_SVM > nativeValueForGas) {
899
+ const adjustedValue = currentBalance - OUTBOUND_GAS_RESERVE_R3_SVM;
900
+ (0, context_1.printLog)(ctx, `executeCeaToPushSvm — adjusting nativeValueForGas from ${nativeValueForGas.toString()} to ${adjustedValue.toString()} (UEA balance: ${currentBalance.toString()})`);
901
+ nativeValueForGas = adjustedValue;
902
+ }
903
+ // Build Push Chain multicalls (approvals + sendUniversalTxOutbound)
904
+ const pushChainMulticalls = (0, payload_builders_1.buildOutboundApprovalAndCall)({
905
+ prc20Token,
906
+ gasToken,
907
+ burnAmount,
908
+ gasFee,
909
+ nativeValueForGas,
910
+ gatewayPcAddress,
911
+ outboundRequest: outboundReq,
912
+ });
913
+ // Sum native values from multicall entries for proper fee calculation
914
+ const multicallNativeValue = pushChainMulticalls.reduce((sum, mc) => { var _a; return sum + ((_a = mc.value) !== null && _a !== void 0 ? _a : BigInt(0)); }, BigInt(0));
915
+ // Execute through the normal execute() flow
916
+ const executeParams = {
917
+ to: ueaAddress,
918
+ value: multicallNativeValue, // ensures correct requiredFunds calculation
919
+ data: pushChainMulticalls,
920
+ _ueaStatus: {
921
+ isDeployed: isUEADeployed,
922
+ nonce: ueaNonce,
923
+ balance: ueaBalance,
924
+ },
925
+ _skipFeeLocking: true, // outbound executes on Push Chain, no external fee locking
926
+ };
927
+ const response = yield executeFn(executeParams);
928
+ // Add Route 3 SVM context to response
929
+ response.chain = sourceChain;
930
+ const chainInfo = chain_1.CHAIN_INFO[sourceChain];
931
+ response.chainNamespace = `${chain_1.VM_NAMESPACE[chainInfo.vm]}:${chainInfo.chainId}`;
932
+ return response;
933
+ });
934
+ }
935
+ // ---------------------------------------------------------------------------
936
+ // executeCeaToCea (Route 4 — stub)
937
+ // ---------------------------------------------------------------------------
938
+ /**
939
+ * Route 4: Execute CEA to CEA transaction via Push Chain.
940
+ */
941
+ function executeCeaToCea(ctx, params, executeFn) {
942
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
943
+ // CEA -> CEA requires chaining Route 3 (CEA -> Push) and Route 2 (Push -> CEA)
944
+ // This is a complex flow that requires coordination
945
+ throw new Error('CEA -> CEA transactions are not yet fully implemented. ' +
946
+ 'Use prepareTransaction().thenOn() to chain Route 3 -> Route 2 manually.');
947
+ });
948
+ }
949
+ // ---------------------------------------------------------------------------
950
+ // buildPayloadForRoute
951
+ // ---------------------------------------------------------------------------
952
+ /**
953
+ * Build payload for a specific route.
954
+ */
955
+ function buildPayloadForRoute(ctx, params, route, nonce) {
956
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
957
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
958
+ const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
959
+ switch (route) {
960
+ case route_detector_1.TransactionRoute.UOA_TO_PUSH: {
961
+ // Build standard Push Chain payload
962
+ const executeParams = (0, helpers_1.toExecuteParams)(params);
963
+ const multicallData = (0, payload_builders_1.buildExecuteMulticall)({
964
+ execute: executeParams,
965
+ ueaAddress,
966
+ });
967
+ const payload = (0, payload_builder_1.buildMulticallPayloadData)(ctx, executeParams.to, multicallData);
968
+ const req = (0, payload_builder_1.buildUniversalTxRequest)(ctx.universalSigner.account.address, {
969
+ recipient: viem_1.zeroAddress,
970
+ token: viem_1.zeroAddress,
971
+ amount: BigInt(0),
972
+ payload,
973
+ });
974
+ return { payload, gatewayRequest: req };
975
+ }
976
+ case route_detector_1.TransactionRoute.UOA_TO_CEA: {
977
+ const target = params.to;
978
+ // Branch: SVM vs EVM
979
+ if ((0, payload_builders_1.isSvmChain)(target.chain)) {
980
+ // SVM: build SVM payload (binary or empty for withdraw)
981
+ let payload = '0x';
982
+ if (params.svmExecute) {
983
+ payload = (0, payload_builders_1.encodeSvmExecutePayload)({
984
+ targetProgram: params.svmExecute.targetProgram,
985
+ accounts: params.svmExecute.accounts,
986
+ ixData: params.svmExecute.ixData,
987
+ instructionId: 2,
988
+ });
989
+ }
990
+ const targetBytes = (_b = (_a = params.svmExecute) === null || _a === void 0 ? void 0 : _a.targetProgram) !== null && _b !== void 0 ? _b : target.address;
991
+ let prc20Token = selectors_1.ZERO_ADDRESS;
992
+ let burnAmount = BigInt(0);
993
+ if ((_c = params.funds) === null || _c === void 0 ? void 0 : _c.amount) {
994
+ const token = params.funds.token;
995
+ if (token) {
996
+ prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
997
+ burnAmount = params.funds.amount;
998
+ }
999
+ }
1000
+ else if (params.value && params.value > BigInt(0)) {
1001
+ prc20Token = (0, helpers_1.getNativePRC20ForChain)(target.chain, ctx.pushNetwork);
1002
+ burnAmount = params.value;
1003
+ }
1004
+ else if (params.svmExecute) {
1005
+ prc20Token = (0, helpers_1.getNativePRC20ForChain)(target.chain, ctx.pushNetwork);
1006
+ burnAmount = BigInt(1);
1007
+ }
1008
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, (_d = params.gasLimit) !== null && _d !== void 0 ? _d : BigInt(0), payload, ueaAddress);
1009
+ return { payload, gatewayRequest: outboundReq };
1010
+ }
1011
+ // EVM path: Resolve CEA address first (needed for self-transfer check)
1012
+ const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, target.chain, (_e = ctx.rpcUrls[target.chain]) === null || _e === void 0 ? void 0 : _e[0]);
1013
+ // Build CEA outbound payload
1014
+ const multicalls = [];
1015
+ if (params.data) {
1016
+ if (Array.isArray(params.data)) {
1017
+ multicalls.push(...params.data);
1018
+ }
1019
+ else {
1020
+ // Single call with data. Native value (if any) is already delivered to
1021
+ // CEA by the Vault via executeUniversalTx{value: amount}(). Attaching
1022
+ // value to the call would revert if the target function is not payable.
1023
+ // To call a payable function with value, use explicit multicalls.
1024
+ multicalls.push({
1025
+ to: target.address,
1026
+ value: BigInt(0),
1027
+ data: params.data,
1028
+ });
1029
+ }
1030
+ }
1031
+ else if (params.value) {
1032
+ // Skip multicall when sending native value to own CEA — gateway deposits directly.
1033
+ // Self-call with value would revert (CEA._handleMulticall rejects it).
1034
+ if (target.address.toLowerCase() !== ceaAddress.toLowerCase()) {
1035
+ multicalls.push({
1036
+ to: target.address,
1037
+ value: params.value,
1038
+ data: '0x',
1039
+ });
1040
+ }
1041
+ }
1042
+ const payload = (0, payload_builders_1.buildCeaMulticallPayload)(multicalls);
1043
+ let prc20Token = selectors_1.ZERO_ADDRESS;
1044
+ let burnAmount = BigInt(0);
1045
+ if ((_f = params.funds) === null || _f === void 0 ? void 0 : _f.amount) {
1046
+ const token = params.funds.token;
1047
+ if (token) {
1048
+ prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
1049
+ burnAmount = params.funds.amount;
1050
+ }
1051
+ }
1052
+ const targetBytes = ceaAddress;
1053
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, (_g = params.gasLimit) !== null && _g !== void 0 ? _g : BigInt(0), payload, ueaAddress);
1054
+ return { payload, gatewayRequest: outboundReq };
1055
+ }
1056
+ case route_detector_1.TransactionRoute.CEA_TO_PUSH: {
1057
+ // Route 3: CEA -> Push Chain
1058
+ // Build CEA multicall (approve + sendUniversalTxFromCEA) and wrap in outbound
1059
+ if (!((_h = params.from) === null || _h === void 0 ? void 0 : _h.chain)) {
1060
+ throw new Error('Route 3 (CEA -> Push) requires from.chain');
1061
+ }
1062
+ const sourceChain = params.from.chain;
1063
+ const pushDestination = params.to;
1064
+ // SVM chains use gateway self-call, not CEA multicall
1065
+ if ((0, payload_builders_1.isSvmChain)(sourceChain)) {
1066
+ const lockerContract = chain_1.CHAIN_INFO[sourceChain].lockerContract;
1067
+ if (!lockerContract) {
1068
+ throw new Error(`No SVM gateway program configured for chain ${sourceChain}`);
1069
+ }
1070
+ const programPk = new web3_js_1.PublicKey(lockerContract);
1071
+ const gatewayProgramHex = ('0x' + Buffer.from(programPk.toBytes()).toString('hex'));
1072
+ let drainAmount = BigInt(0);
1073
+ let tokenMintHex;
1074
+ let prc20Token;
1075
+ if (((_j = params.funds) === null || _j === void 0 ? void 0 : _j.amount) && params.funds.amount > BigInt(0)) {
1076
+ drainAmount = params.funds.amount;
1077
+ const token = params.funds.token;
1078
+ if (token && token.address) {
1079
+ const mintPk = new web3_js_1.PublicKey(token.address);
1080
+ tokenMintHex = ('0x' + Buffer.from(mintPk.toBytes()).toString('hex'));
1081
+ prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
1082
+ }
1083
+ else {
1084
+ prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
1085
+ }
1086
+ }
1087
+ else if (params.value && params.value > BigInt(0)) {
1088
+ drainAmount = params.value;
1089
+ prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
1090
+ }
1091
+ else {
1092
+ // Payload-only Route 3 SVM: no funds to drain, just execute data on Push Chain
1093
+ drainAmount = BigInt(0);
1094
+ prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
1095
+ }
1096
+ // Derive CEA PDA as revert recipient
1097
+ const ueaBytes2 = Buffer.from(ueaAddress.slice(2), 'hex');
1098
+ const [ceaPda2] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('push_identity'), ueaBytes2], programPk);
1099
+ const ceaPdaHex2 = ('0x' + Buffer.from(ceaPda2.toBytes()).toString('hex'));
1100
+ const svmPayload = (0, payload_builders_1.encodeSvmCeaToUeaPayload)({
1101
+ gatewayProgramHex,
1102
+ drainAmount,
1103
+ tokenMintHex,
1104
+ revertRecipientHex: ceaPdaHex2,
1105
+ });
1106
+ // burnAmount = 1 (minimum for precompile; drain amount lives inside the ixData)
1107
+ const burnAmount = BigInt(1);
1108
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(gatewayProgramHex, prc20Token, burnAmount, (_k = params.gasLimit) !== null && _k !== void 0 ? _k : BigInt(0), svmPayload, ueaAddress);
1109
+ return { payload: svmPayload, gatewayRequest: outboundReq };
1110
+ }
1111
+ const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, sourceChain, (_l = ctx.rpcUrls[sourceChain]) === null || _l === void 0 ? void 0 : _l[0]);
1112
+ const gatewayAddr = chain_1.UNIVERSAL_GATEWAY_ADDRESSES[sourceChain];
1113
+ if (!gatewayAddr) {
1114
+ throw new Error(`No UniversalGateway address configured for chain ${sourceChain}`);
1115
+ }
1116
+ // Build CEA multicalls (self-calls via sendUniversalTxToUEA)
1117
+ const ceaMulticalls = [];
1118
+ let tokenAddress = selectors_1.ZERO_ADDRESS;
1119
+ let amount = BigInt(0);
1120
+ if ((_m = params.funds) === null || _m === void 0 ? void 0 : _m.amount) {
1121
+ const token = params.funds.token;
1122
+ if (token) {
1123
+ if (token.mechanism === 'native') {
1124
+ tokenAddress = selectors_1.ZERO_ADDRESS;
1125
+ amount = params.funds.amount;
1126
+ }
1127
+ else {
1128
+ tokenAddress = token.address;
1129
+ amount = params.funds.amount;
1130
+ // Reset allowance to 0 first for USDT-style tokens, then approve
1131
+ const approveZeroData = (0, viem_1.encodeFunctionData)({
1132
+ abi: erc20_evm_1.ERC20_EVM,
1133
+ functionName: 'approve',
1134
+ args: [gatewayAddr, BigInt(0)],
1135
+ });
1136
+ ceaMulticalls.push({
1137
+ to: tokenAddress,
1138
+ value: BigInt(0),
1139
+ data: approveZeroData,
1140
+ });
1141
+ const approveData = (0, viem_1.encodeFunctionData)({
1142
+ abi: erc20_evm_1.ERC20_EVM,
1143
+ functionName: 'approve',
1144
+ args: [gatewayAddr, amount],
1145
+ });
1146
+ ceaMulticalls.push({
1147
+ to: tokenAddress,
1148
+ value: BigInt(0),
1149
+ data: approveData,
1150
+ });
1151
+ }
1152
+ }
1153
+ }
1154
+ else if (params.value && params.value > BigInt(0)) {
1155
+ tokenAddress = selectors_1.ZERO_ADDRESS;
1156
+ amount = params.value;
1157
+ }
1158
+ // Fetch UEA nonce for inbound UniversalPayload
1159
+ const ueaCodeHop = yield ctx.pushClient.publicClient.getCode({ address: ueaAddress });
1160
+ const ueaNonceHop = ueaCodeHop !== undefined ? yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress) : BigInt(0);
1161
+ // Build Push Chain payload (what executes after inbound arrives)
1162
+ // Wrap in UniversalPayload struct for the relay.
1163
+ let pushPayload = '0x';
1164
+ if (params.data) {
1165
+ const multicallData = (0, payload_builders_1.buildExecuteMulticall)({
1166
+ execute: {
1167
+ to: pushDestination,
1168
+ value: params.value,
1169
+ data: params.data,
1170
+ },
1171
+ ueaAddress,
1172
+ });
1173
+ const multicallPayload = (0, payload_builder_1.buildMulticallPayloadData)(ctx, pushDestination, multicallData);
1174
+ pushPayload = (0, payload_builders_1.buildInboundUniversalPayload)(multicallPayload, { nonce: ueaNonceHop + BigInt(1) });
1175
+ }
1176
+ // Build sendUniversalTxToUEA self-call on CEA (to=CEA, value=0)
1177
+ const sendCall = (0, payload_builders_1.buildSendUniversalTxToUEA)(ceaAddress, tokenAddress, amount, pushPayload, ceaAddress);
1178
+ ceaMulticalls.push(sendCall);
1179
+ const ceaPayload = (0, payload_builders_1.buildCeaMulticallPayload)(ceaMulticalls);
1180
+ const prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
1181
+ // burnAmount = actual amount needed by CEA. Vault deposits this as msg.value.
1182
+ // Fallback to BigInt(1) for payload-only outbound (precompile rejects amount=0).
1183
+ const burnAmount = amount > BigInt(0) ? amount : BigInt(1);
1184
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, prc20Token, burnAmount, (_o = params.gasLimit) !== null && _o !== void 0 ? _o : BigInt(0), ceaPayload, ueaAddress);
1185
+ return { payload: ceaPayload, gatewayRequest: outboundReq };
1186
+ }
1187
+ default:
1188
+ throw new Error(`Cannot build payload for route: ${route}`);
1189
+ }
1190
+ });
1191
+ }
1192
+ //# sourceMappingURL=route-handlers.js.map