@pushchain/core 2.1.0 → 2.1.2
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 +6 -0
- package/package.json +4 -3
- package/src/lib/constants/abi/erc20.evm.d.ts +33 -0
- package/src/lib/constants/abi/erc20.evm.js +19 -0
- package/src/lib/constants/abi/erc20.evm.js.map +1 -1
- package/src/lib/constants/chain.d.ts +16 -0
- package/src/lib/constants/chain.js +50 -1
- package/src/lib/constants/chain.js.map +1 -1
- package/src/lib/constants/tokens.js +12 -0
- package/src/lib/constants/tokens.js.map +1 -1
- package/src/lib/orchestrator/orchestrator.d.ts +12 -1
- package/src/lib/orchestrator/orchestrator.js +358 -37
- package/src/lib/orchestrator/orchestrator.js.map +1 -1
- package/src/lib/orchestrator/orchestrator.types.d.ts +8 -0
- package/src/lib/push-chain/push-chain.js +14 -79
- package/src/lib/push-chain/push-chain.js.map +1 -1
- package/src/lib/push-client/push-client.d.ts +5 -0
- package/src/lib/vm-client/evm-client.d.ts +10 -0
- package/src/lib/vm-client/evm-client.js +20 -0
- package/src/lib/vm-client/evm-client.js.map +1 -1
|
@@ -63,7 +63,7 @@ class Orchestrator {
|
|
|
63
63
|
*/
|
|
64
64
|
execute(execute) {
|
|
65
65
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
66
|
-
var _a, _b, _c;
|
|
66
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
67
67
|
try {
|
|
68
68
|
// FUNDS_TX short-circuit: Bridge tokens from origin chain to Push Chain
|
|
69
69
|
// - EVM (Sepolia): UniversalGatewayV0
|
|
@@ -138,16 +138,41 @@ class Orchestrator {
|
|
|
138
138
|
const bridgeToken = execute.funds.token.mechanism === 'approve'
|
|
139
139
|
? tokenAddr
|
|
140
140
|
: '0x0000000000000000000000000000000000000000';
|
|
141
|
+
const { nonce } = yield this.getUeaStatusAndNonce();
|
|
142
|
+
const { payload: universalPayload } = yield this.buildGatewayPayloadAndGas(execute, nonce, 'sendFunds', bridgeAmount);
|
|
143
|
+
// Get UEA info
|
|
144
|
+
const ueaAddress = this.computeUEAOffchain();
|
|
145
|
+
const ueaVersion = yield this.fetchUEAVersion();
|
|
146
|
+
const eip712Signature = yield this.signUniversalPayload(universalPayload, ueaAddress, ueaVersion);
|
|
147
|
+
const eip712SignatureHex = (0, viem_1.bytesToHex)(eip712Signature);
|
|
141
148
|
let txHash;
|
|
142
149
|
try {
|
|
150
|
+
// Compute minimal native amount to deposit for gas on Push Chain
|
|
151
|
+
const ueaAddressForGas = this.computeUEAOffchain();
|
|
152
|
+
const ueaBalanceForGas = yield this.pushClient.getBalance(ueaAddressForGas);
|
|
153
|
+
const nativeAmount = yield this.calculateNativeAmountForDeposit(chain, BigInt(0), ueaBalanceForGas);
|
|
143
154
|
txHash = yield evmClient.writeContract({
|
|
144
155
|
abi: abi_1.UNIVERSAL_GATEWAY_V0,
|
|
145
156
|
address: gatewayAddress,
|
|
146
|
-
functionName: '
|
|
147
|
-
args: [
|
|
157
|
+
functionName: 'sendTxWithFunds',
|
|
158
|
+
args: [
|
|
159
|
+
tokenAddr,
|
|
160
|
+
bridgeAmount,
|
|
161
|
+
universalPayload,
|
|
162
|
+
revertCFG,
|
|
163
|
+
eip712SignatureHex,
|
|
164
|
+
],
|
|
148
165
|
signer: this.universalSigner,
|
|
149
|
-
value:
|
|
166
|
+
value: nativeAmount,
|
|
150
167
|
});
|
|
168
|
+
// txHash = await evmClient.writeContract({
|
|
169
|
+
// abi: UNIVERSAL_GATEWAY_V0 as unknown as Abi,
|
|
170
|
+
// address: gatewayAddress,
|
|
171
|
+
// functionName: 'sendFunds',
|
|
172
|
+
// args: [recipient, bridgeToken, bridgeAmount, revertCFG],
|
|
173
|
+
// signer: this.universalSigner,
|
|
174
|
+
// value: isNative ? bridgeAmount : BigInt(0),
|
|
175
|
+
// });
|
|
151
176
|
}
|
|
152
177
|
catch (err) {
|
|
153
178
|
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_04_04);
|
|
@@ -169,6 +194,10 @@ class Orchestrator {
|
|
|
169
194
|
const [vaultPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('vault')], programId);
|
|
170
195
|
const [whitelistPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('whitelist')], programId);
|
|
171
196
|
const userPk = new web3_js_1.PublicKey(this.universalSigner.account.address);
|
|
197
|
+
// pay-with-token gas abstraction is not supported on Solana
|
|
198
|
+
if (((_a = execute.funds) === null || _a === void 0 ? void 0 : _a.payWith) !== undefined) {
|
|
199
|
+
throw new Error('Pay-with token is not supported on Solana');
|
|
200
|
+
}
|
|
172
201
|
let txSignature;
|
|
173
202
|
// SVM-specific RevertSettings: bytes must be a Buffer
|
|
174
203
|
const revertSvm = {
|
|
@@ -329,13 +358,13 @@ class Orchestrator {
|
|
|
329
358
|
}
|
|
330
359
|
const mechanism = execute.funds.token.mechanism;
|
|
331
360
|
const { deployed, nonce } = yield this.getUeaStatusAndNonce();
|
|
332
|
-
const { payload: universalPayload } = yield this.buildGatewayPayloadAndGas(execute, nonce);
|
|
361
|
+
const { payload: universalPayload } = yield this.buildGatewayPayloadAndGas(execute, nonce, 'sendTxWithFunds');
|
|
333
362
|
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_02_01);
|
|
334
363
|
// Compute required gas funding on Push Chain and current UEA balance
|
|
335
364
|
const gasEstimate = execute.gasLimit || BigInt(1e7);
|
|
336
365
|
const gasPrice = yield this.pushClient.getGasPrice();
|
|
337
366
|
const requiredGasFee = gasEstimate * gasPrice;
|
|
338
|
-
const payloadValue = (
|
|
367
|
+
const payloadValue = (_b = execute.value) !== null && _b !== void 0 ? _b : BigInt(0);
|
|
339
368
|
const requiredFunds = requiredGasFee + payloadValue;
|
|
340
369
|
const ueaAddress = this.computeUEAOffchain();
|
|
341
370
|
const [ueaBalance] = yield Promise.all([
|
|
@@ -368,8 +397,8 @@ class Orchestrator {
|
|
|
368
397
|
functionName: 'config',
|
|
369
398
|
args: [configPda.toBase58()],
|
|
370
399
|
});
|
|
371
|
-
const minField = (
|
|
372
|
-
const maxField = (
|
|
400
|
+
const minField = (_c = cfg.minCapUniversalTxUsd) !== null && _c !== void 0 ? _c : cfg.min_cap_universal_tx_usd;
|
|
401
|
+
const maxField = (_d = cfg.maxCapUniversalTxUsd) !== null && _d !== void 0 ? _d : cfg.max_cap_universal_tx_usd;
|
|
373
402
|
const minCapUsd = BigInt(minField.toString());
|
|
374
403
|
const maxCapUsd = BigInt(maxField.toString());
|
|
375
404
|
if (depositUsd < minCapUsd)
|
|
@@ -381,7 +410,7 @@ class Orchestrator {
|
|
|
381
410
|
if (depositUsd > maxCapUsd)
|
|
382
411
|
depositUsd = maxCapUsd;
|
|
383
412
|
}
|
|
384
|
-
catch (
|
|
413
|
+
catch (_m) {
|
|
385
414
|
// best-effort; fallback to previous bounds if read fails
|
|
386
415
|
}
|
|
387
416
|
}
|
|
@@ -430,20 +459,73 @@ class Orchestrator {
|
|
|
430
459
|
: (0, viem_1.bytesToHex)(eip712Signature);
|
|
431
460
|
const evmClientEvm = evmClient;
|
|
432
461
|
const gatewayAddressEvm = gatewayAddress;
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
462
|
+
// New behavior: if user provided a gasTokenAddress, pay gas in that token via Uniswap quote
|
|
463
|
+
// Determine pay-with token address, min-out and slippage
|
|
464
|
+
const payWith = execute.funds.payWith;
|
|
465
|
+
const gasTokenAddress = (_e = payWith === null || payWith === void 0 ? void 0 : payWith.token) === null || _e === void 0 ? void 0 : _e.address;
|
|
466
|
+
if (gasTokenAddress) {
|
|
467
|
+
if (chain !== enums_1.CHAIN.ETHEREUM_SEPOLIA) {
|
|
468
|
+
throw new Error(`Only ${push_chain_1.PushChain.utils.chains.getChainName(enums_1.CHAIN.ETHEREUM_SEPOLIA)} is supported for paying gas fees with ERC-20 tokens`);
|
|
469
|
+
}
|
|
470
|
+
let amountOutMinETH = (payWith === null || payWith === void 0 ? void 0 : payWith.minAmountOut) !== undefined
|
|
471
|
+
? BigInt(payWith.minAmountOut)
|
|
472
|
+
: nativeAmount;
|
|
473
|
+
const slippageBps = (_f = payWith === null || payWith === void 0 ? void 0 : payWith.slippageBps) !== null && _f !== void 0 ? _f : 100;
|
|
474
|
+
amountOutMinETH = BigInt(push_chain_1.PushChain.utils.conversion.slippageToMinAmount(amountOutMinETH.toString(), { slippageBps }));
|
|
475
|
+
const { gasAmount } = yield this.calculateGasAmountFromAmountOutMinETH(gasTokenAddress, amountOutMinETH);
|
|
476
|
+
const deadline = BigInt(0);
|
|
477
|
+
// Ensure caller has enough balance of the gas token to cover fees
|
|
478
|
+
const ownerAddress = this.universalSigner.account
|
|
479
|
+
.address;
|
|
480
|
+
const gasTokenBalance = yield evmClientEvm.getErc20Balance({
|
|
481
|
+
tokenAddress: gasTokenAddress,
|
|
482
|
+
ownerAddress,
|
|
483
|
+
});
|
|
484
|
+
if (gasTokenBalance < gasAmount) {
|
|
485
|
+
const sym = (_h = (_g = payWith === null || payWith === void 0 ? void 0 : payWith.token) === null || _g === void 0 ? void 0 : _g.symbol) !== null && _h !== void 0 ? _h : 'gas token';
|
|
486
|
+
const decimals = (_k = (_j = payWith === null || payWith === void 0 ? void 0 : payWith.token) === null || _j === void 0 ? void 0 : _j.decimals) !== null && _k !== void 0 ? _k : 18;
|
|
487
|
+
const needFmt = push_chain_1.PushChain.utils.helpers.formatUnits(gasAmount, decimals);
|
|
488
|
+
const haveFmt = push_chain_1.PushChain.utils.helpers.formatUnits(gasTokenBalance, decimals);
|
|
489
|
+
throw new Error(`Insufficient ${sym} balance to cover gas fees: need ${needFmt}, have ${haveFmt}`);
|
|
490
|
+
}
|
|
491
|
+
// Approve gas token to gateway
|
|
492
|
+
yield this.ensureErc20Allowance(evmClientEvm, gasTokenAddress, gatewayAddressEvm, gasAmount);
|
|
493
|
+
// Approve bridge token already done above; now call new gateway signature (nonpayable)
|
|
494
|
+
txHash = yield evmClientEvm.writeContract({
|
|
495
|
+
abi: abi_1.UNIVERSAL_GATEWAY_V0,
|
|
496
|
+
address: gatewayAddressEvm,
|
|
497
|
+
functionName: 'sendTxWithFunds',
|
|
498
|
+
args: [
|
|
499
|
+
tokenAddr,
|
|
500
|
+
bridgeAmount,
|
|
501
|
+
gasTokenAddress,
|
|
502
|
+
gasAmount,
|
|
503
|
+
amountOutMinETH,
|
|
504
|
+
deadline,
|
|
505
|
+
universalPayload,
|
|
506
|
+
revertCFG,
|
|
507
|
+
eip712SignatureHex,
|
|
508
|
+
],
|
|
509
|
+
signer: this.universalSigner,
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
// Existing native-ETH value path
|
|
514
|
+
txHash = yield evmClientEvm.writeContract({
|
|
515
|
+
abi: abi_1.UNIVERSAL_GATEWAY_V0,
|
|
516
|
+
address: gatewayAddressEvm,
|
|
517
|
+
functionName: 'sendTxWithFunds',
|
|
518
|
+
args: [
|
|
519
|
+
tokenAddr,
|
|
520
|
+
bridgeAmount,
|
|
521
|
+
universalPayload,
|
|
522
|
+
revertCFG,
|
|
523
|
+
eip712SignatureHex,
|
|
524
|
+
],
|
|
525
|
+
signer: this.universalSigner,
|
|
526
|
+
value: nativeAmount,
|
|
527
|
+
});
|
|
528
|
+
}
|
|
447
529
|
}
|
|
448
530
|
else {
|
|
449
531
|
// SVM funds+payload path
|
|
@@ -457,6 +539,10 @@ class Orchestrator {
|
|
|
457
539
|
// whitelistPda already computed above
|
|
458
540
|
const userPk = new web3_js_1.PublicKey(this.universalSigner.account.address);
|
|
459
541
|
const priceUpdatePk = new web3_js_1.PublicKey('7UVimffxr9ow1uXYxsr4LHAcV58mLzhmwaeKvJ1pjLiE');
|
|
542
|
+
// pay-with-token gas abstraction is not supported on Solana
|
|
543
|
+
if (((_l = execute.funds) === null || _l === void 0 ? void 0 : _l.payWith) !== undefined) {
|
|
544
|
+
throw new Error('Pay-with token is not supported on Solana');
|
|
545
|
+
}
|
|
460
546
|
const isNative = mechanism === 'native' || execute.funds.token.symbol === 'SOL';
|
|
461
547
|
const revertSvm2 = {
|
|
462
548
|
fundRecipient: userPk,
|
|
@@ -1193,6 +1279,118 @@ class Orchestrator {
|
|
|
1193
1279
|
}
|
|
1194
1280
|
});
|
|
1195
1281
|
}
|
|
1282
|
+
/**
|
|
1283
|
+
* Quotes exact-output on Uniswap V3 for EVM origin chains using QuoterV2.
|
|
1284
|
+
* Returns the minimum required input (amountIn) to receive the target amountOut.
|
|
1285
|
+
*/
|
|
1286
|
+
_quoteExactOutput(amountOut_1, _a) {
|
|
1287
|
+
return tslib_1.__awaiter(this, arguments, void 0, function* (amountOut, { from, to, }) {
|
|
1288
|
+
var _b, _c, _d;
|
|
1289
|
+
const originChain = this.universalSigner.account.chain;
|
|
1290
|
+
if (originChain !== enums_1.CHAIN.ETHEREUM_MAINNET &&
|
|
1291
|
+
originChain !== enums_1.CHAIN.ETHEREUM_SEPOLIA) {
|
|
1292
|
+
throw new Error('Exact-output quoting is only supported on Ethereum Mainnet and Sepolia for now');
|
|
1293
|
+
}
|
|
1294
|
+
if (!from) {
|
|
1295
|
+
throw new Error('from token is required');
|
|
1296
|
+
}
|
|
1297
|
+
if (!to) {
|
|
1298
|
+
throw new Error('to token is required');
|
|
1299
|
+
}
|
|
1300
|
+
const rpcUrls = this.getRpcUrls()[originChain] || chain_1.CHAIN_INFO[originChain].defaultRPC;
|
|
1301
|
+
const evm = new evm_client_1.EvmClient({ rpcUrls });
|
|
1302
|
+
const factoryFromConfig = (_b = chain_1.CHAIN_INFO[originChain].dex) === null || _b === void 0 ? void 0 : _b.uniV3Factory;
|
|
1303
|
+
const quoterFromConfig = (_c = chain_1.CHAIN_INFO[originChain].dex) === null || _c === void 0 ? void 0 : _c.uniV3QuoterV2;
|
|
1304
|
+
if (!factoryFromConfig || !quoterFromConfig) {
|
|
1305
|
+
throw new Error('Uniswap V3 addresses not configured for this chain');
|
|
1306
|
+
}
|
|
1307
|
+
const UNISWAP_V3_FACTORY = factoryFromConfig;
|
|
1308
|
+
const UNISWAP_V3_QUOTER_V2 = quoterFromConfig;
|
|
1309
|
+
const factoryAbi = (0, viem_1.parseAbi)([
|
|
1310
|
+
'function getPool(address tokenA, address tokenB, uint24 fee) view returns (address)',
|
|
1311
|
+
]);
|
|
1312
|
+
const quoterAbi = (0, viem_1.parseAbi)([
|
|
1313
|
+
'function quoteExactOutputSingle((address tokenIn, address tokenOut, uint256 amount, uint24 fee, uint160 sqrtPriceLimitX96) params) returns (uint256 amountIn, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate)',
|
|
1314
|
+
]);
|
|
1315
|
+
const poolAbi = (0, viem_1.parseAbi)([
|
|
1316
|
+
'function liquidity() view returns (uint128)',
|
|
1317
|
+
]);
|
|
1318
|
+
const feeTiers = [100, 500, 3000, 10000];
|
|
1319
|
+
let bestAmountIn = null;
|
|
1320
|
+
let bestFee = null;
|
|
1321
|
+
for (const fee of feeTiers) {
|
|
1322
|
+
// Find pool address for this fee tier
|
|
1323
|
+
const poolAddress = yield evm.readContract({
|
|
1324
|
+
abi: factoryAbi,
|
|
1325
|
+
address: UNISWAP_V3_FACTORY,
|
|
1326
|
+
functionName: 'getPool',
|
|
1327
|
+
args: [from.address, to.address, fee],
|
|
1328
|
+
});
|
|
1329
|
+
const isZero = !poolAddress ||
|
|
1330
|
+
poolAddress.toLowerCase() ===
|
|
1331
|
+
'0x0000000000000000000000000000000000000000';
|
|
1332
|
+
if (isZero)
|
|
1333
|
+
continue;
|
|
1334
|
+
// Skip uninitialized/empty pools to avoid Quoter reverts
|
|
1335
|
+
try {
|
|
1336
|
+
const liquidity = yield evm.readContract({
|
|
1337
|
+
abi: poolAbi,
|
|
1338
|
+
address: poolAddress,
|
|
1339
|
+
functionName: 'liquidity',
|
|
1340
|
+
args: [],
|
|
1341
|
+
});
|
|
1342
|
+
if (!liquidity || liquidity === BigInt(0))
|
|
1343
|
+
continue;
|
|
1344
|
+
}
|
|
1345
|
+
catch (_e) {
|
|
1346
|
+
continue;
|
|
1347
|
+
}
|
|
1348
|
+
// Quote exact output single for this fee tier
|
|
1349
|
+
try {
|
|
1350
|
+
const result = yield evm.readContract({
|
|
1351
|
+
abi: quoterAbi,
|
|
1352
|
+
address: UNISWAP_V3_QUOTER_V2,
|
|
1353
|
+
functionName: 'quoteExactOutputSingle',
|
|
1354
|
+
args: [
|
|
1355
|
+
{
|
|
1356
|
+
tokenIn: from.address,
|
|
1357
|
+
tokenOut: to.address,
|
|
1358
|
+
amount: amountOut,
|
|
1359
|
+
fee,
|
|
1360
|
+
sqrtPriceLimitX96: BigInt(0),
|
|
1361
|
+
},
|
|
1362
|
+
],
|
|
1363
|
+
});
|
|
1364
|
+
const amountIn = (_d = result === null || result === void 0 ? void 0 : result[0]) !== null && _d !== void 0 ? _d : BigInt(0);
|
|
1365
|
+
if (amountIn === BigInt(0))
|
|
1366
|
+
continue;
|
|
1367
|
+
if (bestAmountIn === null || amountIn < bestAmountIn) {
|
|
1368
|
+
bestAmountIn = amountIn;
|
|
1369
|
+
bestFee = fee;
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
catch (_f) {
|
|
1373
|
+
// try next fee
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
if (bestAmountIn === null || bestFee === null) {
|
|
1377
|
+
throw new Error('No direct Uniswap V3 pool found for the given token pair on common fee tiers');
|
|
1378
|
+
}
|
|
1379
|
+
const amountInBig = BigInt(bestAmountIn);
|
|
1380
|
+
const amountInHuman = parseFloat(push_chain_1.PushChain.utils.helpers.formatUnits(amountInBig, {
|
|
1381
|
+
decimals: from.decimals,
|
|
1382
|
+
}));
|
|
1383
|
+
const amountOutHuman = parseFloat(push_chain_1.PushChain.utils.helpers.formatUnits(amountOut, { decimals: to.decimals }));
|
|
1384
|
+
const rate = amountInHuman > 0 ? amountOutHuman / amountInHuman : 0;
|
|
1385
|
+
return {
|
|
1386
|
+
amountIn: bestAmountIn.toString(),
|
|
1387
|
+
amountOut: amountOut.toString(),
|
|
1388
|
+
rate,
|
|
1389
|
+
route: [from.symbol, to.symbol],
|
|
1390
|
+
timestamp: Date.now(),
|
|
1391
|
+
};
|
|
1392
|
+
});
|
|
1393
|
+
}
|
|
1196
1394
|
ensureErc20Allowance(evmClient, tokenAddress, spender, requiredAmount) {
|
|
1197
1395
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1198
1396
|
const chain = this.universalSigner.account.chain;
|
|
@@ -1303,26 +1501,53 @@ class Orchestrator {
|
|
|
1303
1501
|
});
|
|
1304
1502
|
}
|
|
1305
1503
|
/**
|
|
1306
|
-
*
|
|
1504
|
+
* For sendFunds, we will call internally the sendTxWithFunds.
|
|
1307
1505
|
*/
|
|
1308
|
-
buildGatewayPayloadAndGas(execute, nonce) {
|
|
1506
|
+
buildGatewayPayloadAndGas(execute, nonce, type, fundsValue) {
|
|
1309
1507
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1310
1508
|
var _a, _b;
|
|
1311
1509
|
const gasEstimate = execute.gasLimit || BigInt(1e7);
|
|
1312
1510
|
const payloadValue = (_a = execute.value) !== null && _a !== void 0 ? _a : BigInt(0);
|
|
1313
1511
|
const gasAmount = (_b = execute.value) !== null && _b !== void 0 ? _b : BigInt(0);
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1512
|
+
if (type === 'sendTxWithFunds') {
|
|
1513
|
+
if (fundsValue)
|
|
1514
|
+
throw new Error('fundsValue property must be empty');
|
|
1515
|
+
const universalPayload = {
|
|
1516
|
+
to: execute.to,
|
|
1517
|
+
value: payloadValue,
|
|
1518
|
+
data: execute.data || '0x',
|
|
1519
|
+
gasLimit: gasEstimate,
|
|
1520
|
+
maxFeePerGas: execute.maxFeePerGas || BigInt(1e10),
|
|
1521
|
+
maxPriorityFeePerGas: execute.maxPriorityFeePerGas || BigInt(0),
|
|
1522
|
+
nonce,
|
|
1523
|
+
deadline: execute.deadline || BigInt(9999999999),
|
|
1524
|
+
vType: tx_1.VerificationType.signedVerification,
|
|
1525
|
+
};
|
|
1526
|
+
return { payload: universalPayload, gasAmount };
|
|
1527
|
+
}
|
|
1528
|
+
else {
|
|
1529
|
+
if (!fundsValue)
|
|
1530
|
+
throw new Error('fundsValue property must not be empty');
|
|
1531
|
+
// The data will be the abi-encoded transfer function from erc-20 function. The recipient will be `execute.to`, the value
|
|
1532
|
+
// will be the fundsValue property.
|
|
1533
|
+
const data = (0, viem_1.encodeFunctionData)({
|
|
1534
|
+
abi: abi_1.ERC20_EVM,
|
|
1535
|
+
functionName: 'transfer',
|
|
1536
|
+
args: [execute.to, fundsValue],
|
|
1537
|
+
});
|
|
1538
|
+
const universalPayload = {
|
|
1539
|
+
to: viem_1.zeroAddress, // We can't simply do `0x` because we will get an error when eip712 signing the transaction.
|
|
1540
|
+
value: payloadValue,
|
|
1541
|
+
data,
|
|
1542
|
+
gasLimit: gasEstimate,
|
|
1543
|
+
maxFeePerGas: execute.maxFeePerGas || BigInt(1e10),
|
|
1544
|
+
maxPriorityFeePerGas: execute.maxPriorityFeePerGas || BigInt(0),
|
|
1545
|
+
nonce,
|
|
1546
|
+
deadline: execute.deadline || BigInt(9999999999),
|
|
1547
|
+
vType: tx_1.VerificationType.signedVerification,
|
|
1548
|
+
};
|
|
1549
|
+
return { payload: universalPayload, gasAmount };
|
|
1550
|
+
}
|
|
1326
1551
|
});
|
|
1327
1552
|
}
|
|
1328
1553
|
/********************************** HELPER FUNCTIONS **************************************************/
|
|
@@ -1719,6 +1944,102 @@ class Orchestrator {
|
|
|
1719
1944
|
return version;
|
|
1720
1945
|
});
|
|
1721
1946
|
}
|
|
1947
|
+
// Build EVM gas payment parameters when paying gas with an ERC-20 token
|
|
1948
|
+
calculateGasAmountFromAmountOutMinETH(gasTokenAddress, amountOutMinETH) {
|
|
1949
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1950
|
+
var _a, _b, _c;
|
|
1951
|
+
const originChain = this.universalSigner.account.chain;
|
|
1952
|
+
if (originChain !== enums_1.CHAIN.ETHEREUM_SEPOLIA &&
|
|
1953
|
+
originChain !== enums_1.CHAIN.ARBITRUM_SEPOLIA &&
|
|
1954
|
+
originChain !== enums_1.CHAIN.BASE_SEPOLIA) {
|
|
1955
|
+
throw new Error('Gas payment in ERC-20 is supported only on Ethereum Sepolia, Arbitrum Sepolia, and Base Sepolia for now');
|
|
1956
|
+
}
|
|
1957
|
+
// Resolve WETH: prefer chain config, fallback to registry
|
|
1958
|
+
const WETH = (_a = chain_1.CHAIN_INFO[originChain].dex) === null || _a === void 0 ? void 0 : _a.weth;
|
|
1959
|
+
if (!WETH)
|
|
1960
|
+
throw new Error('WETH address not configured for this chain');
|
|
1961
|
+
let gasAmount;
|
|
1962
|
+
if (gasTokenAddress.toLowerCase() === WETH.toLowerCase()) {
|
|
1963
|
+
gasAmount = BigInt(amountOutMinETH);
|
|
1964
|
+
}
|
|
1965
|
+
else {
|
|
1966
|
+
// Resolve token objects from registries
|
|
1967
|
+
const fromList = (_b = tokens_1.PAYABLE_TOKENS[originChain]) !== null && _b !== void 0 ? _b : [];
|
|
1968
|
+
const fromToken = fromList.find((t) => (t.address || '').toLowerCase() === gasTokenAddress.toLowerCase());
|
|
1969
|
+
const toList = ((_c = tokens_1.MOVEABLE_TOKENS[originChain]) !== null && _c !== void 0 ? _c : []);
|
|
1970
|
+
const toToken = toList.find((t) => t.symbol === 'WETH' ||
|
|
1971
|
+
(t.address || '').toLowerCase() === (WETH || '').toLowerCase());
|
|
1972
|
+
if (!fromToken || !toToken) {
|
|
1973
|
+
throw new Error('Token not supported for quoting');
|
|
1974
|
+
}
|
|
1975
|
+
const targetOut = BigInt(amountOutMinETH);
|
|
1976
|
+
const exactOutQuote = yield this._quoteExactOutput(targetOut, {
|
|
1977
|
+
from: fromToken,
|
|
1978
|
+
to: toToken,
|
|
1979
|
+
});
|
|
1980
|
+
const requiredIn = BigInt(exactOutQuote.amountIn);
|
|
1981
|
+
gasAmount = (requiredIn * BigInt(101)) / BigInt(100); // 1% safety margin
|
|
1982
|
+
}
|
|
1983
|
+
return { gasAmount };
|
|
1984
|
+
});
|
|
1985
|
+
}
|
|
1986
|
+
calculateNativeAmountForDeposit(chain, requiredFunds, ueaBalance) {
|
|
1987
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1988
|
+
var _a, _b;
|
|
1989
|
+
// Determine USD to deposit via gateway (8 decimals) with caps: min=$1, max=$10
|
|
1990
|
+
const oneUsd = push_chain_1.PushChain.utils.helpers.parseUnits('1', 8);
|
|
1991
|
+
const tenUsd = push_chain_1.PushChain.utils.helpers.parseUnits('10', 8);
|
|
1992
|
+
const deficit = requiredFunds > ueaBalance ? requiredFunds - ueaBalance : BigInt(0);
|
|
1993
|
+
let depositUsd = deficit > BigInt(0) ? this.pushClient.pushToUSDC(deficit) : oneUsd;
|
|
1994
|
+
if (depositUsd < oneUsd)
|
|
1995
|
+
depositUsd = oneUsd;
|
|
1996
|
+
if (depositUsd > tenUsd)
|
|
1997
|
+
throw new Error('Deposit value exceeds max $10 worth of native token');
|
|
1998
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_02_02, depositUsd);
|
|
1999
|
+
// If SVM, clamp depositUsd to on-chain Config caps
|
|
2000
|
+
if (chain_1.CHAIN_INFO[chain].vm === enums_1.VM.SVM) {
|
|
2001
|
+
const svmClient = new svm_client_1.SvmClient({
|
|
2002
|
+
rpcUrls: this.rpcUrls[enums_1.CHAIN.SOLANA_DEVNET] ||
|
|
2003
|
+
chain_1.CHAIN_INFO[enums_1.CHAIN.SOLANA_DEVNET].defaultRPC,
|
|
2004
|
+
});
|
|
2005
|
+
const programId = new web3_js_1.PublicKey(abi_1.SVM_GATEWAY_IDL.address);
|
|
2006
|
+
const [configPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('config')], programId);
|
|
2007
|
+
try {
|
|
2008
|
+
const cfg = yield svmClient.readContract({
|
|
2009
|
+
abi: abi_1.SVM_GATEWAY_IDL,
|
|
2010
|
+
address: abi_1.SVM_GATEWAY_IDL.address,
|
|
2011
|
+
functionName: 'config',
|
|
2012
|
+
args: [configPda.toBase58()],
|
|
2013
|
+
});
|
|
2014
|
+
const minField = (_a = cfg.minCapUniversalTxUsd) !== null && _a !== void 0 ? _a : cfg.min_cap_universal_tx_usd;
|
|
2015
|
+
const maxField = (_b = cfg.maxCapUniversalTxUsd) !== null && _b !== void 0 ? _b : cfg.max_cap_universal_tx_usd;
|
|
2016
|
+
const minCapUsd = BigInt(minField.toString());
|
|
2017
|
+
const maxCapUsd = BigInt(maxField.toString());
|
|
2018
|
+
if (depositUsd < minCapUsd)
|
|
2019
|
+
depositUsd = minCapUsd;
|
|
2020
|
+
// Add 20% safety margin to avoid BelowMinCap due to price drift
|
|
2021
|
+
const withMargin = (minCapUsd * BigInt(12)) / BigInt(10);
|
|
2022
|
+
if (depositUsd < withMargin)
|
|
2023
|
+
depositUsd = withMargin;
|
|
2024
|
+
if (depositUsd > maxCapUsd)
|
|
2025
|
+
depositUsd = maxCapUsd;
|
|
2026
|
+
}
|
|
2027
|
+
catch (_c) {
|
|
2028
|
+
// best-effort; fallback to previous bounds if read fails
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
// Convert USD(8) -> native units using pricing path
|
|
2032
|
+
const nativeTokenUsdPrice = yield new price_fetch_1.PriceFetch(this.rpcUrls).getPrice(chain); // 8 decimals
|
|
2033
|
+
const nativeDecimals = chain_1.CHAIN_INFO[chain].vm === enums_1.VM.SVM ? 9 : 18;
|
|
2034
|
+
const oneNativeUnit = push_chain_1.PushChain.utils.helpers.parseUnits('1', nativeDecimals);
|
|
2035
|
+
// Ceil division to avoid rounding below min USD on-chain
|
|
2036
|
+
let nativeAmount = (depositUsd * oneNativeUnit + (nativeTokenUsdPrice - BigInt(1))) /
|
|
2037
|
+
nativeTokenUsdPrice;
|
|
2038
|
+
// Add 1 unit safety to avoid BelowMinCap from rounding differences
|
|
2039
|
+
nativeAmount = nativeAmount + BigInt(1);
|
|
2040
|
+
return nativeAmount;
|
|
2041
|
+
});
|
|
2042
|
+
}
|
|
1722
2043
|
}
|
|
1723
2044
|
exports.Orchestrator = Orchestrator;
|
|
1724
2045
|
//# sourceMappingURL=orchestrator.js.map
|