@pushchain/core 5.1.4-alpha.9 → 5.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/lib/orchestrator/internals/execute-standard.js +54 -1
- package/src/lib/orchestrator/internals/gas-calculator.d.ts +9 -0
- package/src/lib/orchestrator/internals/gas-calculator.js +94 -0
- package/src/lib/orchestrator/internals/push-chain-tx.js +17 -3
- package/src/lib/orchestrator/internals/response-builder.js +10 -3
- package/src/lib/orchestrator/internals/route-handlers.js +98 -112
- package/src/lib/orchestrator/orchestrator.types.d.ts +8 -0
package/package.json
CHANGED
|
@@ -28,6 +28,9 @@ const confirmation_1 = require("./confirmation");
|
|
|
28
28
|
const gateway_client_1 = require("./gateway-client");
|
|
29
29
|
const response_builder_1 = require("./response-builder");
|
|
30
30
|
const push_chain_tx_1 = require("./push-chain-tx");
|
|
31
|
+
const gas_calculator_1 = require("./gas-calculator");
|
|
32
|
+
const helpers_2 = require("./helpers");
|
|
33
|
+
const price_fetch_1 = require("../../price-fetch/price-fetch");
|
|
31
34
|
function executeStandardPayload(ctx, execute, eventBuffer, getResponseCallbacks) {
|
|
32
35
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
33
36
|
var _a, _b;
|
|
@@ -198,7 +201,57 @@ function executeStandardPayload(ctx, execute, eventBuffer, getResponseCallbacks)
|
|
|
198
201
|
const fixedPushAmount = utils_1.Utils.helpers.parseUnits('0.001', 18);
|
|
199
202
|
const lockAmount = funds < requiredFunds ? fundDifference : fixedPushAmount;
|
|
200
203
|
const lockAmountInUSD = ctx.pushClient.pushToUSDC(lockAmount);
|
|
201
|
-
|
|
204
|
+
// Apply minimum deposit override (e.g., Route 2 needs enough UPC for outbound swap)
|
|
205
|
+
const effectiveLockAmountInUSD = execute._minimumDepositUsd && execute._minimumDepositUsd > lockAmountInUSD
|
|
206
|
+
? execute._minimumDepositUsd
|
|
207
|
+
: lockAmountInUSD;
|
|
208
|
+
// Pre-flight: predict the actual $PC the UEA will receive after the
|
|
209
|
+
// on-chain pETH → WPC swap and validate it covers requiredFunds.
|
|
210
|
+
// Uses the same Uniswap V3 quoter the chain uses (execute_inbound_gas.go).
|
|
211
|
+
// Mirrors the pattern in route-handlers.ts (Route 2/3).
|
|
212
|
+
const { vm: originVm } = chain_1.CHAIN_INFO[chain];
|
|
213
|
+
if (originVm === enums_1.VM.EVM) {
|
|
214
|
+
try {
|
|
215
|
+
// Replicate lockFee's USD → native conversion (with its $1–$10 clamp)
|
|
216
|
+
const oneUsd = utils_1.Utils.helpers.parseUnits('1', 8);
|
|
217
|
+
const tenUsd = utils_1.Utils.helpers.parseUnits('10', 8);
|
|
218
|
+
let depositUsd = effectiveLockAmountInUSD < oneUsd ? oneUsd : effectiveLockAmountInUSD;
|
|
219
|
+
if (depositUsd > tenUsd)
|
|
220
|
+
depositUsd = tenUsd;
|
|
221
|
+
const ethPrice = yield new price_fetch_1.PriceFetch(ctx.rpcUrls).getPrice(chain);
|
|
222
|
+
if (ethPrice > BigInt(0)) {
|
|
223
|
+
const nativeAmountETH = (depositUsd * BigInt(1e18) + (ethPrice - BigInt(1))) / ethPrice + BigInt(1);
|
|
224
|
+
const originPrc20 = (0, helpers_2.getNativePRC20ForChain)(chain, ctx.pushNetwork);
|
|
225
|
+
const predictedUPC = yield (0, gas_calculator_1.estimateDepositFromLockedNative)(ctx, nativeAmountETH, originPrc20);
|
|
226
|
+
if (predictedUPC > BigInt(0)) {
|
|
227
|
+
// 10% slippage margin (same as route-handlers.ts)
|
|
228
|
+
const safeUPC = (predictedUPC * BigInt(90)) / BigInt(100);
|
|
229
|
+
const totalAvailable = safeUPC + funds;
|
|
230
|
+
(0, context_1.printLog)(ctx, `Fee-lock pre-flight: deposit ${depositUsd.toString()} USD → ` +
|
|
231
|
+
`~${nativeAmountETH.toString()} wei ETH → ~${predictedUPC.toString()} UPC ` +
|
|
232
|
+
`(safe: ${safeUPC.toString()}), existing: ${funds.toString()}, ` +
|
|
233
|
+
`total: ${totalAvailable.toString()}, required: ${requiredFunds.toString()}`);
|
|
234
|
+
if (totalAvailable < requiredFunds) {
|
|
235
|
+
const shortfall = requiredFunds - totalAvailable;
|
|
236
|
+
throw new Error(`Insufficient deposit: the fee-lock will deposit ~${(Number(safeUPC) / 1e18).toFixed(4)} $PC ` +
|
|
237
|
+
`but the transaction requires ~${(Number(requiredFunds) / 1e18).toFixed(4)} $PC ` +
|
|
238
|
+
`(shortfall: ~${(Number(shortfall) / 1e18).toFixed(4)} $PC). ` +
|
|
239
|
+
`The fee-lock deposit is capped at $10 USD. Reduce the transfer value or ` +
|
|
240
|
+
`pre-fund the UEA (${UEA}) on Push Chain.`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
catch (err) {
|
|
246
|
+
// If the error is our own insufficient-deposit error, rethrow it
|
|
247
|
+
if (err instanceof Error && err.message.startsWith('Insufficient deposit')) {
|
|
248
|
+
throw err;
|
|
249
|
+
}
|
|
250
|
+
// Otherwise quoter failed — log and continue (same fallback as route-handlers)
|
|
251
|
+
(0, context_1.printLog)(ctx, `Fee-lock pre-flight quote failed, proceeding without validation: ${err instanceof Error ? err.message : String(err)}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
const feeLockTxHashBytes = yield (0, gateway_client_1.lockFee)(ctx, effectiveLockAmountInUSD, universalPayload, req);
|
|
202
255
|
feeLockTxHash = (0, viem_1.bytesToHex)(feeLockTxHashBytes);
|
|
203
256
|
verificationData = (0, viem_1.bytesToHex)(feeLockTxHashBytes);
|
|
204
257
|
const { vm } = chain_1.CHAIN_INFO[chain];
|
|
@@ -36,3 +36,12 @@ export declare function calculateGasAmountFromAmountOutMinETH(ctx: OrchestratorC
|
|
|
36
36
|
}) => Promise<ConversionQuote>): Promise<{
|
|
37
37
|
gasAmount: bigint;
|
|
38
38
|
}>;
|
|
39
|
+
/**
|
|
40
|
+
* Predicts how much UPC (WPC) the chain will deposit to the UEA from a given
|
|
41
|
+
* amount of locked native token (e.g., ETH on Sepolia). The chain converts
|
|
42
|
+
* locked ETH → pETH (PRC-20) → WPC via Uniswap V3 swap on Push Chain.
|
|
43
|
+
*
|
|
44
|
+
* Queries the same Uniswap V3 QuoterV2 the chain uses in execute_inbound_gas.go.
|
|
45
|
+
* Returns 0 on any failure (caller should fall back to other logic).
|
|
46
|
+
*/
|
|
47
|
+
export declare function estimateDepositFromLockedNative(ctx: OrchestratorContext, nativeAmountForLocker: bigint, prc20Token: `0x${string}`): Promise<bigint>;
|
|
@@ -10,6 +10,7 @@ exports.estimateNativeValueForSwap = estimateNativeValueForSwap;
|
|
|
10
10
|
exports.queryRescueGasFee = queryRescueGasFee;
|
|
11
11
|
exports.calculateNativeAmountForDeposit = calculateNativeAmountForDeposit;
|
|
12
12
|
exports.calculateGasAmountFromAmountOutMinETH = calculateGasAmountFromAmountOutMinETH;
|
|
13
|
+
exports.estimateDepositFromLockedNative = estimateDepositFromLockedNative;
|
|
13
14
|
const tslib_1 = require("tslib");
|
|
14
15
|
const web3_js_1 = require("@solana/web3.js");
|
|
15
16
|
const viem_1 = require("viem");
|
|
@@ -421,4 +422,97 @@ function calculateGasAmountFromAmountOutMinETH(ctx, gasTokenAddress, amountOutMi
|
|
|
421
422
|
return { gasAmount };
|
|
422
423
|
});
|
|
423
424
|
}
|
|
425
|
+
// ============================================================================
|
|
426
|
+
// Estimate UPC deposit from locked ETH via Uniswap V3 quote
|
|
427
|
+
// ============================================================================
|
|
428
|
+
/**
|
|
429
|
+
* Predicts how much UPC (WPC) the chain will deposit to the UEA from a given
|
|
430
|
+
* amount of locked native token (e.g., ETH on Sepolia). The chain converts
|
|
431
|
+
* locked ETH → pETH (PRC-20) → WPC via Uniswap V3 swap on Push Chain.
|
|
432
|
+
*
|
|
433
|
+
* Queries the same Uniswap V3 QuoterV2 the chain uses in execute_inbound_gas.go.
|
|
434
|
+
* Returns 0 on any failure (caller should fall back to other logic).
|
|
435
|
+
*/
|
|
436
|
+
function estimateDepositFromLockedNative(ctx, nativeAmountForLocker, prc20Token) {
|
|
437
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
438
|
+
var _a, _b;
|
|
439
|
+
try {
|
|
440
|
+
const pushChain = (0, helpers_1.getPushChainForNetwork)(ctx.pushNetwork);
|
|
441
|
+
const quoterAddress = (_b = (_a = chain_1.CHAIN_INFO[pushChain]) === null || _a === void 0 ? void 0 : _a.dex) === null || _b === void 0 ? void 0 : _b.uniV3QuoterV2;
|
|
442
|
+
if (!quoterAddress || quoterAddress.startsWith('0xTBD'))
|
|
443
|
+
return BigInt(0);
|
|
444
|
+
// Get UniversalCore → WPC address + fee tier (reuses existing ABI)
|
|
445
|
+
const gatewayPcAddress = (0, helpers_1.getUniversalGatewayPCAddress)();
|
|
446
|
+
const universalCoreAddress = yield ctx.pushClient.readContract({
|
|
447
|
+
address: gatewayPcAddress,
|
|
448
|
+
abi: abi_1.UNIVERSAL_GATEWAY_PC,
|
|
449
|
+
functionName: 'UNIVERSAL_CORE',
|
|
450
|
+
args: [],
|
|
451
|
+
});
|
|
452
|
+
const [wpcAddress, feeTier] = yield Promise.all([
|
|
453
|
+
ctx.pushClient.readContract({
|
|
454
|
+
address: universalCoreAddress,
|
|
455
|
+
abi: UNIVERSAL_CORE_SWAP_HELPERS_ABI,
|
|
456
|
+
functionName: 'WPC',
|
|
457
|
+
args: [],
|
|
458
|
+
}),
|
|
459
|
+
ctx.pushClient.readContract({
|
|
460
|
+
address: universalCoreAddress,
|
|
461
|
+
abi: UNIVERSAL_CORE_SWAP_HELPERS_ABI,
|
|
462
|
+
functionName: 'defaultFeeTier',
|
|
463
|
+
args: [prc20Token],
|
|
464
|
+
}),
|
|
465
|
+
]);
|
|
466
|
+
if (!feeTier || !wpcAddress)
|
|
467
|
+
return BigInt(0);
|
|
468
|
+
// QuoterV2.quoteExactInputSingle — same call the chain relay makes
|
|
469
|
+
const QUOTER_ABI = [
|
|
470
|
+
{
|
|
471
|
+
type: 'function',
|
|
472
|
+
name: 'quoteExactInputSingle',
|
|
473
|
+
inputs: [
|
|
474
|
+
{
|
|
475
|
+
type: 'tuple',
|
|
476
|
+
components: [
|
|
477
|
+
{ name: 'tokenIn', type: 'address' },
|
|
478
|
+
{ name: 'tokenOut', type: 'address' },
|
|
479
|
+
{ name: 'amountIn', type: 'uint256' },
|
|
480
|
+
{ name: 'fee', type: 'uint24' },
|
|
481
|
+
{ name: 'sqrtPriceLimitX96', type: 'uint160' },
|
|
482
|
+
],
|
|
483
|
+
},
|
|
484
|
+
],
|
|
485
|
+
outputs: [
|
|
486
|
+
{ name: 'amountOut', type: 'uint256' },
|
|
487
|
+
{ name: 'sqrtPriceX96After', type: 'uint160' },
|
|
488
|
+
{ name: 'initializedTicksCrossed', type: 'uint32' },
|
|
489
|
+
{ name: 'gasEstimate', type: 'uint256' },
|
|
490
|
+
],
|
|
491
|
+
stateMutability: 'view',
|
|
492
|
+
},
|
|
493
|
+
];
|
|
494
|
+
const result = yield ctx.pushClient.readContract({
|
|
495
|
+
address: quoterAddress,
|
|
496
|
+
abi: QUOTER_ABI,
|
|
497
|
+
functionName: 'quoteExactInputSingle',
|
|
498
|
+
args: [
|
|
499
|
+
{
|
|
500
|
+
tokenIn: prc20Token,
|
|
501
|
+
tokenOut: wpcAddress,
|
|
502
|
+
amountIn: nativeAmountForLocker,
|
|
503
|
+
fee: feeTier,
|
|
504
|
+
sqrtPriceLimitX96: BigInt(0),
|
|
505
|
+
},
|
|
506
|
+
],
|
|
507
|
+
});
|
|
508
|
+
const amountOut = result[0];
|
|
509
|
+
(0, context_1.printLog)(ctx, `estimateDepositFromLockedNative — quote: ${nativeAmountForLocker.toString()} pETH → ${amountOut.toString()} WPC (fee tier: ${feeTier})`);
|
|
510
|
+
return amountOut;
|
|
511
|
+
}
|
|
512
|
+
catch (err) {
|
|
513
|
+
(0, context_1.printLog)(ctx, `estimateDepositFromLockedNative — failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
514
|
+
return BigInt(0);
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
}
|
|
424
518
|
//# sourceMappingURL=gas-calculator.js.map
|
|
@@ -195,10 +195,24 @@ function extractPcTxAndTransform(ctx, pushChainUniversalTx, gatewayTxHash, event
|
|
|
195
195
|
const lastPcTransaction = pushChainUniversalTx.pcTx.at(-1);
|
|
196
196
|
(0, context_1.printLog)(ctx, `${label} — pcTx: ` + JSON.stringify(pushChainUniversalTx.pcTx.map((p) => ({ txHash: p.txHash, status: p.status, errorMsg: p.errorMsg })), null, 2));
|
|
197
197
|
(0, context_1.printLog)(ctx, `${label} — using lastPcTransaction: ` + JSON.stringify(lastPcTransaction, null, 2));
|
|
198
|
-
if (!(lastPcTransaction === null || lastPcTransaction === void 0 ? void 0 : lastPcTransaction.txHash)) {
|
|
199
|
-
const failedPcTx =
|
|
198
|
+
if (!(lastPcTransaction === null || lastPcTransaction === void 0 ? void 0 : lastPcTransaction.txHash) || lastPcTransaction.status === 'FAILED') {
|
|
199
|
+
const failedPcTx = (lastPcTransaction === null || lastPcTransaction === void 0 ? void 0 : lastPcTransaction.status) === 'FAILED'
|
|
200
|
+
? lastPcTransaction
|
|
201
|
+
: pushChainUniversalTx.pcTx.find((pcTx) => pcTx.status === 'FAILED' && pcTx.errorMsg);
|
|
200
202
|
const errorDetails = (failedPcTx === null || failedPcTx === void 0 ? void 0 : failedPcTx.errorMsg) ? `: ${failedPcTx.errorMsg}` : '';
|
|
201
|
-
|
|
203
|
+
// Parse known UEA error selectors for actionable hints
|
|
204
|
+
let hint = '';
|
|
205
|
+
if (failedPcTx === null || failedPcTx === void 0 ? void 0 : failedPcTx.errorMsg) {
|
|
206
|
+
if (failedPcTx.errorMsg.includes('0xacfdb444')) {
|
|
207
|
+
hint = ' [ExecutionFailed: the subcall to the target contract reverted. ' +
|
|
208
|
+
'Check that the target address can receive native tokens (has receive/fallback) ' +
|
|
209
|
+
'and that calldata matches a valid function signature.]';
|
|
210
|
+
}
|
|
211
|
+
else if (failedPcTx.errorMsg.includes('0x179a867c')) {
|
|
212
|
+
hint = ' [ExpiredDeadline: the transaction deadline has passed.]';
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
throw new Error(`Push Chain transaction failed for gateway tx: ${gatewayTxHash}${errorDetails}${hint}`);
|
|
202
216
|
}
|
|
203
217
|
const tx = yield ctx.pushClient.getTransaction(lastPcTransaction.txHash);
|
|
204
218
|
return transformFn(tx, eventBuffer);
|
|
@@ -150,7 +150,7 @@ function trackTransaction(ctx, txHash, options, callbacks) {
|
|
|
150
150
|
throw new Error('trackTransaction requires ResponseBuilderCallbacks');
|
|
151
151
|
}
|
|
152
152
|
const { chain = (0, helpers_1.getPushChainForNetwork)(ctx.pushNetwork), progressHook, waitForCompletion = true, advanced = {}, } = options !== null && options !== void 0 ? options : {};
|
|
153
|
-
const { timeout = 300000, rpcUrls = {} } = advanced;
|
|
153
|
+
const { timeout = 300000, pollingIntervalMs = 1000, rpcUrls = {} } = advanced;
|
|
154
154
|
// Event buffer for replay via response.progressHook()
|
|
155
155
|
const eventBuffer = [];
|
|
156
156
|
// Helper to emit progress events
|
|
@@ -190,7 +190,7 @@ function trackTransaction(ctx, txHash, options, callbacks) {
|
|
|
190
190
|
throw new Error(`Timeout: transaction ${txHash} not confirmed within ${timeout}ms`);
|
|
191
191
|
}
|
|
192
192
|
// Brief delay before retry
|
|
193
|
-
yield new Promise((r) => setTimeout(r,
|
|
193
|
+
yield new Promise((r) => setTimeout(r, pollingIntervalMs));
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
196
|
// Try to get UniversalTx data for richer progress reconstruction
|
|
@@ -256,7 +256,7 @@ function trackTransaction(ctx, txHash, options, callbacks) {
|
|
|
256
256
|
function transformToUniversalTxResponse(ctx_1, tx_1) {
|
|
257
257
|
return tslib_1.__awaiter(this, arguments, void 0, function* (ctx, tx, eventBuffer = [], callbacks) {
|
|
258
258
|
const chain = ctx.universalSigner.account.chain;
|
|
259
|
-
|
|
259
|
+
let { vm, chainId } = chain_1.CHAIN_INFO[chain];
|
|
260
260
|
let from;
|
|
261
261
|
let to;
|
|
262
262
|
let value;
|
|
@@ -269,6 +269,13 @@ function transformToUniversalTxResponse(ctx_1, tx_1) {
|
|
|
269
269
|
throw new Error('UEA origin account is null');
|
|
270
270
|
}
|
|
271
271
|
originAddress = ueaOrigin.account.address;
|
|
272
|
+
// Use the resolved origin chain for vm/chainId so that the `origin`
|
|
273
|
+
// field reflects the transaction's actual origin, not the tracker's.
|
|
274
|
+
const resolvedChainInfo = chain_1.CHAIN_INFO[ueaOrigin.account.chain];
|
|
275
|
+
if (resolvedChainInfo) {
|
|
276
|
+
vm = resolvedChainInfo.vm;
|
|
277
|
+
chainId = resolvedChainInfo.chainId;
|
|
278
|
+
}
|
|
272
279
|
from = (0, viem_1.getAddress)(tx.to);
|
|
273
280
|
let decoded;
|
|
274
281
|
if (tx.input !== '0x') {
|
|
@@ -31,6 +31,8 @@ const payload_builder_1 = require("./payload-builder");
|
|
|
31
31
|
const uea_manager_1 = require("./uea-manager");
|
|
32
32
|
const gas_calculator_1 = require("./gas-calculator");
|
|
33
33
|
const chain_1 = require("../../constants/chain");
|
|
34
|
+
const utils_1 = require("../../utils");
|
|
35
|
+
const price_fetch_1 = require("../../price-fetch/price-fetch");
|
|
34
36
|
const route_detector_1 = require("../route-detector");
|
|
35
37
|
const cea_utils_1 = require("../cea-utils");
|
|
36
38
|
const payload_builders_1 = require("../payload-builders");
|
|
@@ -277,60 +279,48 @@ function executeUoaToCea(ctx, params, executeFn) {
|
|
|
277
279
|
throw new Error(`Failed to query outbound gas fee: ${err}`);
|
|
278
280
|
}
|
|
279
281
|
}
|
|
280
|
-
//
|
|
281
|
-
// The
|
|
282
|
-
//
|
|
283
|
-
// pool's slot0, computes the true cost, and adds a 2x buffer.
|
|
284
|
-
// This matches the SVM path (executeUoaToCeaSvm) which already uses this.
|
|
285
|
-
// Excess msg.value is refunded by the contract's swapAndBurnGas.
|
|
282
|
+
// Balance-aware adjustment: MAXIMIZE nativeValueForGas within available balance.
|
|
283
|
+
// The contract's swapAndBurnGas does exactOutputSingle and refunds excess PC,
|
|
284
|
+
// so overshooting within balance is safe. Undershooting causes the swap to revert.
|
|
286
285
|
const EVM_NATIVE_VALUE_TARGET = BigInt(200e18); // 200 UPC
|
|
287
286
|
const EVM_GAS_RESERVE = BigInt(3e18); // 3 UPC for tx overhead
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
287
|
+
const ROUTE2_MINIMUM_DEPOSIT_USD = utils_1.Utils.helpers.parseUnits('10', 8); // $10
|
|
288
|
+
// Determine effective balance for the adjustment.
|
|
289
|
+
// For deployed UEAs: use real balance.
|
|
290
|
+
// For fresh wallets: predict the post-fee-locking balance by querying the same
|
|
291
|
+
// Uniswap V3 quoter the chain uses (pETH → WPC swap in execute_inbound_gas.go).
|
|
292
|
+
let effectiveBalance = ueaBalance;
|
|
293
|
+
if (!isUEADeployed && effectiveBalance <= EVM_GAS_RESERVE) {
|
|
294
|
+
// Calculate the ETH amount lockFee will send (same formula as lockFee)
|
|
295
|
+
const ethPrice = yield new price_fetch_1.PriceFetch(ctx.rpcUrls).getPrice(signerChain);
|
|
296
|
+
const nativeAmountETH = ethPrice > BigInt(0)
|
|
297
|
+
? (ROUTE2_MINIMUM_DEPOSIT_USD * BigInt(1e18) + (ethPrice - BigInt(1))) / ethPrice + BigInt(1)
|
|
298
|
+
: BigInt(0);
|
|
299
|
+
if (nativeAmountETH > BigInt(0)) {
|
|
300
|
+
// Query the real swap quote: how much WPC (UPC) for this pETH amount?
|
|
301
|
+
const originPrc20 = (0, helpers_1.getNativePRC20ForChain)(signerChain, ctx.pushNetwork);
|
|
302
|
+
const predictedUPC = yield (0, gas_calculator_1.estimateDepositFromLockedNative)(ctx, nativeAmountETH, originPrc20);
|
|
303
|
+
if (predictedUPC > BigInt(0)) {
|
|
304
|
+
// 10% safety margin for slippage between quote and actual execution
|
|
305
|
+
effectiveBalance = (predictedUPC * BigInt(90)) / BigInt(100);
|
|
306
|
+
(0, context_1.printLog)(ctx, `executeUoaToCea — fresh wallet: Uniswap quote predicts ${predictedUPC.toString()} UPC ` +
|
|
307
|
+
`from ${nativeAmountETH.toString()} wei pETH, using ${effectiveBalance.toString()} (90%)`);
|
|
308
|
+
}
|
|
297
309
|
}
|
|
298
310
|
}
|
|
299
|
-
// Balance-aware adjustment: avoid draining the entire UEA balance for gas.
|
|
300
|
-
// The contract's swapAndBurnGas does exactOutputSingle and refunds excess PC,
|
|
301
|
-
// so overshooting within balance is safe.
|
|
302
|
-
const currentBalance = ueaBalance;
|
|
303
311
|
let adjustedValue;
|
|
304
|
-
if (
|
|
305
|
-
|
|
306
|
-
adjustedValue = nativeValueForGas < EVM_NATIVE_VALUE_TARGET
|
|
307
|
-
? nativeValueForGas
|
|
308
|
-
: EVM_NATIVE_VALUE_TARGET;
|
|
312
|
+
if (effectiveBalance > EVM_NATIVE_VALUE_TARGET + EVM_GAS_RESERVE) {
|
|
313
|
+
adjustedValue = EVM_NATIVE_VALUE_TARGET;
|
|
309
314
|
}
|
|
310
|
-
else if (
|
|
311
|
-
|
|
312
|
-
const available = currentBalance - EVM_GAS_RESERVE;
|
|
313
|
-
adjustedValue = nativeValueForGas < available ? nativeValueForGas : available;
|
|
314
|
-
}
|
|
315
|
-
else if (!isUEADeployed) {
|
|
316
|
-
// Fresh wallet: UEA not deployed, fee-locking WILL run and deposit funds.
|
|
317
|
-
// Use EVM_NATIVE_VALUE_TARGET (200 UPC) — fee-locking auto-covers this
|
|
318
|
-
// because requiredFunds includes nativeValueForGas, and lockFee converts
|
|
319
|
-
// the UPC amount to USD and deposits the equivalent ETH.
|
|
320
|
-
// The contract's swapAndBurnGas refunds any excess.
|
|
321
|
-
// If pool estimate produced a usable value, prefer it (capped at 200 UPC).
|
|
322
|
-
adjustedValue = nativeValueForGas > EVM_NATIVE_VALUE_TARGET
|
|
323
|
-
? EVM_NATIVE_VALUE_TARGET
|
|
324
|
-
: nativeValueForGas > BigInt(0) ? nativeValueForGas : EVM_NATIVE_VALUE_TARGET;
|
|
325
|
-
(0, context_1.printLog)(ctx, `executeUoaToCea — fresh wallet (UEA not deployed): using ${adjustedValue.toString()} for nativeValueForGas`);
|
|
315
|
+
else if (effectiveBalance > EVM_GAS_RESERVE) {
|
|
316
|
+
adjustedValue = effectiveBalance - EVM_GAS_RESERVE;
|
|
326
317
|
}
|
|
327
318
|
else {
|
|
328
|
-
// Deployed UEA with near-zero balance: best-effort with pool/1M estimate.
|
|
329
|
-
// User may need to add funds to UEA manually.
|
|
330
319
|
adjustedValue = nativeValueForGas;
|
|
331
320
|
}
|
|
332
321
|
if (adjustedValue !== nativeValueForGas) {
|
|
333
|
-
(0, context_1.printLog)(ctx, `executeUoaToCea — adjusting nativeValueForGas from ${nativeValueForGas.toString()}
|
|
322
|
+
(0, context_1.printLog)(ctx, `executeUoaToCea — adjusting nativeValueForGas from ${nativeValueForGas.toString()} ` +
|
|
323
|
+
`to ${adjustedValue.toString()} (effective balance: ${effectiveBalance.toString()})`);
|
|
334
324
|
nativeValueForGas = adjustedValue;
|
|
335
325
|
}
|
|
336
326
|
// Build outbound multicalls (approve burn + sendUniversalTxOutbound with native value)
|
|
@@ -374,17 +364,11 @@ function executeUoaToCea(ctx, params, executeFn) {
|
|
|
374
364
|
// This handles fee-locking on origin chain and executes the multicall from UEA context
|
|
375
365
|
// Sum native values from multicall entries for proper fee calculation
|
|
376
366
|
const multicallNativeValue = pushChainMulticalls.reduce((sum, mc) => { var _a; return sum + ((_a = mc.value) !== null && _a !== void 0 ? _a : BigInt(0)); }, BigInt(0));
|
|
377
|
-
const executeParams = {
|
|
378
|
-
to: ueaAddress, // multicall executes from UEA
|
|
379
|
-
value: multicallNativeValue, // ensures correct requiredFunds calculation
|
|
380
|
-
data: pushChainMulticalls, // array triggers multicall mode
|
|
381
|
-
_ueaStatus: {
|
|
367
|
+
const executeParams = Object.assign({ to: ueaAddress, value: multicallNativeValue, data: pushChainMulticalls, _ueaStatus: {
|
|
382
368
|
isDeployed: isUEADeployed,
|
|
383
369
|
nonce: ueaNonce,
|
|
384
370
|
balance: ueaBalance,
|
|
385
|
-
},
|
|
386
|
-
_skipFeeLocking: isUEADeployed, // skip fee-locking only if UEA is already deployed
|
|
387
|
-
};
|
|
371
|
+
}, _skipFeeLocking: isUEADeployed }, (!isUEADeployed ? { _minimumDepositUsd: ROUTE2_MINIMUM_DEPOSIT_USD } : {}));
|
|
388
372
|
const response = yield executeFn(executeParams);
|
|
389
373
|
// Add chain info to response
|
|
390
374
|
response.chain = targetChain;
|
|
@@ -581,7 +565,7 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
|
|
|
581
565
|
*/
|
|
582
566
|
function executeCeaToPush(ctx, params, executeFn) {
|
|
583
567
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
584
|
-
var _a, _b, _c, _d, _e, _f
|
|
568
|
+
var _a, _b, _c, _d, _e, _f;
|
|
585
569
|
// 1. Validate and extract source chain
|
|
586
570
|
if (!((_a = params.from) === null || _a === void 0 ? void 0 : _a.chain)) {
|
|
587
571
|
throw new Error('Route 3 (CEA -> Push) requires from.chain to specify the source CEA chain');
|
|
@@ -716,27 +700,24 @@ function executeCeaToPush(ctx, params, executeFn) {
|
|
|
716
700
|
// CEA will self-execute this multicall (to=CEA, value=0)
|
|
717
701
|
const ceaPayload = (0, payload_builders_1.buildCeaMulticallPayload)(ceaMulticalls);
|
|
718
702
|
(0, context_1.printLog)(ctx, `executeCeaToPush — CEA payload (first 100 chars): ${ceaPayload.slice(0, 100)}...`);
|
|
719
|
-
// 8. Determine PRC-20 token for the outbound
|
|
720
|
-
//
|
|
721
|
-
//
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
// burnAmount = PRC20 to burn on Push Chain (NOT the bridge amount).
|
|
731
|
-
// Vault deposits burnAmount to CEA. CEA uses burnAmount + pre-existing balance for the bridge.
|
|
732
|
-
const burnAmount = amount;
|
|
703
|
+
// 8. Determine PRC-20 token for the outbound relay.
|
|
704
|
+
// Route 3: ALWAYS use native PRC-20 for chain namespace lookup + gas fees.
|
|
705
|
+
// The CEA uses its OWN pre-existing balance on the external chain to bridge
|
|
706
|
+
// tokens back — no PRC-20 burn is needed on Push Chain.
|
|
707
|
+
// burnAmount = 0 makes this a payload-only outbound relay.
|
|
708
|
+
//
|
|
709
|
+
// Previously this burned pUSDT/pToken equal to the bridge amount, which failed
|
|
710
|
+
// because the UEA never holds pUSDT — the whole point of Route 3 is to get
|
|
711
|
+
// tokens FROM the CEA back to Push Chain, not to round-trip them.
|
|
712
|
+
const prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
|
|
713
|
+
const burnAmount = BigInt(0);
|
|
733
714
|
(0, context_1.printLog)(ctx, `executeCeaToPush — prc20Token: ${prc20Token}, burnAmount: ${burnAmount.toString()}`);
|
|
734
715
|
// 9. Build outbound request (same structure as Route 2)
|
|
735
716
|
// target = CEA address (for self-execution), value = 0 in payload
|
|
736
717
|
const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, // target: CEA address (to=CEA for self-execution)
|
|
737
718
|
prc20Token, // token: native PRC-20 for source chain (for namespace lookup)
|
|
738
|
-
burnAmount, // amount:
|
|
739
|
-
(
|
|
719
|
+
burnAmount, // amount: 0 (payload-only, CEA uses its own balance)
|
|
720
|
+
(_f = params.gasLimit) !== null && _f !== void 0 ? _f : BigInt(0), ceaPayload, // payload: ABI-encoded CEA multicall
|
|
740
721
|
ueaAddress // revertRecipient: UEA
|
|
741
722
|
);
|
|
742
723
|
(0, context_1.printLog)(ctx, `executeCeaToPush — outbound request: ${JSON.stringify({
|
|
@@ -773,9 +754,43 @@ function executeCeaToPush(ctx, params, executeFn) {
|
|
|
773
754
|
const currentBalance = ueaBalance;
|
|
774
755
|
// Cosmos-EVM tx overhead costs ~1 PC per operation; 3 PC covers approve(s) + buffer.
|
|
775
756
|
const OUTBOUND_GAS_RESERVE_R3 = BigInt(3e18);
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
757
|
+
const ROUTE3_MINIMUM_DEPOSIT_USD = utils_1.Utils.helpers.parseUnits('10', 8); // $10
|
|
758
|
+
// For fresh wallets: predict post-fee-locking UPC balance using the same
|
|
759
|
+
// Uniswap V3 quoter the chain uses (pETH → WPC swap), matching Route 2 logic.
|
|
760
|
+
let effectiveBalance = currentBalance;
|
|
761
|
+
if (!isUEADeployed && effectiveBalance <= OUTBOUND_GAS_RESERVE_R3) {
|
|
762
|
+
const signerChain = ctx.universalSigner.account.chain;
|
|
763
|
+
const ethPrice = yield new price_fetch_1.PriceFetch(ctx.rpcUrls).getPrice(signerChain);
|
|
764
|
+
const nativeAmountETH = ethPrice > BigInt(0)
|
|
765
|
+
? (ROUTE3_MINIMUM_DEPOSIT_USD * BigInt(1e18) + (ethPrice - BigInt(1))) / ethPrice + BigInt(1)
|
|
766
|
+
: BigInt(0);
|
|
767
|
+
if (nativeAmountETH > BigInt(0)) {
|
|
768
|
+
const originPrc20 = (0, helpers_1.getNativePRC20ForChain)(signerChain, ctx.pushNetwork);
|
|
769
|
+
const predictedUPC = yield (0, gas_calculator_1.estimateDepositFromLockedNative)(ctx, nativeAmountETH, originPrc20);
|
|
770
|
+
if (predictedUPC > BigInt(0)) {
|
|
771
|
+
effectiveBalance = (predictedUPC * BigInt(90)) / BigInt(100);
|
|
772
|
+
(0, context_1.printLog)(ctx, `executeCeaToPush — fresh wallet: Uniswap quote predicts ${predictedUPC.toString()} UPC ` +
|
|
773
|
+
`from ${nativeAmountETH.toString()} wei pETH, using ${effectiveBalance.toString()} (90%)`);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
// Balance-aware adjustment matching Route 2 logic:
|
|
778
|
+
// - If balance exceeds target + reserve: use target (200 UPC)
|
|
779
|
+
// - If balance exceeds reserve: use balance - reserve (maximize within budget)
|
|
780
|
+
// - Otherwise: keep original nativeValueForGas (fallback)
|
|
781
|
+
const EVM_NATIVE_VALUE_TARGET_R3 = BigInt(200e18); // 200 UPC
|
|
782
|
+
let adjustedValue;
|
|
783
|
+
if (effectiveBalance > EVM_NATIVE_VALUE_TARGET_R3 + OUTBOUND_GAS_RESERVE_R3) {
|
|
784
|
+
adjustedValue = EVM_NATIVE_VALUE_TARGET_R3;
|
|
785
|
+
}
|
|
786
|
+
else if (effectiveBalance > OUTBOUND_GAS_RESERVE_R3) {
|
|
787
|
+
adjustedValue = effectiveBalance - OUTBOUND_GAS_RESERVE_R3;
|
|
788
|
+
}
|
|
789
|
+
else {
|
|
790
|
+
adjustedValue = nativeValueForGas;
|
|
791
|
+
}
|
|
792
|
+
if (adjustedValue !== nativeValueForGas) {
|
|
793
|
+
(0, context_1.printLog)(ctx, `executeCeaToPush — adjusting nativeValueForGas from ${nativeValueForGas.toString()} to ${adjustedValue.toString()} (effective balance: ${effectiveBalance.toString()})`);
|
|
779
794
|
nativeValueForGas = adjustedValue;
|
|
780
795
|
}
|
|
781
796
|
// 12. Build Push Chain multicalls (approvals + sendUniversalTxOutbound)
|
|
@@ -792,17 +807,11 @@ function executeCeaToPush(ctx, params, executeFn) {
|
|
|
792
807
|
// Sum native values from multicall entries for proper fee calculation
|
|
793
808
|
const multicallNativeValue = pushChainMulticalls.reduce((sum, mc) => { var _a; return sum + ((_a = mc.value) !== null && _a !== void 0 ? _a : BigInt(0)); }, BigInt(0));
|
|
794
809
|
// 13. Execute through the normal execute() flow
|
|
795
|
-
const executeParams = {
|
|
796
|
-
to: ueaAddress,
|
|
797
|
-
value: multicallNativeValue, // ensures correct requiredFunds calculation
|
|
798
|
-
data: pushChainMulticalls,
|
|
799
|
-
_ueaStatus: {
|
|
810
|
+
const executeParams = Object.assign({ to: ueaAddress, value: multicallNativeValue, data: pushChainMulticalls, _ueaStatus: {
|
|
800
811
|
isDeployed: isUEADeployed,
|
|
801
812
|
nonce: ueaNonce,
|
|
802
813
|
balance: ueaBalance,
|
|
803
|
-
},
|
|
804
|
-
_skipFeeLocking: true, // outbound executes on Push Chain, no external fee locking
|
|
805
|
-
};
|
|
814
|
+
}, _skipFeeLocking: isUEADeployed }, (!isUEADeployed ? { _minimumDepositUsd: ROUTE3_MINIMUM_DEPOSIT_USD } : {}));
|
|
806
815
|
const response = yield executeFn(executeParams);
|
|
807
816
|
// Add Route 3 context to response
|
|
808
817
|
response.chain = sourceChain;
|
|
@@ -840,7 +849,6 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
|
|
|
840
849
|
// Determine drain amount and token
|
|
841
850
|
let drainAmount = BigInt(0);
|
|
842
851
|
let tokenMintHex;
|
|
843
|
-
let prc20Token;
|
|
844
852
|
if (((_a = params.funds) === null || _a === void 0 ? void 0 : _a.amount) && params.funds.amount > BigInt(0)) {
|
|
845
853
|
// SPL token drain
|
|
846
854
|
drainAmount = params.funds.amount;
|
|
@@ -849,22 +857,15 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
|
|
|
849
857
|
// Convert SPL mint address to 32-byte hex
|
|
850
858
|
const mintPk = new web3_js_1.PublicKey(token.address);
|
|
851
859
|
tokenMintHex = ('0x' + Buffer.from(mintPk.toBytes()).toString('hex'));
|
|
852
|
-
prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
|
|
853
|
-
}
|
|
854
|
-
else {
|
|
855
|
-
prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
|
|
856
860
|
}
|
|
857
861
|
}
|
|
858
862
|
else if (params.value && params.value > BigInt(0)) {
|
|
859
863
|
// Native SOL drain
|
|
860
864
|
drainAmount = params.value;
|
|
861
|
-
prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
|
|
862
|
-
}
|
|
863
|
-
else {
|
|
864
|
-
// Payload-only Route 3 SVM: no funds to drain, just execute data on Push Chain
|
|
865
|
-
drainAmount = BigInt(0);
|
|
866
|
-
prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
|
|
867
865
|
}
|
|
866
|
+
// Route 3 SVM: ALWAYS use native PRC-20 for chain namespace lookup + gas fees.
|
|
867
|
+
// CEA uses its own pre-existing balance — no PRC-20 burn needed on Push Chain.
|
|
868
|
+
const prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
|
|
868
869
|
// Build the SVM CPI payload (send_universal_tx_to_uea wrapped in execute)
|
|
869
870
|
// If params.data is provided, pass it as extraPayload for Push Chain execution
|
|
870
871
|
let extraPayload;
|
|
@@ -936,17 +937,11 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
|
|
|
936
937
|
// Sum native values from multicall entries for proper fee calculation
|
|
937
938
|
const multicallNativeValue = pushChainMulticalls.reduce((sum, mc) => { var _a; return sum + ((_a = mc.value) !== null && _a !== void 0 ? _a : BigInt(0)); }, BigInt(0));
|
|
938
939
|
// Execute through the normal execute() flow
|
|
939
|
-
const executeParams = {
|
|
940
|
-
to: ueaAddress,
|
|
941
|
-
value: multicallNativeValue, // ensures correct requiredFunds calculation
|
|
942
|
-
data: pushChainMulticalls,
|
|
943
|
-
_ueaStatus: {
|
|
940
|
+
const executeParams = Object.assign({ to: ueaAddress, value: multicallNativeValue, data: pushChainMulticalls, _ueaStatus: {
|
|
944
941
|
isDeployed: isUEADeployed,
|
|
945
942
|
nonce: ueaNonce,
|
|
946
943
|
balance: ueaBalance,
|
|
947
|
-
},
|
|
948
|
-
_skipFeeLocking: true, // outbound executes on Push Chain, no external fee locking
|
|
949
|
-
};
|
|
944
|
+
}, _skipFeeLocking: isUEADeployed }, (!isUEADeployed ? { _minimumDepositUsd: utils_1.Utils.helpers.parseUnits('10', 8) } : {}));
|
|
950
945
|
const response = yield executeFn(executeParams);
|
|
951
946
|
// Add Route 3 SVM context to response
|
|
952
947
|
response.chain = sourceChain;
|
|
@@ -1094,28 +1089,19 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
|
|
|
1094
1089
|
const gatewayProgramHex = ('0x' + Buffer.from(programPk.toBytes()).toString('hex'));
|
|
1095
1090
|
let drainAmount = BigInt(0);
|
|
1096
1091
|
let tokenMintHex;
|
|
1097
|
-
let prc20Token;
|
|
1098
1092
|
if (((_j = params.funds) === null || _j === void 0 ? void 0 : _j.amount) && params.funds.amount > BigInt(0)) {
|
|
1099
1093
|
drainAmount = params.funds.amount;
|
|
1100
1094
|
const token = params.funds.token;
|
|
1101
1095
|
if (token && token.address) {
|
|
1102
1096
|
const mintPk = new web3_js_1.PublicKey(token.address);
|
|
1103
1097
|
tokenMintHex = ('0x' + Buffer.from(mintPk.toBytes()).toString('hex'));
|
|
1104
|
-
prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
|
|
1105
|
-
}
|
|
1106
|
-
else {
|
|
1107
|
-
prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
|
|
1108
1098
|
}
|
|
1109
1099
|
}
|
|
1110
1100
|
else if (params.value && params.value > BigInt(0)) {
|
|
1111
1101
|
drainAmount = params.value;
|
|
1112
|
-
prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
|
|
1113
|
-
}
|
|
1114
|
-
else {
|
|
1115
|
-
// Payload-only Route 3 SVM: no funds to drain, just execute data on Push Chain
|
|
1116
|
-
drainAmount = BigInt(0);
|
|
1117
|
-
prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
|
|
1118
1102
|
}
|
|
1103
|
+
// Route 3 SVM: ALWAYS use native PRC-20, CEA uses its own balance.
|
|
1104
|
+
const prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
|
|
1119
1105
|
// Derive CEA PDA as revert recipient
|
|
1120
1106
|
const ueaBytes2 = Buffer.from(ueaAddress.slice(2), 'hex');
|
|
1121
1107
|
const [ceaPda2] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('push_identity'), ueaBytes2], programPk);
|
|
@@ -1203,9 +1189,9 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
|
|
|
1203
1189
|
ceaMulticalls.push(sendCall);
|
|
1204
1190
|
const ceaPayload = (0, payload_builders_1.buildCeaMulticallPayload)(ceaMulticalls);
|
|
1205
1191
|
const prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
|
|
1206
|
-
//
|
|
1207
|
-
//
|
|
1208
|
-
const burnAmount =
|
|
1192
|
+
// Route 3: CEA uses its own pre-existing balance — no PRC-20 burn needed.
|
|
1193
|
+
// burnAmount = 0 makes this a payload-only outbound relay.
|
|
1194
|
+
const burnAmount = BigInt(0);
|
|
1209
1195
|
const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, prc20Token, burnAmount, (_o = params.gasLimit) !== null && _o !== void 0 ? _o : BigInt(0), ceaPayload, ueaAddress);
|
|
1210
1196
|
return { payload: ceaPayload, gatewayRequest: outboundReq };
|
|
1211
1197
|
}
|
|
@@ -106,6 +106,14 @@ export type ExecuteParams = {
|
|
|
106
106
|
* @internal
|
|
107
107
|
*/
|
|
108
108
|
_skipFeeLocking?: boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Internal: Minimum fee-locking deposit in USD (8 decimals).
|
|
111
|
+
* Overrides the default $1 floor when the caller needs a larger deposit
|
|
112
|
+
* (e.g., Route 2 needs enough UPC for the outbound swap).
|
|
113
|
+
* Still capped at $10 by lockFee.
|
|
114
|
+
* @internal
|
|
115
|
+
*/
|
|
116
|
+
_minimumDepositUsd?: bigint;
|
|
109
117
|
};
|
|
110
118
|
/**
|
|
111
119
|
* New Universal Transaction Receipt interface with prioritized field ordering
|