@pushchain/core 5.1.5-alpha.1 → 5.1.6
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/CHANGELOG.md +21 -0
- package/package.json +1 -1
- package/src/lib/constants/abi/index.d.ts +2 -0
- package/src/lib/constants/abi/index.js +6 -1
- package/src/lib/constants/abi/uniswap-v3-router.evm.d.ts +58 -0
- package/src/lib/constants/abi/uniswap-v3-router.evm.js +41 -0
- package/src/lib/constants/abi/wpc.evm.d.ts +24 -0
- package/src/lib/constants/abi/wpc.evm.js +27 -0
- package/src/lib/constants/chain.d.ts +1 -0
- package/src/lib/constants/chain.js +8 -6
- package/src/lib/constants/tokens.js +3 -3
- package/src/lib/generated/uexecutor/v2/types.d.ts +14 -0
- package/src/lib/generated/uexecutor/v2/types.js +96 -3
- package/src/lib/orchestrator/internals/cascade.d.ts +1 -1
- package/src/lib/orchestrator/internals/cascade.js +521 -239
- package/src/lib/orchestrator/internals/confirmation.js +60 -15
- package/src/lib/orchestrator/internals/context.d.ts +23 -0
- package/src/lib/orchestrator/internals/context.js +58 -0
- package/src/lib/orchestrator/internals/execute-funds-only.js +47 -21
- package/src/lib/orchestrator/internals/execute-funds-payload.js +70 -37
- package/src/lib/orchestrator/internals/execute-standard.js +137 -26
- package/src/lib/orchestrator/internals/gas-calculator.d.ts +5 -2
- package/src/lib/orchestrator/internals/gas-calculator.js +59 -11
- package/src/lib/orchestrator/internals/gas-usd-sizer.d.ts +53 -0
- package/src/lib/orchestrator/internals/gas-usd-sizer.js +165 -0
- package/src/lib/orchestrator/internals/helpers.js +2 -0
- package/src/lib/orchestrator/internals/inbound-tracker.d.ts +83 -0
- package/src/lib/orchestrator/internals/inbound-tracker.js +322 -0
- package/src/lib/orchestrator/internals/index.d.ts +3 -1
- package/src/lib/orchestrator/internals/index.js +8 -2
- package/src/lib/orchestrator/internals/outbound-sync.d.ts +32 -1
- package/src/lib/orchestrator/internals/outbound-sync.js +154 -17
- package/src/lib/orchestrator/internals/pc-usd-oracle.d.ts +28 -0
- package/src/lib/orchestrator/internals/pc-usd-oracle.js +285 -0
- package/src/lib/orchestrator/internals/progress-route-hooks.d.ts +23 -0
- package/src/lib/orchestrator/internals/progress-route-hooks.js +72 -0
- package/src/lib/orchestrator/internals/push-chain-tx.d.ts +27 -1
- package/src/lib/orchestrator/internals/push-chain-tx.js +111 -15
- package/src/lib/orchestrator/internals/rescue.js +1 -1
- package/src/lib/orchestrator/internals/response-builder.d.ts +5 -0
- package/src/lib/orchestrator/internals/response-builder.js +242 -66
- package/src/lib/orchestrator/internals/route-handlers.js +480 -206
- package/src/lib/orchestrator/internals/tx-transformer.js +293 -35
- package/src/lib/orchestrator/orchestrator.d.ts +7 -1
- package/src/lib/orchestrator/orchestrator.js +108 -56
- package/src/lib/orchestrator/orchestrator.types.d.ts +111 -5
- package/src/lib/orchestrator/payload-builders.d.ts +16 -0
- package/src/lib/orchestrator/payload-builders.js +6 -5
- package/src/lib/orchestrator/route-detector.js +20 -8
- package/src/lib/orchestrator/svm-idl/normalize-address.d.ts +19 -0
- package/src/lib/orchestrator/svm-idl/normalize-address.js +56 -0
- package/src/lib/orchestrator/svm-idl/registry.d.ts +13 -4
- package/src/lib/orchestrator/svm-idl/registry.js +15 -14
- package/src/lib/orchestrator/svm-idl/resolve.js +5 -2
- package/src/lib/progress-hook/progress-hook.d.ts +28 -0
- package/src/lib/progress-hook/progress-hook.js +552 -133
- package/src/lib/progress-hook/progress-hook.types.d.ts +77 -29
- package/src/lib/progress-hook/progress-hook.types.js +75 -35
- package/src/lib/push-client/push-client.d.ts +7 -0
- package/src/lib/push-client/push-client.js +18 -1
- package/src/lib/universal-tx-detector/__tests__/fixtures/builders.d.ts +32 -0
- package/src/lib/universal-tx-detector/__tests__/fixtures/builders.js +65 -0
- package/src/lib/universal-tx-detector/__tests__/fixtures/scenarios.d.ts +72 -0
- package/src/lib/universal-tx-detector/__tests__/fixtures/scenarios.js +669 -0
- package/src/lib/universal-tx-detector/auto-detect.d.ts +37 -0
- package/src/lib/universal-tx-detector/auto-detect.js +112 -0
- package/src/lib/universal-tx-detector/cascade.d.ts +64 -0
- package/src/lib/universal-tx-detector/cascade.js +254 -0
- package/src/lib/universal-tx-detector/child-inbounds.d.ts +58 -0
- package/src/lib/universal-tx-detector/child-inbounds.js +145 -0
- package/src/lib/universal-tx-detector/classify.d.ts +33 -0
- package/src/lib/universal-tx-detector/classify.js +228 -0
- package/src/lib/universal-tx-detector/detector-svm.d.ts +46 -0
- package/src/lib/universal-tx-detector/detector-svm.js +324 -0
- package/src/lib/universal-tx-detector/detector.d.ts +13 -0
- package/src/lib/universal-tx-detector/detector.js +233 -0
- package/src/lib/universal-tx-detector/events.d.ts +765 -0
- package/src/lib/universal-tx-detector/events.js +241 -0
- package/src/lib/universal-tx-detector/index.d.ts +18 -0
- package/src/lib/universal-tx-detector/index.js +36 -0
- package/src/lib/universal-tx-detector/svm-events.d.ts +41 -0
- package/src/lib/universal-tx-detector/svm-events.js +68 -0
- package/src/lib/universal-tx-detector/types.d.ts +100 -0
- package/src/lib/universal-tx-detector/types.js +3 -0
- package/src/lib/utils.d.ts +30 -13
- package/src/lib/utils.js +47 -21
|
@@ -26,6 +26,7 @@ const tslib_1 = require("tslib");
|
|
|
26
26
|
const web3_js_1 = require("@solana/web3.js");
|
|
27
27
|
const viem_1 = require("viem");
|
|
28
28
|
const context_1 = require("./context");
|
|
29
|
+
const progress_hook_types_1 = require("../../progress-hook/progress-hook.types");
|
|
29
30
|
const helpers_1 = require("./helpers");
|
|
30
31
|
const payload_builder_1 = require("./payload-builder");
|
|
31
32
|
const uea_manager_1 = require("./uea-manager");
|
|
@@ -40,6 +41,152 @@ const payload_builders_1 = require("../payload-builders");
|
|
|
40
41
|
const selectors_1 = require("../../constants/selectors");
|
|
41
42
|
const erc20_evm_1 = require("../../constants/abi/erc20.evm");
|
|
42
43
|
const push_chain_1 = require("../../push-chain/push-chain");
|
|
44
|
+
// =============================================================================
|
|
45
|
+
// Route 2 (UEA → CEA) phase helpers
|
|
46
|
+
// =============================================================================
|
|
47
|
+
/**
|
|
48
|
+
* Decide which PRC-20 token to burn on Push Chain and how much of it to burn
|
|
49
|
+
* for a Route 2 EVM outbound call. Chain-local — does NOT require a resolved
|
|
50
|
+
* CEA address, so it can run before the 203 account-resolution phase.
|
|
51
|
+
*/
|
|
52
|
+
function resolveR2Prc20TokenEvm(params, targetChain, pushNetwork) {
|
|
53
|
+
var _a;
|
|
54
|
+
let prc20Token = selectors_1.ZERO_ADDRESS;
|
|
55
|
+
let burnAmount = BigInt(0);
|
|
56
|
+
if (params.migration) {
|
|
57
|
+
// Migration is logic-only — no funds. CEA rejects msg.value != 0.
|
|
58
|
+
prc20Token = (0, helpers_1.getNativePRC20ForChain)(targetChain, pushNetwork);
|
|
59
|
+
burnAmount = BigInt(0);
|
|
60
|
+
}
|
|
61
|
+
else if ((_a = params.funds) === null || _a === void 0 ? void 0 : _a.amount) {
|
|
62
|
+
// User explicitly specified funds with token
|
|
63
|
+
const token = params.funds.token;
|
|
64
|
+
if (token) {
|
|
65
|
+
prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
|
|
66
|
+
burnAmount = params.funds.amount;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else if (params.value && params.value > BigInt(0)) {
|
|
70
|
+
// Native value transfer: auto-select the PRC-20 token for target chain
|
|
71
|
+
prc20Token = (0, helpers_1.getNativePRC20ForChain)(targetChain, pushNetwork);
|
|
72
|
+
burnAmount = params.value;
|
|
73
|
+
}
|
|
74
|
+
else if (params.data) {
|
|
75
|
+
// PAYLOAD-only (no value transfer): still need native token for chain namespace + gas fees
|
|
76
|
+
prc20Token = (0, helpers_1.getNativePRC20ForChain)(targetChain, pushNetwork);
|
|
77
|
+
burnAmount = BigInt(0);
|
|
78
|
+
}
|
|
79
|
+
return { prc20Token, burnAmount };
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* SVM counterpart to `resolveR2Prc20TokenEvm`. SVM adds an EXECUTE-only
|
|
83
|
+
* branch (no value, no funds, but `hasSvmExecute === true`) that still needs
|
|
84
|
+
* a PRC-20 token for the chain-namespace lookup.
|
|
85
|
+
*/
|
|
86
|
+
function resolveR2Prc20TokenSvm(params, targetChain, pushNetwork, hasSvmExecute) {
|
|
87
|
+
var _a;
|
|
88
|
+
let prc20Token = selectors_1.ZERO_ADDRESS;
|
|
89
|
+
let burnAmount = BigInt(0);
|
|
90
|
+
if ((_a = params.funds) === null || _a === void 0 ? void 0 : _a.amount) {
|
|
91
|
+
const token = params.funds.token;
|
|
92
|
+
if (token) {
|
|
93
|
+
prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
|
|
94
|
+
burnAmount = params.funds.amount;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else if (params.value && params.value > BigInt(0)) {
|
|
98
|
+
prc20Token = (0, helpers_1.getNativePRC20ForChain)(targetChain, pushNetwork);
|
|
99
|
+
burnAmount = params.value;
|
|
100
|
+
}
|
|
101
|
+
else if (hasSvmExecute) {
|
|
102
|
+
const token = params.funds && params.funds.token;
|
|
103
|
+
if (token) {
|
|
104
|
+
prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
prc20Token = (0, helpers_1.getNativePRC20ForChain)(targetChain, pushNetwork);
|
|
108
|
+
}
|
|
109
|
+
burnAmount = BigInt(0);
|
|
110
|
+
}
|
|
111
|
+
return { prc20Token, burnAmount };
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Build the CEA-side multicall payload for a Route 2 EVM outbound. Needs the
|
|
115
|
+
* resolved `ceaAddress` because the native-value self-call branch skips the
|
|
116
|
+
* multicall when the target IS the CEA (avoids a CEA._handleMulticall revert).
|
|
117
|
+
*/
|
|
118
|
+
function buildR2CeaPayloadEvm(params, ceaAddress, targetAddress) {
|
|
119
|
+
var _a, _b;
|
|
120
|
+
if (params.migration) {
|
|
121
|
+
return (0, payload_builders_1.buildMigrationPayload)();
|
|
122
|
+
}
|
|
123
|
+
const ceaMulticalls = [];
|
|
124
|
+
if (params.data) {
|
|
125
|
+
if (Array.isArray(params.data)) {
|
|
126
|
+
ceaMulticalls.push(...params.data);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
// When ERC-20 funds are provided with a single payload, auto-prepend a
|
|
130
|
+
// transfer() call so the tokens minted to the CEA are forwarded to the
|
|
131
|
+
// target address. Mirrors the Route 1 behavior in buildExecuteMulticall.
|
|
132
|
+
if ((_a = params.funds) === null || _a === void 0 ? void 0 : _a.amount) {
|
|
133
|
+
const token = params.funds.token;
|
|
134
|
+
if (token && token.mechanism !== 'native') {
|
|
135
|
+
const erc20Transfer = (0, viem_1.encodeFunctionData)({
|
|
136
|
+
abi: erc20_evm_1.ERC20_EVM,
|
|
137
|
+
functionName: 'transfer',
|
|
138
|
+
args: [targetAddress, params.funds.amount],
|
|
139
|
+
});
|
|
140
|
+
ceaMulticalls.push({
|
|
141
|
+
to: token.address,
|
|
142
|
+
value: BigInt(0),
|
|
143
|
+
data: erc20Transfer,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Single call with data. Forward native value (if any) so the target
|
|
148
|
+
// contract receives it alongside the payload call.
|
|
149
|
+
ceaMulticalls.push({
|
|
150
|
+
to: targetAddress,
|
|
151
|
+
value: (_b = params.value) !== null && _b !== void 0 ? _b : BigInt(0),
|
|
152
|
+
data: params.data,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
else if (params.value) {
|
|
157
|
+
// Native value transfer only. Skip the multicall when sending to the CEA
|
|
158
|
+
// itself — a value-bearing self-call would revert in CEA._handleMulticall.
|
|
159
|
+
if (targetAddress.toLowerCase() !== ceaAddress.toLowerCase()) {
|
|
160
|
+
ceaMulticalls.push({
|
|
161
|
+
to: targetAddress,
|
|
162
|
+
value: params.value,
|
|
163
|
+
data: '0x',
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return (0, payload_builders_1.buildCeaMulticallPayload)(ceaMulticalls);
|
|
168
|
+
}
|
|
169
|
+
const SIZER_HOOK_BY_ROUTE = {
|
|
170
|
+
R3: {
|
|
171
|
+
A: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_303_03_01,
|
|
172
|
+
B: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_303_03_02,
|
|
173
|
+
C: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_303_03_03,
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
/**
|
|
177
|
+
* Fire the route-scoped sizing progress hook based on the sizer's decision.
|
|
178
|
+
* Emits the consumer-facing shape { gasRequired, extraDepositPC, totalDepositUSD, chain }.
|
|
179
|
+
* - gasRequired = sizer's gas leg in native PC wei
|
|
180
|
+
* - extraDepositPC = overflow in native PC wei (> 0 only for Case C)
|
|
181
|
+
* - totalDepositUSD = sized total in USD (8-dec): $1 for Case A (padded),
|
|
182
|
+
* raw gasUsd for Case B/C (which for Case C equals gasLegUsd + overflowUsd).
|
|
183
|
+
*/
|
|
184
|
+
function fireSizingHook(ctx, route, chain, sizing) {
|
|
185
|
+
const hook = SIZER_HOOK_BY_ROUTE[route][sizing.category];
|
|
186
|
+
const ONE_USD_8D = BigInt(100000000);
|
|
187
|
+
const totalDepositUSD = sizing.category === 'A' ? ONE_USD_8D : sizing.gasUsd;
|
|
188
|
+
(0, context_1.fireProgressHook)(ctx, hook, chain, sizing.gasLegNativePc, sizing.overflowNativePc, totalDepositUSD);
|
|
189
|
+
}
|
|
43
190
|
// ---------------------------------------------------------------------------
|
|
44
191
|
// executeMultiChain
|
|
45
192
|
// ---------------------------------------------------------------------------
|
|
@@ -100,7 +247,7 @@ function executeMultiChain(ctx, params, executeFn) {
|
|
|
100
247
|
*/
|
|
101
248
|
function executeUoaToCea(ctx, params, executeFn) {
|
|
102
249
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
103
|
-
var _a, _b, _c
|
|
250
|
+
var _a, _b, _c;
|
|
104
251
|
const target = params.to;
|
|
105
252
|
const targetChain = target.chain;
|
|
106
253
|
const targetAddress = target.address;
|
|
@@ -137,100 +284,63 @@ function executeUoaToCea(ctx, params, executeFn) {
|
|
|
137
284
|
return executeUoaToCeaSvm(ctx, params, target, executeFn);
|
|
138
285
|
}
|
|
139
286
|
// ===== EVM path (existing logic) =====
|
|
140
|
-
//
|
|
287
|
+
// R2 pre-broadcast progress: external chain detected
|
|
288
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_201, targetChain, targetAddress);
|
|
289
|
+
// Compute UEA address (sync, offchain derivation — no RPC).
|
|
141
290
|
const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
|
|
142
291
|
(0, context_1.printLog)(ctx, `executeUoaToCea — target chain: ${targetChain}, target address: ${targetAddress}, UEA: ${ueaAddress}`);
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
let burnAmount = BigInt(0);
|
|
150
|
-
if (params.migration) {
|
|
151
|
-
ceaPayload = (0, payload_builders_1.buildMigrationPayload)();
|
|
152
|
-
prc20Token = (0, helpers_1.getNativePRC20ForChain)(targetChain, ctx.pushNetwork);
|
|
153
|
-
burnAmount = BigInt(0); // Migration is logic-only — no funds. CEA rejects msg.value != 0.
|
|
154
|
-
(0, context_1.printLog)(ctx, `executeUoaToCea — MIGRATION: using raw MIGRATION_SELECTOR payload (${ceaPayload}), native PRC-20 ${prc20Token}`);
|
|
292
|
+
// --- Token resolution (chain-local, before account resolution) ---
|
|
293
|
+
// Resolve the PRC-20 burn token + burn amount ahead of CEA resolution.
|
|
294
|
+
// Chain-local — see `resolveR2Prc20TokenEvm` above.
|
|
295
|
+
const { prc20Token, burnAmount } = resolveR2Prc20TokenEvm(params, targetChain, ctx.pushNetwork);
|
|
296
|
+
if (params.value && params.value > BigInt(0)) {
|
|
297
|
+
(0, context_1.printLog)(ctx, `executeUoaToCea — auto-selected native PRC-20 ${prc20Token} for chain ${targetChain}, amount: ${burnAmount.toString()}`);
|
|
155
298
|
}
|
|
156
|
-
else {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
args: [targetAddress, params.funds.amount],
|
|
176
|
-
});
|
|
177
|
-
ceaMulticalls.push({
|
|
178
|
-
to: token.address,
|
|
179
|
-
value: BigInt(0),
|
|
180
|
-
data: erc20Transfer,
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
// Single call with data. Forward native value (if any) so the target
|
|
185
|
-
// contract receives it alongside the payload call. The vault deposits
|
|
186
|
-
// native value to the CEA, and the multicall forwards it to the target.
|
|
187
|
-
ceaMulticalls.push({
|
|
188
|
-
to: targetAddress,
|
|
189
|
-
value: (_c = params.value) !== null && _c !== void 0 ? _c : BigInt(0),
|
|
190
|
-
data: params.data,
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
else if (params.value) {
|
|
195
|
-
// Native value transfer only.
|
|
196
|
-
// If sending to the CEA itself, skip the multicall — the gateway deposits native
|
|
197
|
-
// value directly to CEA. A self-call with value would revert (CEA._handleMulticall
|
|
198
|
-
// rejects value-bearing self-calls).
|
|
199
|
-
if (targetAddress.toLowerCase() !== ceaAddress.toLowerCase()) {
|
|
200
|
-
ceaMulticalls.push({
|
|
201
|
-
to: targetAddress,
|
|
202
|
-
value: params.value,
|
|
203
|
-
data: '0x',
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
// Build CEA multicall payload (this is what gets executed on the external chain)
|
|
208
|
-
ceaPayload = (0, payload_builders_1.buildCeaMulticallPayload)(ceaMulticalls);
|
|
209
|
-
// Determine token to burn on Push Chain
|
|
210
|
-
// NOTE: Even for PAYLOAD-only (no value), we need a valid PRC-20 token to:
|
|
211
|
-
// 1. Look up the target chain namespace in the gateway
|
|
212
|
-
// 2. Query and pay gas fees for the relay
|
|
213
|
-
if ((_d = params.funds) === null || _d === void 0 ? void 0 : _d.amount) {
|
|
214
|
-
// User explicitly specified funds with token
|
|
215
|
-
const token = params.funds.token;
|
|
216
|
-
if (token) {
|
|
217
|
-
prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
|
|
218
|
-
burnAmount = params.funds.amount;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
else if (params.value && params.value > BigInt(0)) {
|
|
222
|
-
// Native value transfer: auto-select the PRC-20 token for target chain
|
|
223
|
-
prc20Token = (0, helpers_1.getNativePRC20ForChain)(targetChain, ctx.pushNetwork);
|
|
224
|
-
burnAmount = params.value;
|
|
225
|
-
(0, context_1.printLog)(ctx, `executeUoaToCea — auto-selected native PRC-20 ${prc20Token} for chain ${targetChain}, amount: ${burnAmount.toString()}`);
|
|
299
|
+
else if (!params.migration && !((_a = params.funds) === null || _a === void 0 ? void 0 : _a.amount) && params.data) {
|
|
300
|
+
(0, context_1.printLog)(ctx, `executeUoaToCea — PAYLOAD-only: using native PRC-20 ${prc20Token} for chain ${targetChain} with zero burn amount`);
|
|
301
|
+
}
|
|
302
|
+
const gasLimitForQuery = (_b = params.gasLimit) !== null && _b !== void 0 ? _b : BigInt(0);
|
|
303
|
+
// --- 202: Gas estimation (spec-ordered before 203) ---
|
|
304
|
+
let gasFee = BigInt(0);
|
|
305
|
+
let protocolFee = BigInt(0);
|
|
306
|
+
let gasToken = selectors_1.ZERO_ADDRESS;
|
|
307
|
+
let universalCoreAddress;
|
|
308
|
+
if (prc20Token !== selectors_1.ZERO_ADDRESS) {
|
|
309
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_202_01, targetChain);
|
|
310
|
+
try {
|
|
311
|
+
const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, gasLimitForQuery, targetChain);
|
|
312
|
+
gasFee = result.gasFee;
|
|
313
|
+
protocolFee = result.protocolFee;
|
|
314
|
+
gasToken = result.gasToken;
|
|
315
|
+
universalCoreAddress = result.universalCoreAddress;
|
|
316
|
+
(0, context_1.printLog)(ctx, `executeUoaToCea — queried gas fee: ${gasFee.toString()}, gasToken: ${gasToken}`);
|
|
317
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_202_02, targetChain, protocolFee, gasFee);
|
|
226
318
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
prc20Token = (0, helpers_1.getNativePRC20ForChain)(targetChain, ctx.pushNetwork);
|
|
230
|
-
burnAmount = BigInt(0);
|
|
231
|
-
(0, context_1.printLog)(ctx, `executeUoaToCea — PAYLOAD-only: using native PRC-20 ${prc20Token} for chain ${targetChain} with zero burn amount`);
|
|
319
|
+
catch (err) {
|
|
320
|
+
throw new Error(`Failed to query outbound gas fee: ${err}`);
|
|
232
321
|
}
|
|
233
322
|
}
|
|
323
|
+
// --- 203: CEA resolution + UEA status (single round-trip burst) ---
|
|
324
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_203_01, targetChain);
|
|
325
|
+
const signerChain = ctx.universalSigner.account.chain;
|
|
326
|
+
const isNativePushEOA = (0, helpers_1.isPushChain)(signerChain);
|
|
327
|
+
const [{ cea: ceaAddress, isDeployed: ceaDeployed }, [ueaCode, ueaBalance],] = yield Promise.all([
|
|
328
|
+
(0, cea_utils_1.getCEAAddress)(ueaAddress, targetChain, (_c = ctx.rpcUrls[targetChain]) === null || _c === void 0 ? void 0 : _c[0]),
|
|
329
|
+
Promise.all([
|
|
330
|
+
ctx.pushClient.publicClient.getCode({ address: ueaAddress }),
|
|
331
|
+
ctx.pushClient.getBalance(ueaAddress),
|
|
332
|
+
]),
|
|
333
|
+
]);
|
|
334
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_203_02, ueaAddress, ceaAddress, targetChain, ceaDeployed);
|
|
335
|
+
(0, context_1.printLog)(ctx, `executeUoaToCea — CEA address: ${ceaAddress}, deployed: ${ceaDeployed}`);
|
|
336
|
+
const isUEADeployed = isNativePushEOA || ueaCode !== undefined;
|
|
337
|
+
const ueaNonce = (!isUEADeployed || isNativePushEOA) ? BigInt(0) : yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress);
|
|
338
|
+
// --- Build CEA payload (requires ceaAddress for self-call check) ---
|
|
339
|
+
// Delegates to `buildR2CeaPayloadEvm` — see helper definition above.
|
|
340
|
+
const ceaPayload = buildR2CeaPayloadEvm(params, ceaAddress, targetAddress);
|
|
341
|
+
if (params.migration) {
|
|
342
|
+
(0, context_1.printLog)(ctx, `executeUoaToCea — MIGRATION: using raw MIGRATION_SELECTOR payload (${ceaPayload}), native PRC-20 ${prc20Token}`);
|
|
343
|
+
}
|
|
234
344
|
// Build outbound request struct for the gateway
|
|
235
345
|
// NOTE: `target` is a LEGACY/DUMMY parameter for contract compatibility.
|
|
236
346
|
// The deployed UniversalGatewayPC still expects this field, but the relay does NOT use it
|
|
@@ -238,7 +348,7 @@ function executeUoaToCea(ctx, params, executeFn) {
|
|
|
238
348
|
// SOURCE_CHAIN_NAMESPACE. We pass the CEA address as a non-zero placeholder.
|
|
239
349
|
// This field will be removed in future contract upgrades.
|
|
240
350
|
const targetBytes = ceaAddress; // Dummy value - any non-zero address works
|
|
241
|
-
const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount,
|
|
351
|
+
const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, gasLimitForQuery, ceaPayload, ueaAddress // revert recipient is the UEA
|
|
242
352
|
);
|
|
243
353
|
(0, context_1.printLog)(ctx, `executeUoaToCea — outbound request: ${JSON.stringify({
|
|
244
354
|
target: outboundReq.target,
|
|
@@ -250,55 +360,32 @@ function executeUoaToCea(ctx, params, executeFn) {
|
|
|
250
360
|
}, null, 2)}`);
|
|
251
361
|
// Get UniversalGatewayPC address
|
|
252
362
|
const gatewayPcAddress = (0, helpers_1.getUniversalGatewayPCAddress)();
|
|
253
|
-
// Pre-fetch UEA status early — balance is needed for gas value calculation
|
|
254
|
-
const signerChain = ctx.universalSigner.account.chain;
|
|
255
|
-
const isNativePushEOA = (0, helpers_1.isPushChain)(signerChain);
|
|
256
|
-
const [ueaCode, ueaBalance] = yield Promise.all([
|
|
257
|
-
ctx.pushClient.publicClient.getCode({ address: ueaAddress }),
|
|
258
|
-
ctx.pushClient.getBalance(ueaAddress),
|
|
259
|
-
]);
|
|
260
|
-
const isUEADeployed = isNativePushEOA || ueaCode !== undefined;
|
|
261
|
-
const ueaNonce = (!isUEADeployed || isNativePushEOA) ? BigInt(0) : yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress);
|
|
262
363
|
// Build the multicall that will execute ON Push Chain from UEA context
|
|
263
364
|
// This includes: 1) approve PRC-20 (if needed), 2) call sendUniversalTxOutbound
|
|
264
365
|
const pushChainMulticalls = [];
|
|
265
|
-
//
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
catch (err) {
|
|
280
|
-
throw new Error(`Failed to query outbound gas fee: ${err}`);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
// Balance-aware adjustment: MAXIMIZE nativeValueForGas within available balance.
|
|
284
|
-
// The contract's swapAndBurnGas does exactOutputSingle and refunds excess PC,
|
|
285
|
-
// so overshooting within balance is safe. Undershooting causes the swap to revert.
|
|
286
|
-
const EVM_NATIVE_VALUE_TARGET = BigInt(200e18); // 200 UPC
|
|
287
|
-
const EVM_GAS_RESERVE = BigInt(3e18); // 3 UPC for tx overhead
|
|
366
|
+
// ---------------------------------------------------------------------
|
|
367
|
+
// Native-value selection — R2 does NOT use Case A/B/C gas abstraction.
|
|
368
|
+
// That feature is scoped to R1 (fee-lock USD caps) and R3 (CEA → Push
|
|
369
|
+
// outbound msg.value). For R2 we size `nativeValueForGas` from the
|
|
370
|
+
// live WPC/gasToken Uniswap V3 pool quote plus a 10% safety buffer;
|
|
371
|
+
// `swapAndBurnGas` refunds any excess as PC back to UEA via
|
|
372
|
+
// refundUnusedGas.
|
|
373
|
+
//
|
|
374
|
+
// Fresh-wallet prediction (below) is retained: it estimates post-fee-lock
|
|
375
|
+
// UPC balance for UEAs that haven't been funded yet, independent of
|
|
376
|
+
// Case A/B/C sizing.
|
|
377
|
+
// ---------------------------------------------------------------------
|
|
378
|
+
const EVM_GAS_RESERVE = BigInt(3e18); // 3 UPC for outer-tx gas
|
|
379
|
+
const EVM_NATIVE_VALUE_SAFETY_CAP = BigInt(200e18); // 200 UPC absolute ceiling
|
|
288
380
|
const ROUTE2_MINIMUM_DEPOSIT_USD = utils_1.Utils.helpers.parseUnits('10', 8); // $10
|
|
289
|
-
//
|
|
290
|
-
// For deployed UEAs: use real balance.
|
|
291
|
-
// For fresh wallets: predict the post-fee-locking balance by querying the same
|
|
292
|
-
// Uniswap V3 quoter the chain uses (pETH → WPC swap in execute_inbound_gas.go).
|
|
381
|
+
// Effective balance: real for deployed UEAs; predicted post-fee-lock for fresh.
|
|
293
382
|
let effectiveBalance = ueaBalance;
|
|
294
383
|
if (!isUEADeployed && effectiveBalance <= EVM_GAS_RESERVE) {
|
|
295
|
-
// Calculate the ETH amount lockFee will send (same formula as lockFee)
|
|
296
384
|
const ethPrice = yield new price_fetch_1.PriceFetch(ctx.rpcUrls).getPrice(signerChain);
|
|
297
385
|
const nativeAmountETH = ethPrice > BigInt(0)
|
|
298
386
|
? (ROUTE2_MINIMUM_DEPOSIT_USD * BigInt(1e18) + (ethPrice - BigInt(1))) / ethPrice + BigInt(1)
|
|
299
387
|
: BigInt(0);
|
|
300
388
|
if (nativeAmountETH > BigInt(0)) {
|
|
301
|
-
// Query the real swap quote: how much WPC (UPC) for this pETH amount?
|
|
302
389
|
const originPrc20 = (0, helpers_1.getNativePRC20ForChain)(signerChain, ctx.pushNetwork);
|
|
303
390
|
const predictedUPC = yield (0, gas_calculator_1.estimateDepositFromLockedNative)(ctx, nativeAmountETH, originPrc20);
|
|
304
391
|
if (predictedUPC > BigInt(0)) {
|
|
@@ -309,15 +396,35 @@ function executeUoaToCea(ctx, params, executeFn) {
|
|
|
309
396
|
}
|
|
310
397
|
}
|
|
311
398
|
}
|
|
399
|
+
// Pool-quote + 10% safety buffer. Over-send is refunded by the contract.
|
|
400
|
+
let nativeValueForGas = BigInt(0);
|
|
401
|
+
if (universalCoreAddress && gasFee > BigInt(0)) {
|
|
402
|
+
const estimated = yield (0, gas_calculator_1.estimateNativeValueForSwap)(ctx, universalCoreAddress, gasToken, gasFee, effectiveBalance);
|
|
403
|
+
nativeValueForGas = (estimated * BigInt(110)) / BigInt(100);
|
|
404
|
+
(0, context_1.printLog)(ctx, `executeUoaToCea — nativeValueForGas: pool-quote=${estimated.toString()}, with 10% buffer=${nativeValueForGas.toString()}`);
|
|
405
|
+
}
|
|
406
|
+
// Hard safety cap so a broken pool price can't drain the UEA.
|
|
407
|
+
if (nativeValueForGas > EVM_NATIVE_VALUE_SAFETY_CAP) {
|
|
408
|
+
(0, context_1.printLog)(ctx, `executeUoaToCea — capping nativeValueForGas at 200 UPC ceiling (was ${nativeValueForGas.toString()})`);
|
|
409
|
+
nativeValueForGas = EVM_NATIVE_VALUE_SAFETY_CAP;
|
|
410
|
+
}
|
|
312
411
|
let adjustedValue;
|
|
313
|
-
if (effectiveBalance
|
|
314
|
-
adjustedValue =
|
|
412
|
+
if (effectiveBalance >= nativeValueForGas + EVM_GAS_RESERVE) {
|
|
413
|
+
adjustedValue = nativeValueForGas;
|
|
315
414
|
}
|
|
316
415
|
else if (effectiveBalance > EVM_GAS_RESERVE) {
|
|
416
|
+
// Balance-starved: send everything we can afford. swapAndBurnGas
|
|
417
|
+
// refunds unused; undershoot will revert with a concrete error.
|
|
317
418
|
adjustedValue = effectiveBalance - EVM_GAS_RESERVE;
|
|
318
419
|
}
|
|
420
|
+
else if (effectiveBalance > BigInt(0)) {
|
|
421
|
+
// Drained UEA: overshoot would return (false, "") and surface as an
|
|
422
|
+
// opaque ExecutionFailed() — clamp to what we have.
|
|
423
|
+
adjustedValue = effectiveBalance < nativeValueForGas ? effectiveBalance : nativeValueForGas;
|
|
424
|
+
}
|
|
319
425
|
else {
|
|
320
|
-
|
|
426
|
+
throw new Error(`UEA ${ueaAddress} has zero UPC balance; cannot fund outbound gas swap. ` +
|
|
427
|
+
`Bridge UPC to the UEA before retrying.`);
|
|
321
428
|
}
|
|
322
429
|
if (adjustedValue !== nativeValueForGas) {
|
|
323
430
|
(0, context_1.printLog)(ctx, `executeUoaToCea — adjusting nativeValueForGas from ${nativeValueForGas.toString()} ` +
|
|
@@ -370,7 +477,27 @@ function executeUoaToCea(ctx, params, executeFn) {
|
|
|
370
477
|
nonce: ueaNonce,
|
|
371
478
|
balance: ueaBalance,
|
|
372
479
|
}, _skipFeeLocking: isUEADeployed }, (!isUEADeployed ? { _minimumDepositUsd: ROUTE2_MINIMUM_DEPOSIT_USD } : {}));
|
|
373
|
-
|
|
480
|
+
// Signature request — user is prompted to sign the universal payload.
|
|
481
|
+
// executeFn is monolithic (sign → broadcast), so we emit the logical
|
|
482
|
+
// post-sign / pre-broadcast markers as a burst after it returns so
|
|
483
|
+
// consumers see the spec-ordered stream: 204-01 → 204-02 → 204-03 → 207.
|
|
484
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_204_01);
|
|
485
|
+
let response;
|
|
486
|
+
try {
|
|
487
|
+
response = yield executeFn(executeParams);
|
|
488
|
+
}
|
|
489
|
+
catch (err) {
|
|
490
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_204_04, err instanceof Error ? err.message : String(err));
|
|
491
|
+
// Suppress the outer orchestrator catch's 299-02 terminal — 204-04
|
|
492
|
+
// already surfaced the signature/broadcast failure to the consumer.
|
|
493
|
+
ctx._routeTerminalEmitted = true;
|
|
494
|
+
throw err;
|
|
495
|
+
}
|
|
496
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_204_02);
|
|
497
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_204_03);
|
|
498
|
+
// R2 broadcast marker — Push Chain tx has been built, signed, and
|
|
499
|
+
// accepted by the node. Replaces the suppressed 107 emission for this route.
|
|
500
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_207, targetChain);
|
|
374
501
|
// Add chain info to response
|
|
375
502
|
response.chain = targetChain;
|
|
376
503
|
response.chainNamespace = (0, helpers_1.getChainNamespace)(targetChain);
|
|
@@ -394,6 +521,8 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
|
|
|
394
521
|
const targetChain = target.chain;
|
|
395
522
|
const targetAddress = target.address; // 0x-prefixed, 32 bytes
|
|
396
523
|
const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
|
|
524
|
+
// R2 pre-broadcast progress (SVM) — external chain detected
|
|
525
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_201, targetChain, targetAddress);
|
|
397
526
|
const { svmPayload, targetBytes, hasExecute: hasSvmExecute, } = (0, build_payload_1.buildSvmPayloadFromParams)({
|
|
398
527
|
data: params.data,
|
|
399
528
|
to: target,
|
|
@@ -404,38 +533,55 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
|
|
|
404
533
|
if (hasSvmExecute) {
|
|
405
534
|
(0, context_1.printLog)(ctx, `executeUoaToCeaSvm — encoded execute payload: ${(svmPayload.length - 2) / 2} bytes`);
|
|
406
535
|
}
|
|
407
|
-
// --- Determine PRC-20 token and burn amount ---
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
if ((_c = params.funds) === null || _c === void 0 ? void 0 : _c.amount) {
|
|
411
|
-
// User explicitly specified funds with token
|
|
412
|
-
const token = params.funds.token;
|
|
413
|
-
if (token) {
|
|
414
|
-
prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
|
|
415
|
-
burnAmount = params.funds.amount;
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
else if (params.value && params.value > BigInt(0)) {
|
|
419
|
-
// Native value transfer: auto-select pSOL for Solana chains
|
|
420
|
-
prc20Token = (0, helpers_1.getNativePRC20ForChain)(targetChain, ctx.pushNetwork);
|
|
421
|
-
burnAmount = params.value;
|
|
536
|
+
// --- Determine PRC-20 token and burn amount (chain-local, pre-203) ---
|
|
537
|
+
const { prc20Token, burnAmount } = resolveR2Prc20TokenSvm(params, targetChain, ctx.pushNetwork, hasSvmExecute);
|
|
538
|
+
if (params.value && params.value > BigInt(0)) {
|
|
422
539
|
(0, context_1.printLog)(ctx, `executeUoaToCeaSvm — auto-selected native PRC-20 ${prc20Token} for chain ${targetChain}, amount: ${burnAmount.toString()}`);
|
|
423
540
|
}
|
|
424
|
-
else if (hasSvmExecute) {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
541
|
+
else if (!((_c = params.funds) === null || _c === void 0 ? void 0 : _c.amount) && hasSvmExecute) {
|
|
542
|
+
(0, context_1.printLog)(ctx, `executeUoaToCeaSvm — EXECUTE-only: using PRC-20 ${prc20Token} with zero burn amount`);
|
|
543
|
+
}
|
|
544
|
+
// --- 202: Gas estimation (spec-ordered before 203) ---
|
|
545
|
+
let gasFee = BigInt(0);
|
|
546
|
+
let protocolFeeSvm = BigInt(0);
|
|
547
|
+
let gasToken = selectors_1.ZERO_ADDRESS;
|
|
548
|
+
let universalCoreAddress;
|
|
549
|
+
// Effective gas limit seeded from the user param. When the user omits
|
|
550
|
+
// gasLimit, it'll be derived from the gasFee/gasPrice response so the
|
|
551
|
+
// on-chain record carries a non-zero compute budget the relay can use.
|
|
552
|
+
let effectiveGasLimit = (_d = params.gasLimit) !== null && _d !== void 0 ? _d : BigInt(0);
|
|
553
|
+
if (prc20Token !== selectors_1.ZERO_ADDRESS) {
|
|
554
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_202_01, targetChain);
|
|
555
|
+
try {
|
|
556
|
+
const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, effectiveGasLimit, targetChain);
|
|
557
|
+
gasFee = result.gasFee;
|
|
558
|
+
protocolFeeSvm = result.protocolFee;
|
|
559
|
+
gasToken = result.gasToken;
|
|
560
|
+
universalCoreAddress = result.universalCoreAddress;
|
|
561
|
+
// When user omits gasLimit (sent as 0), the contract computes fees using its internal
|
|
562
|
+
// baseGasLimitByChainNamespace. But the relay reads the stored gasLimit=0 from the
|
|
563
|
+
// on-chain outbound record and uses it as the Solana compute budget — 0 CU means the
|
|
564
|
+
// relay cannot execute the tx. Derive the effective limit from gasFee/gasPrice so
|
|
565
|
+
// the stored record has a non-zero compute budget the relay can use.
|
|
566
|
+
if (!params.gasLimit && result.gasPrice > BigInt(0)) {
|
|
567
|
+
effectiveGasLimit = result.gasFee / result.gasPrice;
|
|
568
|
+
(0, context_1.printLog)(ctx, `executeUoaToCeaSvm — derived effectiveGasLimit: ${effectiveGasLimit} (gasFee=${result.gasFee} / gasPrice=${result.gasPrice})`);
|
|
569
|
+
}
|
|
570
|
+
(0, context_1.printLog)(ctx, `executeUoaToCeaSvm — queried gas fee: ${gasFee.toString()}, gasToken: ${gasToken}`);
|
|
571
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_202_02, targetChain, protocolFeeSvm, gasFee);
|
|
429
572
|
}
|
|
430
|
-
|
|
431
|
-
|
|
573
|
+
catch (err) {
|
|
574
|
+
throw new Error(`Failed to query outbound gas fee: ${err}`);
|
|
432
575
|
}
|
|
433
|
-
burnAmount = BigInt(0);
|
|
434
|
-
(0, context_1.printLog)(ctx, `executeUoaToCeaSvm — EXECUTE-only: using PRC-20 ${prc20Token} with zero burn amount`);
|
|
435
576
|
}
|
|
577
|
+
// --- 203: Execution-account resolution (SVM has a trivial CEA — the
|
|
578
|
+
// target address IS the CEA, no async lookup needed). Fire the burst
|
|
579
|
+
// after 202 to keep the progress-hook stream in spec order. ---
|
|
580
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_203_01, targetChain);
|
|
581
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_203_02, ueaAddress, targetAddress, targetChain, true);
|
|
436
582
|
// --- Build outbound request ---
|
|
437
583
|
// targetBytes: for execute = program id (resolver output); for withdraw = to.address
|
|
438
|
-
const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount,
|
|
584
|
+
const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, effectiveGasLimit, svmPayload, ueaAddress // revert recipient is the UEA
|
|
439
585
|
);
|
|
440
586
|
(0, context_1.printLog)(ctx, `executeUoaToCeaSvm — outbound request: ${JSON.stringify({
|
|
441
587
|
target: outboundReq.target,
|
|
@@ -445,7 +591,7 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
|
|
|
445
591
|
payloadLength: (outboundReq.payload.length - 2) / 2,
|
|
446
592
|
revertRecipient: outboundReq.revertRecipient,
|
|
447
593
|
}, null, 2)}`);
|
|
448
|
-
// --- Pre-fetch UEA status
|
|
594
|
+
// --- Pre-fetch UEA status — balance is needed for gas value calculation ---
|
|
449
595
|
const gatewayPcAddress = (0, helpers_1.getUniversalGatewayPCAddress)();
|
|
450
596
|
const signerChainSvm = ctx.universalSigner.account.chain;
|
|
451
597
|
const isNativePushEOASvm = (0, helpers_1.isPushChain)(signerChainSvm);
|
|
@@ -466,45 +612,43 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
|
|
|
466
612
|
ueaBalance = balance;
|
|
467
613
|
}
|
|
468
614
|
const ueaNonce = (!isUEADeployed || isNativePushEOASvm) ? BigInt(0) : yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress);
|
|
469
|
-
//
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
615
|
+
// R2 does NOT use Case A/B/C gas abstraction (scoped to R1 + R3 only).
|
|
616
|
+
// Size msg.value from the live WPC/gasToken Uniswap V3 pool quote plus
|
|
617
|
+
// a 10% safety buffer. swapAndBurnGas refunds the excess as PC via
|
|
618
|
+
// refundUnusedGas.
|
|
619
|
+
//
|
|
620
|
+
// SVM pools (WPC/pSOL) are thinner than EVM pools so the pool-quote can
|
|
621
|
+
// easily exceed the UEA's balance. Apply a balance-aware clamp so the
|
|
622
|
+
// outer tx doesn't fail with InsufficientFunds before the swap runs.
|
|
623
|
+
// No hard safety cap here — pSOL pool skew means even legitimate swaps
|
|
624
|
+
// can need hundreds of PC; capping would trigger Uniswap STF reverts.
|
|
625
|
+
const SVM_GAS_RESERVE = BigInt(3e18); // 3 UPC for outer-tx gas
|
|
473
626
|
let nativeValueForGas = BigInt(0);
|
|
474
|
-
if (prc20Token !== selectors_1.ZERO_ADDRESS) {
|
|
475
|
-
const gasLimit = outboundReq.gasLimit;
|
|
476
|
-
try {
|
|
477
|
-
const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, gasLimit);
|
|
478
|
-
gasFee = result.gasFee;
|
|
479
|
-
gasToken = result.gasToken;
|
|
480
|
-
nativeValueForGas = result.nativeValueForGas;
|
|
481
|
-
universalCoreAddress = result.universalCoreAddress;
|
|
482
|
-
// When user omits gasLimit (sent as 0), the contract computes fees using its internal
|
|
483
|
-
// baseGasLimitByChainNamespace. But the relay reads the stored gasLimit=0 from the
|
|
484
|
-
// on-chain outbound record and uses it as the Solana compute budget — 0 CU means the
|
|
485
|
-
// relay cannot execute the tx. Derive the effective limit from gasFee/gasPrice so
|
|
486
|
-
// the stored record has a non-zero compute budget the relay can use.
|
|
487
|
-
if (!params.gasLimit && result.gasPrice > BigInt(0)) {
|
|
488
|
-
outboundReq.gasLimit = result.gasFee / result.gasPrice;
|
|
489
|
-
(0, context_1.printLog)(ctx, `executeUoaToCeaSvm — derived effectiveGasLimit: ${outboundReq.gasLimit} (gasFee=${result.gasFee} / gasPrice=${result.gasPrice})`);
|
|
490
|
-
}
|
|
491
|
-
(0, context_1.printLog)(ctx, `executeUoaToCeaSvm — queried gas fee: ${gasFee.toString()}, gasToken: ${gasToken}, nativeValueForGas: ${nativeValueForGas.toString()}`);
|
|
492
|
-
}
|
|
493
|
-
catch (err) {
|
|
494
|
-
throw new Error(`Failed to query outbound gas fee: ${err}`);
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
// Estimate the actual WPC needed for the swap using on-chain pool price.
|
|
498
|
-
// The static 1Mx multiplier from queryOutboundGasFee doesn't reflect the real
|
|
499
|
-
// WPC/gasToken exchange rate. estimateNativeValueForSwap reads the Uniswap V3
|
|
500
|
-
// pool's slot0, computes the true cost, and adds a 2x buffer.
|
|
501
|
-
// Excess msg.value is refunded by the contract's swapAndBurnGas.
|
|
502
627
|
if (universalCoreAddress && gasFee > BigInt(0)) {
|
|
503
628
|
const estimated = yield (0, gas_calculator_1.estimateNativeValueForSwap)(ctx, universalCoreAddress, gasToken, gasFee, ueaBalance);
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
629
|
+
nativeValueForGas = (estimated * BigInt(110)) / BigInt(100);
|
|
630
|
+
(0, context_1.printLog)(ctx, `executeUoaToCeaSvm — nativeValueForGas: pool-quote=${estimated.toString()}, with 10% buffer=${nativeValueForGas.toString()}`);
|
|
631
|
+
}
|
|
632
|
+
// Balance-aware clamp: if UEA balance can't cover target + gas reserve,
|
|
633
|
+
// send only what we can afford. swapAndBurnGas refunds any excess.
|
|
634
|
+
let adjustedValueSvm;
|
|
635
|
+
if (ueaBalance >= nativeValueForGas + SVM_GAS_RESERVE) {
|
|
636
|
+
adjustedValueSvm = nativeValueForGas;
|
|
637
|
+
}
|
|
638
|
+
else if (ueaBalance > SVM_GAS_RESERVE) {
|
|
639
|
+
adjustedValueSvm = ueaBalance - SVM_GAS_RESERVE;
|
|
640
|
+
}
|
|
641
|
+
else if (ueaBalance > BigInt(0)) {
|
|
642
|
+
adjustedValueSvm = ueaBalance < nativeValueForGas ? ueaBalance : nativeValueForGas;
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
throw new Error(`UEA ${ueaAddress} has zero UPC balance; cannot fund outbound gas swap. ` +
|
|
646
|
+
`Bridge UPC to the UEA before retrying.`);
|
|
647
|
+
}
|
|
648
|
+
if (adjustedValueSvm !== nativeValueForGas) {
|
|
649
|
+
(0, context_1.printLog)(ctx, `executeUoaToCeaSvm — adjusting nativeValueForGas from ${nativeValueForGas.toString()} ` +
|
|
650
|
+
`to ${adjustedValueSvm.toString()} (UEA balance: ${ueaBalance.toString()})`);
|
|
651
|
+
nativeValueForGas = adjustedValueSvm;
|
|
508
652
|
}
|
|
509
653
|
// --- Build Push Chain multicalls (approve + sendUniversalTxOutbound) ---
|
|
510
654
|
// Reuse the same builder as EVM — this part is identical
|
|
@@ -531,7 +675,23 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
|
|
|
531
675
|
},
|
|
532
676
|
_skipFeeLocking: isUEADeployed, // skip fee-locking only if UEA is already deployed
|
|
533
677
|
};
|
|
534
|
-
|
|
678
|
+
// Signature request — user is prompted to sign the universal payload.
|
|
679
|
+
// Emit spec-ordered burst after executeFn returns (sign → verify → broadcast).
|
|
680
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_204_01);
|
|
681
|
+
let response;
|
|
682
|
+
try {
|
|
683
|
+
response = yield executeFn(executeParams);
|
|
684
|
+
}
|
|
685
|
+
catch (err) {
|
|
686
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_204_04, err instanceof Error ? err.message : String(err));
|
|
687
|
+
// Suppress the outer orchestrator catch's 299-02 terminal.
|
|
688
|
+
ctx._routeTerminalEmitted = true;
|
|
689
|
+
throw err;
|
|
690
|
+
}
|
|
691
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_204_02);
|
|
692
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_204_03);
|
|
693
|
+
// R2 broadcast marker (SVM)
|
|
694
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_207, targetChain);
|
|
535
695
|
// Add chain info to response
|
|
536
696
|
response.chain = targetChain;
|
|
537
697
|
response.chainNamespace = (0, helpers_1.getChainNamespace)(targetChain);
|
|
@@ -554,7 +714,7 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
|
|
|
554
714
|
*/
|
|
555
715
|
function executeCeaToPush(ctx, params, executeFn) {
|
|
556
716
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
557
|
-
var _a, _b, _c, _d, _e, _f;
|
|
717
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
558
718
|
// 1. Validate and extract source chain
|
|
559
719
|
if (!((_a = params.from) === null || _a === void 0 ? void 0 : _a.chain)) {
|
|
560
720
|
throw new Error('Route 3 (CEA -> Push) requires from.chain to specify the source CEA chain');
|
|
@@ -571,9 +731,23 @@ function executeCeaToPush(ctx, params, executeFn) {
|
|
|
571
731
|
throw new Error('Route 3 (CEA -> Push): to must be a Push Chain address (string), not a ChainTarget');
|
|
572
732
|
}
|
|
573
733
|
// 3. Get UEA address (will be recipient on Push Chain from CEA's perspective)
|
|
574
|
-
const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
|
|
575
734
|
// 4. Get CEA address on source chain
|
|
576
|
-
|
|
735
|
+
// Both happen before the 301 hook fires — wrap them so a pre-sign RPC
|
|
736
|
+
// failure produces a descriptive message in the subsequent 399-02 body
|
|
737
|
+
// instead of a bare "failed to fetch" surface.
|
|
738
|
+
let ueaAddress;
|
|
739
|
+
let ceaAddress;
|
|
740
|
+
let ceaDeployed;
|
|
741
|
+
try {
|
|
742
|
+
ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
|
|
743
|
+
const ceaResult = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, sourceChain, (_b = ctx.rpcUrls[sourceChain]) === null || _b === void 0 ? void 0 : _b[0]);
|
|
744
|
+
ceaAddress = ceaResult.cea;
|
|
745
|
+
ceaDeployed = ceaResult.isDeployed;
|
|
746
|
+
}
|
|
747
|
+
catch (err) {
|
|
748
|
+
throw new Error(`Route 3 setup failed: could not resolve CEA on ${sourceChain}: ${err instanceof Error ? err.message : String(err)}`);
|
|
749
|
+
}
|
|
750
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_301, sourceChain, ceaAddress);
|
|
577
751
|
(0, context_1.printLog)(ctx, `executeCeaToPush — sourceChain: ${sourceChain}, CEA: ${ceaAddress}, deployed: ${ceaDeployed}`);
|
|
578
752
|
// CEA auto-deploys on-chain: Vault.finalizeUniversalTx calls CEAFactory.deployCEA()
|
|
579
753
|
// if CEA doesn't exist yet. No SDK-side blocking needed.
|
|
@@ -723,21 +897,46 @@ function executeCeaToPush(ctx, params, executeFn) {
|
|
|
723
897
|
const ueaBalance = yield ctx.pushClient.getBalance(ueaAddress);
|
|
724
898
|
// 11. Query gas fees from UniversalCore
|
|
725
899
|
let gasFee = BigInt(0);
|
|
900
|
+
let protocolFeeR3 = BigInt(0);
|
|
726
901
|
let nativeValueForGas = BigInt(0);
|
|
727
902
|
let gasToken = selectors_1.ZERO_ADDRESS;
|
|
903
|
+
let sizingDecisionR3;
|
|
728
904
|
if (prc20Token !== selectors_1.ZERO_ADDRESS) {
|
|
729
905
|
const gasLimit = outboundReq.gasLimit;
|
|
906
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_302_01, sourceChain);
|
|
730
907
|
try {
|
|
731
|
-
const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, gasLimit);
|
|
908
|
+
const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, gasLimit, sourceChain);
|
|
732
909
|
gasToken = result.gasToken;
|
|
733
910
|
gasFee = result.gasFee;
|
|
911
|
+
protocolFeeR3 = result.protocolFee;
|
|
734
912
|
nativeValueForGas = result.nativeValueForGas;
|
|
735
|
-
|
|
913
|
+
sizingDecisionR3 = result.sizing;
|
|
914
|
+
(0, context_1.printLog)(ctx, `executeCeaToPush — queried gas fee: ${gasFee.toString()}, gasToken: ${gasToken}, nativeValueForGas: ${nativeValueForGas.toString()}, sizing=${(_h = (_g = result.sizing) === null || _g === void 0 ? void 0 : _g.category) !== null && _h !== void 0 ? _h : 'legacy'}`);
|
|
915
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_302_02, sourceChain, protocolFeeR3, gasFee);
|
|
736
916
|
}
|
|
737
917
|
catch (err) {
|
|
738
918
|
throw new Error(`Failed to query outbound gas fee for Route 3: ${err}`);
|
|
739
919
|
}
|
|
740
920
|
}
|
|
921
|
+
// Account-resolution checkpoint fires before the gas-sizing sub-hook so
|
|
922
|
+
// consumers see the full account-resolution phase (303-xx) complete before
|
|
923
|
+
// the sizing decision is surfaced.
|
|
924
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_303_01, sourceChain);
|
|
925
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_303_02, ueaAddress, ceaAddress, sourceChain);
|
|
926
|
+
if (sizingDecisionR3) {
|
|
927
|
+
fireSizingHook(ctx, 'R3', sourceChain, sizingDecisionR3);
|
|
928
|
+
}
|
|
929
|
+
// R3 Case C: unlike R2, R3 has no destination funds-delivery semantic
|
|
930
|
+
// (CEA self-executes and bridges funds BACK to Push Chain). When the
|
|
931
|
+
// sizer returns Case C, we simply top up msg.value with the overflow so
|
|
932
|
+
// `swapAndBurnGas` can afford the full gas cost on destination. No
|
|
933
|
+
// bridge-swap entries, no fold-in — contract refunds any excess.
|
|
934
|
+
if ((sizingDecisionR3 === null || sizingDecisionR3 === void 0 ? void 0 : sizingDecisionR3.category) === 'C' &&
|
|
935
|
+
sizingDecisionR3.overflowNativePc > BigInt(0)) {
|
|
936
|
+
const bumped = nativeValueForGas + sizingDecisionR3.overflowNativePc;
|
|
937
|
+
(0, context_1.printLog)(ctx, `executeCeaToPush — Case C: bumping nativeValueForGas from ${nativeValueForGas} to ${bumped} (overflow=${sizingDecisionR3.overflowNativePc})`);
|
|
938
|
+
nativeValueForGas = bumped;
|
|
939
|
+
}
|
|
741
940
|
// Adjust nativeValueForGas using UEA balance (contract refunds excess)
|
|
742
941
|
// Reuse ueaBalance from line 978 — no tx sent between, balance is stable
|
|
743
942
|
const currentBalance = ueaBalance;
|
|
@@ -775,8 +974,15 @@ function executeCeaToPush(ctx, params, executeFn) {
|
|
|
775
974
|
else if (effectiveBalance > OUTBOUND_GAS_RESERVE_R3) {
|
|
776
975
|
adjustedValue = effectiveBalance - OUTBOUND_GAS_RESERVE_R3;
|
|
777
976
|
}
|
|
977
|
+
else if (effectiveBalance > BigInt(0)) {
|
|
978
|
+
// Drained UEA: overshoot would make the inner .call{value:...} return (false, "")
|
|
979
|
+
// and the UEA reverts with opaque ExecutionFailed(). Clamp to whatever we have so
|
|
980
|
+
// the gateway swap either succeeds or surfaces a concrete revert reason.
|
|
981
|
+
adjustedValue = effectiveBalance < nativeValueForGas ? effectiveBalance : nativeValueForGas;
|
|
982
|
+
}
|
|
778
983
|
else {
|
|
779
|
-
|
|
984
|
+
throw new Error(`UEA ${ueaAddress} has zero UPC balance; cannot fund outbound gas swap. ` +
|
|
985
|
+
`Bridge UPC to the UEA before retrying.`);
|
|
780
986
|
}
|
|
781
987
|
if (adjustedValue !== nativeValueForGas) {
|
|
782
988
|
(0, context_1.printLog)(ctx, `executeCeaToPush — adjusting nativeValueForGas from ${nativeValueForGas.toString()} to ${adjustedValue.toString()} (effective balance: ${effectiveBalance.toString()})`);
|
|
@@ -801,11 +1007,35 @@ function executeCeaToPush(ctx, params, executeFn) {
|
|
|
801
1007
|
nonce: ueaNonce,
|
|
802
1008
|
balance: ueaBalance,
|
|
803
1009
|
}, _skipFeeLocking: isUEADeployed }, (!isUEADeployed ? { _minimumDepositUsd: ROUTE3_MINIMUM_DEPOSIT_USD } : {}));
|
|
804
|
-
|
|
1010
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_304_01);
|
|
1011
|
+
let response;
|
|
1012
|
+
try {
|
|
1013
|
+
response = yield executeFn(executeParams);
|
|
1014
|
+
}
|
|
1015
|
+
catch (err) {
|
|
1016
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_304_04, err instanceof Error ? err.message : String(err));
|
|
1017
|
+
// Suppress the outer orchestrator catch's 399-02 terminal — 304-04
|
|
1018
|
+
// already surfaced the signature/broadcast failure to the consumer.
|
|
1019
|
+
ctx._routeTerminalEmitted = true;
|
|
1020
|
+
throw err;
|
|
1021
|
+
}
|
|
1022
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_304_02);
|
|
1023
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_304_03);
|
|
1024
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_307, sourceChain);
|
|
805
1025
|
// Add Route 3 context to response
|
|
806
1026
|
response.chain = sourceChain;
|
|
807
1027
|
const chainInfo = chain_1.CHAIN_INFO[sourceChain];
|
|
808
1028
|
response.chainNamespace = `${chain_1.VM_NAMESPACE[chainInfo.vm]}:${chainInfo.chainId}`;
|
|
1029
|
+
// R3 inbound round-trip tracking: enable only when funds actually flow
|
|
1030
|
+
// back (amount > 0). Payload-only R3 has no child inbound UTX on Push
|
|
1031
|
+
// Chain, so polling would hang until timeout — skip the inbound block
|
|
1032
|
+
// for amount == 0.
|
|
1033
|
+
//
|
|
1034
|
+
// Child-utxId correlation uses the universal-tx-detector's deterministic
|
|
1035
|
+
// sha256(caip:txHash:logIndex) derivation (see inbound-tracker.ts
|
|
1036
|
+
// findChildUtxIdFromExternalTx), with the original cosmos
|
|
1037
|
+
// `universal_tx_created.inbound_tx_hash` search kept as a fallback.
|
|
1038
|
+
response._expectsInboundRoundTrip = amount > BigInt(0);
|
|
809
1039
|
return response;
|
|
810
1040
|
});
|
|
811
1041
|
}
|
|
@@ -822,7 +1052,7 @@ function executeCeaToPush(ctx, params, executeFn) {
|
|
|
822
1052
|
*/
|
|
823
1053
|
function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
|
|
824
1054
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
825
|
-
var _a, _b;
|
|
1055
|
+
var _a, _b, _c, _d;
|
|
826
1056
|
if (typeof params.to !== 'string') {
|
|
827
1057
|
throw new Error('Route 3 SVM (CEA -> Push): to must be a Push Chain address (string), not a ChainTarget');
|
|
828
1058
|
}
|
|
@@ -834,6 +1064,7 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
|
|
|
834
1064
|
}
|
|
835
1065
|
const programPk = new web3_js_1.PublicKey(lockerContract);
|
|
836
1066
|
const gatewayProgramHex = ('0x' + Buffer.from(programPk.toBytes()).toString('hex'));
|
|
1067
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_301, sourceChain, lockerContract);
|
|
837
1068
|
(0, context_1.printLog)(ctx, `executeCeaToPushSvm — sourceChain: ${sourceChain}, gateway: ${lockerContract}`);
|
|
838
1069
|
// Determine drain amount and token
|
|
839
1070
|
let drainAmount = BigInt(0);
|
|
@@ -889,20 +1120,43 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
|
|
|
889
1120
|
const ueaNonce = isUEADeployed ? yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress) : BigInt(0);
|
|
890
1121
|
// Query gas fees
|
|
891
1122
|
let gasFee = BigInt(0);
|
|
1123
|
+
let protocolFeeR3Svm = BigInt(0);
|
|
892
1124
|
let nativeValueForGas = BigInt(0);
|
|
893
1125
|
let gasToken = selectors_1.ZERO_ADDRESS;
|
|
1126
|
+
let sizingDecisionR3Svm;
|
|
894
1127
|
if (prc20Token !== selectors_1.ZERO_ADDRESS) {
|
|
1128
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_302_01, sourceChain);
|
|
895
1129
|
try {
|
|
896
|
-
const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, outboundReq.gasLimit);
|
|
1130
|
+
const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, outboundReq.gasLimit, sourceChain);
|
|
897
1131
|
gasToken = result.gasToken;
|
|
898
1132
|
gasFee = result.gasFee;
|
|
1133
|
+
protocolFeeR3Svm = result.protocolFee;
|
|
899
1134
|
nativeValueForGas = result.nativeValueForGas;
|
|
900
|
-
|
|
1135
|
+
sizingDecisionR3Svm = result.sizing;
|
|
1136
|
+
(0, context_1.printLog)(ctx, `executeCeaToPushSvm — gasFee: ${gasFee.toString()}, gasToken: ${gasToken}, nativeValueForGas: ${nativeValueForGas.toString()}, sizing=${(_d = (_c = result.sizing) === null || _c === void 0 ? void 0 : _c.category) !== null && _d !== void 0 ? _d : 'legacy'}`);
|
|
1137
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_302_02, sourceChain, protocolFeeR3Svm, gasFee);
|
|
901
1138
|
}
|
|
902
1139
|
catch (err) {
|
|
903
1140
|
throw new Error(`Failed to query outbound gas fee for SVM Route 3: ${err}`);
|
|
904
1141
|
}
|
|
905
1142
|
}
|
|
1143
|
+
// Account-resolution checkpoint fires before the gas-sizing sub-hook so
|
|
1144
|
+
// consumers see the full account-resolution phase (303-xx) complete before
|
|
1145
|
+
// the sizing decision is surfaced.
|
|
1146
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_303_01, sourceChain);
|
|
1147
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_303_02, ueaAddress, lockerContract, sourceChain);
|
|
1148
|
+
if (sizingDecisionR3Svm) {
|
|
1149
|
+
fireSizingHook(ctx, 'R3', sourceChain, sizingDecisionR3Svm);
|
|
1150
|
+
}
|
|
1151
|
+
// R3 SVM Case C: same minimal interpretation as R3 EVM — bump msg.value
|
|
1152
|
+
// by the overflow so swapAndBurnGas can afford the full gas cost. No
|
|
1153
|
+
// bridge-swap entries (R3 has no destination funds-delivery semantic).
|
|
1154
|
+
if ((sizingDecisionR3Svm === null || sizingDecisionR3Svm === void 0 ? void 0 : sizingDecisionR3Svm.category) === 'C' &&
|
|
1155
|
+
sizingDecisionR3Svm.overflowNativePc > BigInt(0)) {
|
|
1156
|
+
const bumped = nativeValueForGas + sizingDecisionR3Svm.overflowNativePc;
|
|
1157
|
+
(0, context_1.printLog)(ctx, `executeCeaToPushSvm — Case C: bumping nativeValueForGas from ${nativeValueForGas} to ${bumped} (overflow=${sizingDecisionR3Svm.overflowNativePc})`);
|
|
1158
|
+
nativeValueForGas = bumped;
|
|
1159
|
+
}
|
|
906
1160
|
// Adjust nativeValueForGas using UEA balance (contract refunds excess)
|
|
907
1161
|
// Re-fetch balance to minimize staleness from gas fee query RPC roundtrips
|
|
908
1162
|
const currentBalance = yield ctx.pushClient.getBalance(ueaAddress);
|
|
@@ -931,11 +1185,31 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
|
|
|
931
1185
|
nonce: ueaNonce,
|
|
932
1186
|
balance: ueaBalance,
|
|
933
1187
|
}, _skipFeeLocking: isUEADeployed }, (!isUEADeployed ? { _minimumDepositUsd: utils_1.Utils.helpers.parseUnits('10', 8) } : {}));
|
|
934
|
-
|
|
1188
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_304_01);
|
|
1189
|
+
let response;
|
|
1190
|
+
try {
|
|
1191
|
+
response = yield executeFn(executeParams);
|
|
1192
|
+
}
|
|
1193
|
+
catch (err) {
|
|
1194
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_304_04, err instanceof Error ? err.message : String(err));
|
|
1195
|
+
// Suppress the outer orchestrator catch's 399-02 terminal.
|
|
1196
|
+
ctx._routeTerminalEmitted = true;
|
|
1197
|
+
throw err;
|
|
1198
|
+
}
|
|
1199
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_304_02);
|
|
1200
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_304_03);
|
|
1201
|
+
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_307, sourceChain);
|
|
935
1202
|
// Add Route 3 SVM context to response
|
|
936
1203
|
response.chain = sourceChain;
|
|
937
1204
|
const chainInfo = chain_1.CHAIN_INFO[sourceChain];
|
|
938
1205
|
response.chainNamespace = `${chain_1.VM_NAMESPACE[chainInfo.vm]}:${chainInfo.chainId}`;
|
|
1206
|
+
// R3 SVM inbound round-trip tracking: enable only when funds flow back
|
|
1207
|
+
// (drainAmount > 0). See the EVM executeCeaToPush note — child-utxId
|
|
1208
|
+
// correlation uses the universal-tx-detector's deterministic derivation.
|
|
1209
|
+
// NOTE: the detector is EVM-only today; SVM source-chain logs can't be
|
|
1210
|
+
// decoded yet, so this branch will fall back to the (broken) cosmos
|
|
1211
|
+
// search. Disable if SVM R3 tests start timing out in CI.
|
|
1212
|
+
response._expectsInboundRoundTrip = drainAmount > BigInt(0);
|
|
939
1213
|
return response;
|
|
940
1214
|
});
|
|
941
1215
|
}
|